/* @jsxRuntime classic */ /* @jsx h */ /* @jsxFrag Fragment */ import type { RenderElement, RenderNode } from 'claude-code' import type Kit from '../../kit' import Layout from '../../layout' import { BACKGROUNDS } from '../backgrounds' import { MARKER_CELLS } from '../marker-cells' import { MAX_RUN_CHARS } from '../max-run-chars' import { runsOf } from '../runs-of' import type Types from '../types' import { WORD_BACKGROUNDS } from '../word-backgrounds' import type { BodyCells } from './body-cells' /** * A file's rows drawn as StructuredDiff's fallback draws them, in few * nodes: one dim gutter Text beside a column of one Text per run. * * Added and removed runs sit on their theme bands, changed words on the * word bands, every row is padded to the band's width less its text's * chargedWidth; a run's text and the gutter split at MAX_RUN_CHARS a string. * * @param ui the surface's elements * @param rows the laid-out rows (rowsOf), each exactly one terminal row * @param cells the gutter's and the content's widths * @returns the body element */ export function hunkBody( ui: Kit.Ui, rows: readonly Types.Row[], cells: BodyCells, ): RenderElement { const { Box, Text } = ui const bandCells = MARKER_CELLS + cells.contentCells function gutterChildren(): string[] { const children: string[] = [] let pending = '' rows.forEach((row, at) => { const label = row.gutter.padStart(cells.gutterCells) const text = `${at === 0 ? '' : '\n'}${label}` if (pending.length + text.length > MAX_RUN_CHARS) { children.push(pending) pending = '' } pending += text }) return pending === '' ? children : [...children, pending] } function runChildren(run: Types.Run): RenderNode[] { const children: RenderNode[] = [] let pending = '' function flush() { if (pending !== '') { children.push(pending) pending = '' } } function plain(text: string) { if (pending.length + text.length > MAX_RUN_CHARS) { flush() } pending += text } function marked(text: string) { flush() children.push( {text}, ) } run.rows.forEach((row, at) => { const used = row.segments.reduce( (sum, segment) => sum + Layout.chargedWidth(segment.text), MARKER_CELLS, ) plain(`${at === 0 ? '' : '\n'} ${row.marker} `) for (const segment of row.segments) { ;(segment.isChanged ? marked : plain)(segment.text) } plain(' '.repeat(Math.max(0, bandCells - used))) }) flush() return children } return ( {gutterChildren()} {runsOf(rows).map(run => ( {runChildren(run)} ))} ) }