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 docked window's hunks are made printable and split under a leaf's cap, draw only their lines in view, and stop at the tree's budgetBrowse files
poteat authored on Sep 14, 2026 parent 9d302a8 · commit 7d4f9471afb2
8 changed files+90 -61compared with 9d302a8
Mmods/diff/hooks/views/body/draw-window.tsx
Dmods/diff/hooks/views/body/plan/hunk-code-of.tsx
Amods/diff/hooks/views/body/plan/hunk-window-of.ts
Mmods/diff/hooks/views/body/plan/index.ts
Mmods/diff/hooks/views/body/plan/segments/segments-of.ts
Mmods/diff/hooks/views/body/types/index.ts
Amods/diff/hooks/views/body/types/window-cut/index.ts
Amods/diff/hooks/views/body/types/window-cut/window-cut.ts
@@ -4,18 +4,20 @@
44 import type { RenderElement } from 'claude-code'
55
66 import Limits from '../../limits'
7+import Detail from '../detail'
78 import type { Kit } from '../kit'
9+import Layout from '../layout'
810 import Sections from '../sections'
911 import Plan from './plan'
10-import type { BodyLayout, Segment } from './types'
12+import type { BodyLayout, HunkSegment, Segment, WindowCut } from './types'
1113
1214 /**
1315 * The rows of the docked body its window shows from a row down: each
1416 * segment drawn from the row the window cuts it at, until the window fills.
1517 *
16- * A hunk cut by the window's top draws from the line holding that row on;
17- * rows past its foot are the pane's to clip, so a few more are drawn than
18- * fit (SCROLL_MARGIN_ROWS) lest a miscounted wrap leave it short.
18+ * A hunk draws only its lines in view, and past MAX_BODY_CHARS of hunk
19+ * text the rest waits for a scroll (the tree's cap); rows past the foot
20+ * are the pane's to clip, so SCROLL_MARGIN_ROWS more are drawn than fit.
1921 *
2022 * @param kit the elements, the handlers, the width
2123 * @param layout the body laid out
@@ -27,7 +29,7 @@
2729 layout: BodyLayout,
2830 top: number,
2931 ): RenderElement[] {
30- const { Box, Text } = kit.ui
32+ const { Box, Text, Code } = kit.ui
3133 const drawn: RenderElement[] = []
3234 const wanted = layout.visibleRows + Limits.SCROLL_MARGIN_ROWS
3335 const at = Math.max(0, Math.min(top, layout.maxTop))
@@ -35,8 +37,25 @@
3537
3638 let start = 0
3739 let filled = 0
40+ let chars = 0
3841
39- function elementOf(segment: Segment, skip: number): RenderElement {
42+ function codeOf(segment: HunkSegment, cut: WindowCut): RenderElement {
43+ const source = Detail.hunkSourceOf(Plan.hunkWindowOf(segment, cut))
44+ const path = Layout.sanitizeName(segment.path)
45+ chars += source.length
46+
47+ const isOver = chars > Detail.MAX_BODY_CHARS
48+
49+ return isOver ? (
50+ <Text dimColor italic>
51+ …
52+ </Text>
53+ ) : (
54+ <Code source={source} format="diff" path={path} />
55+ )
56+ }
57+
58+ function elementOf(segment: Segment, cut: WindowCut): RenderElement {
4059 switch (segment.kind) {
4160 case 'rule':
4261 return Sections.divider(kit)
@@ -45,7 +64,7 @@
4564 case 'name':
4665 return Sections.nameRow(kit, segment.name)
4766 case 'hunk':
48- return Plan.hunkCodeOf(kit, segment, skip)
67+ return codeOf(segment, cut)
4968 case 'note':
5069 return (
5170 <Text dimColor italic wrap="wrap">
@@ -74,7 +93,7 @@
7493 if (isShown) {
7594 const skip = Math.max(0, at - start)
7695
77- drawn.push(elementOf(segment, skip))
96+ drawn.push(elementOf(segment, { skip, take: wanted - filled }))
7897 filled += rows - skip
7998 }
8099
Dmods/diff/hooks/views/body/plan/hunk-code-of.tsx+0 -47
@@ -1,47 +1,0 @@
1-/* @jsxRuntime classic */
2-/* @jsx h */
3-/* @jsxFrag Fragment */
4-import type { RenderElement } from 'claude-code'
5-
6-import Git from '../../../git'
7-import Detail from '../../detail'
8-import type { Kit } from '../../kit'
9-import Layout from '../../layout'
10-import type { HunkSegment } from '../types'
11-
12-/**
13- * One hunk of the docked body as the engine's diff `Code` block, drawn from
14- * the line holding a wrapped row of it on (hunkAfter); whole at row 0.
15- *
16- * @param kit the elements
17- * @param segment the hunk, whose file it is, the rows its lines take
18- * @param skip the wrapped rows the window's top cuts from it
19- * @returns the block
20- */
21-export function hunkCodeOf(
22- kit: Kit,
23- segment: HunkSegment,
24- skip: number,
25-): RenderElement {
26- const { Code } = kit.ui
27-
28- let passed = 0
29- let first = 0
30-
31- for (const rows of segment.lineRows) {
32- if (passed + rows > skip) {
33- break
34- }
35-
36- passed += rows
37- first += 1
38- }
39-
40- return (
41- <Code
42- source={Detail.hunkSourceOf(Git.hunkAfter(segment.hunk, first))}
43- format="diff"
44- path={Layout.sanitizeName(segment.path).slice(-Detail.MAX_CODE_CHARS)}
45- />
46- )
47-}
@@ -1,0 +1,32 @@
1+import Git from '../../../git'
2+import type { HunkSegment, WindowCut } from '../types'
3+
4+/**
5+ * The part of a hunk the window shows: from the line holding its first
6+ * shown row (hunkAfter renumbers) to the last that starts in the room left.
7+ *
8+ * @param segment the hunk and the rows each of its lines takes
9+ * @param cut the rows hidden above the window and the rows it has left
10+ * @returns the hunk's shown lines
11+ */
12+export function hunkWindowOf(segment: HunkSegment, cut: WindowCut): Git.Hunk {
13+ let passed = 0
14+ let first = 0
15+ let shown = 0
16+ let last = 0
17+
18+ for (const rows of segment.lineRows) {
19+ if (passed + rows <= cut.skip) {
20+ first += 1
21+ } else if (shown < cut.take) {
22+ shown += rows
23+ last += 1
24+ }
25+
26+ passed += rows
27+ }
28+
29+ const tail = Git.hunkAfter(segment.hunk, first)
30+
31+ return { ...tail, lines: tail.lines.slice(0, last) }
32+}
@@ -1,6 +1,6 @@
11 export * from './body-layout-of.js'
22 export * from './dock-plan-of.js'
3-export * from './hunk-code-of.jsx'
3+export * from './hunk-window-of.js'
44 export * from './segments'
55
66 export * as default from '.'
@@ -1,5 +1,5 @@
11 import { countOf } from '../../../../count-of'
2-import type Git from '../../../../git'
2+import Git from '../../../../git'
33 import { keptOf } from '../../../../kept-of'
44 import Limits from '../../../../limits'
55 import type PaneState from '../../../../pane-state'
@@ -13,8 +13,8 @@
1313 * box): each listed file's block, a blank between, the pre-session section.
1414 *
1515 * Under an empty state whose pre-session section is open, the empty lines
16- * head the body instead. A hunk carries the rows its lines take wrapped
17- * past the engine's gutter at the body's width; pre-session bodies stack.
16+ * head the body instead. Hunks are made printable and split under a leaf's
17+ * cap (subHunksOf), each carrying the rows its lines take wrapped.
1818 *
1919 * @param model the pane's state: which file is armed, which section is open
2020 * @param plan the docked pane's decisions over that state
@@ -64,14 +64,27 @@
6464 }
6565 }
6666
67+ const splitOf = (hunk: Git.Hunk) =>
68+ Detail.subHunksOf(
69+ {
70+ ...hunk,
71+ lines: hunk.lines.filter(Git.isBodyLine).map(Detail.drawnLineOf),
72+ },
73+ Detail.MAX_CODE_CHARS,
74+ )
75+
6776 function fileOf(entry: Entries.BodyEntry): Segment[] {
6877 const detail = Entries.detailModelOf(entry, model)
6978 const notes = Detail.placeholderOf(detail)
70- const isTruncated = entry.body?.isTruncated === true
79+ const splits = (entry.body?.hunks ?? []).map(splitOf)
7180
81+ const isTruncated =
82+ entry.body?.isTruncated === true ||
83+ splits.some(split => split.isTruncated)
84+
7285 const body: Segment[] = notes
7386 ? notes.map(text => ({ kind: 'note', text }))
74- : (entry.body?.hunks ?? []).map(hunk => hunkOf(entry, hunk))
87+ : splits.flatMap(split => split.hunks).map(hunk => hunkOf(entry, hunk))
7588
7689 return [
7790 { kind: 'rule', path: entry.path },
@@ -2,5 +2,6 @@
22 export type * from './dock-plan.js'
33 export type * from './list-window.js'
44 export * from './segment'
5+export * from './window-cut'
56
67 export * as default from '.'
@@ -1,0 +1,3 @@
1+export type * from './window-cut.js'
2+
3+export * as default from '.'
@@ -1,0 +1,8 @@
1+/**
2+ * How the body's window cuts one hunk: the wrapped rows its top hides, and
3+ * the rows it still has room for from there.
4+ */
5+export type WindowCut = {
6+ skip: number
7+ take: number
8+}