@aragon-agent/cli 0.6.7 → 0.6.9
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 +27 -0
- package/README.md +107 -77
- package/dist/agent/controller.d.ts +11 -7
- package/dist/agent/controller.js +35 -48
- package/dist/agent/headless.d.ts +2 -1
- package/dist/agent/prompt-options.d.ts +15 -0
- package/dist/agent/prompt-options.js +1 -0
- package/dist/agent/reducer.d.ts +7 -113
- package/dist/agent/reducer.js +0 -62
- package/dist/cli.js +111 -182
- package/dist/commands/builtins.js +3 -1
- package/dist/commands/perf.d.ts +32 -2
- package/dist/commands/perf.js +32 -2
- package/dist/commands/registry.d.ts +4 -5
- package/dist/config/env.js +0 -130
- package/dist/config/load.d.ts +0 -2
- package/dist/config/load.js +2 -31
- package/dist/config/schema.d.ts +1 -14
- package/dist/config/schema.js +0 -1
- package/dist/config/store.js +2 -0
- package/dist/input/interrupt-gesture.d.ts +18 -0
- package/dist/input/interrupt-gesture.js +19 -0
- package/dist/input/pointer-router.d.ts +11 -0
- package/dist/input/pointer-router.js +14 -0
- package/dist/logging/install.d.ts +2 -2
- package/dist/logging/install.js +2 -2
- package/dist/todo/follow-through.d.ts +0 -4
- package/dist/todo/follow-through.js +0 -4
- package/dist/todo/limits.d.ts +0 -10
- package/dist/todo/limits.js +0 -10
- package/dist/todo/prompt.d.ts +0 -15
- package/dist/todo/store.d.ts +8 -13
- package/dist/todo/store.js +13 -14
- package/dist/todo/types.d.ts +2 -1
- package/dist/ui/ActivityLine.d.ts +26 -34
- package/dist/ui/ActivityLine.js +30 -19
- package/dist/ui/App.d.ts +3 -41
- package/dist/ui/App.js +223 -247
- package/dist/ui/BottomStatusRow.d.ts +1 -3
- package/dist/ui/BottomStatusRow.js +4 -8
- package/dist/ui/Composer.d.ts +38 -5
- package/dist/ui/Composer.js +45 -23
- package/dist/ui/Header.d.ts +2 -16
- package/dist/ui/Header.js +3 -18
- package/dist/ui/Logo.d.ts +1 -17
- package/dist/ui/Logo.js +0 -16
- package/dist/ui/ModeChip.d.ts +7 -18
- package/dist/ui/ModeChip.js +9 -0
- package/dist/ui/PromptCaret.d.ts +11 -0
- package/dist/ui/PromptCaret.js +31 -0
- package/dist/ui/PromptInput.d.ts +10 -33
- package/dist/ui/PromptInput.js +37 -20
- package/dist/ui/StatusBar.d.ts +1 -11
- package/dist/ui/StatusBar.js +14 -8
- package/dist/ui/ToastStack.d.ts +2 -18
- package/dist/ui/ToastStack.js +4 -11
- package/dist/ui/Transcript.d.ts +5 -71
- package/dist/ui/Transcript.js +10 -199
- package/dist/ui/composer-rows.js +50 -9
- package/dist/ui/entries/AssistantEntry.d.ts +0 -23
- package/dist/ui/entries/AssistantEntry.js +3 -4
- package/dist/ui/entries/DiffView.d.ts +1 -7
- package/dist/ui/entries/DiffView.js +2 -2
- package/dist/ui/entries/ToolCard.d.ts +0 -6
- package/dist/ui/entries/ToolCard.js +4 -13
- package/dist/ui/frame-differ.d.ts +13 -0
- package/dist/ui/frame-differ.js +37 -24
- package/dist/ui/frame-observer.d.ts +17 -0
- package/dist/ui/frame-observer.js +81 -0
- package/dist/ui/frame-parser.d.ts +7 -0
- package/dist/ui/frame-parser.js +18 -0
- package/dist/ui/ink-runtime.d.ts +1 -0
- package/dist/ui/ink-runtime.js +21 -0
- package/dist/ui/layout/AppShell.d.ts +4 -58
- package/dist/ui/layout/AppShell.js +3 -6
- package/dist/ui/layout/OverlayFrame.d.ts +1 -25
- package/dist/ui/layout/OverlayFrame.js +0 -24
- package/dist/ui/layout/ScrollIndicator.d.ts +4 -3
- package/dist/ui/layout/ScrollIndicator.js +4 -4
- package/dist/ui/layout/ScrollViewport.d.ts +16 -66
- package/dist/ui/layout/ScrollViewport.js +195 -185
- package/dist/ui/layout/budget.d.ts +3 -51
- package/dist/ui/layout/budget.js +4 -53
- package/dist/ui/layout/document-layout.d.ts +13 -0
- package/dist/ui/layout/document-layout.js +11 -0
- package/dist/ui/layout/follow-state.d.ts +1 -0
- package/dist/ui/layout/follow-state.js +1 -3
- package/dist/ui/layout/frame.d.ts +0 -22
- package/dist/ui/layout/frame.js +0 -36
- package/dist/ui/layout/scroll-indicator.d.ts +1 -1
- package/dist/ui/layout/scroll-indicator.js +3 -2
- package/dist/ui/layout/scrollbar-geometry.d.ts +17 -0
- package/dist/ui/layout/scrollbar-geometry.js +13 -0
- package/dist/ui/layout/todo-layout.d.ts +2 -3
- package/dist/ui/layout/todo-layout.js +9 -9
- package/dist/ui/layout/viewport-geometry.d.ts +2 -1
- package/dist/ui/layout/viewport-geometry.js +2 -1
- package/dist/ui/layout/virtual-window.d.ts +1 -0
- package/dist/ui/layout/virtual-window.js +1 -1
- package/dist/ui/live-clamp.d.ts +1 -29
- package/dist/ui/live-clamp.js +1 -29
- package/dist/ui/overlays/HelpOverlay.js +6 -2
- package/dist/ui/run-status-row.d.ts +61 -0
- package/dist/ui/run-status-row.js +70 -0
- package/dist/ui/screen.d.ts +1 -1
- package/dist/ui/screen.js +4 -6
- package/dist/ui/scrollbar-controller.d.ts +30 -0
- package/dist/ui/scrollbar-controller.js +124 -0
- package/dist/ui/transcript-text.d.ts +0 -8
- package/dist/ui/transcript-text.js +0 -8
- package/dist/ui/use-startup-notices.d.ts +1 -57
- package/dist/ui/use-startup-notices.js +3 -54
- package/dist/ui/use-wheel-routing.d.ts +3 -1
- package/dist/ui/use-wheel-routing.js +6 -0
- package/package.json +2 -2
- package/dist/ui/TodoStrip.d.ts +0 -36
- package/dist/ui/TodoStrip.js +0 -32
|
@@ -54,9 +54,7 @@
|
|
|
54
54
|
import React from 'react';
|
|
55
55
|
import type { Theme } from './theme.js';
|
|
56
56
|
import type { Toast } from '../agent/reducer.js';
|
|
57
|
-
import type { RenderMode } from './layout/frame.js';
|
|
58
57
|
export interface BottomStatusRowProps {
|
|
59
|
-
mode: RenderMode;
|
|
60
58
|
toasts: Toast[];
|
|
61
59
|
/** `<ActivityLine>` while a run owns the screen, else `null`. */
|
|
62
60
|
activity: React.ReactNode;
|
|
@@ -94,4 +92,4 @@ export interface BottomStatusRowProps {
|
|
|
94
92
|
update?: React.ReactNode;
|
|
95
93
|
theme: Theme;
|
|
96
94
|
}
|
|
97
|
-
export declare function BottomStatusRow({
|
|
95
|
+
export declare function BottomStatusRow({ toasts, activity, activityGlyph, update, theme, }: BottomStatusRowProps): React.ReactElement | null;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { ToastStack } from './ToastStack.js';
|
|
4
|
-
export function BottomStatusRow({
|
|
4
|
+
export function BottomStatusRow({ toasts, activity, activityGlyph, update, theme, }) {
|
|
5
5
|
// A toast wins the row outright — but a run in flight keeps its spinner on it.
|
|
6
6
|
if (toasts.length > 0) {
|
|
7
7
|
const spinner = activity ? activityGlyph : null;
|
|
8
8
|
// Byte-identical to the old branch whenever there is no live spinner to
|
|
9
9
|
// carry: no run, an ASCII terminal, or `reducedMotion`.
|
|
10
10
|
if (!spinner)
|
|
11
|
-
return _jsx(ToastStack, { toasts: toasts, theme: theme
|
|
12
|
-
const fullscreen = mode === 'fullscreen';
|
|
11
|
+
return _jsx(ToastStack, { toasts: toasts, theme: theme });
|
|
13
12
|
return (
|
|
14
13
|
// `flexDirection` SPELLED OUT even though `row` is Yoga's default: it is
|
|
15
14
|
// what keeps two children on ONE row, and the header above turns on that
|
|
@@ -17,15 +16,12 @@ export function BottomStatusRow({ mode, toasts, activity, activityGlyph, update,
|
|
|
17
16
|
// below that declares `column` — and "make them match" is a one-word edit
|
|
18
17
|
// that doubles the row, takes it from the transcript, and desynchronises
|
|
19
18
|
// every `viewportRows()` consumer without failing a type or a lint.
|
|
20
|
-
_jsxs(Box, { flexDirection: "row", flexShrink: 0, children: [_jsx(Box, { flexShrink: 0,
|
|
19
|
+
_jsxs(Box, { flexDirection: "row", flexShrink: 0, children: [_jsx(Box, { flexShrink: 0, children: _jsxs(Text, { color: theme.thinking, children: [' ', spinner] }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, flexShrink: 1, overflow: "hidden", children: _jsx(ToastStack, { toasts: toasts, theme: theme }) })] }));
|
|
21
20
|
}
|
|
22
|
-
// Inline has no frame and therefore nothing to protect: `ToastStack`'s inline
|
|
23
|
-
// branch is already a conditional 0-3 row stack, and these are simply one more
|
|
24
|
-
// conditional row above it.
|
|
25
21
|
if (activity)
|
|
26
22
|
return _jsx(Box, { flexShrink: 0, children: activity });
|
|
27
23
|
if (update)
|
|
28
24
|
return _jsx(Box, { flexShrink: 0, children: update });
|
|
29
25
|
// The blank full-screen row that IS the budget must keep being emitted.
|
|
30
|
-
return _jsx(ToastStack, { toasts: toasts, theme: theme
|
|
26
|
+
return _jsx(ToastStack, { toasts: toasts, theme: theme });
|
|
31
27
|
}
|
package/dist/ui/Composer.d.ts
CHANGED
|
@@ -9,20 +9,42 @@
|
|
|
9
9
|
* "the input is no longer empty", and routing that through App would re-render
|
|
10
10
|
* the transcript on the first keystroke of every message.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* them 31 columns wide, were both redundant and the reason the status bar had a
|
|
15
|
-
* `cols >= 110` breakpoint.
|
|
12
|
+
* The fixed status row retains emergency and history-navigation hints while
|
|
13
|
+
* this footer scrolls outside the viewport. Editor state stays mounted.
|
|
16
14
|
*/
|
|
17
15
|
import React from 'react';
|
|
16
|
+
import { type DOMElement } from 'ink';
|
|
18
17
|
import type { Theme } from './theme.js';
|
|
19
18
|
import type { TermCapabilities } from './capabilities.js';
|
|
20
19
|
import { pickGlyphs } from './glyphs.js';
|
|
21
20
|
import { type CommandOption } from './PromptInput.js';
|
|
21
|
+
import { type RunActivity } from './run-status-row.js';
|
|
22
22
|
import { type AgentMode } from '../agent/agent-mode.js';
|
|
23
|
+
/**
|
|
24
|
+
* The run status row (tui-scrollbar-edge-and-run-row §3.3): the spinner, the phrase
|
|
25
|
+
* and the `steer / interrupt / exit` clauses on ONE row above the input box.
|
|
26
|
+
* `App` passes it only while a run is in flight AND the idle hint row would have
|
|
27
|
+
* been shown, so enabling it never changes the footer's height.
|
|
28
|
+
*/
|
|
29
|
+
export interface RunRowProps {
|
|
30
|
+
activity: RunActivity;
|
|
31
|
+
/** The row is in the viewport with no overlay: draw the animated spinner. */
|
|
32
|
+
live: boolean;
|
|
33
|
+
/** Measured by `ScrollViewport` to learn whether the row has scrolled away. */
|
|
34
|
+
rowRef?: React.RefObject<DOMElement>;
|
|
35
|
+
}
|
|
23
36
|
export interface ComposerProps {
|
|
37
|
+
/** Present only while the run status row replaces the hint row below the input. */
|
|
38
|
+
runRow?: RunRowProps | null;
|
|
39
|
+
cols?: number;
|
|
40
|
+
cursorVisible?: boolean;
|
|
41
|
+
onInteraction?: () => void;
|
|
42
|
+
measureRef?: React.RefObject<DOMElement>;
|
|
24
43
|
/** False while an overlay owns the keyboard (the input is visually blurred). */
|
|
25
44
|
isActive: boolean;
|
|
45
|
+
reducedMotion?: boolean;
|
|
46
|
+
onEscape?: () => void;
|
|
47
|
+
onEscapeDismiss?: () => void;
|
|
26
48
|
running: boolean;
|
|
27
49
|
history: string[];
|
|
28
50
|
commands: CommandOption[];
|
|
@@ -90,6 +112,17 @@ export interface ComposerProps {
|
|
|
90
112
|
onSubmit: (text: string) => void;
|
|
91
113
|
onHelp?: () => void;
|
|
92
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* The running-state hint as separate clauses, most urgent first.
|
|
117
|
+
*
|
|
118
|
+
* `hintText` joins them (its output is pinned by AC-43), and the run status row
|
|
119
|
+
* consumes the SAME array so it can drop whole clauses from the tail on a narrow
|
|
120
|
+
* terminal instead of cutting `interrupt` in half. One source, two consumers.
|
|
121
|
+
*/
|
|
122
|
+
export declare function runningHintClauses(opts: {
|
|
123
|
+
glyphs: ReturnType<typeof pickGlyphs>;
|
|
124
|
+
services: number;
|
|
125
|
+
}): string[];
|
|
93
126
|
/**
|
|
94
127
|
* Build the hint row.
|
|
95
128
|
*
|
|
@@ -119,5 +152,5 @@ declare function hintText(opts: {
|
|
|
119
152
|
* than fishing the string out of a rendered frame.
|
|
120
153
|
*/
|
|
121
154
|
export declare const hintTextForTest: typeof hintText;
|
|
122
|
-
export declare function Composer({ isActive, running, history, commands, cwd, showHint, submitCount, hintsEnabled, agentMode, services, modeToggleKey, popupMaxRows, popupMaxHeight, onPopupRowsChange, scrolledLines, onDraftRows, onNotice, theme, caps, onSubmit, onHelp, }: ComposerProps): React.ReactElement;
|
|
155
|
+
export declare function Composer({ runRow, cols, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, showHint, submitCount, hintsEnabled, agentMode, services, modeToggleKey, popupMaxRows, popupMaxHeight, onPopupRowsChange, scrolledLines, onDraftRows, onNotice, theme, caps, onSubmit, onHelp, }: ComposerProps): React.ReactElement;
|
|
123
156
|
export {};
|
package/dist/ui/Composer.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Composer (spec §4.7) — the thin shell around the existing `PromptInput`.
|
|
4
4
|
*
|
|
@@ -10,18 +10,35 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
10
10
|
* "the input is no longer empty", and routing that through App would re-render
|
|
11
11
|
* the transcript on the first keystroke of every message.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* them 31 columns wide, were both redundant and the reason the status bar had a
|
|
16
|
-
* `cols >= 110` breakpoint.
|
|
13
|
+
* The fixed status row retains emergency and history-navigation hints while
|
|
14
|
+
* this footer scrolls outside the viewport. Editor state stays mounted.
|
|
17
15
|
*/
|
|
18
16
|
import { useCallback, useState } from 'react';
|
|
19
17
|
import { Box, Text } from 'ink';
|
|
18
|
+
import stringWidth from 'string-width';
|
|
20
19
|
import { pickGlyphs } from './glyphs.js';
|
|
21
20
|
import { HINT_FADE_AFTER } from '../config/schema.js';
|
|
22
21
|
import { PromptInput } from './PromptInput.js';
|
|
23
|
-
import { ModeChip } from './ModeChip.js';
|
|
22
|
+
import { ModeChip, modeChipCols } from './ModeChip.js';
|
|
23
|
+
import { ActivityLabel, resolveActivityLabel } from './ActivityLine.js';
|
|
24
|
+
import { RUN_ROW_LEAD, planRunRow } from './run-status-row.js';
|
|
24
25
|
import { MODE_LABEL, MODE_TOGGLE_KEYS, nextMode } from '../agent/agent-mode.js';
|
|
26
|
+
/**
|
|
27
|
+
* The running-state hint as separate clauses, most urgent first.
|
|
28
|
+
*
|
|
29
|
+
* `hintText` joins them (its output is pinned by AC-43), and the run status row
|
|
30
|
+
* consumes the SAME array so it can drop whole clauses from the tail on a narrow
|
|
31
|
+
* terminal instead of cutting `interrupt` in half. One source, two consumers.
|
|
32
|
+
*/
|
|
33
|
+
export function runningHintClauses(opts) {
|
|
34
|
+
const { glyphs, services } = opts;
|
|
35
|
+
const clauses = [`${glyphs.enterKey} steer`, `esc${glyphs.times}2 interrupt`];
|
|
36
|
+
// Confirmation is named even when service controls also need space.
|
|
37
|
+
if (services > 0)
|
|
38
|
+
clauses.push(`ctrl+c stop ${services}`);
|
|
39
|
+
clauses.push(`ctrl+c${glyphs.times}2 exit`);
|
|
40
|
+
return clauses;
|
|
41
|
+
}
|
|
25
42
|
/**
|
|
26
43
|
* Build the hint row.
|
|
27
44
|
*
|
|
@@ -42,20 +59,8 @@ function hintText(opts) {
|
|
|
42
59
|
// clauses are shown; the row is `wrap="truncate"` and they are ordered
|
|
43
60
|
// most-urgent-first, so a terminal too narrow for all of them drops the exit
|
|
44
61
|
// clause last rather than the abort key.
|
|
45
|
-
if (running)
|
|
46
|
-
|
|
47
|
-
return [
|
|
48
|
-
`${glyphs.enterKey} steer`,
|
|
49
|
-
// Rung two is named because the whole point of the ladder is that the
|
|
50
|
-
// first press can fail; a user who does not know there is a second press
|
|
51
|
-
// is left exactly where the reported screenshot left them.
|
|
52
|
-
`esc abort${glyphs.times}2 force`,
|
|
53
|
-
`ctrl+c stop ${services}`,
|
|
54
|
-
`ctrl+c${glyphs.times}2 exit`,
|
|
55
|
-
].join(dot);
|
|
56
|
-
}
|
|
57
|
-
return [`${glyphs.enterKey} steer`, 'esc abort', `ctrl+c${glyphs.times}2 exit`].join(dot);
|
|
58
|
-
}
|
|
62
|
+
if (running)
|
|
63
|
+
return runningHintClauses({ glyphs, services }).join(dot);
|
|
59
64
|
// The hint names the DESTINATION, not the current state — the correct label
|
|
60
65
|
// for a toggle affordance, and the reason the mode word here is the OPPOSITE
|
|
61
66
|
// of the one on the chip beside it.
|
|
@@ -90,7 +95,21 @@ function hintText(opts) {
|
|
|
90
95
|
* than fishing the string out of a rendered frame.
|
|
91
96
|
*/
|
|
92
97
|
export const hintTextForTest = hintText;
|
|
93
|
-
|
|
98
|
+
function RunRow({ runRow, cols, services, agentMode, theme, caps, }) {
|
|
99
|
+
const glyphs = pickGlyphs(caps);
|
|
100
|
+
const separator = ` ${glyphs.midDot} `;
|
|
101
|
+
const clauses = runningHintClauses({ glyphs, services });
|
|
102
|
+
const plan = planRunRow({
|
|
103
|
+
cols,
|
|
104
|
+
// Spinner glyph + the space after it, then the label.
|
|
105
|
+
labelCols: 2 + stringWidth(resolveActivityLabel(runRow.activity, glyphs)),
|
|
106
|
+
hintClauseCols: clauses.map((clause) => stringWidth(clause)),
|
|
107
|
+
separatorCols: stringWidth(separator),
|
|
108
|
+
chipCols: modeChipCols(agentMode),
|
|
109
|
+
});
|
|
110
|
+
return (_jsxs(Box, { ref: runRow.rowRef, flexDirection: "row", flexShrink: 0, children: [_jsx(Box, { width: RUN_ROW_LEAD, flexShrink: 0, children: _jsx(Text, { children: ' ' }) }), _jsx(Box, { width: plan.labelCols, flexShrink: 0, children: _jsx(ActivityLabel, { ...runRow.activity, spinnerLive: runRow.live, theme: theme, caps: caps }) }), plan.hintClauses > 0 && (_jsxs(Text, { wrap: "truncate", color: theme.hintFg ?? theme.muted, children: [separator, clauses.slice(0, plan.hintClauses).join(separator)] })), plan.chip && (_jsxs(_Fragment, { children: [_jsx(Box, { flexGrow: 1 }), _jsx(ModeChip, { mode: agentMode, theme: theme, caps: caps })] }))] }));
|
|
111
|
+
}
|
|
112
|
+
export function Composer({ runRow, cols, cursorVisible, onInteraction, measureRef, isActive, reducedMotion, onEscape, onEscapeDismiss, running, history, commands, cwd, showHint, submitCount, hintsEnabled, agentMode, services = 0, modeToggleKey = MODE_TOGGLE_KEYS.primary, popupMaxRows, popupMaxHeight, onPopupRowsChange, scrolledLines = 0, onDraftRows, onNotice, theme, caps, onSubmit, onHelp, }) {
|
|
94
113
|
const [hasDraft, setHasDraft] = useState(false);
|
|
95
114
|
const onDraftChange = useCallback((next) => {
|
|
96
115
|
setHasDraft(next.hasDraft);
|
|
@@ -109,8 +128,11 @@ export function Composer({ isActive, running, history, commands, cwd, showHint,
|
|
|
109
128
|
? theme.focusBorder ?? theme.primary
|
|
110
129
|
: theme.idleBorder ?? theme.border;
|
|
111
130
|
const glyphs = pickGlyphs(caps);
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
// While the run row is up it REPLACES the hint row below the input (same row
|
|
132
|
+
// count, so starting a run never moves the layout).
|
|
133
|
+
const runRowActive = running && !!runRow;
|
|
134
|
+
const visible = showHint && hintsEnabled && !runRowActive;
|
|
135
|
+
return (_jsxs(Box, { ref: measureRef, flexDirection: "column", flexShrink: 0, children: [runRowActive && (_jsx(RunRow, { runRow: runRow, cols: cols ?? process.stdout.columns ?? 80, services: services, agentMode: agentMode, theme: theme, caps: caps })), _jsx(PromptInput, { cols: cols, cursorVisible: cursorVisible, onInteraction: onInteraction, isActive: isActive, reducedMotion: reducedMotion, onEscape: onEscape, onEscapeDismiss: onEscapeDismiss, running: running, history: history, commands: commands, cwd: cwd, theme: theme, caps: caps, popupMaxRows: popupMaxRows, popupMaxHeight: popupMaxHeight, onPopupRowsChange: onPopupRowsChange, onSubmit: onSubmit, onHelp: onHelp, agentMode: agentMode, borderColor: borderColor, onDraftChange: onDraftChange, onNotice: onNotice, scrolledLines: scrolledLines }), visible && (
|
|
114
136
|
// The chip rides the EXISTING hint row rather than adding one, so it
|
|
115
137
|
// costs zero rows on a 24-row terminal.
|
|
116
138
|
_jsxs(Box, { flexDirection: "row", flexShrink: 0, children: [_jsx(Text, { children: ' ' }), _jsx(ModeChip, { mode: agentMode, theme: theme, caps: caps }), _jsx(Text, { wrap: "truncate", color: theme.hintFg ?? theme.muted, children: hintText({
|
package/dist/ui/Header.d.ts
CHANGED
|
@@ -1,23 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Header (spec §4.2) — the always-present brand bar.
|
|
3
|
-
*
|
|
4
|
-
* In full-screen this is EXACTLY ONE ROW at every size, which is what makes the
|
|
5
|
-
* viewport height monotonic in `rows` and the first submit jump-free. The 6-row
|
|
6
|
-
* wordmark it used to host now lives in `SessionOpener`, inside the viewport.
|
|
7
|
-
*
|
|
8
|
-
* `banner` survives for the inline path only (`App.tsx` picks it with a local
|
|
9
|
-
* ternary and never calls `pickHeaderVariant`), so inline keeps its v0.3.0
|
|
10
|
-
* geometry. `art` is no longer reachable from here.
|
|
11
|
-
*
|
|
12
|
-
* Every line is `wrap="truncate"`: a long cwd silently costing a second row
|
|
13
|
-
* would push the bottom chrome off-frame and break R2.
|
|
14
|
-
*/
|
|
1
|
+
/** One-row brand bar, with a compact variant for narrow terminals. */
|
|
15
2
|
import React from 'react';
|
|
16
3
|
import type { Theme } from './theme.js';
|
|
17
4
|
import type { TermCapabilities } from './capabilities.js';
|
|
18
5
|
import { type HeaderVariant } from './Logo.js';
|
|
19
6
|
interface HeaderProps {
|
|
20
|
-
version: string;
|
|
21
7
|
cwd: string;
|
|
22
8
|
provider: string;
|
|
23
9
|
model: string;
|
|
@@ -26,5 +12,5 @@ interface HeaderProps {
|
|
|
26
12
|
theme: Theme;
|
|
27
13
|
caps: TermCapabilities;
|
|
28
14
|
}
|
|
29
|
-
export declare function Header({
|
|
15
|
+
export declare function Header({ cwd, provider, model, hasKey, variant, theme, caps, }: HeaderProps): React.ReactElement;
|
|
30
16
|
export {};
|
package/dist/ui/Header.js
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { homedir } from 'node:os';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
2
|
import { basename } from 'node:path';
|
|
4
3
|
import { Box, Text } from 'ink';
|
|
5
4
|
import { Logo } from './Logo.js';
|
|
6
|
-
|
|
7
|
-
function tildeCwd(cwd) {
|
|
8
|
-
const home = homedir();
|
|
9
|
-
if (home && cwd.startsWith(home))
|
|
10
|
-
return `~${cwd.slice(home.length)}`;
|
|
11
|
-
return cwd;
|
|
12
|
-
}
|
|
13
|
-
function KeyDot({ hasKey, theme }) {
|
|
14
|
-
return (_jsxs(Text, { color: hasKey ? theme.toolDone : theme.toolRunning, children: [hasKey ? theme.symbols.keyOn : theme.symbols.keyOff, " ", hasKey ? 'key set' : 'no key'] }));
|
|
15
|
-
}
|
|
16
|
-
export function Header({ version, cwd, provider, model, hasKey, variant, theme, caps, }) {
|
|
5
|
+
export function Header({ cwd, provider, model, hasKey, variant, theme, caps, }) {
|
|
17
6
|
if (variant === 'mini') {
|
|
18
7
|
return (_jsx(Box, { flexDirection: "column", flexShrink: 0, children: _jsx(Text, { wrap: "truncate", children: _jsx(Logo, { variant: "mini", theme: theme, caps: caps }) }) }));
|
|
19
8
|
}
|
|
20
|
-
|
|
21
|
-
return (_jsx(Box, { flexDirection: "column", flexShrink: 0, children: _jsxs(Text, { wrap: "truncate", children: [_jsx(Logo, { variant: "bar", theme: theme, caps: caps }), _jsxs(Text, { color: theme.accent, children: [' ', provider, ":", model] }), _jsxs(Text, { color: theme.muted, children: [' ', basename(cwd) || cwd] }), _jsxs(Text, { color: hasKey ? theme.toolDone : theme.toolRunning, children: [' ', hasKey ? theme.symbols.keyOn : theme.symbols.keyOff] })] }) }));
|
|
22
|
-
}
|
|
23
|
-
// `banner` (inline only): wordmark row + meta row.
|
|
24
|
-
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [_jsx(Logo, { variant: variant, theme: theme, caps: caps }), _jsxs(Text, { wrap: "truncate", children: [_jsxs(Text, { color: theme.muted, children: ["v", version] }), _jsxs(Text, { color: theme.accent, children: [' ', provider, ":", model] }), _jsx(Text, { children: ' ' }), _jsx(KeyDot, { hasKey: hasKey, theme: theme }), _jsxs(Text, { color: theme.muted, children: [' ', tildeCwd(cwd)] })] })] }));
|
|
9
|
+
return (_jsx(Box, { flexDirection: "column", flexShrink: 0, children: _jsxs(Text, { wrap: "truncate", children: [_jsx(Logo, { variant: "bar", theme: theme, caps: caps }), _jsxs(Text, { color: theme.accent, children: [' ', provider, ":", model] }), _jsxs(Text, { color: theme.muted, children: [' ', basename(cwd) || cwd] }), _jsxs(Text, { color: hasKey ? theme.toolDone : theme.toolRunning, children: [' ', hasKey ? theme.symbols.keyOn : theme.symbols.keyOff] })] }) }));
|
|
25
10
|
}
|
package/dist/ui/Logo.d.ts
CHANGED
|
@@ -8,25 +8,9 @@ import type { TermCapabilities } from './capabilities.js';
|
|
|
8
8
|
/** 6 rows × 52 columns, every row equal width (pinned by logo.test.ts). */
|
|
9
9
|
export declare const LOGO_ART: readonly string[];
|
|
10
10
|
export declare const LOGO_ART_WIDTH = 52;
|
|
11
|
-
export type HeaderVariant = '
|
|
11
|
+
export type HeaderVariant = 'bar' | 'mini';
|
|
12
12
|
/** How the one-off opening wordmark renders inside the viewport (§4.2). */
|
|
13
13
|
export type OpenerVariant = 'art' | 'banner' | 'none';
|
|
14
|
-
/**
|
|
15
|
-
* Pick the header tier (spec §4.2).
|
|
16
|
-
*
|
|
17
|
-
* `cols` is the only input, and that is the point. The old signature also took
|
|
18
|
-
* `empty` and `rows` and could return `art`, so the brand region was 8 rows tall
|
|
19
|
-
* on an empty session and 1 row afterwards: sending the first message made the
|
|
20
|
-
* viewport jump by 7 rows, and widening a 27-row terminal to 28 rows made the
|
|
21
|
-
* visible content SHRINK. A constant-height brand bar removes both, which is
|
|
22
|
-
* what lets `budget.ts::viewportRows` be monotonic (A-3/A-4/A-9).
|
|
23
|
-
*
|
|
24
|
-
* The wordmark did not disappear — it moved into the viewport as
|
|
25
|
-
* `SessionOpener`, where it scrolls away like any other content.
|
|
26
|
-
*
|
|
27
|
-
* NOTE: `Header` still renders `banner`; the inline path picks it directly at
|
|
28
|
-
* `App.tsx` and does not call this function.
|
|
29
|
-
*/
|
|
30
14
|
export declare function pickHeaderVariant(cols: number): HeaderVariant;
|
|
31
15
|
/**
|
|
32
16
|
* Pick the opening wordmark tier.
|
package/dist/ui/Logo.js
CHANGED
|
@@ -22,22 +22,6 @@ const ART_MIN_VIEWPORT_ROWS = 14;
|
|
|
22
22
|
const ART_MIN_COLS = LOGO_ART_WIDTH + 4;
|
|
23
23
|
const BANNER_MIN_COLS = 48;
|
|
24
24
|
const BAR_MIN_COLS = 40;
|
|
25
|
-
/**
|
|
26
|
-
* Pick the header tier (spec §4.2).
|
|
27
|
-
*
|
|
28
|
-
* `cols` is the only input, and that is the point. The old signature also took
|
|
29
|
-
* `empty` and `rows` and could return `art`, so the brand region was 8 rows tall
|
|
30
|
-
* on an empty session and 1 row afterwards: sending the first message made the
|
|
31
|
-
* viewport jump by 7 rows, and widening a 27-row terminal to 28 rows made the
|
|
32
|
-
* visible content SHRINK. A constant-height brand bar removes both, which is
|
|
33
|
-
* what lets `budget.ts::viewportRows` be monotonic (A-3/A-4/A-9).
|
|
34
|
-
*
|
|
35
|
-
* The wordmark did not disappear — it moved into the viewport as
|
|
36
|
-
* `SessionOpener`, where it scrolls away like any other content.
|
|
37
|
-
*
|
|
38
|
-
* NOTE: `Header` still renders `banner`; the inline path picks it directly at
|
|
39
|
-
* `App.tsx` and does not call this function.
|
|
40
|
-
*/
|
|
41
25
|
export function pickHeaderVariant(cols) {
|
|
42
26
|
return cols >= BAR_MIN_COLS ? 'bar' : 'mini';
|
|
43
27
|
}
|
package/dist/ui/ModeChip.d.ts
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ModeChip — the `PLAN` badge on the composer's hint row (plan-mode §6.1).
|
|
3
|
-
*
|
|
4
|
-
* `theme.chip = { fg, bg }` exists in every palette and was, until now, unused.
|
|
5
|
-
* A mode badge is what it was for.
|
|
6
|
-
*
|
|
7
|
-
* SHOWN ONLY IN PLAN MODE. `BUILD` is the default, and a badge that is always on
|
|
8
|
-
* is furniture rather than signal; it also means the Build-mode composer renders
|
|
9
|
-
* exactly as it did before this feature existed.
|
|
10
|
-
*
|
|
11
|
-
* THE CHIP IS AN ENHANCEMENT, NOT THE GUARANTEE (P1-6). It rides the hint row,
|
|
12
|
-
* which disappears on a short terminal (`showHint`), under `hints: false`, and
|
|
13
|
-
* in inline render mode (which mounts a bare `PromptInput` and never mounts
|
|
14
|
-
* `Composer` at all). The GUARANTEED carrier is the status-bar word, which
|
|
15
|
-
* renders in both `AppShell` branches and has no opt-out. If you ever move the
|
|
16
|
-
* mode indicator, that is the constraint to preserve: the status bar must always
|
|
17
|
-
* name a non-default mode; the chip may.
|
|
18
|
-
*/
|
|
19
1
|
import React from 'react';
|
|
20
2
|
import type { Theme } from './theme.js';
|
|
21
3
|
import type { TermCapabilities } from './capabilities.js';
|
|
@@ -25,5 +7,12 @@ interface ModeChipProps {
|
|
|
25
7
|
theme: Theme;
|
|
26
8
|
caps: TermCapabilities;
|
|
27
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Columns `ModeChip` occupies, including its trailing gap (plan => label + 3, any
|
|
12
|
+
* other mode 0). Both render branches (padded background chip, bracketed text)
|
|
13
|
+
* come to the same width, which is what lets the run status row budget for it
|
|
14
|
+
* without measuring a rendered element.
|
|
15
|
+
*/
|
|
16
|
+
export declare function modeChipCols(mode: AgentMode): number;
|
|
28
17
|
export declare function ModeChip({ mode, theme, caps }: ModeChipProps): React.ReactElement | null;
|
|
29
18
|
export {};
|
package/dist/ui/ModeChip.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { MODE_LABEL } from '../agent/agent-mode.js';
|
|
4
|
+
/**
|
|
5
|
+
* Columns `ModeChip` occupies, including its trailing gap (plan => label + 3, any
|
|
6
|
+
* other mode 0). Both render branches (padded background chip, bracketed text)
|
|
7
|
+
* come to the same width, which is what lets the run status row budget for it
|
|
8
|
+
* without measuring a rendered element.
|
|
9
|
+
*/
|
|
10
|
+
export function modeChipCols(mode) {
|
|
11
|
+
return mode === 'plan' ? MODE_LABEL[mode].length + 3 : 0;
|
|
12
|
+
}
|
|
4
13
|
export function ModeChip({ mode, theme, caps }) {
|
|
5
14
|
if (mode !== 'plan')
|
|
6
15
|
return null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface PromptCaretProps {
|
|
3
|
+
text: string;
|
|
4
|
+
active: boolean;
|
|
5
|
+
reducedMotion: boolean;
|
|
6
|
+
colorLevel: 0 | 1 | 2 | 3;
|
|
7
|
+
color?: string;
|
|
8
|
+
resetKey: object;
|
|
9
|
+
}
|
|
10
|
+
/** The only blinking state belongs to this leaf, never to the editor. */
|
|
11
|
+
export declare function PromptCaret({ text, active, reducedMotion, colorLevel, color, resetKey, }: PromptCaretProps): React.ReactElement;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useLayoutEffect, useState } from 'react';
|
|
3
|
+
import { Text } from 'ink';
|
|
4
|
+
import { displayWidth } from './composer-rows.js';
|
|
5
|
+
function monochromeMarker(text) {
|
|
6
|
+
// Ink's output grid advances for combining marks, even though they have no
|
|
7
|
+
// terminal width. Retain them in both phases so its cell count stays stable.
|
|
8
|
+
const replace = (marker) => Array.from(text, (point) => {
|
|
9
|
+
const width = displayWidth(point);
|
|
10
|
+
return width > 0 ? marker.repeat(width) : point;
|
|
11
|
+
}).join('');
|
|
12
|
+
const underline = replace('_');
|
|
13
|
+
return underline === text ? replace('^') : underline;
|
|
14
|
+
}
|
|
15
|
+
/** The only blinking state belongs to this leaf, never to the editor. */
|
|
16
|
+
export function PromptCaret({ text, active, reducedMotion, colorLevel, color, resetKey, }) {
|
|
17
|
+
const [visible, setVisible] = useState(true);
|
|
18
|
+
useLayoutEffect(() => {
|
|
19
|
+
setVisible(true);
|
|
20
|
+
if (!active || reducedMotion)
|
|
21
|
+
return;
|
|
22
|
+
const timer = setInterval(() => setVisible((previous) => !previous), 500);
|
|
23
|
+
return () => clearInterval(timer);
|
|
24
|
+
}, [resetKey, active, reducedMotion]);
|
|
25
|
+
const bright = active && (reducedMotion || visible);
|
|
26
|
+
let content = text;
|
|
27
|
+
if (bright && colorLevel === 0) {
|
|
28
|
+
content = monochromeMarker(text);
|
|
29
|
+
}
|
|
30
|
+
return _jsx(Text, { color: color, inverse: bright && colorLevel > 0, children: content });
|
|
31
|
+
}
|
package/dist/ui/PromptInput.d.ts
CHANGED
|
@@ -31,6 +31,9 @@ export interface CommandOption {
|
|
|
31
31
|
description: string;
|
|
32
32
|
}
|
|
33
33
|
interface PromptInputProps {
|
|
34
|
+
cols?: number;
|
|
35
|
+
cursorVisible?: boolean;
|
|
36
|
+
onInteraction?: () => void;
|
|
34
37
|
isActive: boolean;
|
|
35
38
|
running: boolean;
|
|
36
39
|
history: string[];
|
|
@@ -41,11 +44,8 @@ interface PromptInputProps {
|
|
|
41
44
|
/**
|
|
42
45
|
* Row budget for the completion popup (§5.2 / R-14).
|
|
43
46
|
*
|
|
44
|
-
* The popup
|
|
45
|
-
*
|
|
46
|
-
* status bar past the frame height, where the root box's `overflow: hidden`
|
|
47
|
-
* clips it away — the one piece of chrome R2 promises is always visible.
|
|
48
|
-
* `App` derives this from `budget.ts` so the popup shrinks instead.
|
|
47
|
+
* The popup shares the scrolling footer with the editor. Its height budget
|
|
48
|
+
* reserves visible message rows without changing the viewport or TODO rail.
|
|
49
49
|
*/
|
|
50
50
|
popupMaxRows?: number;
|
|
51
51
|
/** Total menu rows, including borders and the overflow hint. */
|
|
@@ -53,21 +53,16 @@ interface PromptInputProps {
|
|
|
53
53
|
onPopupRowsChange?: (rows: number) => void;
|
|
54
54
|
onSubmit: (text: string) => void;
|
|
55
55
|
onHelp?: () => void;
|
|
56
|
+
onEscape?: () => void;
|
|
57
|
+
onEscapeDismiss?: () => void;
|
|
58
|
+
reducedMotion?: boolean;
|
|
56
59
|
/**
|
|
57
60
|
* Mirror of the session mode. Read for the placeholder only — the toggle
|
|
58
61
|
* itself belongs to `App`, and this component's job is to get out of its way
|
|
59
62
|
* (see the first statement of the input handler).
|
|
60
63
|
*/
|
|
61
64
|
agentMode?: AgentMode;
|
|
62
|
-
|
|
63
|
-
* Set by `Composer` in full-screen mode: draw the rounded frame around the
|
|
64
|
-
* input row only, so the autocomplete popup lands OUTSIDE it (spec §4.7)
|
|
65
|
-
* without lifting the editor's buffer/cursor state out of this component.
|
|
66
|
-
* Unset ⇒ the v0.2.0 borderless inline rendering.
|
|
67
|
-
*/
|
|
68
|
-
bordered?: {
|
|
69
|
-
color?: string;
|
|
70
|
-
};
|
|
65
|
+
borderColor?: string;
|
|
71
66
|
/**
|
|
72
67
|
* Fires when the draft flips empty ⇄ non-empty (for the border color) OR when
|
|
73
68
|
* its ROW COUNT changes (for `layout/budget.ts`) — never per keystroke.
|
|
@@ -92,24 +87,6 @@ interface PromptInputProps {
|
|
|
92
87
|
* component is the only place that holds both facts.
|
|
93
88
|
*/
|
|
94
89
|
onNotice?: (level: 'warn' | 'error', text: string) => void;
|
|
95
|
-
/**
|
|
96
|
-
* Rows of newer output hidden BELOW the viewport — `ScrollViewport`'s offset
|
|
97
|
-
* (tui-selection-and-scroll-follow §4.2 / G7).
|
|
98
|
-
*
|
|
99
|
-
* Rendered as a right-aligned chip ON THE INPUT ROW, INSIDE THE BORDER, which
|
|
100
|
-
* is what the requirement asks for (输入框内部) and what makes the hint cost
|
|
101
|
-
* zero rows. It used to take the viewport's own last row, which spent a line of
|
|
102
|
-
* the user's content exactly when the transcript was longest AND fed the number
|
|
103
|
-
* back into its own derivation (§3.3).
|
|
104
|
-
*
|
|
105
|
-
* IT IS STATE, NOT A HINT. It deliberately ignores `showHint` / `hintsEnabled`
|
|
106
|
-
* — those govern the teaching row BELOW the box, which fades with experience
|
|
107
|
-
* and is dropped on a short terminal. "You are 12 rows behind the newest
|
|
108
|
-
* output" must survive both (T-23).
|
|
109
|
-
*
|
|
110
|
-
* Only meaningful in full-screen mode, so it renders only when `bordered` is
|
|
111
|
-
* set: inline mode has no self-drawn viewport and this is always 0 there.
|
|
112
|
-
*/
|
|
113
90
|
scrolledLines?: number;
|
|
114
91
|
}
|
|
115
92
|
export interface ScrollChip {
|
|
@@ -176,5 +153,5 @@ export declare function fileTokenAt(buffer: string, cursor: number): {
|
|
|
176
153
|
end: number;
|
|
177
154
|
query: string;
|
|
178
155
|
} | null;
|
|
179
|
-
export declare function PromptInput({ isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, agentMode, onSubmit, onHelp,
|
|
156
|
+
export declare function PromptInput({ cols: colsProp, cursorVisible, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion, borderColor, onDraftChange, onNotice, scrolledLines, }: PromptInputProps): React.ReactElement;
|
|
180
157
|
export {};
|