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