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
| @@ -9,18 +9,18 @@ | ||
| 9 | 9 | * path is never listed, descended or stat'ed: session work (the engine |
| 10 | 10 | * lstats); a path the listing budget never reached is the caller's to place. |
| 11 | 11 | * |
| 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 | |
| 14 | 14 | * @param paths repository-relative paths, `/`-separated as git prints them |
| 15 | 15 | * @returns each path's Dating |
| 16 | 16 | */ |
| 17 | 17 | export async function datingsOf( |
| 18 | - probe: Types.StampProbe, | |
| 19 | - sessionStartMs: number, | |
| 18 | + context: Types.DatingContext, | |
| 20 | 19 | paths: readonly string[], |
| 21 | 20 | ): Promise<ReadonlyMap<string, Types.Dating>> { |
| 21 | + const { sessionStartMs } = context.deps | |
| 22 | 22 | const stamps: readonly Types.Stamp[] = await Promise.all( |
| 23 | - paths.map(path => stampIfFile(probe, path)), | |
| 23 | + paths.map(path => stampIfFile(context.stamps, path)), | |
| 24 | 24 | ) |
| 25 | 25 | |
| 26 | 26 | function datingOf(stamp: Types.Stamp): Types.Dating { |
| @@ -17,8 +17,7 @@ | ||
| 17 | 17 | files: readonly Types.FileStat[], |
| 18 | 18 | ): Promise<readonly Types.FileStat[]> { |
| 19 | 19 | const datings = await datingsOf( |
| 20 | - context.stamps, | |
| 21 | - context.deps.sessionStartMs, | |
| 20 | + context, | |
| 22 | 21 | files.map(file => file.path), |
| 23 | 22 | ) |
| 24 | 23 |
| @@ -22,11 +22,7 @@ | ||
| 22 | 22 | place: UntrackedPlace, |
| 23 | 23 | ): Promise<readonly Types.FileStat[]> { |
| 24 | 24 | 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) | |
| 30 | 26 | const probed = probedPaths.map(path => ({ |
| 31 | 27 | path, |
| 32 | 28 | isPreSession: datings.get(path) !== 'session', |
| @@ -1,9 +1,14 @@ | ||
| 1 | +import type Backend from '../../backend' | |
| 2 | + | |
| 1 | 3 | /** |
| 2 | 4 | * The pane's word for a fetch whose untracked listing the backend did not |
| 3 | 5 | * give whole: the tracked rows stand, the new files are not counted. |
| 4 | 6 | * |
| 5 | - * @param lister the program that lists untracked files (BackendWords) | |
| 7 | + * @param words the backend's words, naming the program that lists them | |
| 6 | 8 | * @returns the note |
| 7 | 9 | */ |
| 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' | |
| 3 | 1 | import { modeLabelOf } from './mode-label-of' |
| 2 | +import type { PaneModel } from './pane-model' | |
| 4 | 3 | |
| 5 | 4 | /** |
| 6 | 5 | * The dim line under the header naming what the diff compares against |
| @@ -10,18 +9,17 @@ | ||
| 10 | 9 | * with an ellipsis while its fetch is pending; on an unborn HEAD with |
| 11 | 10 | * rows, what the rows are instead. |
| 12 | 11 | * |
| 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 | |
| 15 | 14 | * @param filesCount the header's session file count |
| 16 | - * @param words the backend's words | |
| 17 | 15 | * @returns the line, or null |
| 18 | 16 | */ |
| 19 | 17 | export function baseLabelOf( |
| 20 | - requested: Git.BaseMode, | |
| 21 | - data: Git.DiffData | null, | |
| 18 | + model: Pick<PaneModel, 'requestedMode' | 'data' | 'words'>, | |
| 22 | 19 | filesCount: number, |
| 23 | - words: Pick<Backend.BackendWords, 'base'>, | |
| 24 | 20 | ): string | null { |
| 21 | + const { requestedMode, data } = model | |
| 22 | + | |
| 25 | 23 | if (!data) { |
| 26 | 24 | return null |
| 27 | 25 | } |
| @@ -32,15 +30,15 @@ | ||
| 32 | 30 | return hasRows ? 'no commits yet — showing staged and new files' : null |
| 33 | 31 | } |
| 34 | 32 | |
| 35 | - const isPending = requested !== data.mode | |
| 36 | - const isSettledSession = !isPending && requested === 'session' | |
| 33 | + const isPending = requestedMode !== data.mode | |
| 34 | + const isSettledSession = !isPending && requestedMode === 'session' | |
| 37 | 35 | |
| 38 | 36 | if (isSettledSession) { |
| 39 | 37 | return null |
| 40 | 38 | } |
| 41 | 39 | |
| 42 | 40 | const phase = isPending ? 'pending' : 'settled' |
| 43 | - const label = modeLabelOf(requested, data.source, phase, words) | |
| 41 | + const label = modeLabelOf(model, data.source, phase) | |
| 44 | 42 | |
| 45 | 43 | return isPending ? `${label}…` : label |
| 46 | 44 | } |
| @@ -24,7 +24,9 @@ | ||
| 24 | 24 | if (!data) { |
| 25 | 25 | return { |
| 26 | 26 | 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', | |
| 28 | 30 | } |
| 29 | 31 | } |
| 30 | 32 | |
| @@ -35,7 +37,7 @@ | ||
| 35 | 37 | if (data.isUntrackedWithheld) { |
| 36 | 38 | return { |
| 37 | 39 | headline: 'No tracked changes', |
| 38 | - hint: Names.untrackedWithheldTextOf(words.lister), | |
| 40 | + hint: Names.untrackedWithheldTextOf(words), | |
| 39 | 41 | } |
| 40 | 42 | } |
| 41 | 43 |
| @@ -1,5 +1,5 @@ | ||
| 1 | -import type Backend from '../../backend' | |
| 2 | 1 | import type Git from '../../git' |
| 2 | +import type { PaneModel } from '../pane-model' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * A requested mode's name for the base line. |
| @@ -8,21 +8,19 @@ | ||
| 8 | 8 | * while none is known. A working-tree diff names its own base (`HEAD`, a |
| 9 | 9 | * short sha); anything else falls back to the backend's word for it. |
| 10 | 10 | * |
| 11 | - * @param requested the mode the person picked | |
| 11 | + * @param model the mode the person picked, and the backend's words | |
| 12 | 12 | * @param source what the data on screen compares |
| 13 | 13 | * @param phase `pending` while the requested mode's fetch has not landed |
| 14 | - * @param words the backend's words | |
| 15 | 14 | * @returns the label without its pending ellipsis |
| 16 | 15 | */ |
| 17 | 16 | export function modeLabelOf( |
| 18 | - requested: Git.BaseMode, | |
| 17 | + model: Pick<PaneModel, 'requestedMode' | 'words'>, | |
| 19 | 18 | source: Git.DiffSource, |
| 20 | 19 | phase: 'pending' | 'settled', |
| 21 | - words: Pick<Backend.BackendWords, 'base'>, | |
| 22 | 20 | ) { |
| 23 | - const base = source.kind === 'working-tree' ? source.base : words.base | |
| 21 | + const base = source.kind === 'working-tree' ? source.base : model.words.base | |
| 24 | 22 | |
| 25 | - switch (requested) { | |
| 23 | + switch (model.requestedMode) { | |
| 26 | 24 | case 'session': |
| 27 | 25 | return 'this session' |
| 28 | 26 | case 'uncommitted': |
| @@ -54,12 +54,7 @@ | ||
| 54 | 54 | ? PaneState.headerTotalsOf(data, partition) |
| 55 | 55 | : PaneState.ZERO_TOTALS |
| 56 | 56 | 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) | |
| 63 | 58 | const selected = PaneState.selectionOf( |
| 64 | 59 | PaneState.listedOf(partition, preSession), |
| 65 | 60 | model.selectedPath, |
| @@ -103,7 +98,7 @@ | ||
| 103 | 98 | totals.notShown > 0 ? `${totals.notShown} not shown` : null, |
| 104 | 99 | ) |
| 105 | 100 | const untrackedNote = noteOf( |
| 106 | - isUntrackedNoted ? Names.untrackedWithheldTextOf(model.words.lister) : null, | |
| 101 | + isUntrackedNoted ? Names.untrackedWithheldTextOf(model.words) : null, | |
| 107 | 102 | ) |
| 108 | 103 | const detail = selected |
| 109 | 104 | ? [ |
| @@ -132,7 +127,7 @@ | ||
| 132 | 127 | top: Sections.present([ |
| 133 | 128 | header, |
| 134 | 129 | noteOf(baseLabel), |
| 135 | - Sections.todoBar(kit, model.todos), | |
| 130 | + Sections.todoBar(kit, model), | |
| 136 | 131 | noiseToggle, |
| 137 | 132 | notShownNote, |
| 138 | 133 | untrackedNote, |
| @@ -147,7 +142,7 @@ | ||
| 147 | 142 | {Sections.present([ |
| 148 | 143 | header, |
| 149 | 144 | noteOf(baseLabel), |
| 150 | - Sections.todoBar(kit, model.todos), | |
| 145 | + Sections.todoBar(kit, model), | |
| 151 | 146 | Sections.controlsView(kit, model), |
| 152 | 147 | noiseToggle, |
| 153 | 148 | ...listed.map(line => |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | export * from './current-pane' |
| 2 | 2 | export * from './detail' |
| 3 | +export * from './inset-of' | |
| 3 | 4 | export * from './kit' |
| 4 | 5 | export * from './layout' |
| 5 | 6 | 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 @@ | ||
| 7 | 7 | import Names from '../names' |
| 8 | 8 | import PaneState from '../pane-state' |
| 9 | 9 | import { currentPane } from './current-pane' |
| 10 | +import { insetOf } from './inset-of' | |
| 10 | 11 | import type { Kit } from './kit' |
| 11 | 12 | import Sections from './sections' |
| 12 | 13 | import { turnPane } from './turn-pane' |
| @@ -37,16 +38,14 @@ | ||
| 37 | 38 | return <Box>{Sections.dimNote(kit, Names.RESIZE_TERMINAL_TEXT)}</Box> |
| 38 | 39 | } |
| 39 | 40 | |
| 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) | |
| 47 | 42 | |
| 48 | 43 | 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 | + > | |
| 50 | 49 | {isCurrent ? currentPane(inset, model) : turnPane(inset, model, turn)} |
| 51 | 50 | </Box> |
| 52 | 51 | ) |
| @@ -3,7 +3,7 @@ | ||
| 3 | 3 | /* @jsxFrag Fragment */ |
| 4 | 4 | import type { RenderElement } from 'claude-code' |
| 5 | 5 | |
| 6 | -import type Todos from '../../todos' | |
| 6 | +import type PaneState from '../../pane-state' | |
| 7 | 7 | import type { Kit } from '../kit' |
| 8 | 8 | import { TODO_BAR_CELLS } from './todo-bar-cells' |
| 9 | 9 | import { TODO_BAR_RESERVE } from './todo-bar-reserve' |
| @@ -13,13 +13,15 @@ | ||
| 13 | 13 | * a todo list exists (ReplDiffSidebar's ProgressBar row). |
| 14 | 14 | * |
| 15 | 15 | * @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 | |
| 17 | 17 | * @returns the row, or null without todos |
| 18 | 18 | */ |
| 19 | 19 | export function todoBar( |
| 20 | 20 | kit: Kit, |
| 21 | - todos: Todos.TodoProgress, | |
| 21 | + model: Pick<PaneState.PaneModel, 'todos'>, | |
| 22 | 22 | ): RenderElement | null { |
| 23 | + const { todos } = model | |
| 24 | + | |
| 23 | 25 | if (todos.total === 0) { |
| 24 | 26 | return null |
| 25 | 27 | } |