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