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
99 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 Sections from '../sections'
9import { codeBlocksOf } from './code-blocks-of'
10import type { DetailModel } from './detail-model'
11import { HUNK_DIVIDER } from './hunk-divider'
12import { MAX_CODE_CHARS } from './max-code-chars'
13import { placeholderOf } from './placeholder-of'
14
15/**
16 * The selected file under the list: its bold path and asides, the ask
17 * Button, the built-in's dim rule under them, the body.
18 *
19 * Each name is cut from its start to the width. The body is a placeholder,
20 * or the hunks as the engine's diff `Code` blocks (codeBlocksOf), a dim
21 * divider where two meet, then the footer when anything was cut.
22 *
23 * @param kit the elements and the width
24 * @param detail the selected file
25 * @param onToggleAsk arms or disarms the file for the next prompt
26 * @returns the detail element
27 */
28export function detailView(
29 kit: Kit,
30 detail: DetailModel,
31 onToggleAsk: () => void,
32): RenderElement {
33 const { Box, Text, Button, Code } = kit.ui
34 const placeholder = placeholderOf(detail)
35 const code = codeBlocksOf(placeholder ? [] : (detail.body?.hunks ?? []))
36 const isTruncated = detail.body?.isTruncated === true || code.isTruncated
37 const path = Layout.sanitizeName(detail.path).slice(-MAX_CODE_CHARS)
38
39 const nameOf = (name: string) =>
40 Layout.truncateStart(Layout.sanitizeName(name), kit.columns)
41
42 const renamedFrom =
43 detail.renamedFrom === null ? null : nameOf(detail.renamedFrom)
44
45 const asides = [
46 renamedFrom === null ? null : `renamed from ${renamedFrom}`,
47 detail.isUntracked ? 'untracked' : null,
48 isTruncated ? 'truncated' : null,
49 ].filter(word => word !== null)
50
51 const aside = asides.length === 0 ? '' : ` (${asides.join(', ')})`
52
53 const ask = placeholder
54 ? []
55 : [
56 <Button key="ask" onPress={onToggleAsk}>
57 {detail.isArmed ? 'asked ✓' : 'ask'}
58 </Button>,
59 ]
60
61 const footer = isTruncated
62 ? [
63 <Text dimColor italic>
64 … diff truncated (exceeded 400 line limit)
65 </Text>,
66 ]
67 : []
68
69 const notes = (placeholder ?? []).map(line => (
70 <Text dimColor italic wrap="wrap">
71 {line}
72 </Text>
73 ))
74
75 const hunks = code.blocks.flatMap(block => [
76 ...(block.hasDivider ? [<Text dimColor>{HUNK_DIVIDER}</Text>] : []),
77 <Code source={block.source} format="diff" path={path} />,
78 ])
79
80 return (
81 <Box flexDirection="column">
82 {[
83 <Box flexDirection="row">
84 {[
85 <Text bold wrap="truncate-start">
86 {nameOf(detail.path)}
87 </Text>,
88 <Text dimColor>{aside}</Text>,
89 <Box flexGrow={1} />,
90 ...ask,
91 ]}
92 </Box>,
93 Sections.divider(kit),
94 ...(placeholder ? notes : hunks),
95 ...footer,
96 ]}
97 </Box>
98 )
99}