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 Git from '../../git' |
| 7 | import Names from '../../names' |
| 8 | import PaneState from '../../pane-state' |
| 9 | import Detail from '../detail' |
| 10 | import type { Kit } from '../kit' |
| 11 | import Layout from '../layout' |
| 12 | import Sections from '../sections' |
| 13 | import { fileRowOf } from './file-row-of' |
| 14 | import { listBodyOf } from './list-body-of' |
| 15 | import { messagePaneOf } from './message-pane-of' |
| 16 | |
| 17 | /** |
| 18 | * The pane over the repository's diff now (ReplDiffSidebarBody, one file's |
| 19 | * body at a time): header, base line, todo bar, pickers, rows, detail. |
| 20 | * |
| 21 | * The tests-and-generated toggle sits above the rows; the elision count, the |
| 22 | * withheld-untracked note, the pre-session line and rows below them, a blank |
| 23 | * row above that line and above its rows as the built-in leaves; the |
| 24 | * selected file last. With no rows to list, the loading line, the empty |
| 25 | * state or the too-many and only-hidden lines take the body instead |
| 26 | * (messagePaneOf), the header keeping only the close. |
| 27 | * |
| 28 | * @param kit the elements, the handlers, the width, the rows |
| 29 | * @param model the pane's state |
| 30 | * @returns the pane's tree |
| 31 | */ |
| 32 | export function currentPane( |
| 33 | kit: Kit, |
| 34 | model: PaneState.PaneModel, |
| 35 | ): RenderElement { |
| 36 | const { Box } = kit.ui |
| 37 | const { data } = model |
| 38 | |
| 39 | if (model.isOutsideRepository) { |
| 40 | return <Box>{Sections.dimNote(kit, Names.NOT_IN_REPOSITORY_TEXT)}</Box> |
| 41 | } |
| 42 | |
| 43 | if (!data && !model.hasSettled) { |
| 44 | return messagePaneOf(kit, { |
| 45 | top: [Sections.headerView(kit, null)], |
| 46 | message: ['Loading diff…'], |
| 47 | controls: null, |
| 48 | earlier: null, |
| 49 | rest: [], |
| 50 | }) |
| 51 | } |
| 52 | |
| 53 | const noise = model.isNoiseShown ? 'shown' : 'hidden' |
| 54 | const preSession = model.isPreSessionShown ? 'shown' : 'hidden' |
| 55 | const partition = PaneState.partitionOf(data?.files ?? [], noise) |
| 56 | const totals = data |
| 57 | ? PaneState.headerTotalsOf(data, partition) |
| 58 | : PaneState.ZERO_TOTALS |
| 59 | const empty = PaneState.emptyStateOf(data, totals.filesCount, model.words) |
| 60 | const baseLabel = PaneState.baseLabelOf( |
| 61 | model.requestedMode, |
| 62 | data, |
| 63 | totals.filesCount, |
| 64 | model.words, |
| 65 | ) |
| 66 | const selected = PaneState.selectionOf( |
| 67 | PaneState.listedOf(partition, preSession), |
| 68 | model.selectedPath, |
| 69 | ) |
| 70 | const rowOf = (file: Git.FileStat): RenderElement => |
| 71 | Sections.fileRow(kit, fileRowOf(file, selected?.path ?? null), () => |
| 72 | kit.actions.selectFile(file.path), |
| 73 | ) |
| 74 | const noteOf = (text: string | null): RenderElement | null => |
| 75 | text === null ? null : Sections.dimNote(kit, Layout.sanitizeName(text)) |
| 76 | const noiseFace = model.isNoiseShown ? 'hide' : 'show' |
| 77 | const noiseCount = Layout.plural(partition.noiseCount, 'test') |
| 78 | const hasNoise = partition.noiseCount > 0 |
| 79 | const noiseToggle = hasNoise |
| 80 | ? Sections.toggleRow(kit, 'noise', { |
| 81 | label: `${noiseCount}/generated (${noiseFace})`, |
| 82 | onPress: kit.actions.toggleNoise, |
| 83 | }) |
| 84 | : null |
| 85 | const earlierFace = model.isPreSessionShown ? 'hide' : 'show' |
| 86 | const earlierCount = Layout.plural(partition.preSession.length, 'file') |
| 87 | const hasEarlier = partition.preSession.length > 0 |
| 88 | const earlierToggle = hasEarlier |
| 89 | ? Sections.toggleRow(kit, 'presession', { |
| 90 | label: `+${earlierCount} edited before this session (${earlierFace})`, |
| 91 | onPress: kit.actions.togglePreSession, |
| 92 | }) |
| 93 | : null |
| 94 | const earlierFiles = model.isPreSessionShown ? partition.preSession : [] |
| 95 | const earlierRows = |
| 96 | earlierFiles.length === 0 |
| 97 | ? [] |
| 98 | : [<Box height={1} />, ...earlierFiles.map(rowOf)] |
| 99 | const isUntrackedNoted = data?.isUntrackedWithheld === true && !empty |
| 100 | const listed = listBodyOf(kit, { data, partition, totals, empty }) |
| 101 | const notes = listed.filter(line => typeof line === 'string') |
| 102 | const hasRows = notes.length < listed.length |
| 103 | const message = empty ? [empty.headline, ...notes] : hasRows ? [] : notes |
| 104 | const header = Sections.headerView(kit, empty ? null : totals) |
| 105 | const notShownNote = noteOf( |
| 106 | totals.notShown > 0 ? `${totals.notShown} not shown` : null, |
| 107 | ) |
| 108 | const untrackedNote = noteOf( |
| 109 | isUntrackedNoted ? Names.untrackedWithheldTextOf(model.words.lister) : null, |
| 110 | ) |
| 111 | const detail = selected |
| 112 | ? [ |
| 113 | Sections.divider(kit), |
| 114 | Detail.detailView( |
| 115 | kit, |
| 116 | { |
| 117 | words: model.words, |
| 118 | path: selected.path, |
| 119 | renamedFrom: selected.renamedFrom, |
| 120 | isUntracked: selected.isUntracked, |
| 121 | isBinary: selected.isBinary, |
| 122 | body: model.body, |
| 123 | bodyState: model.bodyState, |
| 124 | isArmed: model.armedPath === selected.path, |
| 125 | }, |
| 126 | () => kit.actions.toggleAsk(selected.path), |
| 127 | ), |
| 128 | ] |
| 129 | : [] |
| 130 | |
| 131 | if (message.length > 0) { |
| 132 | return messagePaneOf(kit, { |
| 133 | top: Sections.present([ |
| 134 | header, |
| 135 | noteOf(baseLabel), |
| 136 | Sections.todoBar(kit, model.todos), |
| 137 | noiseToggle, |
| 138 | notShownNote, |
| 139 | untrackedNote, |
| 140 | ]), |
| 141 | message, |
| 142 | controls: Sections.controlsView(kit, model), |
| 143 | earlier: earlierToggle, |
| 144 | rest: [...earlierRows, ...detail], |
| 145 | }) |
| 146 | } |
| 147 | |
| 148 | return ( |
| 149 | <Box flexDirection="column"> |
| 150 | {Sections.present([ |
| 151 | header, |
| 152 | noteOf(baseLabel), |
| 153 | Sections.todoBar(kit, model.todos), |
| 154 | Sections.controlsView(kit, model), |
| 155 | noiseToggle, |
| 156 | ...listed.map(line => |
| 157 | typeof line === 'string' ? noteOf(line) : rowOf(line), |
| 158 | ), |
| 159 | notShownNote, |
| 160 | untrackedNote, |
| 161 | hasEarlier ? <Box height={1} /> : null, |
| 162 | earlierToggle, |
| 163 | ...earlierRows, |
| 164 | ...detail, |
| 165 | ])} |
| 166 | </Box> |
| 167 | ) |
| 168 | } |