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

mods/diff: a helper takes the whole shape its callers hold, and the pane's inset kit has a constructorBrowse files
poteat authored on Sep 11, 2026 parent 46ea2a5 · commit 97f86b94e8b3
13 changed files+68 -55compared with 46ea2a5
Mmods/diff/hooks/git/probes/datings-of/datings-of.ts
Mmods/diff/hooks/git/probes/tag-pre-session.ts
Mmods/diff/hooks/git/probes/untracked-rows-of/untracked-rows-of.ts
Mmods/diff/hooks/names/texts/untracked-withheld-text-of.ts
Mmods/diff/hooks/pane-state/base-label-of.ts
Mmods/diff/hooks/pane-state/empty-state-of.ts
Mmods/diff/hooks/pane-state/mode-label-of/mode-label-of.ts
Mmods/diff/hooks/views/current-pane/current-pane.tsx
Mmods/diff/hooks/views/index.ts
Amods/diff/hooks/views/inset-of/index.ts
Amods/diff/hooks/views/inset-of/inset-of.ts
Mmods/diff/hooks/views/pane-view.tsx
Mmods/diff/hooks/views/sections/todo-bar.tsx
@@ -9,18 +9,18 @@
99 * path is never listed, descended or stat'ed: session work (the engine
1010 * lstats); a path the listing budget never reached is the caller's to place.
1111 *
12- * @param probe the fetch's stamp probe over the working tree's top
13- * @param sessionStartMs when the session began
12+ * @param context the fetch's stamp probe over the working tree's top, and
13+ * when the session began
1414 * @param paths repository-relative paths, `/`-separated as git prints them
1515 * @returns each path's Dating
1616 */
1717 export async function datingsOf(
18- probe: Types.StampProbe,
19- sessionStartMs: number,
18+ context: Types.DatingContext,
2019 paths: readonly string[],
2120 ): Promise<ReadonlyMap<string, Types.Dating>> {
21+ const { sessionStartMs } = context.deps
2222 const stamps: readonly Types.Stamp[] = await Promise.all(
23- paths.map(path => stampIfFile(probe, path)),
23+ paths.map(path => stampIfFile(context.stamps, path)),
2424 )
2525
2626 function datingOf(stamp: Types.Stamp): Types.Dating {
@@ -17,8 +17,7 @@
1717 files: readonly Types.FileStat[],
1818 ): Promise<readonly Types.FileStat[]> {
1919 const datings = await datingsOf(
20- context.stamps,
21- context.deps.sessionStartMs,
20+ context,
2221 files.map(file => file.path),
2322 )
2423
@@ -22,11 +22,7 @@
2222 place: UntrackedPlace,
2323 ): Promise<readonly Types.FileStat[]> {
2424 const probedPaths = paths.slice(0, Limits.MAX_UNTRACKED_PROBES)
25- const datings = await datingsOf(
26- context.stamps,
27- context.deps.sessionStartMs,
28- probedPaths,
29- )
25+ const datings = await datingsOf(context, probedPaths)
3026 const probed = probedPaths.map(path => ({
3127 path,
3228 isPreSession: datings.get(path) !== 'session',
@@ -1,9 +1,14 @@
1+import type Backend from '../../backend'
2+
13 /**
24 * The pane's word for a fetch whose untracked listing the backend did not
35 * give whole: the tracked rows stand, the new files are not counted.
46 *
5- * @param lister the program that lists untracked files (BackendWords)
7+ * @param words the backend's words, naming the program that lists them
68 * @returns the note
79 */
8-export const untrackedWithheldTextOf = (lister: string) =>
9- `Untracked files unavailable (${lister} could not list them); not counted`
10+export const untrackedWithheldTextOf = (
11+ words: Pick<Backend.BackendWords, 'lister'>,
12+) =>
13+ `Untracked files unavailable (${words.lister} could not list them); ` +
14+ 'not counted'
@@ -1,6 +1,5 @@
1-import type Backend from '../backend'
2-import type Git from '../git'
31 import { modeLabelOf } from './mode-label-of'
2+import type { PaneModel } from './pane-model'
43
54 /**
65 * The dim line under the header naming what the diff compares against
@@ -10,18 +9,17 @@
109 * with an ellipsis while its fetch is pending; on an unborn HEAD with
1110 * rows, what the rows are instead.
1211 *
13- * @param requested the mode the person picked
14- * @param data the last good fetch
12+ * @param model the mode the person picked, the last good fetch, and the
13+ * backend's words
1514 * @param filesCount the header's session file count
16- * @param words the backend's words
1715 * @returns the line, or null
1816 */
1917 export function baseLabelOf(
20- requested: Git.BaseMode,
21- data: Git.DiffData | null,
18+ model: Pick<PaneModel, 'requestedMode' | 'data' | 'words'>,
2219 filesCount: number,
23- words: Pick<Backend.BackendWords, 'base'>,
2420 ): string | null {
21+ const { requestedMode, data } = model
22+
2523 if (!data) {
2624 return null
2725 }
@@ -32,15 +30,15 @@
3230 return hasRows ? 'no commits yet — showing staged and new files' : null
3331 }
3432
35- const isPending = requested !== data.mode
36- const isSettledSession = !isPending && requested === 'session'
33+ const isPending = requestedMode !== data.mode
34+ const isSettledSession = !isPending && requestedMode === 'session'
3735
3836 if (isSettledSession) {
3937 return null
4038 }
4139
4240 const phase = isPending ? 'pending' : 'settled'
43- const label = modeLabelOf(requested, data.source, phase, words)
41+ const label = modeLabelOf(model, data.source, phase)
4442
4543 return isPending ? `${label}…` : label
4644 }
@@ -24,7 +24,9 @@
2424 if (!data) {
2525 return {
2626 headline: 'Diff unavailable',
27- hint: `Couldn't read the ${words.diffCommand} — it will retry on the next change`,
27+ hint:
28+ `Couldn't read the ${words.diffCommand} — it will retry on the ` +
29+ 'next change',
2830 }
2931 }
3032
@@ -35,7 +37,7 @@
3537 if (data.isUntrackedWithheld) {
3638 return {
3739 headline: 'No tracked changes',
38- hint: Names.untrackedWithheldTextOf(words.lister),
40+ hint: Names.untrackedWithheldTextOf(words),
3941 }
4042 }
4143
@@ -1,5 +1,5 @@
1-import type Backend from '../../backend'
21 import type Git from '../../git'
2+import type { PaneModel } from '../pane-model'
33
44 /**
55 * A requested mode's name for the base line.
@@ -8,21 +8,19 @@
88 * while none is known. A working-tree diff names its own base (`HEAD`, a
99 * short sha); anything else falls back to the backend's word for it.
1010 *
11- * @param requested the mode the person picked
11+ * @param model the mode the person picked, and the backend's words
1212 * @param source what the data on screen compares
1313 * @param phase `pending` while the requested mode's fetch has not landed
14- * @param words the backend's words
1514 * @returns the label without its pending ellipsis
1615 */
1716 export function modeLabelOf(
18- requested: Git.BaseMode,
17+ model: Pick<PaneModel, 'requestedMode' | 'words'>,
1918 source: Git.DiffSource,
2019 phase: 'pending' | 'settled',
21- words: Pick<Backend.BackendWords, 'base'>,
2220 ) {
23- const base = source.kind === 'working-tree' ? source.base : words.base
21+ const base = source.kind === 'working-tree' ? source.base : model.words.base
2422
25- switch (requested) {
23+ switch (model.requestedMode) {
2624 case 'session':
2725 return 'this session'
2826 case 'uncommitted':
@@ -54,12 +54,7 @@
5454 ? PaneState.headerTotalsOf(data, partition)
5555 : PaneState.ZERO_TOTALS
5656 const empty = PaneState.emptyStateOf(data, totals.filesCount, model.words)
57- const baseLabel = PaneState.baseLabelOf(
58- model.requestedMode,
59- data,
60- totals.filesCount,
61- model.words,
62- )
57+ const baseLabel = PaneState.baseLabelOf(model, totals.filesCount)
6358 const selected = PaneState.selectionOf(
6459 PaneState.listedOf(partition, preSession),
6560 model.selectedPath,
@@ -103,7 +98,7 @@
10398 totals.notShown > 0 ? `${totals.notShown} not shown` : null,
10499 )
105100 const untrackedNote = noteOf(
106- isUntrackedNoted ? Names.untrackedWithheldTextOf(model.words.lister) : null,
101+ isUntrackedNoted ? Names.untrackedWithheldTextOf(model.words) : null,
107102 )
108103 const detail = selected
109104 ? [
@@ -132,7 +127,7 @@
132127 top: Sections.present([
133128 header,
134129 noteOf(baseLabel),
135- Sections.todoBar(kit, model.todos),
130+ Sections.todoBar(kit, model),
136131 noiseToggle,
137132 notShownNote,
138133 untrackedNote,
@@ -147,7 +142,7 @@
147142 {Sections.present([
148143 header,
149144 noteOf(baseLabel),
150- Sections.todoBar(kit, model.todos),
145+ Sections.todoBar(kit, model),
151146 Sections.controlsView(kit, model),
152147 noiseToggle,
153148 ...listed.map(line =>
@@ -1,5 +1,6 @@
11 export * from './current-pane'
22 export * from './detail'
3+export * from './inset-of'
34 export * from './kit'
45 export * from './layout'
56 export * from './pane-actions'
@@ -1,0 +1,3 @@
1+export * from './inset-of.js'
2+
3+export * as default from '.'
@@ -1,0 +1,15 @@
1+import Limits from '../../limits'
2+import type { Kit } from '../kit'
3+
4+/**
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.
7+ *
8+ * @param kit the drawing's kit at the pane's full size
9+ * @returns the kit its body draws with
10+ */
11+export const insetOf = (kit: Kit): Kit => ({
12+ ...kit,
13+ columns: Math.max(1, kit.columns - Limits.PANE_RIGHT_PAD_COLUMNS),
14+ rows: Math.max(0, kit.rows - Limits.PANE_TOP_PAD_ROWS),
15+})
@@ -7,6 +7,7 @@
77 import Names from '../names'
88 import PaneState from '../pane-state'
99 import { currentPane } from './current-pane'
10+import { insetOf } from './inset-of'
1011 import type { Kit } from './kit'
1112 import Sections from './sections'
1213 import { turnPane } from './turn-pane'
@@ -37,16 +38,14 @@
3738 return <Box>{Sections.dimNote(kit, Names.RESIZE_TERMINAL_TEXT)}</Box>
3839 }
3940
40- const right = Limits.PANE_RIGHT_PAD_COLUMNS
41- const top = Limits.PANE_TOP_PAD_ROWS
42- const inset: Kit = {
43- ...kit,
44- columns: Math.max(1, kit.columns - right),
45- rows: Math.max(0, kit.rows - top),
46- }
41+ const inset = insetOf(kit)
4742
4843 return (
49- <Box flexDirection="column" paddingTop={top} paddingRight={right}>
44+ <Box
45+ flexDirection="column"
46+ paddingTop={Limits.PANE_TOP_PAD_ROWS}
47+ paddingRight={Limits.PANE_RIGHT_PAD_COLUMNS}
48+ >
5049 {isCurrent ? currentPane(inset, model) : turnPane(inset, model, turn)}
5150 </Box>
5251 )
@@ -3,7 +3,7 @@
33 /* @jsxFrag Fragment */
44 import type { RenderElement } from 'claude-code'
55
6-import type Todos from '../../todos'
6+import type PaneState from '../../pane-state'
77 import type { Kit } from '../kit'
88 import { TODO_BAR_CELLS } from './todo-bar-cells'
99 import { TODO_BAR_RESERVE } from './todo-bar-reserve'
@@ -13,13 +13,15 @@
1313 * a todo list exists (ReplDiffSidebar's ProgressBar row).
1414 *
1515 * @param kit the elements and the width
16- * @param todos completed over total
16+ * @param model the pane's state, holding its todos' completed over total
1717 * @returns the row, or null without todos
1818 */
1919 export function todoBar(
2020 kit: Kit,
21- todos: Todos.TodoProgress,
21+ model: Pick<PaneState.PaneModel, 'todos'>,
2222 ): RenderElement | null {
23+ const { todos } = model
24+
2325 if (todos.total === 0) {
2426 return null
2527 }