@aragon-agent/cli 0.6.14 → 0.6.15
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/CHANGELOG.md +14 -0
- package/README.md +250 -233
- package/dist/agent/controller.d.ts +27 -1
- package/dist/agent/controller.js +182 -45
- package/dist/agent/plan-prompt.d.ts +1 -1
- package/dist/agent/plan-prompt.js +4 -3
- package/dist/agent/project-guidance-prompt.d.ts +4 -0
- package/dist/agent/project-guidance-prompt.js +104 -0
- package/dist/agent/reducer.d.ts +2 -0
- package/dist/agent/reducer.js +4 -0
- package/dist/agent/system-prompt.js +4 -0
- package/dist/commands/builtins.js +2 -2
- package/dist/compaction/child.d.ts +9 -1
- package/dist/compaction/child.js +39 -13
- package/dist/compaction/command.js +20 -10
- package/dist/compaction/compactor.d.ts +33 -204
- package/dist/compaction/compactor.js +311 -726
- package/dist/compaction/digest.d.ts +15 -51
- package/dist/compaction/digest.js +113 -148
- package/dist/compaction/limits.d.ts +19 -142
- package/dist/compaction/limits.js +20 -147
- package/dist/compaction/memory-identity.d.ts +16 -0
- package/dist/compaction/memory-identity.js +63 -0
- package/dist/compaction/memory-input.d.ts +25 -0
- package/dist/compaction/memory-input.js +106 -0
- package/dist/compaction/memory-render.d.ts +4 -0
- package/dist/compaction/memory-render.js +47 -0
- package/dist/compaction/memory-validation.d.ts +21 -0
- package/dist/compaction/memory-validation.js +268 -0
- package/dist/compaction/memory.d.ts +79 -0
- package/dist/compaction/memory.js +118 -0
- package/dist/compaction/meter.d.ts +14 -10
- package/dist/compaction/meter.js +53 -19
- package/dist/compaction/operation.d.ts +18 -0
- package/dist/compaction/operation.js +68 -0
- package/dist/compaction/pressure.d.ts +5 -20
- package/dist/compaction/pressure.js +13 -23
- package/dist/compaction/summarize-call.d.ts +3 -16
- package/dist/compaction/summarize-call.js +20 -20
- package/dist/compaction/summary-prompt.d.ts +29 -66
- package/dist/compaction/summary-prompt.js +132 -178
- package/dist/compaction/types.d.ts +21 -3
- package/dist/compaction/wiring.d.ts +10 -25
- package/dist/compaction/wiring.js +56 -66
- package/dist/config/schema.d.ts +1 -1
- package/dist/config/schema.js +1 -1
- package/dist/exec/events.d.ts +8 -0
- package/dist/exec/index.js +13 -6
- package/dist/exec/runner.d.ts +8 -0
- package/dist/exec/runner.js +26 -2
- package/dist/session/persist.d.ts +3 -0
- package/dist/session/persist.js +9 -0
- package/dist/session/store.js +2 -0
- package/dist/session/validate-session.js +34 -0
- package/dist/team/subagent.js +8 -0
- package/dist/ui/App.js +218 -116
- package/dist/ui/BottomStatusRow.d.ts +5 -10
- package/dist/ui/BottomStatusRow.js +7 -11
- package/dist/ui/Composer.d.ts +4 -2
- package/dist/ui/Composer.js +2 -2
- package/dist/ui/Header.d.ts +6 -2
- package/dist/ui/Header.js +7 -6
- package/dist/ui/PromptInput.d.ts +3 -1
- package/dist/ui/PromptInput.js +32 -41
- package/dist/ui/StatusBar.d.ts +6 -0
- package/dist/ui/StatusBar.js +8 -7
- package/dist/ui/Transcript.js +1 -1
- package/dist/ui/entries/CompactionCard.d.ts +3 -1
- package/dist/ui/entries/CompactionCard.js +7 -5
- package/dist/ui/frame-differ.js +27 -0
- package/dist/ui/frame-observer.js +18 -5
- package/dist/ui/interaction-copy.d.ts +73 -73
- package/dist/ui/interaction-copy.js +30 -38
- package/dist/ui/interaction-hints.d.ts +8 -1
- package/dist/ui/interaction-hints.js +56 -69
- package/dist/ui/layout/AppShell.d.ts +6 -2
- package/dist/ui/layout/AppShell.js +2 -2
- package/dist/ui/layout/OverlayFrame.d.ts +2 -0
- package/dist/ui/layout/OverlayFrame.js +23 -14
- package/dist/ui/layout/ScrollViewport.d.ts +3 -1
- package/dist/ui/layout/ScrollViewport.js +10 -2
- package/dist/ui/layout/budget.d.ts +25 -26
- package/dist/ui/layout/budget.js +29 -22
- package/dist/ui/layout/document-layout.d.ts +2 -2
- package/dist/ui/layout/document-layout.js +2 -3
- package/dist/ui/layout/queue-layout.js +4 -4
- package/dist/ui/layout/status-detail-layout.d.ts +9 -0
- package/dist/ui/layout/status-detail-layout.js +56 -0
- package/dist/ui/layout/status-layout.d.ts +19 -2
- package/dist/ui/layout/status-layout.js +95 -84
- package/dist/ui/layout/wrap-rows.d.ts +2 -0
- package/dist/ui/layout/wrap-rows.js +21 -7
- package/dist/ui/overlays/ConfirmDialog.d.ts +2 -1
- package/dist/ui/overlays/ConfirmDialog.js +22 -3
- package/dist/ui/overlays/HelpOverlay.js +9 -2
- package/dist/ui/overlays/ModelPicker.d.ts +5 -8
- package/dist/ui/overlays/ModelPicker.js +44 -10
- package/dist/ui/overlays/ModelProfileEditor.js +2 -0
- package/dist/ui/overlays/ModelProfilePicker.d.ts +1 -0
- package/dist/ui/overlays/ModelProfilePicker.js +4 -2
- package/dist/ui/overlays/PlanReviewOverlay.d.ts +2 -1
- package/dist/ui/overlays/PlanReviewOverlay.js +7 -4
- package/dist/ui/overlays/QuestionOverlay.d.ts +2 -1
- package/dist/ui/overlays/QuestionOverlay.js +45 -32
- package/dist/ui/overlays/QueueOverlay.js +2 -2
- package/dist/ui/overlays/SettingsScreen.d.ts +1 -0
- package/dist/ui/overlays/SettingsScreen.js +10 -3
- package/dist/ui/run-metrics.d.ts +23 -0
- package/dist/ui/run-metrics.js +27 -0
- package/dist/ui/status-feedback.d.ts +33 -0
- package/dist/ui/status-feedback.js +36 -0
- package/dist/ui/stdout-frame-writer.js +28 -0
- package/package.json +2 -2
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Built-in product text; user content retains its original language. */
|
|
2
2
|
export declare const interactionCopy: {
|
|
3
|
-
readonly copied: "
|
|
4
|
-
readonly lines: "
|
|
5
|
-
readonly copySent: "
|
|
3
|
+
readonly copied: "Copied";
|
|
4
|
+
readonly lines: "lines";
|
|
5
|
+
readonly copySent: "Copy sent; clipboard confirmation unavailable";
|
|
6
6
|
readonly copyReasons: {
|
|
7
|
-
readonly empty: "
|
|
8
|
-
readonly unavailable: "
|
|
9
|
-
readonly write: "
|
|
10
|
-
readonly timeout: "
|
|
11
|
-
readonly 'too-large': "
|
|
12
|
-
readonly cancelled: "
|
|
7
|
+
readonly empty: "Nothing to copy";
|
|
8
|
+
readonly unavailable: "Clipboard tool unavailable";
|
|
9
|
+
readonly write: "Clipboard write failed";
|
|
10
|
+
readonly timeout: "Clipboard tool timed out";
|
|
11
|
+
readonly 'too-large': "Content exceeds clipboard limit";
|
|
12
|
+
readonly cancelled: "Copy cancelled";
|
|
13
13
|
};
|
|
14
|
-
readonly stopServicesShort: "^C
|
|
15
|
-
readonly approveTool: "y
|
|
16
|
-
readonly rejectTool: "n/Enter
|
|
17
|
-
readonly saveSettings: "Enter
|
|
18
|
-
readonly modeLabel: "
|
|
19
|
-
readonly idle: readonly ["
|
|
20
|
-
readonly starting: readonly ["
|
|
21
|
-
readonly waiting: readonly ["
|
|
22
|
-
readonly thinking: readonly ["
|
|
23
|
-
readonly generating: readonly ["
|
|
24
|
-
readonly preparingTool: readonly ["
|
|
25
|
-
readonly tool: readonly ["
|
|
26
|
-
readonly confirming: readonly ["
|
|
27
|
-
readonly stopping: readonly ["
|
|
28
|
-
readonly compacting: readonly ["
|
|
29
|
-
readonly retrying: readonly ["
|
|
30
|
-
readonly ended: readonly ["
|
|
31
|
-
readonly interrupted: readonly ["
|
|
32
|
-
readonly failed: readonly ["
|
|
33
|
-
readonly readFile: readonly ["
|
|
34
|
-
readonly writeFile: readonly ["
|
|
35
|
-
readonly command: readonly ["
|
|
36
|
-
readonly queue: "
|
|
37
|
-
readonly pending: "
|
|
38
|
-
readonly context: "
|
|
39
|
-
readonly contextUnknown: "
|
|
40
|
-
readonly sessionInput: "
|
|
41
|
-
readonly output: "
|
|
42
|
-
readonly estimatedCost: "
|
|
43
|
-
readonly speed: "
|
|
44
|
-
readonly elapsed: "
|
|
45
|
-
readonly services: "
|
|
46
|
-
readonly send: "Enter
|
|
47
|
-
readonly enqueue: "Enter
|
|
48
|
-
readonly newline: "^J
|
|
49
|
-
readonly sendLong: "Enter
|
|
50
|
-
readonly enqueueLong: "Enter
|
|
51
|
-
readonly newlineLong: "Ctrl+J
|
|
52
|
-
readonly interrupt: "
|
|
53
|
-
readonly interruptAgain: "
|
|
54
|
-
readonly forceStop: "
|
|
55
|
-
readonly startingHint: "
|
|
56
|
-
readonly cancelStart: "
|
|
57
|
-
readonly cancelStartAgain: "
|
|
58
|
-
readonly copy: "Ctrl+C
|
|
59
|
-
readonly copying: "
|
|
60
|
-
readonly copyFailed: "
|
|
61
|
-
readonly copyCleanup: "
|
|
62
|
-
readonly close: "Esc
|
|
63
|
-
readonly closeCompletion: "Esc
|
|
64
|
-
readonly executeCommand: "Enter
|
|
65
|
-
readonly complete: "Tab
|
|
66
|
-
readonly select: "Up/Down
|
|
67
|
-
readonly scroll: "PgUp/PgDn
|
|
68
|
-
readonly confirm: "Enter
|
|
69
|
-
readonly stopServices: "Ctrl+C
|
|
70
|
-
readonly exit: "Ctrl+
|
|
71
|
-
readonly help: "/help
|
|
72
|
-
readonly shiftNewline: "Shift+Enter
|
|
73
|
-
readonly mode: "Shift+Tab
|
|
74
|
-
readonly update: "/update
|
|
75
|
-
readonly idlePlaceholder: "
|
|
76
|
-
readonly runningPlaceholder: "
|
|
14
|
+
readonly stopServicesShort: "^C stop services";
|
|
15
|
+
readonly approveTool: "y approve";
|
|
16
|
+
readonly rejectTool: "n/Enter reject";
|
|
17
|
+
readonly saveSettings: "Enter save";
|
|
18
|
+
readonly modeLabel: "switch mode";
|
|
19
|
+
readonly idle: readonly ["Idle", "Idle"];
|
|
20
|
+
readonly starting: readonly ["Start", "Starting"];
|
|
21
|
+
readonly waiting: readonly ["Wait", "Waiting for model"];
|
|
22
|
+
readonly thinking: readonly ["Think", "Thinking"];
|
|
23
|
+
readonly generating: readonly ["Write", "Generating"];
|
|
24
|
+
readonly preparingTool: readonly ["Prep", "Preparing tool"];
|
|
25
|
+
readonly tool: readonly ["Tool", "Running tool"];
|
|
26
|
+
readonly confirming: readonly ["Ask", "Awaiting confirmation"];
|
|
27
|
+
readonly stopping: readonly ["Stop", "Stopping"];
|
|
28
|
+
readonly compacting: readonly ["Pack", "Compacting context"];
|
|
29
|
+
readonly retrying: readonly ["Retry", "Retrying"];
|
|
30
|
+
readonly ended: readonly ["Done", "Done"];
|
|
31
|
+
readonly interrupted: readonly ["Abort", "Interrupted"];
|
|
32
|
+
readonly failed: readonly ["Error", "Failed"];
|
|
33
|
+
readonly readFile: readonly ["Tool", "Running tool read_file"];
|
|
34
|
+
readonly writeFile: readonly ["Tool", "Running tool write_file"];
|
|
35
|
+
readonly command: readonly ["Tool", "Running tool bash"];
|
|
36
|
+
readonly queue: "Q";
|
|
37
|
+
readonly pending: "pending";
|
|
38
|
+
readonly context: "Context";
|
|
39
|
+
readonly contextUnknown: "Context ?";
|
|
40
|
+
readonly sessionInput: "Session input";
|
|
41
|
+
readonly output: "output";
|
|
42
|
+
readonly estimatedCost: "Cost";
|
|
43
|
+
readonly speed: "Run average";
|
|
44
|
+
readonly elapsed: "Elapsed";
|
|
45
|
+
readonly services: "Services";
|
|
46
|
+
readonly send: "Enter send";
|
|
47
|
+
readonly enqueue: "Enter queue";
|
|
48
|
+
readonly newline: "^J newline";
|
|
49
|
+
readonly sendLong: "Enter send";
|
|
50
|
+
readonly enqueueLong: "Enter queue";
|
|
51
|
+
readonly newlineLong: "Ctrl+J newline";
|
|
52
|
+
readonly interrupt: "Esc x2 stop";
|
|
53
|
+
readonly interruptAgain: "Esc stop";
|
|
54
|
+
readonly forceStop: "Esc force";
|
|
55
|
+
readonly startingHint: "Starting; please wait";
|
|
56
|
+
readonly cancelStart: "Esc x2 cancel";
|
|
57
|
+
readonly cancelStartAgain: "Esc cancel";
|
|
58
|
+
readonly copy: "Ctrl+C copy";
|
|
59
|
+
readonly copying: "Copying";
|
|
60
|
+
readonly copyFailed: "Copy failed";
|
|
61
|
+
readonly copyCleanup: "Copy failed; cleaning up";
|
|
62
|
+
readonly close: "Esc close";
|
|
63
|
+
readonly closeCompletion: "Esc menu";
|
|
64
|
+
readonly executeCommand: "Enter command";
|
|
65
|
+
readonly complete: "Tab complete";
|
|
66
|
+
readonly select: "Up/Down select";
|
|
67
|
+
readonly scroll: "PgUp/PgDn scroll";
|
|
68
|
+
readonly confirm: "Enter confirm";
|
|
69
|
+
readonly stopServices: "Ctrl+C stop services";
|
|
70
|
+
readonly exit: "Ctrl+C x2 exit";
|
|
71
|
+
readonly help: "/help";
|
|
72
|
+
readonly shiftNewline: "Shift+Enter newline needs terminal support";
|
|
73
|
+
readonly mode: "Shift+Tab switch mode";
|
|
74
|
+
readonly update: "/update";
|
|
75
|
+
readonly idlePlaceholder: "Ask a question or describe a task...";
|
|
76
|
+
readonly runningPlaceholder: "Add a follow-up...";
|
|
77
77
|
};
|
|
@@ -1,40 +1,32 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Built-in product text; user content retains its original language. */
|
|
2
2
|
export const interactionCopy = {
|
|
3
|
-
copied: '
|
|
4
|
-
copySent: '
|
|
5
|
-
copyReasons: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
cancelStartAgain: '\u518d\u6309 Esc \u53d6\u6d88\u542f\u52a8',
|
|
33
|
-
copy: 'Ctrl+C \u590d\u5236', copying: '\u6b63\u5728\u590d\u5236', copyFailed: '\u590d\u5236\u5931\u8d25', copyCleanup: '\u590d\u5236\u5931\u8d25\uff0c\u6b63\u5728\u6e05\u7406',
|
|
34
|
-
close: 'Esc \u5173\u95ed', closeCompletion: 'Esc \u5173\u95ed\u8865\u5168', executeCommand: 'Enter \u6267\u884c\u547d\u4ee4',
|
|
35
|
-
complete: 'Tab \u8865\u5168', select: 'Up/Down \u9009\u62e9', scroll: 'PgUp/PgDn \u6eda\u52a8',
|
|
36
|
-
confirm: 'Enter \u786e\u8ba4', stopServices: 'Ctrl+C \u505c\u6b62\u670d\u52a1', exit: 'Ctrl+C\u00d72 \u9000\u51fa',
|
|
37
|
-
help: '/help \u5e2e\u52a9', shiftNewline: 'Shift+Enter \u6362\u884c\u9700\u7ec8\u7aef\u652f\u6301',
|
|
38
|
-
mode: 'Shift+Tab \u5207\u6362\u6a21\u5f0f', update: '/update \u66f4\u65b0',
|
|
39
|
-
idlePlaceholder: '\u8f93\u5165\u4efb\u52a1\u6216\u95ee\u9898\u2026', runningPlaceholder: '\u8f93\u5165\u8865\u5145\u8bf4\u660e\u2026',
|
|
3
|
+
copied: 'Copied', lines: 'lines',
|
|
4
|
+
copySent: 'Copy sent; clipboard confirmation unavailable',
|
|
5
|
+
copyReasons: { empty: 'Nothing to copy', unavailable: 'Clipboard tool unavailable',
|
|
6
|
+
write: 'Clipboard write failed', timeout: 'Clipboard tool timed out',
|
|
7
|
+
'too-large': 'Content exceeds clipboard limit', cancelled: 'Copy cancelled' },
|
|
8
|
+
stopServicesShort: '^C stop services', approveTool: 'y approve', rejectTool: 'n/Enter reject',
|
|
9
|
+
saveSettings: 'Enter save', modeLabel: 'switch mode',
|
|
10
|
+
idle: ['Idle', 'Idle'], starting: ['Start', 'Starting'], waiting: ['Wait', 'Waiting for model'],
|
|
11
|
+
thinking: ['Think', 'Thinking'], generating: ['Write', 'Generating'],
|
|
12
|
+
preparingTool: ['Prep', 'Preparing tool'], tool: ['Tool', 'Running tool'],
|
|
13
|
+
confirming: ['Ask', 'Awaiting confirmation'], stopping: ['Stop', 'Stopping'],
|
|
14
|
+
compacting: ['Pack', 'Compacting context'], retrying: ['Retry', 'Retrying'],
|
|
15
|
+
ended: ['Done', 'Done'], interrupted: ['Abort', 'Interrupted'], failed: ['Error', 'Failed'],
|
|
16
|
+
readFile: ['Tool', 'Running tool read_file'], writeFile: ['Tool', 'Running tool write_file'],
|
|
17
|
+
command: ['Tool', 'Running tool bash'],
|
|
18
|
+
queue: 'Q', pending: 'pending', context: 'Context', contextUnknown: 'Context ?',
|
|
19
|
+
sessionInput: 'Session input', output: 'output', estimatedCost: 'Cost',
|
|
20
|
+
speed: 'Run average', elapsed: 'Elapsed', services: 'Services',
|
|
21
|
+
send: 'Enter send', enqueue: 'Enter queue', newline: '^J newline',
|
|
22
|
+
sendLong: 'Enter send', enqueueLong: 'Enter queue', newlineLong: 'Ctrl+J newline',
|
|
23
|
+
interrupt: 'Esc x2 stop', interruptAgain: 'Esc stop', forceStop: 'Esc force',
|
|
24
|
+
startingHint: 'Starting; please wait', cancelStart: 'Esc x2 cancel', cancelStartAgain: 'Esc cancel',
|
|
25
|
+
copy: 'Ctrl+C copy', copying: 'Copying', copyFailed: 'Copy failed', copyCleanup: 'Copy failed; cleaning up',
|
|
26
|
+
close: 'Esc close', closeCompletion: 'Esc menu', executeCommand: 'Enter command',
|
|
27
|
+
complete: 'Tab complete', select: 'Up/Down select', scroll: 'PgUp/PgDn scroll',
|
|
28
|
+
confirm: 'Enter confirm', stopServices: 'Ctrl+C stop services', exit: 'Ctrl+C x2 exit',
|
|
29
|
+
help: '/help', shiftNewline: 'Shift+Enter newline needs terminal support',
|
|
30
|
+
mode: 'Shift+Tab switch mode', update: '/update',
|
|
31
|
+
idlePlaceholder: 'Ask a question or describe a task...', runningPlaceholder: 'Add a follow-up...',
|
|
40
32
|
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import type { StatusFeedback, EscapeAction } from './status-feedback.js';
|
|
1
2
|
export interface ActionHintInput {
|
|
2
3
|
cols: number;
|
|
3
4
|
interactionPhase: 'idle' | 'starting' | 'running';
|
|
4
5
|
interruptHint?: string;
|
|
6
|
+
interruptPhase?: 'ready' | 'armed' | 'stopping';
|
|
7
|
+
escapeAction?: EscapeAction;
|
|
8
|
+
feedback?: StatusFeedback;
|
|
5
9
|
selectionPending?: boolean;
|
|
6
10
|
copyInFlight?: boolean;
|
|
7
11
|
copyCleanupPending?: boolean;
|
|
@@ -14,5 +18,8 @@ export interface ActionHintInput {
|
|
|
14
18
|
exitHint?: string;
|
|
15
19
|
modeToggleKey?: string;
|
|
16
20
|
}
|
|
17
|
-
|
|
21
|
+
export declare function escapeClause(input: ActionHintInput): string;
|
|
22
|
+
export declare function clipStatusText(text: string, cells: number): string;
|
|
23
|
+
/** Ordered actions before width allocation. No interpretation of translated feedback text. */
|
|
24
|
+
export declare function actionCandidates(input: ActionHintInput): string[];
|
|
18
25
|
export declare function buildActionClauses(input: ActionHintInput): readonly string[];
|
|
@@ -1,92 +1,79 @@
|
|
|
1
1
|
import stringWidth from 'string-width';
|
|
2
2
|
import { interactionCopy as copy } from './interaction-copy.js';
|
|
3
3
|
import { cleanStatusText } from './layout/status-layout.js';
|
|
4
|
-
|
|
4
|
+
import { graphemes } from './editor-navigation.js';
|
|
5
|
+
export function escapeClause(input) {
|
|
5
6
|
if (input.overlay)
|
|
6
7
|
return copy.close;
|
|
7
|
-
if (input.completion && input.completion !== 'none')
|
|
8
|
+
if (input.completion && input.completion !== 'none' || input.escapeAction === 'menu')
|
|
8
9
|
return copy.closeCompletion;
|
|
10
|
+
if (input.escapeAction === 'force-stop' || input.interruptPhase === 'stopping')
|
|
11
|
+
return copy.forceStop;
|
|
9
12
|
if (input.interactionPhase === 'starting')
|
|
10
|
-
return
|
|
11
|
-
? copy.cancelStartAgain : copy.cancelStart;
|
|
13
|
+
return input.interruptPhase === 'armed' ? copy.cancelStartAgain : copy.cancelStart;
|
|
12
14
|
if (input.interactionPhase !== 'running')
|
|
13
15
|
return '';
|
|
14
|
-
|
|
15
|
-
return copy.forceStop;
|
|
16
|
-
if (/again|confirm|\u518d\u6309/i.test(input.interruptHint ?? ''))
|
|
17
|
-
return copy.interruptAgain;
|
|
18
|
-
return copy.interrupt;
|
|
16
|
+
return input.interruptPhase === 'armed' ? copy.interruptAgain : copy.interrupt;
|
|
19
17
|
}
|
|
20
|
-
function
|
|
18
|
+
export function clipStatusText(text, cells) {
|
|
19
|
+
let clipped = '';
|
|
20
|
+
for (const part of graphemes(cleanStatusText(text))) {
|
|
21
|
+
if (stringWidth(clipped + part.text) > cells)
|
|
22
|
+
break;
|
|
23
|
+
clipped += part.text;
|
|
24
|
+
}
|
|
25
|
+
return clipped;
|
|
26
|
+
}
|
|
27
|
+
/** Ordered actions before width allocation. No interpretation of translated feedback text. */
|
|
28
|
+
export function actionCandidates(input) {
|
|
21
29
|
if (input.overlay) {
|
|
22
30
|
if (input.overlay === 'confirm')
|
|
23
|
-
return [
|
|
24
|
-
const selects =
|
|
25
|
-
return [
|
|
31
|
+
return [copy.approveTool, copy.rejectTool];
|
|
32
|
+
const selects = ['question', 'model', 'session', 'settings'].includes(input.overlay);
|
|
33
|
+
return [selects ? copy.select : copy.scroll,
|
|
26
34
|
...(selects ? [input.overlay === 'settings' ? copy.saveSettings : copy.confirm] : [])];
|
|
27
35
|
}
|
|
36
|
+
const actions = [];
|
|
37
|
+
if (input.selectionPending)
|
|
38
|
+
actions.push(copy.copy);
|
|
39
|
+
else if (input.services)
|
|
40
|
+
actions.push(copy.stopServicesShort);
|
|
28
41
|
if (input.completion && input.completion !== 'none') {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
: [copy.complete, escape, copy.select, copy.newline];
|
|
32
|
-
if (input.completion === 'file' && input.interactionPhase !== 'starting')
|
|
33
|
-
candidates.push(input.interactionPhase === 'running' ? copy.enqueue : copy.send);
|
|
34
|
-
return candidates;
|
|
42
|
+
actions.push(...(input.completion === 'slash' ? [copy.executeCommand] : []), copy.complete, copy.select);
|
|
43
|
+
return actions;
|
|
35
44
|
}
|
|
36
|
-
if (input.interactionPhase
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const narrow = input.cols < 72;
|
|
40
|
-
return [running ? (narrow ? copy.enqueue : copy.enqueueLong) : (narrow ? copy.send : copy.sendLong),
|
|
41
|
-
narrow ? copy.newline : copy.newlineLong, ...(escape ? [escape] : [])];
|
|
45
|
+
if (input.hintsEnabled !== false && input.interactionPhase !== 'starting')
|
|
46
|
+
actions.push(input.interactionPhase === 'running' ? copy.enqueue : copy.send, input.cols < 72 ? copy.newline : copy.newlineLong);
|
|
47
|
+
return actions;
|
|
42
48
|
}
|
|
43
|
-
function fitClauses(candidates, budget) {
|
|
44
|
-
const result = [];
|
|
45
|
-
for (const text of candidates.filter(Boolean)) {
|
|
46
|
-
if (stringWidth([...result, text].join(' | ')) <= budget)
|
|
47
|
-
result.push(text);
|
|
48
|
-
}
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
/** Esc describes its actual owner, including while transient feedback has the row. */
|
|
52
49
|
export function buildActionClauses(input) {
|
|
53
50
|
const budget = Math.max(0, Math.floor(input.cols) - 1);
|
|
54
51
|
const escape = escapeClause(input);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
: input.copyInFlight ? copy.copying : '';
|
|
58
|
-
const
|
|
52
|
+
const result = [];
|
|
53
|
+
const feedback = input.feedback?.text || input.exitHint || (input.copyCleanupPending ? copy.copyCleanup
|
|
54
|
+
: input.copyInFlight ? copy.copying : input.toast) || '';
|
|
55
|
+
const reserve = escape ? stringWidth(escape) + 3 : 0;
|
|
59
56
|
if (feedback) {
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (stringWidth(text) > remaining) {
|
|
64
|
-
const minimum = text.includes(copy.copyFailed) ? copy.copyFailed : '';
|
|
65
|
-
let clipped = '';
|
|
66
|
-
for (const char of text) {
|
|
67
|
-
if (stringWidth(clipped + char) > remaining)
|
|
68
|
-
break;
|
|
69
|
-
clipped += char;
|
|
70
|
-
}
|
|
71
|
-
text = minimum && stringWidth(minimum) <= remaining ? minimum : clipped;
|
|
72
|
-
}
|
|
73
|
-
return fitClauses([text, ...reserved], budget);
|
|
57
|
+
const text = clipStatusText(feedback, Math.max(0, budget - reserve));
|
|
58
|
+
if (text)
|
|
59
|
+
result.push(text);
|
|
74
60
|
}
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
if (input.hintsEnabled !== false && !input.overlay)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
61
|
+
const add = (text, remaining = budget) => {
|
|
62
|
+
if (stringWidth([...result, text].join(' | ')) <= remaining)
|
|
63
|
+
result.push(cleanStatusText(text));
|
|
64
|
+
};
|
|
65
|
+
if (!feedback) {
|
|
66
|
+
for (const action of actionCandidates(input))
|
|
67
|
+
add(action, budget - reserve);
|
|
68
|
+
}
|
|
69
|
+
if (escape)
|
|
70
|
+
add(escape);
|
|
71
|
+
if (!feedback && input.hintsEnabled !== false && !input.overlay) {
|
|
72
|
+
if (input.updateAvailable)
|
|
73
|
+
add(copy.update);
|
|
74
|
+
add(copy.exit);
|
|
75
|
+
add((input.modeToggleKey ?? 'Shift+Tab') + ' ' + copy.modeLabel);
|
|
76
|
+
add(copy.help);
|
|
77
|
+
}
|
|
78
|
+
return result;
|
|
92
79
|
}
|
|
@@ -5,9 +5,13 @@ export interface AppShellProps {
|
|
|
5
5
|
cols: number;
|
|
6
6
|
header: React.ReactNode;
|
|
7
7
|
viewport: React.ReactNode;
|
|
8
|
-
|
|
8
|
+
composer: React.ReactNode;
|
|
9
|
+
composerSlotRows: number;
|
|
10
|
+
viewportRows: number;
|
|
11
|
+
details?: React.ReactNode;
|
|
12
|
+
statusRows: 0 | 1 | 2;
|
|
9
13
|
status: React.ReactNode;
|
|
10
14
|
inactive?: boolean;
|
|
11
15
|
placeholder?: React.ReactNode;
|
|
12
16
|
}
|
|
13
|
-
export declare function AppShell({ rows, cols, header, viewport,
|
|
17
|
+
export declare function AppShell({ rows, cols, header, viewport, composer, composerSlotRows, viewportRows, details, statusRows, status, inactive, placeholder, }: AppShellProps): React.ReactElement;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box } from 'ink';
|
|
3
3
|
import { frameHeight } from './frame.js';
|
|
4
|
-
export function AppShell({ rows, cols, header, viewport,
|
|
5
|
-
return (_jsxs(Box, { flexDirection: "column", height: frameHeight(rows), width: cols, overflow: "hidden", children: [_jsxs(Box, { display: inactive ? 'none' : 'flex', flexDirection: "column", height: frameHeight(rows), width: cols, overflow: "hidden", children: [_jsx(Box, { height: 1, flexShrink: 0, overflow: "hidden", children: header }), _jsx(Box, { flexDirection: "column",
|
|
4
|
+
export function AppShell({ rows, cols, header, viewport, composer, composerSlotRows, viewportRows, details, statusRows, status, inactive = false, placeholder, }) {
|
|
5
|
+
return (_jsxs(Box, { flexDirection: "column", height: frameHeight(rows), width: cols, overflow: "hidden", flexShrink: 0, children: [_jsxs(Box, { display: inactive ? 'none' : 'flex', flexDirection: "column", height: frameHeight(rows), width: cols, overflow: "hidden", flexShrink: 0, children: [_jsx(Box, { height: 1, flexShrink: 0, overflow: "hidden", children: header }), _jsx(Box, { flexDirection: "column", height: viewportRows, flexShrink: 0, overflow: "hidden", children: viewport }), _jsx(Box, { flexDirection: "column", height: composerSlotRows, justifyContent: "flex-end", flexShrink: 0, overflow: "hidden", children: composer }), _jsx(Box, { height: statusRows > 0 ? 1 : 0, flexShrink: 0, overflow: "hidden", children: status }), statusRows === 2 && _jsx(Box, { height: 1, flexShrink: 0, overflow: "hidden", children: details })] }), _jsx(Box, { display: inactive ? 'flex' : 'none', height: frameHeight(rows), overflow: "hidden", children: placeholder })] }));
|
|
6
6
|
}
|
|
@@ -35,4 +35,6 @@ interface OverlayFrameSelfManaged extends OverlayFrameBase {
|
|
|
35
35
|
}
|
|
36
36
|
export type OverlayFrameProps = OverlayFrameControlled | OverlayFrameSelfManaged;
|
|
37
37
|
export declare function OverlayFrame(props: OverlayFrameProps): React.ReactElement;
|
|
38
|
+
/** Body rows shared by controlled overlays and self-managed focused lists. */
|
|
39
|
+
export declare function overlayBodyRows(maxRows: number, cols: number): number;
|
|
38
40
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect } from 'react';
|
|
2
|
+
import { useEffect, useMemo } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
|
+
import stringWidth from 'string-width';
|
|
4
5
|
import { pickGlyphs } from '../glyphs.js';
|
|
5
6
|
import { sliceWindow } from './overlay-window.js';
|
|
6
7
|
/** Below this width the round border costs more than it conveys (§5.2). */
|
|
@@ -19,14 +20,22 @@ const MARGIN_ROWS = 1;
|
|
|
19
20
|
export function OverlayFrame(props) {
|
|
20
21
|
const { title, hint, maxRows, cols = 80, theme, caps } = props;
|
|
21
22
|
const glyphs = pickGlyphs(caps);
|
|
22
|
-
|
|
23
|
+
// Ink applies changed style keys alone. Recreating this object resets the
|
|
24
|
+
// unchanged borderTop/borderBottom=false edges to one cell on a later render.
|
|
25
|
+
const railBorderStyle = useMemo(() => ({
|
|
26
|
+
topLeft: glyphs.railVertical, top: '', topRight: '', right: '',
|
|
27
|
+
bottomRight: '', bottom: '', bottomLeft: glyphs.railVertical,
|
|
28
|
+
left: glyphs.railVertical,
|
|
29
|
+
}), [glyphs.railVertical]);
|
|
30
|
+
const compact = Number.isFinite(maxRows) && maxRows < (cols >= BORDERLESS_COLS ? 6 : 4);
|
|
31
|
+
const bordered = !compact && cols >= BORDERLESS_COLS;
|
|
23
32
|
const unbounded = !Number.isFinite(maxRows);
|
|
24
33
|
let body;
|
|
25
34
|
let position = '';
|
|
26
35
|
/** Set only in controlled mode; `null` means "nothing to report back". */
|
|
27
36
|
let overflowOffset = null;
|
|
28
37
|
if (props.rows) {
|
|
29
|
-
const chrome = MARGIN_ROWS + (bordered ? CHROME_ROWS : CHROME_ROWS - 2);
|
|
38
|
+
const chrome = compact ? 2 : MARGIN_ROWS + (bordered ? CHROME_ROWS : CHROME_ROWS - 2);
|
|
30
39
|
const height = unbounded ? props.rows.length : Math.max(1, Math.floor(maxRows) - chrome);
|
|
31
40
|
const win = sliceWindow(props.rows.length, props.scrollOffset, height);
|
|
32
41
|
body = props.rows.slice(win.start, win.start + win.visible);
|
|
@@ -49,20 +58,20 @@ export function OverlayFrame(props) {
|
|
|
49
58
|
if (overflowOffset !== null)
|
|
50
59
|
onScrollClamp?.(overflowOffset);
|
|
51
60
|
}, [overflowOffset, onScrollClamp]);
|
|
52
|
-
const inner = (_jsxs(_Fragment, { children: [_jsx(Text, { wrap: "truncate", color: theme.primary, bold: true, children: title }), body,
|
|
61
|
+
const inner = (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "row", height: 1, flexShrink: 0, children: [_jsx(Box, { flexGrow: 1, flexShrink: 1, children: _jsx(Text, { wrap: "truncate", color: theme.primary, bold: true, children: title }) }), position && _jsx(Box, { width: stringWidth(position), flexShrink: 0, children: _jsx(Text, { color: theme.muted, children: position }) })] }), _jsx(Box, { flexDirection: "column", flexShrink: 0, height: unbounded ? undefined : Math.max(1, maxRows - (compact ? 2 : bordered ? 5 : 3)), overflowY: "hidden", children: body }), _jsx(Text, { wrap: "truncate", color: theme.hintFg ?? theme.muted, children: hint })] }));
|
|
62
|
+
if (compact)
|
|
63
|
+
return _jsx(Box, { width: cols, height: Math.max(0, maxRows), flexDirection: "column", flexShrink: 0, overflowY: "hidden", children: inner });
|
|
53
64
|
if (!bordered) {
|
|
54
65
|
// Narrow terminals trade the box for a rail: same grouping, 2 fewer rows
|
|
55
66
|
// and 4 fewer columns (§5.2).
|
|
56
|
-
return (_jsx(Box, { flexDirection: "column", flexShrink: 0, marginTop: 1, children: _jsx(Box, { flexDirection: "column", flexShrink: 0, borderStyle:
|
|
57
|
-
topLeft: glyphs.railVertical,
|
|
58
|
-
top: '',
|
|
59
|
-
topRight: '',
|
|
60
|
-
right: '',
|
|
61
|
-
bottomRight: '',
|
|
62
|
-
bottom: '',
|
|
63
|
-
bottomLeft: glyphs.railVertical,
|
|
64
|
-
left: glyphs.railVertical,
|
|
65
|
-
}, borderTop: false, borderBottom: false, borderRight: false, borderColor: theme.primary, paddingLeft: 1, children: inner }) }));
|
|
67
|
+
return (_jsx(Box, { flexDirection: "column", flexShrink: 0, marginTop: 1, children: _jsx(Box, { flexDirection: "column", flexShrink: 0, borderStyle: railBorderStyle, borderTop: false, borderBottom: false, borderRight: false, borderColor: theme.primary, paddingLeft: 1, children: inner }) }));
|
|
66
68
|
}
|
|
67
69
|
return (_jsx(Box, { flexDirection: "column", flexShrink: 0, borderStyle: glyphs.boxStyle, borderColor: theme.primary, paddingX: 1, marginTop: 1, children: inner }));
|
|
68
70
|
}
|
|
71
|
+
/** Body rows shared by controlled overlays and self-managed focused lists. */
|
|
72
|
+
export function overlayBodyRows(maxRows, cols) {
|
|
73
|
+
if (!Number.isFinite(maxRows))
|
|
74
|
+
return 20;
|
|
75
|
+
const compact = maxRows < (cols >= BORDERLESS_COLS ? 6 : 4);
|
|
76
|
+
return Math.max(1, Math.floor(maxRows) - (compact ? 2 : cols >= BORDERLESS_COLS ? 5 : 3));
|
|
77
|
+
}
|
|
@@ -13,6 +13,8 @@ export interface ScrollViewportProps {
|
|
|
13
13
|
};
|
|
14
14
|
pinToBottomNonce?: number;
|
|
15
15
|
resetKey?: unknown;
|
|
16
|
+
geometryKey?: string;
|
|
17
|
+
onGeometryChange?: () => void;
|
|
16
18
|
onScrolledLinesChange?: (lines: number) => void;
|
|
17
19
|
onViewportShiftChange?: (shift: number) => void;
|
|
18
20
|
tailRowsRef?: TailSink;
|
|
@@ -34,5 +36,5 @@ export interface ScrollViewportProps {
|
|
|
34
36
|
caps: TermCapabilities;
|
|
35
37
|
children: React.ReactNode;
|
|
36
38
|
}
|
|
37
|
-
/** One offset for messages, padding and
|
|
39
|
+
/** One offset for messages, padding and panels; hidden trees keep their local state. */
|
|
38
40
|
export declare function ScrollViewport(props: ScrollViewportProps): React.ReactElement;
|
|
@@ -26,7 +26,7 @@ function elementVisible(element, clip) {
|
|
|
26
26
|
return current === clip && top >= 0 &&
|
|
27
27
|
top + measureElement(element).height <= measureElement(clip).height;
|
|
28
28
|
}
|
|
29
|
-
/** One offset for messages, padding and
|
|
29
|
+
/** One offset for messages, padding and panels; hidden trees keep their local state. */
|
|
30
30
|
export function ScrollViewport(props) {
|
|
31
31
|
const { stdout } = useStdout();
|
|
32
32
|
const { active = true, hold = false, resumeMs = 0, scrollbar } = props;
|
|
@@ -55,6 +55,7 @@ export function ScrollViewport(props) {
|
|
|
55
55
|
const previousActive = useRef(active);
|
|
56
56
|
const geometryRevision = useRef(0);
|
|
57
57
|
const geometryBounds = useRef('');
|
|
58
|
+
const lastGeometryKey = useRef(props.geometryKey);
|
|
58
59
|
const clearResume = useCallback(() => {
|
|
59
60
|
if (resumeTimer.current)
|
|
60
61
|
clearTimeout(resumeTimer.current);
|
|
@@ -125,6 +126,13 @@ export function ScrollViewport(props) {
|
|
|
125
126
|
}, []);
|
|
126
127
|
useEffect(() => { armResume(); }, [hold, resumeMs, armResume]);
|
|
127
128
|
useLayoutEffect(() => {
|
|
129
|
+
if (lastGeometryKey.current !== props.geometryKey) {
|
|
130
|
+
props.onGeometryChange?.();
|
|
131
|
+
scrollbar?.controller?.cancel();
|
|
132
|
+
scrollbar?.invalidate();
|
|
133
|
+
geometryRevision.current++;
|
|
134
|
+
lastGeometryKey.current = props.geometryKey;
|
|
135
|
+
}
|
|
128
136
|
const reset = lastReset.current !== props.resetKey;
|
|
129
137
|
lastReset.current = props.resetKey;
|
|
130
138
|
const pin = lastPin.current !== props.pinToBottomNonce;
|
|
@@ -148,7 +156,7 @@ export function ScrollViewport(props) {
|
|
|
148
156
|
const viewport = clipRef.current ? measureElement(clipRef.current).height : 0;
|
|
149
157
|
const body = bodyRef.current ? measureElement(bodyRef.current).height : 0;
|
|
150
158
|
const footer = footerRef.current ? measureElement(footerRef.current).height : 0;
|
|
151
|
-
const layout = buildDocumentLayout({
|
|
159
|
+
const layout = buildDocumentLayout({ viewportRows: viewport, bodyRows: body, footerRows: footer });
|
|
152
160
|
const padding = props.footer === undefined ? 0 : layout.paddingRows;
|
|
153
161
|
const content = body + footer + padding;
|
|
154
162
|
const tail = props.tailRowsRef?.current.rows ?? 0;
|