@golba98/codexa 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -18
- package/bin/codexa.js +62 -144
- package/package.json +4 -3
- package/src/app.tsx +533 -275
- package/src/commands/handler.ts +2 -2
- package/src/config/buildInfo.ts +2 -2
- package/src/config/layeredConfig.ts +1 -1
- package/src/config/runtimeConfig.ts +1 -1
- package/src/config/settings.ts +1 -1
- package/src/config/trustStore.ts +1 -1
- package/src/core/README.md +52 -0
- package/src/core/agent/loop.ts +282 -0
- package/src/core/agent/protocol.ts +211 -0
- package/src/core/agent/tools.ts +414 -0
- package/src/core/{codexExecArgs.ts → codex/codexExecArgs.ts} +2 -2
- package/src/core/{codexLaunch.ts → codex/codexLaunch.ts} +3 -3
- package/src/core/{codexPrompt.ts → codex/codexPrompt.ts} +3 -3
- package/src/core/debug/modelStateDebug.ts +34 -0
- package/src/core/executables/antigravityExecutable.ts +48 -0
- package/src/core/executables/executableResolver.ts +5 -1
- package/src/core/perf/renderDebug.ts +10 -6
- package/src/core/providerLauncher/registry.ts +30 -14
- package/src/core/providerLauncher/types.ts +11 -9
- package/src/core/providerLauncher/workspaceConfig.ts +41 -26
- package/src/core/providerRuntime/anthropic.ts +7 -1
- package/src/core/providerRuntime/antigravity.ts +305 -0
- package/src/core/providerRuntime/claudeCodeDiscovery.ts +268 -22
- package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
- package/src/core/providerRuntime/contextMetadata.ts +12 -24
- package/src/core/providerRuntime/local.ts +129 -51
- package/src/core/providerRuntime/models.ts +22 -11
- package/src/core/providerRuntime/registry.ts +58 -31
- package/src/core/providerRuntime/types.ts +19 -14
- package/src/core/providers/codexSubprocess.ts +2 -2
- package/src/core/providers/types.ts +1 -1
- package/src/core/{attachments.ts → shared/attachments.ts} +27 -4
- package/src/core/{cleanupFastFail.ts → shared/cleanupFastFail.ts} +1 -1
- package/src/core/{hollowResponseFormat.ts → shared/hollowResponseFormat.ts} +1 -1
- package/src/core/terminal/clearFrameBoundary.ts +814 -0
- package/src/core/terminal/frameLock.ts +109 -0
- package/src/core/terminal/inkRenderReset.ts +123 -0
- package/src/core/terminal/terminalControl.ts +22 -0
- package/src/core/terminal/terminalTitle.ts +16 -102
- package/src/core/{channel.ts → version/channel.ts} +1 -1
- package/src/core/{updateCheck.ts → version/updateCheck.ts} +10 -5
- package/src/core/{launchContext.ts → workspace/launchContext.ts} +9 -16
- package/src/core/{planStorage.ts → workspace/planStorage.ts} +2 -2
- package/src/core/{workspaceGuard.ts → workspace/workspaceGuard.ts} +97 -13
- package/src/headless/execRunner.ts +2 -2
- package/src/index.tsx +43 -89
- package/src/session/appSession.ts +10 -7
- package/src/session/chatLifecycle.ts +1 -1
- package/src/session/liveRenderScheduler.ts +1 -1
- package/src/session/types.ts +1 -1
- package/src/ui/AppShell.tsx +672 -706
- package/src/ui/AttachmentImportPanel.tsx +2 -2
- package/src/ui/BottomComposer.tsx +145 -144
- package/src/ui/ModelPickerScreen.tsx +216 -36
- package/src/ui/ModelReasoningPicker.tsx +1 -1
- package/src/ui/PlanReviewPanel.tsx +1 -1
- package/src/ui/ProviderPicker.tsx +735 -321
- package/src/ui/RuntimeStatusBar.tsx +108 -0
- package/src/ui/SelectionPanel.tsx +4 -0
- package/src/ui/SettingsPanel.tsx +1 -1
- package/src/ui/TextEntryPanel.tsx +1 -1
- package/src/ui/Timeline.tsx +1619 -1470
- package/src/ui/TopHeader.tsx +46 -30
- package/src/ui/TranscriptShell.tsx +322 -0
- package/src/ui/TurnGroup.tsx +2 -2
- package/src/ui/UpdateAvailableCard.tsx +3 -3
- package/src/ui/UpdatePromptPanel.tsx +16 -22
- package/src/ui/layout.ts +298 -24
- package/src/ui/layoutListWindow.ts +145 -0
- package/src/ui/logoVariants.ts +4 -8
- package/src/ui/runtimeDisplay.ts +15 -3
- package/src/ui/textLayout.ts +15 -4
- package/src/ui/timelineMeasure.ts +194 -122
- package/src/core/codex.ts +0 -124
- package/src/ui/StaticTranscriptItem.tsx +0 -56
- /package/src/core/{inputDebug.ts → debug/inputDebug.ts} +0 -0
- /package/src/core/{clipboard.ts → shared/clipboard.ts} +0 -0
- /package/src/core/{githubDiagnostics.ts → shared/githubDiagnostics.ts} +0 -0
- /package/src/core/{projectInstructions.ts → workspace/projectInstructions.ts} +0 -0
- /package/src/core/{workspaceActivity.ts → workspace/workspaceActivity.ts} +0 -0
- /package/src/core/{workspaceRoot.ts → workspace/workspaceRoot.ts} +0 -0
|
@@ -65,7 +65,7 @@ export function AttachmentImportPanel({
|
|
|
65
65
|
|
|
66
66
|
<Box
|
|
67
67
|
borderStyle="round"
|
|
68
|
-
borderColor={theme.
|
|
68
|
+
borderColor={theme.border}
|
|
69
69
|
paddingX={2}
|
|
70
70
|
paddingY={1}
|
|
71
71
|
marginTop={1}
|
|
@@ -90,7 +90,7 @@ export function AttachmentImportPanel({
|
|
|
90
90
|
)}
|
|
91
91
|
|
|
92
92
|
<Box marginTop={1}>
|
|
93
|
-
<Text color={theme.textDim}>
|
|
93
|
+
<Text color={theme.textDim}>Esc to close · Enter to confirm</Text>
|
|
94
94
|
</Box>
|
|
95
95
|
</Box>
|
|
96
96
|
</Box>
|
|
@@ -23,7 +23,7 @@ import { clampVisualText, getShellWidth, type Layout } from "./layout.js";
|
|
|
23
23
|
import { getTextWidth, splitTextAtColumn } from "./textLayout.js";
|
|
24
24
|
import { useThrottledValue } from "./useThrottledValue.js";
|
|
25
25
|
import { sanitizeTerminalOutput } from "../core/terminal/terminalSanitize.js";
|
|
26
|
-
import { getStdinDebugState, traceInputDebug } from "../core/inputDebug.js";
|
|
26
|
+
import { getStdinDebugState, traceInputDebug } from "../core/debug/inputDebug.js";
|
|
27
27
|
import * as renderDebug from "../core/perf/renderDebug.js";
|
|
28
28
|
import { AnimatedStatusText } from "./AnimatedStatusText.js";
|
|
29
29
|
import { isAnimatedBusyState } from "./busyStatusAnimation.js";
|
|
@@ -98,13 +98,13 @@ export function getTokenBarDisplay(tokensUsed: number, modelSpec: ModelSpec) {
|
|
|
98
98
|
interface BottomComposerProps {
|
|
99
99
|
layout: Layout;
|
|
100
100
|
uiState: UIState;
|
|
101
|
-
themeName?: string;
|
|
102
|
-
mode?: string;
|
|
101
|
+
themeName?: string;
|
|
102
|
+
mode?: string;
|
|
103
103
|
model?: string;
|
|
104
104
|
footerModelDisplay?: string;
|
|
105
105
|
reasoningLevel?: string;
|
|
106
106
|
contextDisplay?: string;
|
|
107
|
-
planMode?: boolean;
|
|
107
|
+
planMode?: boolean;
|
|
108
108
|
showBusyLoader?: boolean;
|
|
109
109
|
tokensUsed?: number;
|
|
110
110
|
modelSpec?: ModelSpec;
|
|
@@ -138,11 +138,11 @@ export interface BottomComposerMeasureParams {
|
|
|
138
138
|
mode?: string;
|
|
139
139
|
model?: string;
|
|
140
140
|
reasoningLevel?: string;
|
|
141
|
-
tokensUsed?: number;
|
|
142
|
-
modelSpec?: ModelSpec;
|
|
143
|
-
value: string;
|
|
144
|
-
cursor: number;
|
|
145
|
-
}
|
|
141
|
+
tokensUsed?: number;
|
|
142
|
+
modelSpec?: ModelSpec;
|
|
143
|
+
value: string;
|
|
144
|
+
cursor: number;
|
|
145
|
+
}
|
|
146
146
|
|
|
147
147
|
export interface CommandSuggestionState {
|
|
148
148
|
showSuggestions: boolean;
|
|
@@ -176,9 +176,9 @@ export function shouldRenderBusyFooter(layout: Layout, uiState: UIState): boolea
|
|
|
176
176
|
return false;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
-
export function getComposerToFooterGapRows(layout: Layout): number {
|
|
180
|
-
return
|
|
181
|
-
}
|
|
179
|
+
export function getComposerToFooterGapRows(layout: Layout): number {
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
182
|
|
|
183
183
|
export function getCommandSuggestionState({
|
|
184
184
|
value,
|
|
@@ -206,11 +206,11 @@ export function getCommandSuggestionState({
|
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
export function measureBottomComposerRows({
|
|
209
|
-
layout,
|
|
210
|
-
uiState,
|
|
211
|
-
value,
|
|
212
|
-
cursor,
|
|
213
|
-
}: BottomComposerMeasureParams): number {
|
|
209
|
+
layout,
|
|
210
|
+
uiState,
|
|
211
|
+
value,
|
|
212
|
+
cursor,
|
|
213
|
+
}: BottomComposerMeasureParams): number {
|
|
214
214
|
if (shouldRenderBusyFooter(layout, uiState)) {
|
|
215
215
|
return measureRunFooterRows();
|
|
216
216
|
}
|
|
@@ -236,24 +236,27 @@ export function measureBottomComposerRows({
|
|
|
236
236
|
inputLocked,
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
-
|
|
240
|
-
const
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
239
|
+
const bottomPadding = layout.mode === "compact" ? 0 : 1;
|
|
240
|
+
const footerGapRows = getComposerToFooterGapRows(layout);
|
|
241
|
+
const visibleStatusLine = getVisibleComposerStatusLine({
|
|
242
|
+
uiState,
|
|
243
|
+
value: normalizedValue,
|
|
244
|
+
allowCommands,
|
|
245
|
+
});
|
|
246
|
+
const transientStatusRows = visibleStatusLine.length > 0 ? 1 : 0;
|
|
247
|
+
|
|
248
|
+
const visiblePromptRows = inputLocked ? 1 : promptViewport.visibleRows.length;
|
|
249
|
+
|
|
247
250
|
return (
|
|
248
251
|
visiblePromptRows
|
|
249
|
-
+ 2
|
|
250
|
-
+ (commandSuggestionState.reserveSuggestionRow ? 1 : 0)
|
|
251
|
-
+ footerGapRows
|
|
252
|
-
+
|
|
253
|
-
+
|
|
254
|
-
+ bottomPadding
|
|
255
|
-
);
|
|
256
|
-
}
|
|
252
|
+
+ 2
|
|
253
|
+
+ (commandSuggestionState.reserveSuggestionRow ? 1 : 0)
|
|
254
|
+
+ footerGapRows
|
|
255
|
+
+ transientStatusRows
|
|
256
|
+
+ 1
|
|
257
|
+
+ bottomPadding
|
|
258
|
+
);
|
|
259
|
+
}
|
|
257
260
|
|
|
258
261
|
function getExternalCliLabel(providerId: string): string | null {
|
|
259
262
|
if (providerId === "google") return "Gemini CLI";
|
|
@@ -340,54 +343,54 @@ function getPlaceholder(persona: ComposerPersona): string {
|
|
|
340
343
|
|
|
341
344
|
// ─── Component ────────────────────────────────────────────────────────────────
|
|
342
345
|
|
|
343
|
-
function renderFooterRuntime(displayStr: string, theme: any) {
|
|
344
|
-
// e.g. "Claude Code CLI / Sonnet 4.6 (Low)"
|
|
345
|
-
const slashIndex = displayStr.indexOf("/");
|
|
346
|
-
if (slashIndex === -1) {
|
|
347
|
-
return <Text color={theme.model} wrap="truncate">{displayStr}</Text>;
|
|
348
|
-
}
|
|
349
|
-
const providerPart = displayStr.substring(0, slashIndex).trim();
|
|
350
|
-
let remaining = displayStr.substring(slashIndex + 1).trim();
|
|
351
|
-
|
|
352
|
-
const parenIndex = remaining.indexOf("(");
|
|
353
|
-
if (parenIndex === -1) {
|
|
354
|
-
return (
|
|
355
|
-
<Box flexDirection="row" overflow="hidden">
|
|
356
|
-
<Text color={theme.provider}>{providerPart}</Text>
|
|
357
|
-
<Text color={theme.textMuted}>{" / "}</Text>
|
|
358
|
-
<Text color={theme.model}>{remaining}</Text>
|
|
359
|
-
</Box>
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const modelPart = remaining.substring(0, parenIndex).trim();
|
|
364
|
-
let reasoningPart = remaining.substring(parenIndex + 1).trim();
|
|
365
|
-
if (reasoningPart.endsWith(")")) {
|
|
366
|
-
reasoningPart = reasoningPart.substring(0, reasoningPart.length - 1).trim();
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return (
|
|
370
|
-
<Box flexDirection="row" overflow="hidden">
|
|
371
|
-
<Text color={theme.provider}>{providerPart}</Text>
|
|
372
|
-
<Text color={theme.textMuted}>{" / "}</Text>
|
|
373
|
-
<Text color={theme.model}>{modelPart}</Text>
|
|
374
|
-
<Text color={theme.textMuted}>{" ("}</Text>
|
|
375
|
-
<Text color={theme.accentMuted}>{reasoningPart}</Text>
|
|
376
|
-
<Text color={theme.textMuted}>{")"}</Text>
|
|
377
|
-
</Box>
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
|
-
|
|
346
|
+
function renderFooterRuntime(displayStr: string, theme: any) {
|
|
347
|
+
// e.g. "Claude Code CLI / Sonnet 4.6 (Low)"
|
|
348
|
+
const slashIndex = displayStr.indexOf("/");
|
|
349
|
+
if (slashIndex === -1) {
|
|
350
|
+
return <Text color={theme.model} wrap="truncate">{displayStr}</Text>;
|
|
351
|
+
}
|
|
352
|
+
const providerPart = displayStr.substring(0, slashIndex).trim();
|
|
353
|
+
let remaining = displayStr.substring(slashIndex + 1).trim();
|
|
354
|
+
|
|
355
|
+
const parenIndex = remaining.indexOf("(");
|
|
356
|
+
if (parenIndex === -1) {
|
|
357
|
+
return (
|
|
358
|
+
<Box flexDirection="row" overflow="hidden">
|
|
359
|
+
<Text color={theme.provider}>{providerPart}</Text>
|
|
360
|
+
<Text color={theme.textMuted}>{" / "}</Text>
|
|
361
|
+
<Text color={theme.model}>{remaining}</Text>
|
|
362
|
+
</Box>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const modelPart = remaining.substring(0, parenIndex).trim();
|
|
367
|
+
let reasoningPart = remaining.substring(parenIndex + 1).trim();
|
|
368
|
+
if (reasoningPart.endsWith(")")) {
|
|
369
|
+
reasoningPart = reasoningPart.substring(0, reasoningPart.length - 1).trim();
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return (
|
|
373
|
+
<Box flexDirection="row" overflow="hidden">
|
|
374
|
+
<Text color={theme.provider}>{providerPart}</Text>
|
|
375
|
+
<Text color={theme.textMuted}>{" / "}</Text>
|
|
376
|
+
<Text color={theme.model}>{modelPart}</Text>
|
|
377
|
+
<Text color={theme.textMuted}>{" ("}</Text>
|
|
378
|
+
<Text color={theme.accentMuted}>{reasoningPart}</Text>
|
|
379
|
+
<Text color={theme.textMuted}>{")"}</Text>
|
|
380
|
+
</Box>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
381
384
|
export function BottomComposer({
|
|
382
385
|
layout,
|
|
383
386
|
uiState,
|
|
384
|
-
themeName = "purple",
|
|
385
|
-
mode = "",
|
|
387
|
+
themeName = "purple",
|
|
388
|
+
mode = "",
|
|
386
389
|
model = "",
|
|
387
390
|
footerModelDisplay,
|
|
388
391
|
reasoningLevel = "",
|
|
389
392
|
contextDisplay,
|
|
390
|
-
planMode = false,
|
|
393
|
+
planMode = false,
|
|
391
394
|
showBusyLoader = true,
|
|
392
395
|
tokensUsed = 0,
|
|
393
396
|
modelSpec = FALLBACK_MODEL_SPEC,
|
|
@@ -564,9 +567,10 @@ export function BottomComposer({
|
|
|
564
567
|
.map((suggestion, index) => `${index === selectedIndex ? "›" : "·"} ${suggestion.cmd}`)
|
|
565
568
|
.join(" ");
|
|
566
569
|
|
|
567
|
-
const rawStatusLine = getVisibleComposerStatusLine({ uiState, value, allowCommands, activeProviderId, runElapsedSeconds, externalCliStatus });
|
|
568
|
-
const showStatusLine = rawStatusLine.length > 0;
|
|
569
|
-
const
|
|
570
|
+
const rawStatusLine = getVisibleComposerStatusLine({ uiState, value, allowCommands, activeProviderId, runElapsedSeconds, externalCliStatus });
|
|
571
|
+
const showStatusLine = rawStatusLine.length > 0;
|
|
572
|
+
const showTransientStatusRow = showStatusLine || inputLocked || !!selectionProfile;
|
|
573
|
+
const footerGapRows = getComposerToFooterGapRows(layout);
|
|
570
574
|
|
|
571
575
|
const promptViewport = useMemo(
|
|
572
576
|
() => createInputViewport({
|
|
@@ -849,9 +853,9 @@ export function BottomComposer({
|
|
|
849
853
|
}
|
|
850
854
|
}, { isActive: isFocused });
|
|
851
855
|
|
|
852
|
-
const tokenDisplay = getTokenBarDisplay(tokensUsed, modelSpec);
|
|
853
|
-
const reasoningSuffix = reasoningLevel ? ` (${reasoningLevel})` : "";
|
|
854
|
-
const footerRuntimeDisplay = footerModelDisplay ?? `${model}${reasoningSuffix}`;
|
|
856
|
+
const tokenDisplay = getTokenBarDisplay(tokensUsed, modelSpec);
|
|
857
|
+
const reasoningSuffix = reasoningLevel ? ` (${reasoningLevel})` : "";
|
|
858
|
+
const footerRuntimeDisplay = footerModelDisplay ?? `${model}${reasoningSuffix}`;
|
|
855
859
|
const isAnswerMode = persona === "answer";
|
|
856
860
|
const showBusyFooter = shouldRenderBusyFooter(layout, uiState);
|
|
857
861
|
const promptPrefixColor = inputLocked ? theme.textDim : theme.text;
|
|
@@ -904,11 +908,11 @@ export function BottomComposer({
|
|
|
904
908
|
return <MemoizedRunFooter uiState={uiState} showBusyLoader={showBusyLoader} onCancel={onCancel} onQuit={onQuit} />;
|
|
905
909
|
}
|
|
906
910
|
|
|
907
|
-
return (
|
|
908
|
-
<Box flexDirection="column" paddingBottom={layoutMode === "
|
|
909
|
-
{isAnswerMode ? (
|
|
910
|
-
// Answer mode: Highlighted prompt
|
|
911
|
-
<Box
|
|
911
|
+
return (
|
|
912
|
+
<Box flexDirection="column" paddingBottom={layoutMode === "compact" ? 0 : 1} width="100%">
|
|
913
|
+
{isAnswerMode ? (
|
|
914
|
+
// Answer mode: Highlighted prompt
|
|
915
|
+
<Box
|
|
912
916
|
flexDirection="column"
|
|
913
917
|
width="100%"
|
|
914
918
|
paddingX={1}
|
|
@@ -918,47 +922,17 @@ export function BottomComposer({
|
|
|
918
922
|
>
|
|
919
923
|
{promptLine}
|
|
920
924
|
</Box>
|
|
921
|
-
) : (
|
|
922
|
-
// Normal mode: clean prompt in rounded border
|
|
923
|
-
<Box
|
|
924
|
-
flexDirection="column"
|
|
925
|
+
) : (
|
|
926
|
+
// Normal mode: clean prompt in rounded border
|
|
927
|
+
<Box
|
|
928
|
+
flexDirection="column"
|
|
925
929
|
width="100%"
|
|
926
930
|
paddingX={1}
|
|
927
931
|
paddingY={0}
|
|
928
932
|
borderStyle="round"
|
|
929
933
|
borderColor={theme.border}
|
|
930
934
|
>
|
|
931
|
-
{promptLine}
|
|
932
|
-
</Box>
|
|
933
|
-
)}
|
|
934
|
-
|
|
935
|
-
{layoutMode !== "micro" && (
|
|
936
|
-
<Box paddingLeft={1} paddingRight={1} marginTop={0} width="100%" justifyContent="space-between">
|
|
937
|
-
<Box flexGrow={1} flexShrink={1} overflow="hidden">
|
|
938
|
-
{renderFooterRuntime(footerRuntimeDisplay, theme)}
|
|
939
|
-
</Box>
|
|
940
|
-
<Box flexShrink={0}>
|
|
941
|
-
{contextDisplay ? (
|
|
942
|
-
<Box flexDirection="row">
|
|
943
|
-
<Text color={theme.textMuted}>Context: </Text>
|
|
944
|
-
<Text color={theme.context}>{contextDisplay}</Text>
|
|
945
|
-
</Box>
|
|
946
|
-
) : tokenDisplay.hasKnownLimit ? (
|
|
947
|
-
<Box flexDirection="row">
|
|
948
|
-
<Text color={theme.textMuted}>Context: </Text>
|
|
949
|
-
<Text color={theme.context}>{tokenDisplay.usedText}</Text>
|
|
950
|
-
<Text color={theme.textDim}>
|
|
951
|
-
{" / "}{tokenDisplay.limitText}
|
|
952
|
-
{tokenDisplay.percentage !== null ? ` · ${tokenDisplay.isEstimatedLimit ? "~" : ""}${tokenDisplay.percentage}%` : ""}
|
|
953
|
-
</Text>
|
|
954
|
-
</Box>
|
|
955
|
-
) : (
|
|
956
|
-
<Box flexDirection="row">
|
|
957
|
-
<Text color={theme.textMuted}>Context: </Text>
|
|
958
|
-
<Text color={theme.textDim}>Unknown</Text>
|
|
959
|
-
</Box>
|
|
960
|
-
)}
|
|
961
|
-
</Box>
|
|
935
|
+
{promptLine}
|
|
962
936
|
</Box>
|
|
963
937
|
)}
|
|
964
938
|
|
|
@@ -972,22 +946,21 @@ export function BottomComposer({
|
|
|
972
946
|
<Box height={footerGapRows} />
|
|
973
947
|
)}
|
|
974
948
|
|
|
975
|
-
{
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
{!!getExternalCliLabel(activeProviderId ?? "") && uiState.kind === "THINKING" && (
|
|
949
|
+
{showTransientStatusRow && (
|
|
950
|
+
<Box paddingX={1} marginTop={0} height={1} width="100%" justifyContent="space-between" overflow="hidden">
|
|
951
|
+
<>
|
|
952
|
+
<Box flexShrink={1} flexGrow={1} overflow="hidden" flexDirection="row">
|
|
953
|
+
{!!getExternalCliLabel(activeProviderId ?? "") && uiState.kind === "THINKING" && (
|
|
981
954
|
<>
|
|
982
955
|
<Spinner color={theme.accent} />
|
|
983
956
|
<Text>{" "}</Text>
|
|
984
957
|
</>
|
|
985
958
|
)}
|
|
986
|
-
<AnimatedStatusText
|
|
987
|
-
baseText={rawStatusLine}
|
|
988
|
-
isActive={!getExternalCliLabel(activeProviderId ?? "") && inputLocked && showBusyLoader}
|
|
989
|
-
isError={persona === "error"}
|
|
990
|
-
/>
|
|
959
|
+
<AnimatedStatusText
|
|
960
|
+
baseText={rawStatusLine}
|
|
961
|
+
isActive={!getExternalCliLabel(activeProviderId ?? "") && inputLocked && showBusyLoader}
|
|
962
|
+
isError={persona === "error"}
|
|
963
|
+
/>
|
|
991
964
|
</Box>
|
|
992
965
|
{inputLocked && (
|
|
993
966
|
<Box flexShrink={0}>
|
|
@@ -997,15 +970,43 @@ export function BottomComposer({
|
|
|
997
970
|
{!inputLocked && selectionProfile && (
|
|
998
971
|
<Box flexShrink={0}>
|
|
999
972
|
<Text color={theme.textDim}>{selectionProfile.shortHint}</Text>
|
|
1000
|
-
</Box>
|
|
1001
|
-
)}
|
|
1002
|
-
</>
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
973
|
+
</Box>
|
|
974
|
+
)}
|
|
975
|
+
</>
|
|
976
|
+
</Box>
|
|
977
|
+
)}
|
|
978
|
+
|
|
979
|
+
<Box paddingLeft={1} paddingRight={1} marginTop={0} width="100%" justifyContent="space-between">
|
|
980
|
+
<Box flexGrow={1} flexShrink={1} overflow="hidden">
|
|
981
|
+
{renderFooterRuntime(footerRuntimeDisplay, theme)}
|
|
982
|
+
</Box>
|
|
983
|
+
<Box flexShrink={0}>
|
|
984
|
+
{contextDisplay ? (
|
|
985
|
+
<Box flexDirection="row">
|
|
986
|
+
<Text color={theme.textMuted}>Context: </Text>
|
|
987
|
+
<Text color={theme.context}>{contextDisplay}</Text>
|
|
988
|
+
</Box>
|
|
989
|
+
) : tokenDisplay.hasKnownLimit ? (
|
|
990
|
+
<Box flexDirection="row">
|
|
991
|
+
<Text color={theme.textMuted}>Context: </Text>
|
|
992
|
+
<Text color={theme.context}>{tokenDisplay.usedText}</Text>
|
|
993
|
+
<Text color={theme.textDim}>
|
|
994
|
+
{" / "}{tokenDisplay.limitText}
|
|
995
|
+
{tokenDisplay.percentage !== null ? ` · ${tokenDisplay.isEstimatedLimit ? "~" : ""}${tokenDisplay.percentage}%` : ""}
|
|
996
|
+
</Text>
|
|
997
|
+
</Box>
|
|
998
|
+
) : (
|
|
999
|
+
<Box flexDirection="row">
|
|
1000
|
+
<Text color={theme.textMuted}>Context: </Text>
|
|
1001
|
+
<Text color={theme.textDim}>Unknown</Text>
|
|
1002
|
+
</Box>
|
|
1003
|
+
)}
|
|
1004
|
+
</Box>
|
|
1005
|
+
</Box>
|
|
1006
|
+
|
|
1006
1007
|
</Box>
|
|
1007
1008
|
);
|
|
1008
|
-
}
|
|
1009
|
+
}
|
|
1009
1010
|
|
|
1010
1011
|
// Helper to extract the relevant uiState kind for comparison
|
|
1011
1012
|
function getUiStateKey(uiState: UIState): string {
|
|
@@ -1037,12 +1038,12 @@ export const MemoizedBottomComposer = memo(BottomComposer, (prev, next) => {
|
|
|
1037
1038
|
if (prev.value !== next.value) return false;
|
|
1038
1039
|
if (prev.cursor !== next.cursor) return false;
|
|
1039
1040
|
|
|
1040
|
-
// Re-render if display props change
|
|
1041
|
-
if (prev.mode !== next.mode) return false;
|
|
1042
|
-
if (prev.model !== next.model) return false;
|
|
1043
|
-
if (prev.footerModelDisplay !== next.footerModelDisplay) return false;
|
|
1044
|
-
if (prev.reasoningLevel !== next.reasoningLevel) return false;
|
|
1045
|
-
if (prev.contextDisplay !== next.contextDisplay) return false;
|
|
1041
|
+
// Re-render if display props change
|
|
1042
|
+
if (prev.mode !== next.mode) return false;
|
|
1043
|
+
if (prev.model !== next.model) return false;
|
|
1044
|
+
if (prev.footerModelDisplay !== next.footerModelDisplay) return false;
|
|
1045
|
+
if (prev.reasoningLevel !== next.reasoningLevel) return false;
|
|
1046
|
+
if (prev.contextDisplay !== next.contextDisplay) return false;
|
|
1046
1047
|
if (prev.planMode !== next.planMode) return false;
|
|
1047
1048
|
if (prev.showBusyLoader !== next.showBusyLoader) return false;
|
|
1048
1049
|
if (prev.tokensUsed !== next.tokensUsed) return false;
|