@aragon-agent/cli 0.6.8 → 0.6.10
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 +89 -44
- package/dist/agent/controller.d.ts +1 -1
- package/dist/agent/reducer.d.ts +31 -113
- package/dist/agent/reducer.js +41 -63
- package/dist/cli.js +53 -155
- package/dist/commands/builtins.d.ts +2 -1
- package/dist/commands/builtins.js +47 -2
- 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/input/enter-sequences.d.ts +63 -0
- package/dist/input/enter-sequences.js +151 -0
- package/dist/input/limits.d.ts +11 -0
- package/dist/input/limits.js +11 -0
- package/dist/input/stdin-filter.js +19 -1
- package/dist/logging/install.d.ts +2 -2
- package/dist/logging/install.js +2 -2
- package/dist/session/persist.js +20 -0
- 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 +94 -129
- 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 +46 -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 +38 -9
- 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 +15 -199
- package/dist/ui/enter-frames.d.ts +63 -0
- package/dist/ui/enter-frames.js +136 -0
- 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/QueuedEntry.d.ts +27 -0
- package/dist/ui/entries/QueuedEntry.js +30 -0
- 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/glyphs.d.ts +9 -0
- package/dist/ui/glyphs.js +2 -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 +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 +3 -1
- package/dist/ui/overlays/PlanReviewOverlay.js +5 -1
- package/dist/ui/overlays/QuestionOverlay.js +7 -2
- package/dist/ui/overlays/SettingsScreen.js +8 -1
- package/dist/ui/run-status-row.d.ts +61 -0
- package/dist/ui/run-status-row.js +70 -0
- package/dist/ui/selection/selection-controller.d.ts +23 -7
- package/dist/ui/selection/selection-controller.js +68 -16
- 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 +6 -58
- package/dist/ui/use-startup-notices.js +10 -57
- 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/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
|
@@ -29,6 +29,7 @@ import { AutocompletePopup } from './AutocompletePopup.js';
|
|
|
29
29
|
import { buildAutocompleteLayout } from './layout/autocomplete.js';
|
|
30
30
|
import { editorReducer, INITIAL_EDITOR_STATE, } from './editor-reducer.js';
|
|
31
31
|
import { hasPasteFrame, splitPasteFrames } from './paste-frames.js';
|
|
32
|
+
import { hasEnterFrame, mergeWithPasteRuns, splitEnterFrames, } from './enter-frames.js';
|
|
32
33
|
import { allocatePasteId, expandPastes, expandRangeOverTokens } from './paste-tokens.js';
|
|
33
34
|
import { draftMaxRows } from './composer-limits.js';
|
|
34
35
|
import { layoutComposer, splitRowAtColumn } from './composer-rows.js';
|
|
@@ -248,7 +249,7 @@ function isControlSeq(input) {
|
|
|
248
249
|
// ---------------------------------------------------------------------------
|
|
249
250
|
// Component
|
|
250
251
|
// ---------------------------------------------------------------------------
|
|
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,
|
|
252
|
+
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
253
|
const { stdout } = useStdout();
|
|
253
254
|
const [editor, dispatch] = useReducer(editorReducer, INITIAL_EDITOR_STATE);
|
|
254
255
|
const { buffer, cursor, historyIndex, dismissed, sel } = editor;
|
|
@@ -317,14 +318,11 @@ export function PromptInput({ cols: colsProp, cursorVisible = true, onInteractio
|
|
|
317
318
|
const glyphs = pickGlyphs(caps);
|
|
318
319
|
const cols = colsProp ?? stdout?.columns ?? 80;
|
|
319
320
|
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;
|
|
321
|
+
const chip = scrollChip(scrolledLines, cols, glyphs);
|
|
324
322
|
// Err NARROW. Wrapping wider than the box Ink gives us would make Ink wrap a
|
|
325
323
|
// second time, and then `layout.rows.length` is a lie — which is the one thing
|
|
326
324
|
// this module may not be.
|
|
327
|
-
const chromeCols =
|
|
325
|
+
const chromeCols = 2 /* border */ + 2 /* paddingX */ + 2 /* marker */;
|
|
328
326
|
const baseCols = Math.max(8, cols - chromeCols - (chip ? chip.cells + 1 : 0));
|
|
329
327
|
const measure = (wrapCols) => layoutComposer({
|
|
330
328
|
buffer,
|
|
@@ -546,7 +544,16 @@ export function PromptInput({ cols: colsProp, cursorVisible = true, onInteractio
|
|
|
546
544
|
// The text runs inside `segments` are already sanitised (I-14): Ink drains
|
|
547
545
|
// its whole buffer in one `read()`, so this same string can carry the `\r`
|
|
548
546
|
// of a user who pasted and pressed Enter inside the burst window.
|
|
549
|
-
|
|
547
|
+
//
|
|
548
|
+
// NO NEWLINE INTENT IN THE CHUNK (tui-shift-enter-copy-queue 3.5):
|
|
549
|
+
// that same one-`read()` coalescing means a paste frame can share an
|
|
550
|
+
// `input` with an enter frame or a bare Ctrl+J, and those chunks
|
|
551
|
+
// belong to the newline branch below, whose `mergeWithPasteRuns`
|
|
552
|
+
// interleaves both families in byte order. This branch's
|
|
553
|
+
// `splitPasteFrames` would strip the enter frame's NUL and leak its
|
|
554
|
+
// letter as text. A multi-line payload (a `\n` of its own) routes
|
|
555
|
+
// there too and comes back byte-identical inside its paste segment.
|
|
556
|
+
if (hasPasteFrame(input) && !hasEnterFrame(input) && !input.includes('\n')) {
|
|
550
557
|
const runs = splitPasteFrames(input);
|
|
551
558
|
const segments = runs.map((run) => run.kind === 'paste' ? { kind: 'paste', text: run.text, id: allocatePasteId() } : run);
|
|
552
559
|
if (segments.length === 0)
|
|
@@ -562,6 +569,28 @@ export function PromptInput({ cols: colsProp, cursorVisible = true, onInteractio
|
|
|
562
569
|
dispatch({ type: 'input', segments });
|
|
563
570
|
return;
|
|
564
571
|
}
|
|
572
|
+
// A NEWLINE INTENT, from either source (tui-shift-enter-copy-queue
|
|
573
|
+
// 3.5): the NUL frame the filter wrote for Shift+Enter / Alt+Enter /
|
|
574
|
+
// CSI-u Enter, or a bare `\n` (Ctrl+J), which Ink delivers WITHOUT
|
|
575
|
+
// `key.return`. Sits beside the paste branch -- after every key test so
|
|
576
|
+
// it can never shadow one, before `isControlSeq` because both sources
|
|
577
|
+
// begin with a control byte and would otherwise be dropped. ONE
|
|
578
|
+
// dispatch carries the whole ordered chunk, paste runs included when
|
|
579
|
+
// Ink coalesced the filter's two writes into one input string.
|
|
580
|
+
if (hasEnterFrame(input) || input.includes('\n')) {
|
|
581
|
+
const runs = mergeWithPasteRuns(input, splitEnterFrames(input));
|
|
582
|
+
const segments = runs.map((run) => run.kind === 'paste' ? { kind: 'paste', text: run.text, id: allocatePasteId() } : run);
|
|
583
|
+
if (segments.length === 0)
|
|
584
|
+
return;
|
|
585
|
+
const refusal = draftLimitRefusal(segments, editor);
|
|
586
|
+
if (refusal) {
|
|
587
|
+
onNotice?.('warn', refusal);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
onInteraction?.();
|
|
591
|
+
dispatch({ type: 'input', segments });
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
565
594
|
// Printable input — drop any unrecognized control/escape byte.
|
|
566
595
|
if (input && !key.tab && !isControlSeq(input))
|
|
567
596
|
insert(input);
|
|
@@ -569,7 +598,7 @@ export function PromptInput({ cols: colsProp, cursorVisible = true, onInteractio
|
|
|
569
598
|
const marker = running ? glyphs.steer : glyphs.caret;
|
|
570
599
|
const markerColor = running ? theme.toolRunning : theme.primary;
|
|
571
600
|
const placeholder = running
|
|
572
|
-
? `Type to
|
|
601
|
+
? `Type to queue a message, Esc twice to interrupt${glyphs.ellipsis}`
|
|
573
602
|
: agentMode === 'plan'
|
|
574
603
|
? `Describe what you want to build; I'll research and plan it first${glyphs.ellipsis}`
|
|
575
604
|
: `Send a message (/ for commands, @ for files)${glyphs.ellipsis}`;
|
|
@@ -599,5 +628,5 @@ export function PromptInput({ cols: colsProp, cursorVisible = true, onInteractio
|
|
|
599
628
|
// the user is typing — `↓ 9` becomes `↓ 10` becomes `↓ 100` — and every
|
|
600
629
|
// change would otherwise re-wrap a draft that fills the line.
|
|
601
630
|
_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 })),
|
|
631
|
+
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
632
|
}
|
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 {};
|