@aragon-agent/cli 0.6.8 → 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 +38 -37
- package/dist/agent/controller.d.ts +1 -1
- package/dist/agent/reducer.d.ts +7 -113
- package/dist/agent/reducer.js +0 -62
- package/dist/cli.js +48 -149
- package/dist/commands/perf.d.ts +32 -2
- package/dist/commands/perf.js +32 -2
- package/dist/commands/registry.d.ts +2 -2
- 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/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/ui/ActivityLine.d.ts +26 -34
- package/dist/ui/ActivityLine.js +30 -19
- package/dist/ui/App.d.ts +1 -41
- package/dist/ui/App.js +66 -128
- package/dist/ui/BottomStatusRow.d.ts +1 -3
- package/dist/ui/BottomStatusRow.js +4 -8
- package/dist/ui/Composer.d.ts +28 -1
- package/dist/ui/Composer.js +43 -17
- 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/PromptInput.d.ts +2 -28
- package/dist/ui/PromptInput.js +4 -7
- package/dist/ui/StatusBar.d.ts +0 -11
- 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 +9 -199
- 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 +36 -6
- package/dist/ui/ink-runtime.d.ts +1 -0
- package/dist/ui/ink-runtime.js +21 -0
- package/dist/ui/layout/AppShell.d.ts +1 -10
- package/dist/ui/layout/AppShell.js +2 -5
- package/dist/ui/layout/OverlayFrame.d.ts +1 -25
- package/dist/ui/layout/OverlayFrame.js +0 -24
- package/dist/ui/layout/ScrollViewport.d.ts +3 -0
- package/dist/ui/layout/ScrollViewport.js +17 -5
- package/dist/ui/layout/frame.d.ts +0 -22
- package/dist/ui/layout/frame.js +0 -36
- package/dist/ui/layout/todo-layout.d.ts +1 -3
- package/dist/ui/layout/todo-layout.js +7 -8
- package/dist/ui/layout/viewport-geometry.d.ts +1 -1
- package/dist/ui/layout/viewport-geometry.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 +1 -0
- package/dist/ui/run-status-row.d.ts +61 -0
- package/dist/ui/run-status-row.js +70 -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 +1 -1
- package/package.json +2 -2
- package/dist/ui/TodoStrip.d.ts +0 -36
- package/dist/ui/TodoStrip.js +0 -32
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
|
*
|
|
@@ -15,11 +15,30 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
15
15
|
*/
|
|
16
16
|
import { useCallback, useState } from 'react';
|
|
17
17
|
import { Box, Text } from 'ink';
|
|
18
|
+
import stringWidth from 'string-width';
|
|
18
19
|
import { pickGlyphs } from './glyphs.js';
|
|
19
20
|
import { HINT_FADE_AFTER } from '../config/schema.js';
|
|
20
21
|
import { PromptInput } from './PromptInput.js';
|
|
21
|
-
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';
|
|
22
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
|
+
}
|
|
23
42
|
/**
|
|
24
43
|
* Build the hint row.
|
|
25
44
|
*
|
|
@@ -40,18 +59,8 @@ function hintText(opts) {
|
|
|
40
59
|
// clauses are shown; the row is `wrap="truncate"` and they are ordered
|
|
41
60
|
// most-urgent-first, so a terminal too narrow for all of them drops the exit
|
|
42
61
|
// clause last rather than the abort key.
|
|
43
|
-
if (running)
|
|
44
|
-
|
|
45
|
-
return [
|
|
46
|
-
`${glyphs.enterKey} steer`,
|
|
47
|
-
// Confirmation is named even when service controls also need space.
|
|
48
|
-
`esc${glyphs.times}2 interrupt`,
|
|
49
|
-
`ctrl+c stop ${services}`,
|
|
50
|
-
`ctrl+c${glyphs.times}2 exit`,
|
|
51
|
-
].join(dot);
|
|
52
|
-
}
|
|
53
|
-
return [`${glyphs.enterKey} steer`, `esc${glyphs.times}2 interrupt`, `ctrl+c${glyphs.times}2 exit`].join(dot);
|
|
54
|
-
}
|
|
62
|
+
if (running)
|
|
63
|
+
return runningHintClauses({ glyphs, services }).join(dot);
|
|
55
64
|
// The hint names the DESTINATION, not the current state — the correct label
|
|
56
65
|
// for a toggle affordance, and the reason the mode word here is the OPPOSITE
|
|
57
66
|
// of the one on the chip beside it.
|
|
@@ -86,7 +95,21 @@ function hintText(opts) {
|
|
|
86
95
|
* than fishing the string out of a rendered frame.
|
|
87
96
|
*/
|
|
88
97
|
export const hintTextForTest = hintText;
|
|
89
|
-
|
|
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, }) {
|
|
90
113
|
const [hasDraft, setHasDraft] = useState(false);
|
|
91
114
|
const onDraftChange = useCallback((next) => {
|
|
92
115
|
setHasDraft(next.hasDraft);
|
|
@@ -105,8 +128,11 @@ export function Composer({ cols, cursorVisible, onInteraction, measureRef, isAct
|
|
|
105
128
|
? theme.focusBorder ?? theme.primary
|
|
106
129
|
: theme.idleBorder ?? theme.border;
|
|
107
130
|
const glyphs = pickGlyphs(caps);
|
|
108
|
-
|
|
109
|
-
|
|
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 && (
|
|
110
136
|
// The chip rides the EXISTING hint row rather than adding one, so it
|
|
111
137
|
// costs zero rows on a 24-row terminal.
|
|
112
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;
|
package/dist/ui/PromptInput.d.ts
CHANGED
|
@@ -62,15 +62,7 @@ interface PromptInputProps {
|
|
|
62
62
|
* (see the first statement of the input handler).
|
|
63
63
|
*/
|
|
64
64
|
agentMode?: AgentMode;
|
|
65
|
-
|
|
66
|
-
* Set by `Composer` in full-screen mode: draw the rounded frame around the
|
|
67
|
-
* input row only, so the autocomplete popup lands OUTSIDE it (spec §4.7)
|
|
68
|
-
* without lifting the editor's buffer/cursor state out of this component.
|
|
69
|
-
* Unset ⇒ the v0.2.0 borderless inline rendering.
|
|
70
|
-
*/
|
|
71
|
-
bordered?: {
|
|
72
|
-
color?: string;
|
|
73
|
-
};
|
|
65
|
+
borderColor?: string;
|
|
74
66
|
/**
|
|
75
67
|
* Fires when the draft flips empty ⇄ non-empty (for the border color) OR when
|
|
76
68
|
* its ROW COUNT changes (for `layout/budget.ts`) — never per keystroke.
|
|
@@ -95,24 +87,6 @@ interface PromptInputProps {
|
|
|
95
87
|
* component is the only place that holds both facts.
|
|
96
88
|
*/
|
|
97
89
|
onNotice?: (level: 'warn' | 'error', text: string) => void;
|
|
98
|
-
/**
|
|
99
|
-
* Rows of newer output hidden BELOW the viewport — `ScrollViewport`'s offset
|
|
100
|
-
* (tui-selection-and-scroll-follow §4.2 / G7).
|
|
101
|
-
*
|
|
102
|
-
* Rendered as a right-aligned chip ON THE INPUT ROW, INSIDE THE BORDER, which
|
|
103
|
-
* is what the requirement asks for (输入框内部) and what makes the hint cost
|
|
104
|
-
* zero rows. It used to take the viewport's own last row, which spent a line of
|
|
105
|
-
* the user's content exactly when the transcript was longest AND fed the number
|
|
106
|
-
* back into its own derivation (§3.3).
|
|
107
|
-
*
|
|
108
|
-
* IT IS STATE, NOT A HINT. It deliberately ignores `showHint` / `hintsEnabled`
|
|
109
|
-
* — those govern the teaching row BELOW the box, which fades with experience
|
|
110
|
-
* and is dropped on a short terminal. "You are 12 rows behind the newest
|
|
111
|
-
* output" must survive both (T-23).
|
|
112
|
-
*
|
|
113
|
-
* Only meaningful in full-screen mode, so it renders only when `bordered` is
|
|
114
|
-
* set: inline mode has no self-drawn viewport and this is always 0 there.
|
|
115
|
-
*/
|
|
116
90
|
scrolledLines?: number;
|
|
117
91
|
}
|
|
118
92
|
export interface ScrollChip {
|
|
@@ -179,5 +153,5 @@ export declare function fileTokenAt(buffer: string, cursor: number): {
|
|
|
179
153
|
end: number;
|
|
180
154
|
query: string;
|
|
181
155
|
} | null;
|
|
182
|
-
export declare function PromptInput({ cols: colsProp, cursorVisible, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion,
|
|
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;
|
|
183
157
|
export {};
|
package/dist/ui/PromptInput.js
CHANGED
|
@@ -248,7 +248,7 @@ function isControlSeq(input) {
|
|
|
248
248
|
// ---------------------------------------------------------------------------
|
|
249
249
|
// Component
|
|
250
250
|
// ---------------------------------------------------------------------------
|
|
251
|
-
export function PromptInput({ cols: colsProp, cursorVisible = true, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion = false,
|
|
251
|
+
export function PromptInput({ cols: colsProp, cursorVisible = true, onInteraction, isActive, running, history, commands, cwd, theme, caps, popupMaxRows, popupMaxHeight, onPopupRowsChange, agentMode, onSubmit, onHelp, onEscape, onEscapeDismiss, reducedMotion = false, borderColor, onDraftChange, onNotice, scrolledLines = 0, }) {
|
|
252
252
|
const { stdout } = useStdout();
|
|
253
253
|
const [editor, dispatch] = useReducer(editorReducer, INITIAL_EDITOR_STATE);
|
|
254
254
|
const { buffer, cursor, historyIndex, dismissed, sel } = editor;
|
|
@@ -317,14 +317,11 @@ export function PromptInput({ cols: colsProp, cursorVisible = true, onInteractio
|
|
|
317
317
|
const glyphs = pickGlyphs(caps);
|
|
318
318
|
const cols = colsProp ?? stdout?.columns ?? 80;
|
|
319
319
|
const terminalRows = stdout?.rows ?? 24;
|
|
320
|
-
|
|
321
|
-
// is always 0 there and the branch is unreachable rather than merely unused.
|
|
322
|
-
// The guard is explicit so it stays that way.
|
|
323
|
-
const chip = bordered ? scrollChip(scrolledLines, cols, glyphs) : null;
|
|
320
|
+
const chip = scrollChip(scrolledLines, cols, glyphs);
|
|
324
321
|
// Err NARROW. Wrapping wider than the box Ink gives us would make Ink wrap a
|
|
325
322
|
// second time, and then `layout.rows.length` is a lie — which is the one thing
|
|
326
323
|
// this module may not be.
|
|
327
|
-
const chromeCols =
|
|
324
|
+
const chromeCols = 2 /* border */ + 2 /* paddingX */ + 2 /* marker */;
|
|
328
325
|
const baseCols = Math.max(8, cols - chromeCols - (chip ? chip.cells + 1 : 0));
|
|
329
326
|
const measure = (wrapCols) => layoutComposer({
|
|
330
327
|
buffer,
|
|
@@ -599,5 +596,5 @@ export function PromptInput({ cols: colsProp, cursorVisible = true, onInteractio
|
|
|
599
596
|
// the user is typing — `↓ 9` becomes `↓ 10` becomes `↓ 100` — and every
|
|
600
597
|
// change would otherwise re-wrap a draft that fills the line.
|
|
601
598
|
_jsx(Box, { flexShrink: 0, alignSelf: "flex-start", marginLeft: 1, width: chip.cells, justifyContent: "flex-end", children: _jsx(Text, { wrap: "truncate", color: theme.hintFg ?? theme.muted, children: chip.text }) }))] }));
|
|
602
|
-
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [popupVisible && (_jsx(AutocompletePopup, { items: popupItems, selected: clampedSel, theme: theme, caps: caps, maxRows: popupMaxRows, layout: popupLayout })),
|
|
599
|
+
return (_jsxs(Box, { flexDirection: "column", flexShrink: 0, children: [popupVisible && (_jsx(AutocompletePopup, { items: popupItems, selected: clampedSel, theme: theme, caps: caps, maxRows: popupMaxRows, layout: popupLayout })), (_jsx(Box, { flexDirection: "column", flexShrink: 0, borderStyle: glyphs.boxStyle, borderColor: borderColor, paddingX: 1, children: inputRow }))] }));
|
|
603
600
|
}
|
package/dist/ui/StatusBar.d.ts
CHANGED
|
@@ -52,17 +52,6 @@ interface StatusBarProps {
|
|
|
52
52
|
* Ctrl+L redraw carrier — invariant I-5 (spec §4.13). See `redrawChar`.
|
|
53
53
|
*/
|
|
54
54
|
redrawNonce?: number;
|
|
55
|
-
/**
|
|
56
|
-
* The session mode. THIS IS THE GUARANTEED MODE INDICATOR (plan-mode §6.4 /
|
|
57
|
-
* P1-6): the composer chip disappears on a short terminal, under
|
|
58
|
-
* `hints: false`, and in inline render mode, but `StatusBar` renders in both
|
|
59
|
-
* `AppShell` branches and its left cluster has no opt-out and no width gate.
|
|
60
|
-
*
|
|
61
|
-
* Design intent, stated here so it survives a later refactor: the status bar
|
|
62
|
-
* must ALWAYS name a non-default mode; the chip may. Do not give this a
|
|
63
|
-
* `cols >= N` breakpoint, and do not move it into the right cluster — those
|
|
64
|
-
* children are flexible and drop characters under pressure.
|
|
65
|
-
*/
|
|
66
55
|
agentMode?: AgentMode;
|
|
67
56
|
/** Non-null while a deferred `plan -> build` waits for `agent_end`. */
|
|
68
57
|
pendingAgentMode?: AgentMode | null;
|
package/dist/ui/ToastStack.d.ts
CHANGED
|
@@ -1,24 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Toast strip (spec §4.8). Ephemeral acks created by App/commands and
|
|
3
|
-
* auto-dismissed by App's TTL effect; the reducer stays timer-free.
|
|
4
|
-
*
|
|
5
|
-
* In `fullscreen` this ALWAYS occupies exactly one row — an empty row when there
|
|
6
|
-
* is nothing to say. Rendering conditionally would make the viewport height (and
|
|
7
|
-
* therefore the whole transcript) jump every time a toast appears or expires.
|
|
8
|
-
* There is deliberately no `rows` threshold: jitter is a layout property, not a
|
|
9
|
-
* space property, and a short terminal suffers it proportionally MORE, so the
|
|
10
|
-
* tier that most needs the fixed row is exactly the one a threshold would skip.
|
|
11
|
-
* Only the newest toast is shown; older ones collapse into a `+N` counter.
|
|
12
|
-
*
|
|
13
|
-
* `inline` keeps the v0.2.0 conditional 0–3 row stack: without a fixed frame,
|
|
14
|
-
* growing and shrinking is just normal document flow.
|
|
15
|
-
*/
|
|
1
|
+
/** Fixed one-row toast strip; only the newest toast is shown. */
|
|
16
2
|
import React from 'react';
|
|
17
3
|
import type { Theme } from './theme.js';
|
|
18
4
|
import type { Toast } from '../agent/reducer.js';
|
|
19
|
-
|
|
20
|
-
export declare function ToastStack({ toasts, theme, mode, }: {
|
|
5
|
+
export declare function ToastStack({ toasts, theme, }: {
|
|
21
6
|
toasts: Toast[];
|
|
22
7
|
theme: Theme;
|
|
23
|
-
mode?: RenderMode;
|
|
24
8
|
}): React.ReactElement | null;
|
package/dist/ui/ToastStack.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
|
-
const MAX_VISIBLE = 3;
|
|
4
3
|
function toastColor(level, theme) {
|
|
5
4
|
switch (level) {
|
|
6
5
|
case 'success':
|
|
@@ -25,14 +24,8 @@ function toastGlyph(level, theme) {
|
|
|
25
24
|
return theme.symbols.info;
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
|
-
export function ToastStack({ toasts, theme,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return (_jsx(Box, { flexShrink: 0, children: latest ? (_jsxs(Text, { wrap: "truncate", color: toastColor(latest.level, theme), children: [' ', toastGlyph(latest.level, theme), " ", latest.text, extra > 0 ? ` +${extra}` : ''] })) : (_jsx(Text, { children: " " })) }));
|
|
33
|
-
}
|
|
34
|
-
const shown = toasts.slice(-MAX_VISIBLE);
|
|
35
|
-
if (shown.length === 0)
|
|
36
|
-
return null;
|
|
37
|
-
return (_jsx(Box, { flexDirection: "column", marginTop: 1, children: shown.map((t) => (_jsxs(Text, { color: toastColor(t.level, theme), children: [toastGlyph(t.level, theme), " ", t.text] }, t.id))) }));
|
|
27
|
+
export function ToastStack({ toasts, theme, }) {
|
|
28
|
+
const latest = toasts[toasts.length - 1];
|
|
29
|
+
const extra = Math.max(0, toasts.length - 1);
|
|
30
|
+
return (_jsx(Box, { flexShrink: 0, children: latest ? (_jsxs(Text, { wrap: "truncate", color: toastColor(latest.level, theme), children: [' ', toastGlyph(latest.level, theme), " ", latest.text, extra > 0 ? ` +${extra}` : ''] })) : (_jsx(Text, { children: " " })) }));
|
|
38
31
|
}
|
package/dist/ui/Transcript.d.ts
CHANGED
|
@@ -1,65 +1,23 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
* once into Ink's `<Static>` (native terminal scrollback, never re-rendered)
|
|
4
|
-
* and a live tail rendered normally. This kills per-token full re-renders and
|
|
5
|
-
* flicker on long/fast sessions.
|
|
6
|
-
*
|
|
7
|
-
* `computeSettledCount` is pure and exported for `transcript-static.test.ts`.
|
|
8
|
-
* The settled boundary is held MONOTONIC at render time: a previously-settled
|
|
9
|
-
* entry never leaves `<Static>` (which cannot un-print it) even if it later
|
|
10
|
-
* becomes expanded, so the live tail can never duplicate a scrolled-off entry.
|
|
11
|
-
*
|
|
12
|
-
* `EntryView` is shared by both render paths ON PURPOSE (§4.3 / §12). Forking it
|
|
13
|
-
* to freeze inline's v0.2.0 output would mean writing every future entry-layer
|
|
14
|
-
* change twice and watching the two drift; inline is a degradation path, not a
|
|
15
|
-
* parallel product. What inline still guarantees is its GEOMETRY contract — no
|
|
16
|
-
* fixed frame, `<Static>` keeps the settled history, no private ANSI — not its
|
|
17
|
-
* pixel-for-pixel appearance.
|
|
18
|
-
*
|
|
19
|
-
* Two things changed with tui-render-performance:
|
|
20
|
-
* - `EntryView` is `React.memo`'d with an explicit comparator (L2 / R3), which
|
|
21
|
-
* is only worth anything because `mapEntry` already preserves object
|
|
22
|
-
* identity for untouched entries;
|
|
23
|
-
* - `TranscriptList` (the FULL-SCREEN body) mounts only the entries inside the
|
|
24
|
-
* viewport and replaces the rest with two spacer boxes (L3 / R1), and the
|
|
25
|
-
* inline `Transcript` clamps its live region so `ink.js:121` is unreachable
|
|
26
|
-
* (L5 / R6).
|
|
27
|
-
*/
|
|
1
|
+
/** Virtualized full-screen transcript and entry cards. */
|
|
2
|
+
/** Virtualized full-screen transcript and shared entry cards. */
|
|
28
3
|
import React from 'react';
|
|
29
4
|
import { type Theme } from './theme.js';
|
|
30
5
|
import type { TermCapabilities } from './capabilities.js';
|
|
31
6
|
import { type DensityMode } from './density.js';
|
|
32
7
|
import type { Entry } from '../agent/reducer.js';
|
|
33
|
-
import type { RenderMode } from './layout/frame.js';
|
|
34
8
|
import { type TailSink } from './layout/follow-state.js';
|
|
35
9
|
import type { HeightStore } from './use-height-store.js';
|
|
36
|
-
/**
|
|
37
|
-
* Largest prefix length P such that `entries[0..P)` are all terminal, none is
|
|
38
|
-
* within the last `LIVE_TAIL` entries, and none is currently expanded. Pure and
|
|
39
|
-
* non-decreasing as entries terminalize (expanding a card can lower it — the
|
|
40
|
-
* component clamps with a monotonic high-water mark to avoid Static duplication).
|
|
41
|
-
*/
|
|
42
|
-
export declare function computeSettledCount(entries: Entry[], expandedToolIds: Record<string, true>): number;
|
|
43
10
|
interface TranscriptProps {
|
|
44
11
|
entries: Entry[];
|
|
45
12
|
expandedToolIds: Record<string, true>;
|
|
46
13
|
thinkingVisible: boolean;
|
|
47
14
|
reducedMotion: boolean;
|
|
48
15
|
density: DensityMode;
|
|
49
|
-
/**
|
|
50
|
-
* The render mode, threaded from `App` as a plain scalar prop.
|
|
51
|
-
*
|
|
52
|
-
* It decides whether the collapsed thinking marker may offer `ctrl+t` (D-16):
|
|
53
|
-
* inline prints settled entries into `<Static>`, which cannot re-print them.
|
|
54
|
-
* NOT read from a module-level singleton — `render-memo.test.tsx` asserts prop
|
|
55
|
-
* stability across frames, and a hidden read would be invisible to it.
|
|
56
|
-
*/
|
|
57
|
-
mode: RenderMode;
|
|
58
16
|
/**
|
|
59
17
|
* Wall-clock seconds, threaded from `App`'s existing 200 ms ticker, for the
|
|
60
18
|
* live tool card's stall row (agent-activity-presentation-live §3.3.4 / D-36).
|
|
61
19
|
*
|
|
62
|
-
*
|
|
20
|
+
* The entry map decides per entry whether to pass it on, and it
|
|
63
21
|
* may pass it unconditionally: `ToolCard` is `React.memo` with the default
|
|
64
22
|
* comparator, so a prop that changes every second on every card would defeat
|
|
65
23
|
* that boundary for the whole transcript.
|
|
@@ -76,15 +34,12 @@ export interface EntryViewProps {
|
|
|
76
34
|
thinkingVisible: boolean;
|
|
77
35
|
reducedMotion: boolean;
|
|
78
36
|
density: DensityMode;
|
|
79
|
-
mode: RenderMode;
|
|
80
37
|
theme: Theme;
|
|
81
38
|
caps: TermCapabilities;
|
|
82
|
-
/** Inline mode only (L5); `undefined` in full-screen, which means no clamp. */
|
|
83
|
-
liveClampRows?: number;
|
|
84
39
|
/** Wall-clock seconds; set ONLY on a running tool entry (§3.3.4 / P1-3). */
|
|
85
40
|
nowSec?: number;
|
|
86
41
|
}
|
|
87
|
-
declare function EntryViewImpl({ entry, prev, expanded, thinkingVisible, reducedMotion, density,
|
|
42
|
+
declare function EntryViewImpl({ entry, prev, expanded, thinkingVisible, reducedMotion, density, theme, caps, nowSec, }: EntryViewProps): React.ReactElement | null;
|
|
88
43
|
/**
|
|
89
44
|
* THE memo boundary that makes a long transcript cheap (L2 / R3).
|
|
90
45
|
*
|
|
@@ -100,27 +55,7 @@ declare function EntryViewImpl({ entry, prev, expanded, thinkingVisible, reduced
|
|
|
100
55
|
* test rather than a silent regression.
|
|
101
56
|
*/
|
|
102
57
|
export declare const EntryView: React.MemoExoticComponent<typeof EntryViewImpl>;
|
|
103
|
-
|
|
104
|
-
* Full-screen transcript body — the same entry renderers with NO `<Static>`.
|
|
105
|
-
*
|
|
106
|
-
* `<Static>` prints above the live frame; once the frame is `rows - 1` tall
|
|
107
|
-
* there is exactly one visible line up there, so the mechanism stops being a
|
|
108
|
-
* history view and starts being a leak. Full-screen therefore owns its history
|
|
109
|
-
* inside `ScrollViewport` instead.
|
|
110
|
-
*
|
|
111
|
-
* `computeSettledCount` is deliberately NOT called here: its only job is to keep
|
|
112
|
-
* an entry that already reached `<Static>` from flowing back into the live
|
|
113
|
-
* region, and with no Static there is nothing to protect. The settled high-water
|
|
114
|
-
* mark feeds nothing else — `/save` serializes `entries` in full — so leaving it
|
|
115
|
-
* un-advanced has no side effect.
|
|
116
|
-
*
|
|
117
|
-
* VIRTUALISED (L3 / R1). `windowSize` is now the SCROLL HORIZON — how far back
|
|
118
|
-
* the user can scroll — and no longer the rendering budget, because entries
|
|
119
|
-
* outside the viewport are not mounted at all. What bounds the frame is
|
|
120
|
-
* `selectWindow`, and the two spacers are what keep the scroll maths honest
|
|
121
|
-
* while they are absent.
|
|
122
|
-
*/
|
|
123
|
-
export declare function TranscriptList({ entries, expandedToolIds, thinkingVisible, reducedMotion, density, mode, nowSec, theme, caps, windowSize, cols, heights, mountedSink, tailSink, }: TranscriptProps & {
|
|
58
|
+
export declare function TranscriptList({ entries, expandedToolIds, thinkingVisible, reducedMotion, density, nowSec, theme, caps, windowSize, cols, heights, mountedSink, tailSink, }: TranscriptProps & {
|
|
124
59
|
windowSize: number;
|
|
125
60
|
/** The viewport's own width — narrower than the terminal when the rail is up. */
|
|
126
61
|
cols: number;
|
|
@@ -153,5 +88,4 @@ export declare function TranscriptList({ entries, expandedToolIds, thinkingVisib
|
|
|
153
88
|
*/
|
|
154
89
|
tailSink?: TailSink;
|
|
155
90
|
}): React.ReactElement;
|
|
156
|
-
export declare function Transcript({ entries, expandedToolIds, thinkingVisible, reducedMotion, density, mode, nowSec, theme, caps, }: TranscriptProps): React.ReactElement;
|
|
157
91
|
export {};
|