surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
Docked, the pane no longer pads a blank row of its own above its header: the engine keeps a docked pane's first row for its close mark, and that row is the blank one.
| @@ -1,6 +1,5 @@ | ||
| 1 | 1 | export * from './gutter-chrome.js' |
| 2 | 2 | export * from './max-summary-rows.js' |
| 3 | -export * from './pane-top-pad-rows.js' | |
| 4 | 3 | export * from './pre-session-body-cap.js' |
| 5 | 4 | export * from './scroll-margin-rows.js' |
| 6 | 5 | export * from './wheel-rows.js' |
| @@ -1,5 +1,0 @@ | ||
| 1 | -/** | |
| 2 | - * The blank row the docked pane keeps above its header, as the built-in | |
| 3 | - * sidebar's header does (its `paddingY`). | |
| 4 | - */ | |
| 5 | -export const PANE_TOP_PAD_ROWS = 1 |
| @@ -1,4 +1,3 @@ | ||
| 1 | -import Limits from '../../../limits' | |
| 2 | 1 | import type PaneState from '../../../pane-state' |
| 3 | 2 | import type { BodyLayout, DockPlan } from '../types' |
| 4 | 3 | import Segments from './segments' |
| @@ -39,10 +38,7 @@ | ||
| 39 | 38 | |
| 40 | 39 | const tops = new Map<string, number>() |
| 41 | 40 | |
| 42 | - const visibleRows = Math.max( | |
| 43 | - 0, | |
| 44 | - model.place.rows - Limits.PANE_TOP_PAD_ROWS - headRows - listRows, | |
| 45 | - ) | |
| 41 | + const visibleRows = Math.max(0, model.place.rows - headRows - listRows) | |
| 46 | 42 | |
| 47 | 43 | let extent = 0 |
| 48 | 44 | |
| @@ -54,7 +50,7 @@ | ||
| 54 | 50 | extent += Segments.segmentRowsOf(segment) |
| 55 | 51 | } |
| 56 | 52 | |
| 57 | - const listTop = Limits.PANE_TOP_PAD_ROWS + headRows + 1 | |
| 53 | + const listTop = headRows + 1 | |
| 58 | 54 | |
| 59 | 55 | return { |
| 60 | 56 | segments, |
| @@ -3,13 +3,15 @@ | ||
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * A drawing's kit inside the docked pane's padding: the built-in's blank |
| 6 | - * last column and its blank row above the header taken off. | |
| 6 | + * last column taken off. | |
| 7 | 7 | * |
| 8 | + * No row comes off: the blank row above the header is the engine's own, | |
| 9 | + * the one it keeps over a docked pane's body for its close mark. | |
| 10 | + * | |
| 8 | 11 | * @param kit the drawing's kit at the pane's full size |
| 9 | 12 | * @returns the kit its body draws with |
| 10 | 13 | */ |
| 11 | 14 | export const insetOf = (kit: Kit): Kit => ({ |
| 12 | 15 | ...kit, |
| 13 | 16 | columns: Math.max(1, kit.columns - Limits.PANE_RIGHT_PAD_COLUMNS), |
| 14 | - rows: Math.max(0, kit.rows - Limits.PANE_TOP_PAD_ROWS), | |
| 15 | 17 | }) |
| @@ -40,11 +40,7 @@ | ||
| 40 | 40 | seat.terminalColumns < Limits.OPEN_MIN_COLUMNS |
| 41 | 41 | |
| 42 | 42 | return isDocked ? ( |
| 43 | - <Box | |
| 44 | - flexDirection="column" | |
| 45 | - paddingTop={Limits.PANE_TOP_PAD_ROWS} | |
| 46 | - paddingRight={Limits.PANE_RIGHT_PAD_COLUMNS} | |
| 47 | - > | |
| 43 | + <Box flexDirection="column" paddingRight={Limits.PANE_RIGHT_PAD_COLUMNS}> | |
| 48 | 44 | {sidebarPane(insetOf(kit), model)} |
| 49 | 45 | </Box> |
| 50 | 46 | ) : isNarrow ? ( |
| @@ -60,6 +60,8 @@ | ||
| 60 | 60 | export * from './usage-at.js' |
| 61 | 61 | export * from './vs-main.js' |
| 62 | 62 | export * from './wheel-over-list.js' |
| 63 | +export * from './wheel-over-list-margin.js' | |
| 64 | +export * from './wheel-over-list-top.js' | |
| 63 | 65 | export * from './wheel-tick.js' |
| 64 | 66 | export * from './with-lockfile.js' |
| 65 | 67 | export * from './worktree' |
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | /** |
| 2 | - * The pane's body rows in the ordinary cases: docked in a 40-row terminal. | |
| 2 | + * The pane's body rows in the ordinary cases: docked in a 40-row terminal, | |
| 3 | + * under the row the engine keeps for its close mark. | |
| 3 | 4 | */ |
| 4 | -export const BODY_ROWS = 35 | |
| 5 | +export const BODY_ROWS = 34 |
| @@ -1,0 +1,5 @@ | ||
| 1 | +/** | |
| 2 | + * The rows the engine keeps over a docked pane's body: one, blank but for | |
| 3 | + * its close mark. | |
| 4 | + */ | |
| 5 | +export const ENGINE_HEAD_ROWS = 1 |
| @@ -9,6 +9,7 @@ | ||
| 9 | 9 | export * from './docked-pane.js' |
| 10 | 10 | export * from './docks-pane.js' |
| 11 | 11 | export * from './draws-pane.js' |
| 12 | +export * from './engine-head-rows.js' | |
| 12 | 13 | export * from './long-line-chars.js' |
| 13 | 14 | export * from './long-path-segments.js' |
| 14 | 15 | export * from './narrow-seat.js' |
| @@ -1,0 +1,12 @@ | ||
| 1 | +import type { UiScrollInput } from 'claude-code' | |
| 2 | + | |
| 3 | +import { WHEEL_TICK } from './wheel-tick.js' | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * The same wheel tick with the pointer over the blank row between the | |
| 7 | + * docked header and its list. | |
| 8 | + */ | |
| 9 | +export const WHEEL_OVER_LIST_MARGIN: UiScrollInput = { | |
| 10 | + ...WHEEL_TICK, | |
| 11 | + pointer: { column: 10, row: 1 }, | |
| 12 | +} |
| @@ -1,0 +1,12 @@ | ||
| 1 | +import type { UiScrollInput } from 'claude-code' | |
| 2 | + | |
| 3 | +import { WHEEL_TICK } from './wheel-tick.js' | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * The same wheel tick with the pointer over the docked list's first row | |
| 7 | + * (header, the list's margin, then the rows). | |
| 8 | + */ | |
| 9 | +export const WHEEL_OVER_LIST_TOP: UiScrollInput = { | |
| 10 | + ...WHEEL_TICK, | |
| 11 | + pointer: { column: 10, row: 2 }, | |
| 12 | +} |
| @@ -4,9 +4,9 @@ | ||
| 4 | 4 | |
| 5 | 5 | /** |
| 6 | 6 | * The same wheel tick with the pointer over the docked list's third row |
| 7 | - * (top pad, header, the list's margin, then the rows). | |
| 7 | + * (header, the list's margin, then the rows). | |
| 8 | 8 | */ |
| 9 | 9 | export const WHEEL_OVER_LIST: UiScrollInput = { |
| 10 | 10 | ...WHEEL_TICK, |
| 11 | - pointer: { column: 10, row: 5 }, | |
| 11 | + pointer: { column: 10, row: 4 }, | |
| 12 | 12 | } |
| @@ -365,7 +365,7 @@ | ||
| 365 | 365 | expect(Fixtures.jsonOf(reversed)).not.toContain('') |
| 366 | 366 | }) |
| 367 | 367 | |
| 368 | - test("docked: the built-in's blank top row, last column", async ($, on) => { | |
| 368 | + test("docked: no top pad, the built-in's last column", async ($, on) => { | |
| 369 | 369 | const tree = await Fixtures.dockedPane( |
| 370 | 370 | $, |
| 371 | 371 | on, |
| @@ -381,16 +381,55 @@ | ||
| 381 | 381 | |
| 382 | 382 | expect(tree).toMatchObject({ |
| 383 | 383 | type: 'Box', |
| 384 | - props: { | |
| 385 | - paddingTop: Limits.PANE_TOP_PAD_ROWS, | |
| 386 | - paddingRight: Limits.PANE_RIGHT_PAD_COLUMNS, | |
| 387 | - }, | |
| 384 | + props: { paddingRight: Limits.PANE_RIGHT_PAD_COLUMNS }, | |
| 388 | 385 | }) |
| 389 | 386 | |
| 387 | + expect(Fixtures.jsonOf(tree)).not.toContain('"paddingTop"') | |
| 390 | 388 | expect(Fixtures.stringsOf(tree)).toContain(rule) |
| 391 | 389 | expect(Fixtures.stringsOf(tree)).not.toContain(`${rule}─`) |
| 392 | 390 | }) |
| 393 | 391 | |
| 392 | + test('docked: the window takes every row under the list', () => { | |
| 393 | + const files = [Fixtures.rowOf('src/a.ts'), Fixtures.rowOf('b.ts')] | |
| 394 | + const headerRows = 1 | |
| 395 | + const listMarginRows = 1 | |
| 396 | + | |
| 397 | + const model = modelOf({ | |
| 398 | + data: dataOf(files), | |
| 399 | + place: { ...Fixtures.WHOLE_PLACE, rows: Fixtures.BODY_ROWS }, | |
| 400 | + }) | |
| 401 | + | |
| 402 | + const listTop = headerRows + listMarginRows | |
| 403 | + const listEnd = listTop + files.length | |
| 404 | + | |
| 405 | + expect(Views.bodyLayoutOf(model, Views.dockPlanOf(model))).toMatchObject({ | |
| 406 | + listTop, | |
| 407 | + listEnd, | |
| 408 | + visibleRows: Fixtures.BODY_ROWS - listEnd - listMarginRows, | |
| 409 | + }) | |
| 410 | + }) | |
| 411 | + | |
| 412 | + test('docked: the list takes the wheel from its first row', () => { | |
| 413 | + const model = modelOf({ | |
| 414 | + data: dataOf( | |
| 415 | + Array.from({ length: Limits.MAX_SUMMARY_ROWS + 1 }, (_, index) => | |
| 416 | + Fixtures.rowOf(`file${index}.ts`), | |
| 417 | + ), | |
| 418 | + ), | |
| 419 | + place: { ...Fixtures.WHOLE_PLACE, rows: Fixtures.BODY_ROWS }, | |
| 420 | + }) | |
| 421 | + | |
| 422 | + expect( | |
| 423 | + Views.isWheelOverList(model, Fixtures.WHEEL_OVER_LIST_MARGIN), | |
| 424 | + 'the blank row under the header', | |
| 425 | + ).toBe(false) | |
| 426 | + | |
| 427 | + expect( | |
| 428 | + Views.isWheelOverList(model, Fixtures.WHEEL_OVER_LIST_TOP), | |
| 429 | + "the list's first row", | |
| 430 | + ).toBe(true) | |
| 431 | + }) | |
| 432 | + | |
| 394 | 433 | test("docked rows follow the built-in's, its hunks after", async ($, on) => { |
| 395 | 434 | expect( |
| 396 | 435 | Fixtures.rowShapesOf( |
| @@ -428,11 +467,9 @@ | ||
| 428 | 467 | |
| 429 | 468 | const [header, pad, middle, foot, ...more] = Fixtures.childrenOf(body) |
| 430 | 469 | const [spacer, headline, ...under] = Fixtures.childrenOf(middle) |
| 431 | - const aboveMiddle = Limits.PANE_TOP_PAD_ROWS + 2 | |
| 470 | + const aboveMiddle = Fixtures.ENGINE_HEAD_ROWS + 2 | |
| 432 | 471 | |
| 433 | - expect(body).toMatchObject({ | |
| 434 | - props: { height: Fixtures.BODY_ROWS - Limits.PANE_TOP_PAD_ROWS }, | |
| 435 | - }) | |
| 472 | + expect(body).toMatchObject({ props: { height: Fixtures.BODY_ROWS } }) | |
| 436 | 473 | |
| 437 | 474 | expect(Fixtures.stringsOf(header)).toEqual([]) |
| 438 | 475 | expect(Fixtures.jsonOf(header)).not.toContain('✕') |