spacehub

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

diff: the dialog opens every file it lists, and says nothing when closedBrowse files
poteat authored 23 days ago parent 525d3b3 · commit a56c3b1f9849
3 changed files+61 -17compared with 525d3b3
Mmods/diff/hooks/drawn-files-of/drawn-files-of.ts
Mmods/diff/hooks/register.ts
Mmods/diff/tests/views.test.ts
@@ -1,25 +1,30 @@
11 import type Git from '../git'
22 import Limits from '../limits'
33 import PaneState from '../pane-state'
4+import Views from '../views'
45
56 /**
6- * The files whose bodies the pane draws now, in drawing order: the listed
7- * session rows, then the pre-session rows while their section is open.
7+ * The files whose bodies the pane draws or a press opens: every row its
8+ * seat lists now.
89 *
9- * Past PRE_SESSION_BODY_CAP pre-session files only their legend shows, so
10- * none of their bodies is wanted.
10+ * Docked, the listed session rows, then the pre-session rows while their
11+ * section is open, none past PRE_SESSION_BODY_CAP. Inline, the rows in the
12+ * dialog's window, none for a turn, whose rows carry their own bodies.
1113 *
12- * @param model the pane's state: its fetch and its two toggles
14+ * @param model the pane's state: its fetch, its seat, its toggles, its pick
1315 * @returns the rows, none without a fetch
1416 */
1517 export function drawnFilesOf(
16- model: Pick<
17- PaneState.PaneModel,
18- 'data' | 'isNoiseShown' | 'isPreSessionShown'
19- >,
18+ model: PaneState.PaneModel,
2019 ): readonly Git.FileStat[] {
20+ const files = model.data?.files ?? []
21+ const listed = Views.dialogEntriesOf(model).map(entry => entry.path)
22+ const start = Views.dialogWindowOf(model, listed)
23+ const windowed = listed.slice(start, start + Limits.MAX_VISIBLE_FILES)
24+ const isTurn = PaneState.pickedTurnOf(model) !== undefined
25+
2126 const partition = PaneState.partitionOf(
22- model.data?.files ?? [],
27+ files,
2328 model.isNoiseShown ? 'shown' : 'hidden',
2429 )
2530
@@ -27,5 +32,8 @@
2732 model.isPreSessionShown &&
2833 partition.preSession.length <= Limits.PRE_SESSION_BODY_CAP
2934
30- return [...partition.shown, ...(isBodied ? partition.preSession : [])]
35+ const docked = [...partition.shown, ...(isBodied ? partition.preSession : [])]
36+ const inDialog = files.filter(file => !isTurn && windowed.includes(file.path))
37+
38+ return model.placement === 'inline' ? inDialog : docked
3139 }
@@ -544,6 +544,7 @@
544544 place: isDocked ? Views.placeAtFile(model, path) : model.place,
545545 }
546546
547+ void loadBodies(engine)
547548 redraw(engine)
548549 },
549550 scrollList: delta => {
@@ -727,6 +728,8 @@
727728
728729 columns = e.viewport?.columns ?? columns
729730
731+ const isReseated = e.props.placement !== model.placement
732+
730733 model = {
731734 ...model,
732735 placement: e.props.placement,
@@ -740,6 +743,10 @@
740743 },
741744 }
742745
746+ if (isReseated) {
747+ void loadBodies(host)
748+ }
749+
743750 return Views.paneView(
744751 {
745752 ui: { Box, Text, Button, Select, Code },
@@ -830,13 +837,9 @@
830837 isPaneOpen = false
831838 }
832839
833- const isDialog = model.isFullscreen === false
840+ const isDocking = model.isFullscreen !== false
834841
835- if (isPersons && host && isDialog) {
836- host.uiLog(Names.DIALOG_DISMISSED_TEXT)
837- }
838-
839- if (isPersons && host && !isDialog) {
842+ if (isPersons && host && isDocking) {
840843 markTabSwitch(host, 'convo')
841844 await host.storeSet(Names.STORE_OPEN_KEY, false).catch(() => undefined)
842845 }
@@ -859,6 +862,7 @@
859862 }
860863
861864 model = { ...model, selectedPath: focus.selectedPath }
865+ void loadBodies(host)
862866 fitDialog(host)
863867 host.invalidate()
864868
@@ -92,6 +92,38 @@
9292 expect(drawn).toContain('\u2191/\u2193 to scroll \u00b7 Esc to back')
9393 })
9494
95+ test('inline, a file edited before the session opens too', async ($, on) => {
96+ const world = Fixtures.inRepository(on, Fixtures.MOVED_IN)
97+
98+ on('session.usage', () => ({ value: Fixtures.usageAt(Fixtures.SETTLE_MS) }))
99+ Fixtures.oldFiles(on)
100+
101+ await $.session.start(Fixtures.SESSION)
102+ await $.command.run(Fixtures.DIALOG_DIFF)
103+ await world.clock.advance(Fixtures.SETTLE_MS)
104+
105+ expect(
106+ Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE)),
107+ "the dialog lists it beside the session's file",
108+ ).toContain('old.ts')
109+
110+ expect(await $.ui.press({ plugin: 'diff', key: 'file:old.ts' })).toEqual({
111+ element: 'file:old.ts',
112+ })
113+
114+ await world.clock.advance(Fixtures.SETTLE_MS)
115+
116+ const drawn = Fixtures.textOf(await $.ui.render(Fixtures.INLINE_PANE))
117+
118+ expect(
119+ drawn,
120+ 'a listed row can be pressed, so its hunks were read',
121+ ).not.toContain('Loading diff')
122+
123+ expect(drawn).toContain('+b')
124+ expect(drawn).not.toContain('+d')
125+ })
126+
95127 test('docked, a wheel tick moves the body, not the list', async ($, on) => {
96128 const world = Fixtures.inRepository(on, Fixtures.MANY_FILES)
97129