praxis-agent 0.43.0 → 0.43.1
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 +24 -37
- package/dist/application/native-session-projection.d.ts +4 -0
- package/dist/application/native-session-projection.js +135 -0
- package/dist/application/session-service.d.ts +0 -1
- package/dist/application/session-service.js +9 -170
- package/dist/cli/interactive.js +112 -156
- package/dist/cli/tui/ansi-frame-renderer.d.ts +2 -0
- package/dist/cli/tui/ansi-frame-renderer.js +109 -21
- package/dist/cli/tui/ansi-surface.d.ts +6 -22
- package/dist/cli/tui/ansi-surface.js +6 -126
- package/dist/cli/tui/ansi-theme.d.ts +3 -2
- package/dist/cli/tui/ansi-theme.js +22 -1
- package/dist/cli/tui/quiet-choice-rows.d.ts +26 -0
- package/dist/cli/tui/quiet-choice-rows.js +302 -0
- package/dist/cli/tui/quiet-frame-adapter.d.ts +18 -0
- package/dist/cli/tui/quiet-frame-adapter.js +90 -0
- package/dist/cli/tui/quiet-frame.d.ts +43 -0
- package/dist/cli/tui/quiet-frame.js +346 -0
- package/dist/cli/tui/quiet-operational-rows.d.ts +17 -0
- package/dist/cli/tui/quiet-operational-rows.js +413 -0
- package/dist/cli/tui/quiet-screen-projector.d.ts +25 -0
- package/dist/cli/tui/quiet-screen-projector.js +110 -0
- package/dist/cli/tui/quiet-settings-rows.d.ts +17 -0
- package/dist/cli/tui/quiet-settings-rows.js +490 -0
- package/dist/cli/tui/status-line.d.ts +15 -9
- package/dist/cli/tui/status-line.js +15 -6
- package/dist/cli/tui/theme.js +20 -20
- package/dist/cli/tui/transcript-viewport.d.ts +5 -0
- package/dist/cli/tui/transcript-viewport.js +213 -78
- package/dist/cli/tui/tui-row-ir.js +4 -4
- package/dist/persistence/in-memory-transcript-store.js +1 -7
- package/dist/persistence/native-transcript-store.d.ts +0 -9
- package/dist/persistence/native-transcript-store.js +4 -38
- package/package.json +3 -2
package/dist/cli/interactive.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { randomUUID } from 'node:crypto';
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { join, resolve } from 'node:path';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { useEffect, useMemo, useReducer, useRef, useState
|
|
9
|
-
import {
|
|
8
|
+
import { useEffect, useMemo, useReducer, useRef, useState } from 'react';
|
|
9
|
+
import { render, useApp, useInput } from 'ink';
|
|
10
10
|
import { permissionRuleValueToString } from '../permissions/permission-updates.js';
|
|
11
11
|
import { resolveDataPlane, resolveDataPlaneRoot, } from '../persistence/data-plane.js';
|
|
12
12
|
import { AgentRunCancelledError } from '../core/runtime.js';
|
|
13
13
|
import { claudePermissionActionKey, claudePermissionRuleMatches, } from '../permissions/claude-permission-resolver.js';
|
|
14
14
|
import { redactSensitiveText, sensitiveEnvironmentValues, } from '../platform/sensitive-data.js';
|
|
15
|
-
import { CommandPalette, BtwPanel, Composer, DiffDashboard, DialogFrame, LeafSurface, ExternalEditorWait, HelpMenu, HookDashboard, ListDashboard, MemoryDashboard, MentionPicker, ModelMenu, EffortMenu, SessionPicker, ThemePicker, CustomThemeEditor, SessionIdentity, Transcript, WelcomePanel, } from './tui/claude-style.js';
|
|
16
15
|
import { projectTuiEffortSurface, projectTuiModelSurface, } from './tui/model-effort-surface-model.js';
|
|
17
16
|
import { projectTuiLeafSurface, } from './tui/leaf-surface-model.js';
|
|
18
17
|
import { projectTuiThemeSurface, } from './tui/theme-surface-model.js';
|
|
@@ -24,12 +23,14 @@ import { tuiInkRenderOptions, useTuiPresentationEnvironment, } from './tui/prese
|
|
|
24
23
|
import { StreamingFrameBuffer } from './tui/streaming-frame-buffer.js';
|
|
25
24
|
import { createTuiStoreState, reduceTuiStore } from './tui/kernel/tui-store.js';
|
|
26
25
|
import { TuiEffectRunner } from './tui/kernel/tui-effect-runner.js';
|
|
27
|
-
import { createClaudeStatusLineInput,
|
|
26
|
+
import { createClaudeStatusLineInput, formatClaudeStatusLineState, useClaudeStatusLine, } from './tui/status-line.js';
|
|
28
27
|
import { createTuiAppendHistoryChange } from './tui/transcript-window-model.js';
|
|
29
28
|
import { createTuiHistoryChange, resolveTuiRenderer, } from './tui/tui-view-model.js';
|
|
30
29
|
import { projectTuiScreen, } from './tui/tui-screen-model.js';
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
30
|
+
import { TuiAnsiSurface } from './tui/ansi-surface.js';
|
|
31
|
+
import { QuietInkFrame } from './tui/quiet-frame-adapter.js';
|
|
32
|
+
import { projectQuietScreenFrame, } from './tui/quiet-screen-projector.js';
|
|
33
|
+
import { projectTuiHelpSurface } from './tui/help-surface-model.js';
|
|
33
34
|
import { projectTuiHooksSurface, } from './tui/hooks-surface-model.js';
|
|
34
35
|
import { loadGitDiff } from './tui/git-diff.js';
|
|
35
36
|
import { projectTuiDiffSurface, } from './tui/diff-surface-model.js';
|
|
@@ -56,34 +57,30 @@ import { defaultTuiKeybindings, ensureTuiKeybindingsFile, hasTuiKeybindingPrefix
|
|
|
56
57
|
import { completeTuiWorkspaceDirectory, resolveTuiWorkspaceDirectory, } from './tui/workspace-directories.js';
|
|
57
58
|
import { conversationExportPath, conversationExportText, defaultConversationExportFilename, writeConversationExport, } from './tui/conversation-export.js';
|
|
58
59
|
import { loadTuiThemeSettings, saveTuiThemeSettings, themeSettingsWithCustomTheme, } from './tui/theme-settings.js';
|
|
59
|
-
import { DEFAULT_TUI_THEME_SETTINGS, TUI_THEMES, TuiThemeProvider,
|
|
60
|
+
import { DEFAULT_TUI_THEME_SETTINGS, TUI_THEMES, TuiThemeProvider, } from './tui/theme.js';
|
|
60
61
|
import { CUSTOM_THEME_TOKENS, createTuiCustomTheme, deleteTuiCustomTheme, loadTuiCustomThemes, updateTuiCustomTheme, } from './tui/custom-themes.js';
|
|
61
62
|
import { setupTuiTerminal, terminalSetupTuiSlashCommand, } from './tui/terminal-setup.js';
|
|
62
|
-
import { commitElicitationText, createTuiElicitationForm, elicitationFormIsValid, elicitationTextValue, expandElicitationOptions, focusedElicitationField,
|
|
63
|
+
import { commitElicitationText, createTuiElicitationForm, elicitationFormIsValid, elicitationTextValue, expandElicitationOptions, focusedElicitationField, moveElicitationFocus, moveElicitationOption, selectElicitationOption, toggleElicitationBoolean, typeaheadElicitationOption, unsetElicitationField, validateTuiElicitationForm, } from './tui/mcp-elicitation.js';
|
|
63
64
|
import { projectTuiElicitationSurface, } from './tui/mcp-elicitation-surface-model.js';
|
|
64
65
|
import { openTuiUrl } from './tui/open-url.js';
|
|
65
66
|
import { projectTuiToolPermission } from './tui/tool-permission.js';
|
|
66
67
|
import { projectTuiPermissionSurface, } from './tui/permission-surface-model.js';
|
|
67
|
-
import { PermissionSurface } from './tui/permission-surface.js';
|
|
68
68
|
import { projectTuiDecisionSurface, } from './tui/decision-surface-model.js';
|
|
69
|
-
import {
|
|
70
|
-
import { ConfigDashboard, projectConfigRows } from './tui/config-dashboard.js';
|
|
69
|
+
import { projectConfigRows } from './tui/config-dashboard.js';
|
|
71
70
|
import { projectTuiConfigSurface, } from './tui/config-surface-model.js';
|
|
72
|
-
import { DoctorDashboard } from './tui/doctor-dashboard.js';
|
|
73
71
|
import { projectTuiDoctorSurface, } from './tui/doctor-surface-model.js';
|
|
74
72
|
import { projectTuiMemorySurface, } from './tui/memory-surface-model.js';
|
|
75
|
-
import {
|
|
73
|
+
import { tuiSandboxTabs } from './tui/sandbox-dashboard.js';
|
|
76
74
|
import { projectTuiSandboxSurface, } from './tui/sandbox-surface-model.js';
|
|
77
75
|
import { createTuiSandboxStore, } from './tui/sandbox-settings.js';
|
|
78
76
|
import { loadConfigSettings, saveConfigSetting, configSettingDefinition, resolveConfigSettingsLocation, } from './tui/config-settings.js';
|
|
79
77
|
import { projectRuntimeSettings, loadRuntimeSettings, } from './tui/runtime-settings.js';
|
|
80
|
-
import { autoUpdateTarget, copyCandidates, externalEditorInitialContent,
|
|
78
|
+
import { autoUpdateTarget, copyCandidates, externalEditorInitialContent, questionTimeoutMilliseconds, sessionRecap, spinnerTip, shouldShowCopyPicker, } from './tui/runtime-interactions.js';
|
|
81
79
|
import { notifyTerminal, } from './tui/terminal-notifications.js';
|
|
82
|
-
import { McpPanel } from './tui/mcp-panel.js';
|
|
83
80
|
import { McpPanelController, mcpRuntimeFromSession, } from './tui/mcp-panel-controller.js';
|
|
84
81
|
import { initialTuiMcpPanelState, } from './tui/mcp-panel-projector.js';
|
|
85
82
|
import { projectTuiMcpSurface, } from './tui/mcp-surface-model.js';
|
|
86
|
-
import {
|
|
83
|
+
import { initialTuiTaskPanelState, projectTuiTasks, reconcileTuiTaskPanelState, updateTuiTaskPanelState, } from './tui/task-panel.js';
|
|
87
84
|
import { projectTuiTaskSurface, } from './tui/task-surface-model.js';
|
|
88
85
|
import { ClaudeMcpManagement } from '../mcp/claude-mcp-management.js';
|
|
89
86
|
const EMPTY_SLASH_COMMANDS = [];
|
|
@@ -241,25 +238,6 @@ const HIDDEN_TUI_SLASH_COMMANDS = new Set([
|
|
|
241
238
|
'update',
|
|
242
239
|
'usage',
|
|
243
240
|
]);
|
|
244
|
-
function RewindWarning() {
|
|
245
|
-
const theme = useTuiTheme();
|
|
246
|
-
return (_jsx(Text, { ...theme.text.warning, children: "\u26A0 Rewinding does not affect files edited manually or via bash." }));
|
|
247
|
-
}
|
|
248
|
-
function ExitWarning() {
|
|
249
|
-
const theme = useTuiTheme();
|
|
250
|
-
return _jsx(Text, { ...theme.text.warning, children: "Press Ctrl-C again to exit" });
|
|
251
|
-
}
|
|
252
|
-
function DecisionOption({ selected, screenReader, children, selectedPrefix = '❯ ', unselectedPrefix = ' ', }) {
|
|
253
|
-
const theme = useTuiTheme();
|
|
254
|
-
const accessible = screenReader || theme.noColor;
|
|
255
|
-
return (_jsxs(Text, { ...(selected ? theme.text.selectedRow : {}), children: [selected
|
|
256
|
-
? accessible
|
|
257
|
-
? 'Selected: '
|
|
258
|
-
: selectedPrefix
|
|
259
|
-
: accessible
|
|
260
|
-
? ''
|
|
261
|
-
: unselectedPrefix, children] }));
|
|
262
|
-
}
|
|
263
241
|
/** Advances the React-owned transcript plus its exact local mutation fact. */
|
|
264
242
|
export function advanceInteractiveHistoryState(current, items, changedFrom) {
|
|
265
243
|
if (!Number.isInteger(changedFrom) ||
|
|
@@ -359,7 +337,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
359
337
|
: { viewportOverride: { columns: terminalWidth } }),
|
|
360
338
|
});
|
|
361
339
|
const width = presentation.viewport.columns;
|
|
362
|
-
const rows = presentation.viewport.rows;
|
|
363
340
|
const fixedViewport = presentation.fixedViewport;
|
|
364
341
|
const runtimeSettingsRef = useRef(runtimeSettings);
|
|
365
342
|
runtimeSettingsRef.current = runtimeSettings;
|
|
@@ -386,9 +363,9 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
386
363
|
const statusLineSessionId = useRef(resume?.sessionId ?? randomUUID());
|
|
387
364
|
const sessionIdRef = useRef(resume?.sessionId ?? null);
|
|
388
365
|
sessionIdRef.current = sessionId;
|
|
389
|
-
const [
|
|
366
|
+
const [, setSessionColor] = useState(initialSessionColor);
|
|
390
367
|
const [sessionName, setSessionName] = useState(null);
|
|
391
|
-
const [
|
|
368
|
+
const [, setActiveSessionSummary] = useState(() => resume?.sessionId
|
|
392
369
|
? initialSessions.find((session) => session.sessionId === resume.sessionId)
|
|
393
370
|
: undefined);
|
|
394
371
|
const [pendingFork, setPendingFork] = useState(resume?.forkSession === true);
|
|
@@ -406,7 +383,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
406
383
|
const themeEffectRunnerRef = useRef(null);
|
|
407
384
|
if (themeEffectRunnerRef.current === null)
|
|
408
385
|
themeEffectRunnerRef.current = new TuiEffectRunner();
|
|
409
|
-
const [
|
|
386
|
+
const [, setClipboardBusy] = useState(false);
|
|
410
387
|
const stashedInputRef = useRef('');
|
|
411
388
|
const lastEscapeAtRef = useRef(0);
|
|
412
389
|
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
|
|
@@ -476,7 +453,7 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
476
453
|
});
|
|
477
454
|
}
|
|
478
455
|
const [thinkingExpanded, setThinkingExpanded] = useState(false);
|
|
479
|
-
const [
|
|
456
|
+
const [, setTurnDuration] = useState();
|
|
480
457
|
const [usage, setUsage] = useState();
|
|
481
458
|
const [costUsd, setCostUsd] = useState();
|
|
482
459
|
const [contextWindowTokens, setContextWindowTokens] = useState(display.contextWindowTokens);
|
|
@@ -646,17 +623,6 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
646
623
|
query: commandQuery ?? '',
|
|
647
624
|
selectedIndex: commandSelection,
|
|
648
625
|
}), [allSlashCommands, commandQuery, commandSelection]);
|
|
649
|
-
const commandArgumentHint = useMemo(() => {
|
|
650
|
-
if (shellMode || !input.startsWith('/'))
|
|
651
|
-
return undefined;
|
|
652
|
-
if (inputCursor !== input.length)
|
|
653
|
-
return undefined;
|
|
654
|
-
const match = /^\/(\S+) $/u.exec(input);
|
|
655
|
-
if (!match?.[1])
|
|
656
|
-
return undefined;
|
|
657
|
-
const command = allSlashCommands.find((candidate) => candidate.name === match[1]);
|
|
658
|
-
return command?.argumentHint;
|
|
659
|
-
}, [allSlashCommands, input, inputCursor, shellMode]);
|
|
660
626
|
const commandPaletteVisible = !busy &&
|
|
661
627
|
!permission &&
|
|
662
628
|
!planApproval &&
|
|
@@ -1089,7 +1055,9 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
1089
1055
|
return null;
|
|
1090
1056
|
}, [menu, input, compactProgress]);
|
|
1091
1057
|
const secondarySurface = menu === null
|
|
1092
|
-
?
|
|
1058
|
+
? shortcutsVisible
|
|
1059
|
+
? shortcutHelpSurface
|
|
1060
|
+
: undefined
|
|
1093
1061
|
: menu.kind === 'help'
|
|
1094
1062
|
? (helpSurface ?? undefined)
|
|
1095
1063
|
: menu.kind === 'diff'
|
|
@@ -1216,23 +1184,11 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
1216
1184
|
sessionId,
|
|
1217
1185
|
screenSurfaces,
|
|
1218
1186
|
]);
|
|
1219
|
-
const ansiActive = ansiRequested
|
|
1187
|
+
const ansiActive = ansiRequested;
|
|
1220
1188
|
useEffect(() => {
|
|
1221
1189
|
previousTuiScreenRef.current = screen;
|
|
1222
1190
|
}, [screen]);
|
|
1223
1191
|
const conversationScreen = screen.body.kind === 'conversation' ? screen.body : undefined;
|
|
1224
|
-
const selectedPriority = conversationScreen?.foreground.kind === 'priority'
|
|
1225
|
-
? conversationScreen.foreground.surface
|
|
1226
|
-
: undefined;
|
|
1227
|
-
const selectedSecondarySurface = conversationScreen?.foreground.kind === 'secondary'
|
|
1228
|
-
? (conversationScreen.foreground.surface ?? null)
|
|
1229
|
-
: null;
|
|
1230
|
-
const selectedOverlays = conversationScreen?.foreground.kind === 'compose'
|
|
1231
|
-
? conversationScreen.foreground.overlays
|
|
1232
|
-
: [];
|
|
1233
|
-
const selectedCommandPalette = selectedOverlays.find((overlay) => overlay.kind === 'command-palette');
|
|
1234
|
-
const selectedFilePicker = selectedOverlays.find((overlay) => overlay.kind === 'mention-picker');
|
|
1235
|
-
const selectedExitConfirmation = selectedOverlays.find((overlay) => overlay.kind === 'exit-confirmation');
|
|
1236
1192
|
const transcriptPageRows = conversationScreen?.transcript.pageRows ?? 2;
|
|
1237
1193
|
const maxTranscriptScrollOffset = conversationScreen?.transcript.maxScrollOffset ?? 0;
|
|
1238
1194
|
const permissionOptions = useMemo(() => [
|
|
@@ -6501,94 +6457,93 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
|
|
|
6501
6457
|
return;
|
|
6502
6458
|
}
|
|
6503
6459
|
});
|
|
6504
|
-
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
].join(':'), width: width, ...(settingSources === undefined ? {} : { settingSources }) })] }))] })) }) }));
|
|
6460
|
+
const statusLineInput = useMemo(() => createClaudeStatusLineInput({
|
|
6461
|
+
configRoot: keybindingsRoot,
|
|
6462
|
+
cwd: runtimeCwd,
|
|
6463
|
+
projectDir: display.cwd,
|
|
6464
|
+
sessionId: sessionId ?? statusLineSessionId.current,
|
|
6465
|
+
sessionName,
|
|
6466
|
+
...(runtimeDisplay.model === undefined
|
|
6467
|
+
? {}
|
|
6468
|
+
: { model: runtimeDisplay.model }),
|
|
6469
|
+
version: runtimeDisplay.version,
|
|
6470
|
+
outputStyle: runtimeSettings.outputStyle,
|
|
6471
|
+
permissionMode: runtimePreferences.permissionMode,
|
|
6472
|
+
additionalDirectories: runtimePreferences.additionalDirectories,
|
|
6473
|
+
dataPlane,
|
|
6474
|
+
...(usage === undefined ? {} : { usage }),
|
|
6475
|
+
...(costUsd === undefined ? {} : { costUsd }),
|
|
6476
|
+
...(runtimeDisplay.contextWindowTokens === undefined
|
|
6477
|
+
? {}
|
|
6478
|
+
: { contextWindowTokens: runtimeDisplay.contextWindowTokens }),
|
|
6479
|
+
...(runtimeSettings.editor === 'vim'
|
|
6480
|
+
? { vimMode: vimInsertMode ? 'INSERT' : 'NORMAL' }
|
|
6481
|
+
: {}),
|
|
6482
|
+
}), [
|
|
6483
|
+
keybindingsRoot,
|
|
6484
|
+
runtimeCwd,
|
|
6485
|
+
display.cwd,
|
|
6486
|
+
sessionId,
|
|
6487
|
+
sessionName,
|
|
6488
|
+
runtimeDisplay.model,
|
|
6489
|
+
runtimeDisplay.version,
|
|
6490
|
+
runtimeSettings.outputStyle,
|
|
6491
|
+
runtimePreferences.permissionMode,
|
|
6492
|
+
runtimePreferences.additionalDirectories,
|
|
6493
|
+
dataPlane,
|
|
6494
|
+
usage,
|
|
6495
|
+
costUsd,
|
|
6496
|
+
runtimeDisplay.contextWindowTokens,
|
|
6497
|
+
runtimeSettings.editor,
|
|
6498
|
+
vimInsertMode,
|
|
6499
|
+
]);
|
|
6500
|
+
const statusLineRefreshKey = [
|
|
6501
|
+
history.length,
|
|
6502
|
+
runtimePreferences.permissionMode,
|
|
6503
|
+
runtimeDisplay.model,
|
|
6504
|
+
runtimeSettings.outputStyle,
|
|
6505
|
+
vimInsertMode,
|
|
6506
|
+
sessionName,
|
|
6507
|
+
usage?.inputTokens,
|
|
6508
|
+
usage?.outputTokens,
|
|
6509
|
+
].join(':');
|
|
6510
|
+
const configuredStatusLine = useClaudeStatusLine({
|
|
6511
|
+
configRoot: keybindingsRoot,
|
|
6512
|
+
cwd: runtimeCwd,
|
|
6513
|
+
dataPlane,
|
|
6514
|
+
input: statusLineInput,
|
|
6515
|
+
refreshKey: statusLineRefreshKey,
|
|
6516
|
+
width,
|
|
6517
|
+
...(settingSources === undefined ? {} : { settingSources }),
|
|
6518
|
+
});
|
|
6519
|
+
const configuredStatusText = formatClaudeStatusLineState(configuredStatusLine, width);
|
|
6520
|
+
const conciseStatus = [
|
|
6521
|
+
status,
|
|
6522
|
+
editorFooterMessage
|
|
6523
|
+
? `${editorFooterMessage.isError ? 'Error: ' : ''}${editorFooterMessage.text}`
|
|
6524
|
+
: undefined,
|
|
6525
|
+
configuredStatusText,
|
|
6526
|
+
]
|
|
6527
|
+
.filter((value, index, values) => Boolean(value) && (index === 0 || value !== values[index - 1]))
|
|
6528
|
+
.join(' · ');
|
|
6529
|
+
const quietFrame = useMemo(() => projectQuietScreenFrame({
|
|
6530
|
+
screen,
|
|
6531
|
+
composerText: shellMode ? input.slice(1) : input,
|
|
6532
|
+
composerCursor: shellMode ? Math.max(0, inputCursor - 1) : inputCursor,
|
|
6533
|
+
shellMode,
|
|
6534
|
+
busy,
|
|
6535
|
+
status: conciseStatus,
|
|
6536
|
+
display: runtimeDisplay,
|
|
6537
|
+
}), [
|
|
6538
|
+
screen,
|
|
6539
|
+
shellMode,
|
|
6540
|
+
input,
|
|
6541
|
+
inputCursor,
|
|
6542
|
+
busy,
|
|
6543
|
+
conciseStatus,
|
|
6544
|
+
runtimeDisplay,
|
|
6545
|
+
]);
|
|
6546
|
+
return (_jsx(TuiThemeProvider, { settings: themeSettings, screenReader: axScreenReader, children: ansiActive ? (_jsx(TuiAnsiSurface, { frame: quietFrame, onError: () => setAnsiRendererFailed(true) })) : (_jsx(QuietInkFrame, { frame: quietFrame, screenReader: axScreenReader })) }));
|
|
6592
6547
|
}
|
|
6593
6548
|
/**
|
|
6594
6549
|
* Whether the user explicitly saved a `tui` renderer value in configuration.
|
|
@@ -6624,10 +6579,11 @@ export async function runInteractive(options) {
|
|
|
6624
6579
|
// fallback for screen-reader and non-interactive execution, and an explicit
|
|
6625
6580
|
// renderer saved in configuration is honored over the default.
|
|
6626
6581
|
const rendererExplicitlyConfigured = await tuiRendererExplicitlyConfigured(runtimeSettingsTarget);
|
|
6582
|
+
const interactiveTty = process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
6627
6583
|
let currentRenderer = resolveTuiRenderer({
|
|
6628
6584
|
configured: currentRuntimeSettings.tui,
|
|
6629
6585
|
explicitlyConfigured: rendererExplicitlyConfigured,
|
|
6630
|
-
interactiveTty
|
|
6586
|
+
interactiveTty,
|
|
6631
6587
|
screenReader: options.axScreenReader ?? false,
|
|
6632
6588
|
});
|
|
6633
6589
|
if (currentRenderer !== currentRuntimeSettings.tui) {
|
|
@@ -6726,7 +6682,7 @@ export async function runInteractive(options) {
|
|
|
6726
6682
|
let backgrounded;
|
|
6727
6683
|
rendererChange = null;
|
|
6728
6684
|
const ansiRendererEnabled = currentRenderer === 'fullscreen' &&
|
|
6729
|
-
|
|
6685
|
+
interactiveTty &&
|
|
6730
6686
|
options.axScreenReader !== true;
|
|
6731
6687
|
const instance = render(_jsx(InteractiveApp, { dataPlane: dataPlane, configRoot: configRoot, statePath: statePath, factory: options.factory, initialSessions: initialSessions, slashCommands: initialSlashCommands, agents: initialAgents, initialHistory: history, ...(initialSessionColor === undefined ? {} : { initialSessionColor }), runtimeSettings: currentRuntimeSettings, runtimeSettingsTarget: runtimeSettingsTarget, ...(options.settingSources === undefined
|
|
6732
6688
|
? {}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { QuietFrameCursor } from './quiet-frame.js';
|
|
1
2
|
import type { TuiRow, TuiTextRole } from './tui-row-ir.js';
|
|
2
3
|
export interface AnsiFrameWriter {
|
|
3
4
|
write(chunk: string): void;
|
|
@@ -6,6 +7,7 @@ export interface AnsiFrame {
|
|
|
6
7
|
readonly columns: number;
|
|
7
8
|
readonly rows: number;
|
|
8
9
|
readonly lines: readonly TuiRow[];
|
|
10
|
+
readonly cursor?: QuietFrameCursor;
|
|
9
11
|
}
|
|
10
12
|
export interface AnsiFullscreenRendererOptions {
|
|
11
13
|
readonly writer: AnsiFrameWriter;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { stripVTControlCharacters } from 'node:util';
|
|
2
|
+
import { terminalGraphemes, terminalGraphemeWidth, terminalTextWidth, } from './transcript-viewport.js';
|
|
2
3
|
const ALTERNATE_SCREEN_ENTER = '\u001b[?1049h';
|
|
3
4
|
const ALTERNATE_SCREEN_LEAVE = '\u001b[?1049l';
|
|
4
5
|
const HIDE_CURSOR = '\u001b[?25l';
|
|
@@ -19,22 +20,44 @@ function validateFrame(frame) {
|
|
|
19
20
|
if (!Array.isArray(frame.lines)) {
|
|
20
21
|
throw new TypeError('ANSI frame lines must be an array');
|
|
21
22
|
}
|
|
23
|
+
if (frame.cursor !== undefined) {
|
|
24
|
+
if (frame.cursor === null ||
|
|
25
|
+
typeof frame.cursor !== 'object' ||
|
|
26
|
+
typeof frame.cursor.rowKey !== 'string' ||
|
|
27
|
+
frame.cursor.rowKey.length === 0 ||
|
|
28
|
+
!Number.isFinite(frame.cursor.column) ||
|
|
29
|
+
!Number.isInteger(frame.cursor.column) ||
|
|
30
|
+
frame.cursor.column < 0) {
|
|
31
|
+
throw new TypeError('ANSI frame cursor must have a non-empty row key and non-negative integer column');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
22
34
|
}
|
|
23
35
|
function renderLine(row, styles, columns) {
|
|
24
36
|
const segments = row.segments.map((segment) => ({
|
|
25
37
|
text: stripVTControlCharacters(segment.text).replace(/[\r\n]/gu, ''),
|
|
26
38
|
role: segment.role,
|
|
27
39
|
}));
|
|
28
|
-
const visibleLength = segments.reduce((length, segment) => length +
|
|
40
|
+
const visibleLength = segments.reduce((length, segment) => length + terminalTextWidth(segment.text), 0);
|
|
29
41
|
const wasTruncated = visibleLength > columns;
|
|
30
42
|
const targetLength = wasTruncated ? Math.max(0, columns - 1) : columns;
|
|
31
43
|
let remaining = targetLength;
|
|
32
44
|
let line = '';
|
|
45
|
+
let stopped = false;
|
|
33
46
|
for (const segment of segments) {
|
|
34
|
-
if (remaining === 0)
|
|
47
|
+
if (remaining === 0 || stopped)
|
|
35
48
|
break;
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
let text = '';
|
|
50
|
+
for (const grapheme of terminalGraphemes(segment.text)) {
|
|
51
|
+
const width = terminalGraphemeWidth(grapheme);
|
|
52
|
+
if (remaining < width) {
|
|
53
|
+
stopped = true;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
text += grapheme;
|
|
57
|
+
remaining -= width;
|
|
58
|
+
}
|
|
59
|
+
if (text.length === 0)
|
|
60
|
+
continue;
|
|
38
61
|
const style = styles?.[segment.role];
|
|
39
62
|
line += style === undefined ? text : `${style}${text}${RESET}`;
|
|
40
63
|
}
|
|
@@ -50,6 +73,7 @@ export class AnsiFullscreenRenderer {
|
|
|
50
73
|
#styles;
|
|
51
74
|
#mounted = false;
|
|
52
75
|
#previousLines = [];
|
|
76
|
+
#previousCursor;
|
|
53
77
|
constructor(options) {
|
|
54
78
|
this.#writer = options.writer;
|
|
55
79
|
this.#synchronizedOutput = options.synchronizedOutput === true;
|
|
@@ -64,11 +88,50 @@ export class AnsiFullscreenRenderer {
|
|
|
64
88
|
mount() {
|
|
65
89
|
if (this.#mounted)
|
|
66
90
|
return;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
91
|
+
let alternateScreenEntered = false;
|
|
92
|
+
let cursorHidden = false;
|
|
93
|
+
let synchronizedOutputBegun = false;
|
|
94
|
+
try {
|
|
95
|
+
alternateScreenEntered = true;
|
|
96
|
+
this.#writer.write(ALTERNATE_SCREEN_ENTER);
|
|
97
|
+
cursorHidden = true;
|
|
98
|
+
this.#writer.write(HIDE_CURSOR);
|
|
99
|
+
if (this.#synchronizedOutput) {
|
|
100
|
+
synchronizedOutputBegun = true;
|
|
101
|
+
this.#writer.write(SYNCHRONIZED_BEGIN);
|
|
102
|
+
}
|
|
103
|
+
this.#mounted = true;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.#mounted = false;
|
|
107
|
+
this.#previousLines = [];
|
|
108
|
+
this.#previousCursor = undefined;
|
|
109
|
+
if (synchronizedOutputBegun) {
|
|
110
|
+
try {
|
|
111
|
+
this.#writer.write(SYNCHRONIZED_END);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// Rollback must continue if one restoration write fails.
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (cursorHidden) {
|
|
118
|
+
try {
|
|
119
|
+
this.#writer.write(SHOW_CURSOR);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// Rollback must continue if one restoration write fails.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (alternateScreenEntered) {
|
|
126
|
+
try {
|
|
127
|
+
this.#writer.write(ALTERNATE_SCREEN_LEAVE);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// Rollback must continue if one restoration write fails.
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
72
135
|
}
|
|
73
136
|
draw(frame) {
|
|
74
137
|
if (!this.#mounted)
|
|
@@ -78,24 +141,48 @@ export class AnsiFullscreenRenderer {
|
|
|
78
141
|
const previousLines = this.#previousLines;
|
|
79
142
|
const changed = lines.length !== previousLines.length ||
|
|
80
143
|
lines.some((line, index) => line !== previousLines[index]);
|
|
81
|
-
|
|
144
|
+
const cursorRow = frame.cursor
|
|
145
|
+
? frame.lines.findIndex((row) => row.key === frame.cursor?.rowKey)
|
|
146
|
+
: -1;
|
|
147
|
+
const nextCursor = cursorRow >= 0 && frame.cursor !== undefined
|
|
148
|
+
? {
|
|
149
|
+
rowKey: frame.cursor.rowKey,
|
|
150
|
+
column: Math.min(frame.cursor.column, frame.columns - 1),
|
|
151
|
+
row: cursorRow,
|
|
152
|
+
}
|
|
153
|
+
: undefined;
|
|
154
|
+
const cursorChanged = nextCursor?.rowKey !== this.#previousCursor?.rowKey ||
|
|
155
|
+
nextCursor?.column !== this.#previousCursor?.column ||
|
|
156
|
+
nextCursor?.row !== this.#previousCursor?.row;
|
|
157
|
+
if (!changed && !cursorChanged)
|
|
82
158
|
return;
|
|
83
159
|
if (this.#synchronizedOutput)
|
|
84
160
|
this.#writer.write(SYNCHRONIZED_BEGIN);
|
|
85
161
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
162
|
+
if (changed) {
|
|
163
|
+
if (this.#previousCursor !== undefined || nextCursor !== undefined)
|
|
164
|
+
this.#writer.write(HIDE_CURSOR);
|
|
165
|
+
const commonLength = Math.max(lines.length, previousLines.length);
|
|
166
|
+
for (let index = 0; index < commonLength; index += 1) {
|
|
167
|
+
const line = lines[index];
|
|
168
|
+
if (line !== undefined) {
|
|
169
|
+
if (line === previousLines[index])
|
|
170
|
+
continue;
|
|
171
|
+
this.#writer.write(`\u001b[${index + 1};1H\u001b[2K${line}`);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
this.#writer.write(`\u001b[${index + 1};1H\u001b[2K`);
|
|
175
|
+
}
|
|
96
176
|
}
|
|
177
|
+
this.#previousLines = lines;
|
|
178
|
+
}
|
|
179
|
+
if (nextCursor !== undefined) {
|
|
180
|
+
this.#writer.write(`\u001b[${cursorRow + 1};${nextCursor.column + 1}H`);
|
|
181
|
+
this.#writer.write(SHOW_CURSOR);
|
|
97
182
|
}
|
|
98
|
-
this.#
|
|
183
|
+
else if (this.#previousCursor !== undefined)
|
|
184
|
+
this.#writer.write(HIDE_CURSOR);
|
|
185
|
+
this.#previousCursor = nextCursor;
|
|
99
186
|
}
|
|
100
187
|
finally {
|
|
101
188
|
if (this.#synchronizedOutput)
|
|
@@ -114,6 +201,7 @@ export class AnsiFullscreenRenderer {
|
|
|
114
201
|
finally {
|
|
115
202
|
this.#mounted = false;
|
|
116
203
|
this.#previousLines = [];
|
|
204
|
+
this.#previousCursor = undefined;
|
|
117
205
|
}
|
|
118
206
|
}
|
|
119
207
|
}
|