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 / detail-view.tsx
92 lines · 2.9 KBRaw
1/* @jsxRuntime classic */
2/* @jsx h */
3/* @jsxFrag Fragment */
4import type { RenderElement } from 'claude-code'
5
6import type { Kit } from '../kit'
7import Layout from '../layout'
8import type { DetailModel } from './detail-model'
9import { gutterCellsOf } from './gutter-cells-of'
10import { hunkBody } from './hunk-body'
11import { linesOf } from './lines-of'
12import { MARKER_CELLS } from './marker-cells'
13import { maxRowsOf } from './max-rows-of'
14import { placeholderOf } from './placeholder-of'
15import { rowsOf } from './rows-of'
16
17/**
18 * The selected file under the list: its bold path and asides (renamed
19 * from, untracked, truncated) cut to the width, the ask Button, the body.
20 *
21 * The body is a placeholder or the hunks (cut to the rows the body's char
22 * and node budgets hold, maxRowsOf), then the footer; the gutter fits the
23 * largest number.
24 *
25 * @param kit the elements and the width
26 * @param detail the selected file
27 * @param onToggleAsk arms or disarms the file for the next prompt
28 * @returns the detail element
29 */
30export function detailView(
31 kit: Kit,
32 detail: DetailModel,
33 onToggleAsk: () => void,
34): RenderElement {
35 const { Box, Text, Button } = kit.ui
36 const placeholder = placeholderOf(detail)
37 const hunks = placeholder ? [] : (detail.body?.hunks ?? [])
38 const lines = linesOf(hunks)
39 const gutterCells = gutterCellsOf(lines)
40 const contentCells = Math.max(1, kit.columns - gutterCells - MARKER_CELLS)
41 const rows = placeholder ? [] : rowsOf(lines, contentCells)
42 const cells = { gutterCells, contentCells }
43 const maxRows = maxRowsOf(rows, cells)
44 const isTruncated = detail.body?.isTruncated === true || rows.length > maxRows
45 const nameOf = (path: string) =>
46 Layout.truncateStart(Layout.sanitizeName(path), kit.columns)
47 const renamedFrom =
48 detail.renamedFrom === null ? null : nameOf(detail.renamedFrom)
49 const asides = [
50 renamedFrom === null ? null : `renamed from ${renamedFrom}`,
51 detail.isUntracked ? 'untracked' : null,
52 isTruncated ? 'truncated' : null,
53 ].filter(word => word !== null)
54 const aside = asides.length === 0 ? '' : ` (${asides.join(', ')})`
55 const notes = (placeholder ?? []).map(line => (
56 <Text dimColor italic wrap="wrap">
57 {line}
58 </Text>
59 ))
60 const body = placeholder
61 ? notes
62 : [hunkBody(kit.ui, rows.slice(0, maxRows), cells)]
63 const ask = placeholder
64 ? []
65 : [
66 <Button key="ask" onPress={onToggleAsk}>
67 {detail.isArmed ? 'asked ✓' : 'ask'}
68 </Button>,
69 ]
70 const footer = isTruncated
71 ? [
72 <Text dimColor italic>
73 … diff truncated (exceeded 400 line limit)
74 </Text>,
75 ]
76 : []
77
78 const header = (
79 <Box flexDirection="row">
80 {[
81 <Text bold wrap="truncate-start">
82 {nameOf(detail.path)}
83 </Text>,
84 <Text dimColor>{aside}</Text>,
85 <Box flexGrow={1} />,
86 ...ask,
87 ]}
88 </Box>
89 )
90
91 return <Box flexDirection="column">{[header, ...body, ...footer]}</Box>
92}