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

claude-code / mods / diff / hooks / views / detail / hunk-body / hunk-body.tsx
114 lines · 3.0 KBRaw
1/* @jsxRuntime classic */
2/* @jsx h */
3/* @jsxFrag Fragment */
4import type { RenderElement, RenderNode } from 'claude-code'
5
6import type Kit from '../../kit'
7import Layout from '../../layout'
8import { BACKGROUNDS } from '../backgrounds'
9import { MARKER_CELLS } from '../marker-cells'
10import { MAX_RUN_CHARS } from '../max-run-chars'
11import { runsOf } from '../runs-of'
12import type Types from '../types'
13import { WORD_BACKGROUNDS } from '../word-backgrounds'
14import type { BodyCells } from './body-cells'
15
16/**
17 * A file's rows drawn as StructuredDiff's fallback draws them, in few
18 * nodes: one dim gutter Text beside a column of one Text per run.
19 *
20 * Added and removed runs sit on their theme bands, changed words on the
21 * word bands, every row is padded to the band's width less its text's
22 * chargedWidth; a run's text and the gutter split at MAX_RUN_CHARS a string.
23 *
24 * @param ui the surface's elements
25 * @param rows the laid-out rows (rowsOf), each exactly one terminal row
26 * @param cells the gutter's and the content's widths
27 * @returns the body element
28 */
29export function hunkBody(
30 ui: Kit.Ui,
31 rows: readonly Types.Row[],
32 cells: BodyCells,
33): RenderElement {
34 const { Box, Text } = ui
35 const bandCells = MARKER_CELLS + cells.contentCells
36
37 function gutterChildren(): string[] {
38 const children: string[] = []
39 let pending = ''
40
41 rows.forEach((row, at) => {
42 const label = row.gutter.padStart(cells.gutterCells)
43 const text = `${at === 0 ? '' : '\n'}${label}`
44
45 if (pending.length + text.length > MAX_RUN_CHARS) {
46 children.push(pending)
47 pending = ''
48 }
49
50 pending += text
51 })
52
53 return pending === '' ? children : [...children, pending]
54 }
55
56 function runChildren(run: Types.Run): RenderNode[] {
57 const children: RenderNode[] = []
58 let pending = ''
59
60 function flush() {
61 if (pending !== '') {
62 children.push(pending)
63 pending = ''
64 }
65 }
66
67 function plain(text: string) {
68 if (pending.length + text.length > MAX_RUN_CHARS) {
69 flush()
70 }
71
72 pending += text
73 }
74
75 function marked(text: string) {
76 flush()
77 children.push(
78 <Text backgroundColor={WORD_BACKGROUNDS[run.kind]}>{text}</Text>,
79 )
80 }
81
82 run.rows.forEach((row, at) => {
83 const used = row.segments.reduce(
84 (sum, segment) => sum + Layout.chargedWidth(segment.text),
85 MARKER_CELLS,
86 )
87 plain(`${at === 0 ? '' : '\n'} ${row.marker} `)
88
89 for (const segment of row.segments) {
90 ;(segment.isChanged ? marked : plain)(segment.text)
91 }
92
93 plain(' '.repeat(Math.max(0, bandCells - used)))
94 })
95 flush()
96
97 return children
98 }
99
100 return (
101 <Box flexDirection="row">
102 <Text dimColor wrap="truncate-end">
103 {gutterChildren()}
104 </Text>
105 <Box flexDirection="column" flexShrink={0} width={bandCells}>
106 {runsOf(rows).map(run => (
107 <Text backgroundColor={BACKGROUNDS[run.kind]} wrap="truncate-end">
108 {runChildren(run)}
109 </Text>
110 ))}
111 </Box>
112 </Box>
113 )
114}