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
| @@ -30,11 +30,11 @@ | ||
| 30 | 30 | '--full-name', |
| 31 | 31 | ]) |
| 32 | 32 | |
| 33 | - if (!Parse.isWholeAnswer(listing)) { | |
| 34 | - return null | |
| 35 | - } | |
| 36 | - | |
| 37 | - const paths = listing.stdout.split('\0').filter(path => path !== '') | |
| 38 | - | |
| 39 | - return untrackedRowsOf(context, paths, place) | |
| 33 | + return Parse.isWholeAnswer(listing) | |
| 34 | + ? untrackedRowsOf( | |
| 35 | + context, | |
| 36 | + listing.stdout.split('\0').filter(path => path !== ''), | |
| 37 | + place, | |
| 38 | + ) | |
| 39 | + : null | |
| 40 | 40 | } |
| @@ -22,15 +22,13 @@ | ||
| 22 | 22 | result: Types.NumstatResult, |
| 23 | 23 | scope: Types.UntrackedScope, |
| 24 | 24 | ): Promise<Types.MergedResult> { |
| 25 | - if (isPastDetails(result)) { | |
| 26 | - return mergedResultOf(result, { isUntrackedWithheld: false }) | |
| 27 | - } | |
| 28 | - | |
| 29 | - return mergedWithUntracked( | |
| 30 | - result, | |
| 31 | - await Probes.untrackedFiles(context, { | |
| 32 | - slots: Limits.MAX_FILES - result.files.length, | |
| 33 | - scope, | |
| 34 | - }), | |
| 35 | - ) | |
| 25 | + return isPastDetails(result) | |
| 26 | + ? mergedResultOf(result, { isUntrackedWithheld: false }) | |
| 27 | + : mergedWithUntracked( | |
| 28 | + result, | |
| 29 | + await Probes.untrackedFiles(context, { | |
| 30 | + slots: Limits.MAX_FILES - result.files.length, | |
| 31 | + scope, | |
| 32 | + }), | |
| 33 | + ) | |
| 36 | 34 | } |
| @@ -12,11 +12,12 @@ | ||
| 12 | 12 | * @param pane the plugin's belief, the probe's answer and the width |
| 13 | 13 | * @returns `close`, `open` or `too-narrow` |
| 14 | 14 | */ |
| 15 | -export const paneToggleOf = ( | |
| 15 | +export function paneToggleOf( | |
| 16 | 16 | pane: PaneBelief, |
| 17 | -): 'open' | 'close' | 'too-narrow' => | |
| 18 | - pane.isBelievedOpen && pane.wasDrawnWhenProbed | |
| 19 | - ? 'close' | |
| 20 | - : pane.columns !== null && pane.columns < Limits.OPEN_MIN_COLUMNS | |
| 21 | - ? 'too-narrow' | |
| 22 | - : 'open' | |
| 17 | +): 'open' | 'close' | 'too-narrow' { | |
| 18 | + const isClosing = pane.isBelievedOpen && pane.wasDrawnWhenProbed | |
| 19 | + const isTooNarrow = | |
| 20 | + pane.columns !== null && pane.columns < Limits.OPEN_MIN_COLUMNS | |
| 21 | + | |
| 22 | + return isClosing ? 'close' : isTooNarrow ? 'too-narrow' : 'open' | |
| 23 | +} |
| @@ -595,10 +595,10 @@ | ||
| 595 | 595 | } |
| 596 | 596 | |
| 597 | 597 | const context = e.context ?? [] |
| 598 | - const used = context.reduce((sum, entry) => sum + entry.length, 0) | |
| 599 | 598 | const text = Ask.fittedAskTextOf( |
| 600 | 599 | asked.text, |
| 601 | - Limits.PROMPT_CONTEXT_MAX_CHARS - used, | |
| 600 | + Limits.PROMPT_CONTEXT_MAX_CHARS - | |
| 601 | + context.reduce((sum, entry) => sum + entry.length, 0), | |
| 602 | 602 | ) |
| 603 | 603 | |
| 604 | 604 | if (text === undefined) { |
| @@ -89,10 +89,10 @@ | ||
| 89 | 89 | }) |
| 90 | 90 | : null |
| 91 | 91 | const earlierFiles = model.isPreSessionShown ? partition.preSession : [] |
| 92 | - const earlierRows = | |
| 93 | - earlierFiles.length === 0 | |
| 94 | - ? [] | |
| 95 | - : [<Box height={1} />, ...earlierFiles.map(rowOf)] | |
| 92 | + const isEarlierListed = earlierFiles.length > 0 | |
| 93 | + const earlierRows = isEarlierListed | |
| 94 | + ? [<Box height={1} />, ...earlierFiles.map(rowOf)] | |
| 95 | + : [] | |
| 96 | 96 | const isUntrackedNoted = data?.isUntrackedWithheld === true && !empty |
| 97 | 97 | const listed = listBodyOf(kit, { data, partition, totals, empty }) |
| 98 | 98 | const notes = listed.filter(line => typeof line === 'string') |
| @@ -125,8 +125,10 @@ | ||
| 125 | 125 | ] |
| 126 | 126 | : [] |
| 127 | 127 | |
| 128 | - if (message.length > 0) { | |
| 129 | - return messagePaneOf(kit, { | |
| 128 | + const isMessageShown = message.length > 0 | |
| 129 | + | |
| 130 | + return isMessageShown ? ( | |
| 131 | + messagePaneOf(kit, { | |
| 130 | 132 | top: Sections.present([ |
| 131 | 133 | header, |
| 132 | 134 | noteOf(baseLabel), |
| @@ -140,9 +142,7 @@ | ||
| 140 | 142 | earlier: earlierToggle, |
| 141 | 143 | rest: [...earlierRows, ...detail], |
| 142 | 144 | }) |
| 143 | - } | |
| 144 | - | |
| 145 | - return ( | |
| 145 | + ) : ( | |
| 146 | 146 | <Box flexDirection="column"> |
| 147 | 147 | {Sections.present([ |
| 148 | 148 | header, |
| @@ -1,0 +1,5 @@ | ||
| 1 | +/** | |
| 2 | + * The rows the pre-session line takes at the pane's foot: a blank row, the | |
| 3 | + * line, and a blank row under it. | |
| 4 | + */ | |
| 5 | +export const FOOT_ROWS = 3 |
| @@ -1,0 +1,3 @@ | ||
| 1 | +export * from './foot-rows.js' | |
| 2 | + | |
| 3 | +export * as default from '.' |
| @@ -1,3 +1,5 @@ | ||
| 1 | +export * from './foot-rows' | |
| 2 | +export * from './message-pane' | |
| 1 | 3 | export * from './message-pane-of.jsx' |
| 2 | 4 | |
| 3 | 5 | export * as default from '.' |
| @@ -1,0 +1,3 @@ | ||
| 1 | +export type * from './message-pane.js' | |
| 2 | + | |
| 3 | +export * as default from '.' |
| @@ -1,0 +1,13 @@ | ||
| 1 | +import type { RenderElement } from 'claude-code' | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * What stands around a message in the pane: the header block, the message, | |
| 5 | + * the pickers, the pre-session line, and what is listed under it. | |
| 6 | + */ | |
| 7 | +export type MessagePane = { | |
| 8 | + top: readonly RenderElement[] | |
| 9 | + message: readonly string[] | |
| 10 | + controls: RenderElement | null | |
| 11 | + earlier: RenderElement | null | |
| 12 | + rest: readonly RenderElement[] | |
| 13 | +} |
| @@ -5,6 +5,8 @@ | ||
| 5 | 5 | |
| 6 | 6 | import type { Kit } from '../../kit' |
| 7 | 7 | import Layout from '../../layout' |
| 8 | +import { FOOT_ROWS } from './foot-rows' | |
| 9 | +import type { MessagePane } from './message-pane' | |
| 8 | 10 | |
| 9 | 11 | /** |
| 10 | 12 | * The pane while a message stands in for the list (ReplDiffSidebar's |
| @@ -19,16 +21,7 @@ | ||
| 19 | 21 | * pre-session line, and what is listed under it |
| 20 | 22 | * @returns the pane's tree |
| 21 | 23 | */ |
| 22 | -export function messagePaneOf( | |
| 23 | - kit: Kit, | |
| 24 | - pane: { | |
| 25 | - top: readonly RenderElement[] | |
| 26 | - message: readonly string[] | |
| 27 | - controls: RenderElement | null | |
| 28 | - earlier: RenderElement | null | |
| 29 | - rest: readonly RenderElement[] | |
| 30 | - }, | |
| 31 | -): RenderElement { | |
| 24 | +export function messagePaneOf(kit: Kit, pane: MessagePane): RenderElement { | |
| 32 | 25 | const { Box, Text } = kit.ui |
| 33 | 26 | const { top, controls, earlier, rest } = pane |
| 34 | 27 | const lines = pane.message.flatMap(text => |
| @@ -37,11 +30,13 @@ | ||
| 37 | 30 | const drawn = lines.map(line => <Text dimColor>{line}</Text>) |
| 38 | 31 | const controlRows = controls === null ? [] : [controls] |
| 39 | 32 | const earlierRows = earlier === null ? [] : [earlier] |
| 40 | - const aboveEarlier = | |
| 33 | + const isControlsAboveEarlier = | |
| 41 | 34 | controlRows.length > 0 || earlierRows.length === 0 |
| 42 | - ? controlRows | |
| 43 | - : [<Box height={1} />] | |
| 44 | - const middleRows = kit.rows - top.length - 1 - (earlier === null ? 0 : 3) | |
| 35 | + const aboveEarlier = isControlsAboveEarlier | |
| 36 | + ? controlRows | |
| 37 | + : [<Box height={1} />] | |
| 38 | + const footRows = earlier === null ? 0 : FOOT_ROWS | |
| 39 | + const middleRows = kit.rows - top.length - 1 - footRows | |
| 45 | 40 | const above = Math.floor((middleRows - lines.length) / 2) |
| 46 | 41 | const isCentered = |
| 47 | 42 | rest.length === 0 && middleRows - above - lines.length >= controlRows.length |
| @@ -33,9 +33,8 @@ | ||
| 33 | 33 | if (wordWidth > width) { |
| 34 | 34 | const breaksHere = |
| 35 | 35 | 1 + Math.floor((wordWidth - (width - rowWidth) - 1) / width) |
| 36 | - const breaksNext = Math.floor((wordWidth - 1) / width) | |
| 37 | 36 | |
| 38 | - if (breaksNext < breaksHere) { | |
| 37 | + if (Math.floor((wordWidth - 1) / width) < breaksHere) { | |
| 39 | 38 | rows.push('') |
| 40 | 39 | } |
| 41 | 40 |
| @@ -46,23 +46,23 @@ | ||
| 46 | 46 | ) : null |
| 47 | 47 | const isCurrent = source.kind === 'current' |
| 48 | 48 | const fetchedSource = model.data?.source |
| 49 | - const base = | |
| 50 | - fetchedSource?.kind === 'working-tree' | |
| 51 | - ? fetchedSource.base | |
| 52 | - : model.words.base | |
| 49 | + const isWorkingTree = fetchedSource?.kind === 'working-tree' | |
| 50 | + const base = isWorkingTree ? fetchedSource.base : model.words.base | |
| 53 | 51 | const basePicker = isCurrent ? ( |
| 54 | 52 | <Select |
| 55 | 53 | key="base" |
| 56 | 54 | label="base" |
| 57 | - options={model.baseModes.map(mode => ({ | |
| 58 | - value: mode, | |
| 59 | - label: | |
| 60 | - mode === 'uncommitted' | |
| 61 | - ? `uncommitted (vs ${base})` | |
| 62 | - : mode === 'session' | |
| 63 | - ? 'this session' | |
| 64 | - : mode, | |
| 65 | - }))} | |
| 55 | + options={model.baseModes.map(mode => { | |
| 56 | + const isUncommitted = mode === 'uncommitted' | |
| 57 | + const isSession = mode === 'session' | |
| 58 | + const label = isUncommitted | |
| 59 | + ? `uncommitted (vs ${base})` | |
| 60 | + : isSession | |
| 61 | + ? 'this session' | |
| 62 | + : mode | |
| 63 | + | |
| 64 | + return { value: mode, label } | |
| 65 | + })} | |
| 66 | 66 | value={model.requestedMode} |
| 67 | 67 | onSelect={value => kit.actions.chooseBase(value)} |
| 68 | 68 | /> |
| @@ -24,20 +24,22 @@ | ||
| 24 | 24 | totals: PaneState.HeaderTotals | null, |
| 25 | 25 | ): RenderElement { |
| 26 | 26 | const { Box, Text } = kit.ui |
| 27 | - const counts = | |
| 28 | - totals === null | |
| 29 | - ? [] | |
| 30 | - : [ | |
| 31 | - <Text wrap="truncate-end"> | |
| 32 | - <Text bold>{Layout.plural(totals.filesCount, 'file')}</Text> | |
| 33 | - {' changed '} | |
| 34 | - {diffStat(kit, totals.linesAdded, totals.linesRemoved)} | |
| 35 | - </Text>, | |
| 36 | - ] | |
| 37 | 27 | |
| 38 | 28 | return ( |
| 39 | 29 | <Box flexDirection="row"> |
| 40 | - {[...counts, <Box flexGrow={1} />, closeButton(kit)]} | |
| 30 | + {[ | |
| 31 | + ...(totals | |
| 32 | + ? [ | |
| 33 | + <Text wrap="truncate-end"> | |
| 34 | + <Text bold>{Layout.plural(totals.filesCount, 'file')}</Text> | |
| 35 | + {' changed '} | |
| 36 | + {diffStat(kit, totals.linesAdded, totals.linesRemoved)} | |
| 37 | + </Text>, | |
| 38 | + ] | |
| 39 | + : []), | |
| 40 | + <Box flexGrow={1} />, | |
| 41 | + closeButton(kit), | |
| 42 | + ]} | |
| 41 | 43 | </Box> |
| 42 | 44 | ) |
| 43 | 45 | } |