@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/App.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ import type { AgentController } from '../agent/controller.js';
|
|
|
10
10
|
import type { ConfirmRequest } from '../tools/index.js';
|
|
11
11
|
import { type Overlay, type ToastLevel } from '../agent/reducer.js';
|
|
12
12
|
import type { UpdateBridge } from '../update/types.js';
|
|
13
|
-
import { type RenderMode } from './layout/frame.js';
|
|
14
13
|
import type { MouseSource } from '../input/stdin-filter.js';
|
|
15
14
|
import type { PasteBridge } from '../input/limits.js';
|
|
16
15
|
import type { SelectionBridge } from './selection/selection-controller.js';
|
|
@@ -43,15 +42,11 @@ export interface AppProps {
|
|
|
43
42
|
controller: AgentController;
|
|
44
43
|
version: string;
|
|
45
44
|
/** Decided once in `cli.tsx::runInteractive()`; never switched at run time. */
|
|
46
|
-
mode: RenderMode;
|
|
47
45
|
initialOverlay?: Overlay;
|
|
48
46
|
initialPrompt?: string;
|
|
49
47
|
confirmBridge?: ConfirmBridge;
|
|
50
48
|
/** The plan-mode human channel; absent in tests that do not exercise it. */
|
|
51
49
|
humanInputBridge?: HumanInputBridge;
|
|
52
|
-
/** Wheel events, already stripped out of stdin. Absent whenever mouse support
|
|
53
|
-
* is off: inline mode, `--no-mouse`, a non-TTY, or a filter that failed to
|
|
54
|
-
* build (mouse-wheel-region-routing §4.2). */
|
|
55
50
|
mouseSource?: MouseSource;
|
|
56
51
|
/**
|
|
57
52
|
* Present ONLY on a console that can deliver neither `CSI Z` nor a mouse
|
|
@@ -87,28 +82,8 @@ export interface AppProps {
|
|
|
87
82
|
* on a non-TTY, and in every test that does not exercise it.
|
|
88
83
|
*/
|
|
89
84
|
pasteBridge?: PasteBridge;
|
|
90
|
-
/**
|
|
91
|
-
* The terminal-facing services `cli.tsx` owns and this component cannot build
|
|
92
|
-
* (tui-selection-and-scroll-follow §4.4).
|
|
93
|
-
*
|
|
94
|
-
* ONE PROP RATHER THAN FOUR, and every FIELD of it is independently optional,
|
|
95
|
-
* so `--no-mouse`, `--no-diff-render` and inline mode each drop exactly the
|
|
96
|
-
* services they do not have. Absent altogether on a non-TTY and in every test
|
|
97
|
-
* that does not exercise selection, which is what keeps those trees
|
|
98
|
-
* byte-identical.
|
|
99
|
-
*/
|
|
100
85
|
terminal?: TerminalBridge;
|
|
101
86
|
}
|
|
102
|
-
/**
|
|
103
|
-
* Terminal-level services, almost all of which exist only in a full-screen TTY
|
|
104
|
-
* session — `writeForeign` is the exception, because `/copy` is available in
|
|
105
|
-
* inline mode too and OSC 52 is what makes it work over SSH (§4.4.5).
|
|
106
|
-
*
|
|
107
|
-
* `selection` is a LATE-BOUND BRIDGE for the reason `updateBridge` above is one:
|
|
108
|
-
* the controller is constructed before `render()` and must not hold React state,
|
|
109
|
-
* while the toast sink and the redraw nonce only exist once this component has
|
|
110
|
-
* mounted. The effect below assigns both and clears them on unmount.
|
|
111
|
-
*/
|
|
112
87
|
export interface TerminalBridge {
|
|
113
88
|
scrollbar?: ScrollbarBridge;
|
|
114
89
|
/** Whether drag-select is on for this session (`mouseSelect` + a filter). */
|
|
@@ -117,22 +92,7 @@ export interface TerminalBridge {
|
|
|
117
92
|
setMouseCapture?: (on: boolean) => void;
|
|
118
93
|
/** Whether the mouse is captured right now — read at command time. */
|
|
119
94
|
isMouseCaptured?: () => boolean;
|
|
120
|
-
/**
|
|
121
|
-
* Write bytes that are ours but are not a frame (OSC 52).
|
|
122
|
-
*
|
|
123
|
-
* IT MUST NOT BE `stdout.write` WITH THE DIFFER IN FRONT OF STDOUT (P1-6):
|
|
124
|
-
* that goes through the frame-writer proxy, which does not recognise an OSC 52
|
|
125
|
-
* chunk, counts it as a foreign write and prints `FRAME_FALLBACK_NOTICE` at
|
|
126
|
-
* the user the first time they copy anything. Without a differ — inline mode,
|
|
127
|
-
* `--no-diff-render` — stdout IS the real stream and `cli.tsx` supplies a
|
|
128
|
-
* direct write instead.
|
|
129
|
-
*
|
|
130
|
-
* Absent only when there is no terminal to accept it at all. It is never a
|
|
131
|
-
* function that writes nowhere: `copyText` reports `'osc52'` whenever its door
|
|
132
|
-
* took the text, so a no-op door would make the toast name a mechanism that
|
|
133
|
-
* was never attempted.
|
|
134
|
-
*/
|
|
135
95
|
writeForeign?: (text: string) => void;
|
|
136
96
|
selection?: SelectionBridge;
|
|
137
97
|
}
|
|
138
|
-
export declare function App({ controller, version,
|
|
98
|
+
export declare function App({ controller, version, initialOverlay, initialPrompt, confirmBridge, humanInputBridge, mouseSource, vtInputWarning, updateBridge, pasteBridge, terminal, }: AppProps): React.ReactElement;
|
package/dist/ui/App.js
CHANGED
|
@@ -7,7 +7,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
7
7
|
*/
|
|
8
8
|
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';
|
|
9
9
|
import process from 'node:process';
|
|
10
|
-
import {
|
|
10
|
+
import { Text, useApp, useInput, useStdout } from 'ink';
|
|
11
11
|
import { advanceInterruptGesture, cancelInterruptConfirmation, } from '../input/interrupt-gesture.js';
|
|
12
12
|
import { initialViewState, reduceEvent, viewReducer, } from '../agent/reducer.js';
|
|
13
13
|
import { mergeDeltas } from '../agent/coalesce.js';
|
|
@@ -26,14 +26,12 @@ import { pickGlyphs } from './glyphs.js';
|
|
|
26
26
|
import { Header } from './Header.js';
|
|
27
27
|
import { pickHeaderVariant, pickOpenerVariant } from './Logo.js';
|
|
28
28
|
import { SessionOpener } from './SessionOpener.js';
|
|
29
|
-
import {
|
|
30
|
-
import { PromptInput } from './PromptInput.js';
|
|
29
|
+
import { TranscriptList } from './Transcript.js';
|
|
31
30
|
import { Composer } from './Composer.js';
|
|
32
31
|
import { StatusBar } from './StatusBar.js';
|
|
33
32
|
import { secondsLeft } from '../agent/retry-view.js';
|
|
34
33
|
import { TeamPanel } from './TeamPanel.js';
|
|
35
34
|
import { TodoPanel } from './TodoPanel.js';
|
|
36
|
-
import { TodoStrip } from './TodoStrip.js';
|
|
37
35
|
import { ActivityLine, liveSpinner } from './ActivityLine.js';
|
|
38
36
|
import { BottomStatusRow } from './BottomStatusRow.js';
|
|
39
37
|
import { UpdateLine } from './UpdateLine.js';
|
|
@@ -53,7 +51,7 @@ import { advanceBudget, decideFollowThrough, emptyBudget, } from '../todo/follow
|
|
|
53
51
|
import { useTerminalSize } from './layout/useTerminalSize.js';
|
|
54
52
|
import { useHeightStore } from './use-height-store.js';
|
|
55
53
|
import { useRenderGovernor } from './use-render-governor.js';
|
|
56
|
-
import { setPerfResetHook, setPerfSnapshotProvider } from '../commands/perf.js';
|
|
54
|
+
import { readScrollbarPerf, setPerfResetHook, setPerfSnapshotProvider, } from '../commands/perf.js';
|
|
57
55
|
import { MIN_FULLSCREEN_ROWS, MIN_FULLSCREEN_COLS } from './layout/frame.js';
|
|
58
56
|
import { HINT_MIN_ROWS, clampDraftRows, viewportRows as computeViewportRows } from './layout/budget.js';
|
|
59
57
|
import { OVERLAY_PAGE, useWheelRouting } from './use-wheel-routing.js';
|
|
@@ -122,14 +120,18 @@ function liveServiceCount(services) {
|
|
|
122
120
|
function seedViewState(thinkingVisible) {
|
|
123
121
|
return initialViewState({ thinkingVisible });
|
|
124
122
|
}
|
|
125
|
-
export function App({ controller, version,
|
|
123
|
+
export function App({ controller, version, initialOverlay, initialPrompt, confirmBridge, humanInputBridge, mouseSource, vtInputWarning, updateBridge, pasteBridge, terminal, }) {
|
|
126
124
|
const { exit } = useApp();
|
|
127
125
|
const { stdout } = useStdout();
|
|
128
|
-
const fullscreen = mode === 'fullscreen';
|
|
129
126
|
const { rows, cols } = useTerminalSize();
|
|
130
|
-
const tooSmall =
|
|
127
|
+
const tooSmall = (rows < MIN_FULLSCREEN_ROWS || cols < MIN_FULLSCREEN_COLS);
|
|
131
128
|
const composerRef = useRef(null);
|
|
129
|
+
const activityRef = useRef(null);
|
|
132
130
|
const [cursorVisible, setCursorVisible] = useState(true);
|
|
131
|
+
// Whether the run status row above the input is inside the viewport. It is
|
|
132
|
+
// `true` while the row is not mounted (`elementVisible(null)`), so it is NEVER
|
|
133
|
+
// a decision on its own - always AND it with `runRowEnabled` (`runRowShown`).
|
|
134
|
+
const [activityRowVisible, setActivityRowVisible] = useState(true);
|
|
133
135
|
// --- Render governor (tui-render-performance L4). ------------------------
|
|
134
136
|
//
|
|
135
137
|
// FIRST, BEFORE ANY OTHER WORK IN THIS BODY. The hook stamps
|
|
@@ -236,16 +238,6 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
236
238
|
const servicesRef = useRef(services);
|
|
237
239
|
servicesRef.current = services;
|
|
238
240
|
const runBaselineOut = useRef(0);
|
|
239
|
-
/**
|
|
240
|
-
* Epoch ms the current run began — the activity phrase's seed.
|
|
241
|
-
*
|
|
242
|
-
* ASSIGNED IN RENDER SCOPE, NOT IN THE ELAPSED EFFECT (P1-7). That effect runs
|
|
243
|
-
* AFTER the render that first sees `status === 'running'`, so a ref it wrote
|
|
244
|
-
* would be one frame late — and the first frame of a run is exactly when the
|
|
245
|
-
* phrase is chosen. `Transcript.tsx` updates `highWater` / `prevLen` during
|
|
246
|
-
* render for the same reason: a value derived from a transition is needed by
|
|
247
|
-
* the very render that observes the transition.
|
|
248
|
-
*/
|
|
249
241
|
const runStartedAt = useRef(0);
|
|
250
242
|
const toastTimers = useRef(new Map());
|
|
251
243
|
/** Every outstanding `gate.request()`. Held in a ref, never in state: it is
|
|
@@ -499,14 +491,6 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
499
491
|
budgetRef.current = advanceBudget(budgetRef.current, todos, autoContinuationRef.current);
|
|
500
492
|
autoContinuationRef.current = false;
|
|
501
493
|
const decision = decideFollowThrough({
|
|
502
|
-
// FROM THE CONTROLLER, NOT FROM `cfg` (P1-1). This effect's dependency
|
|
503
|
-
// list never changes, so it closes over the `cfg` object from the render
|
|
504
|
-
// in which it mounted — and `setTodoConfig` builds a NEW object, which
|
|
505
|
-
// that closure will never see. `cfg.todo.followThrough` would therefore
|
|
506
|
-
// be pinned to the launch value for the whole session and
|
|
507
|
-
// `/todo follow auto` would report success and do nothing. Note the
|
|
508
|
-
// asymmetry with `showStrip` further down, which is right to use `cfg`
|
|
509
|
-
// because it runs in render scope where `cfg` is re-read every frame.
|
|
510
494
|
mode: controller.getTodoConfig().followThrough,
|
|
511
495
|
snapshot: todos,
|
|
512
496
|
runEnd: { ...endReasonRef.current },
|
|
@@ -570,21 +554,6 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
570
554
|
unsubscribe();
|
|
571
555
|
};
|
|
572
556
|
}, [controller, patchSource, flushPending, cancelPendingHuman, cancelFollowThrough, resetInterrupt]);
|
|
573
|
-
// --- Background services (background-service-supervision §3.8). ---------
|
|
574
|
-
//
|
|
575
|
-
// A SEPARATE SUBSCRIPTION, not a branch in the one above: `ProcEvent` is
|
|
576
|
-
// deliberately not a member of core's `AgentEvent` union (I-5 / D-1), so it
|
|
577
|
-
// cannot arrive through `controller.subscribe`.
|
|
578
|
-
//
|
|
579
|
-
// TWO SINKS PER EVENT, and they are not redundant. `setServices` holds the
|
|
580
|
-
// LIVE state the status chip, the composer hint and the Ctrl+C rung read;
|
|
581
|
-
// `dispatch` writes the TRANSCRIPT, which records events about a service
|
|
582
|
-
// rather than mirroring it (D-11). Feeding one from the other would make the
|
|
583
|
-
// chip lie about a card that has already been printed into `<Static>`.
|
|
584
|
-
//
|
|
585
|
-
// NOT ROUTED THROUGH THE STREAMING COALESCER. `output` is already coalesced at
|
|
586
|
-
// the SOURCE, inside the supervisor, because `/bg` and `bash_output` consume
|
|
587
|
-
// the same event (§6); a second buffer here would only add latency.
|
|
588
557
|
useEffect(() => {
|
|
589
558
|
return controller.subscribeProc((event) => {
|
|
590
559
|
const service = event.service;
|
|
@@ -666,9 +635,6 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
666
635
|
});
|
|
667
636
|
return () => {
|
|
668
637
|
unsubscribeTeam();
|
|
669
|
-
// Nothing else disposes the runtime on an inline-mode exit or a crash
|
|
670
|
-
// that unmounts the tree, and an orphaned child holds the event loop open
|
|
671
|
-
// (R-15). `cli.tsx` calls this too; `dispose()` is idempotent.
|
|
672
638
|
controller.dispose();
|
|
673
639
|
};
|
|
674
640
|
}, [controller]);
|
|
@@ -937,10 +903,8 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
937
903
|
// Ink's own patchConsole writes straight to stdout, which shifts the fixed
|
|
938
904
|
// frame by a row and breaks its line accounting for good.
|
|
939
905
|
useEffect(() => {
|
|
940
|
-
if (!fullscreen)
|
|
941
|
-
return undefined;
|
|
942
906
|
return installConsoleBridge((level, text) => dispatch({ type: 'notice', level, text }));
|
|
943
|
-
}, [
|
|
907
|
+
}, []);
|
|
944
908
|
// `enabled` is `mouseSource`, not `cfg.mouse`: a user whose terminal drops
|
|
945
909
|
// the events must never be shown advice about a mode not in effect (§6.1).
|
|
946
910
|
// `cli.tsx::wantMouse` now folds the platform check into the same expression,
|
|
@@ -963,21 +927,9 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
963
927
|
// `transcriptRetain` to `transcriptWindow` (tui-render-performance §5.1).
|
|
964
928
|
cfg.transcriptRetainRequested !== undefined
|
|
965
929
|
? { requested: cfg.transcriptRetainRequested, resolved: cfg.transcriptRetain }
|
|
966
|
-
: undefined,
|
|
967
|
-
// Its own one-shot key, NOT `mouseNoticeSeen`: everyone this notice is for
|
|
968
|
-
// has already run aragon at least once and already has that flag set. That
|
|
969
|
-
// key is now a VERSION for the same reason, one turn later — the audience
|
|
970
|
-
// for the text that finally names the fallback key is, again, exactly the
|
|
971
|
-
// set of users who already have the old flag on disk.
|
|
972
|
-
//
|
|
973
|
-
// `fullscreen` goes with it because only half of the damage is mode-
|
|
974
|
-
// independent: `Shift+Tab` is broken either way, but inline never asked for
|
|
975
|
-
// wheel reports and does not own the scroll keys, so it must not be told
|
|
976
|
-
// about either.
|
|
977
|
-
vtInputWarning
|
|
930
|
+
: undefined, vtInputWarning
|
|
978
931
|
? {
|
|
979
932
|
nodeVersion: vtInputWarning.nodeVersion,
|
|
980
|
-
fullscreen,
|
|
981
933
|
onSeen: () => setVtInputNoticeVersion(VT_INPUT_NOTICE_VERSION),
|
|
982
934
|
}
|
|
983
935
|
: undefined);
|
|
@@ -1584,11 +1536,6 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
1584
1536
|
}
|
|
1585
1537
|
resolveHuman({ kind: 'planDecision', decision: verdict.decision, feedback: verdict.feedback });
|
|
1586
1538
|
};
|
|
1587
|
-
// --- Mouse wheel routing (wheel-scrolls-transcript-only §4.1). Table and
|
|
1588
|
-
// coalescer live in the hook (D-13); only the wiring it cannot own stays here.
|
|
1589
|
-
// Inline arrows are fine — the hook re-reads its options through a ref every
|
|
1590
|
-
// render. The `Math.max(0, …)` mirrors the keyboard branch below, and for the
|
|
1591
|
-
// same reason: `OverlayFrame` only ever reports a clamp DOWNWARD.
|
|
1592
1539
|
const { clearCoalescer } = useWheelRouting({
|
|
1593
1540
|
mouseSource,
|
|
1594
1541
|
isPointerCaptured: () => terminal?.scrollbar?.controller?.isCaptured() ?? false,
|
|
@@ -1734,7 +1681,7 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
1734
1681
|
return;
|
|
1735
1682
|
}
|
|
1736
1683
|
if (key.ctrl && (input === 'l' || input === 'L')) {
|
|
1737
|
-
|
|
1684
|
+
{
|
|
1738
1685
|
// Writing \x1B[2J here would erase the screen and then leave it blank:
|
|
1739
1686
|
// Ink sees no output change and skips the repaint (ink.js:132 +
|
|
1740
1687
|
// log-update.js:13), possibly forever on an idle empty session. Bump
|
|
@@ -1750,7 +1697,7 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
1750
1697
|
}
|
|
1751
1698
|
return;
|
|
1752
1699
|
}
|
|
1753
|
-
|
|
1700
|
+
{
|
|
1754
1701
|
if (key.pageUp) {
|
|
1755
1702
|
scrollBy('pageUp');
|
|
1756
1703
|
return;
|
|
@@ -1771,17 +1718,10 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
1771
1718
|
if (key.ctrl && (input === 't' || input === 'T')) {
|
|
1772
1719
|
const revealing = !stateRef.current.thinkingVisible;
|
|
1773
1720
|
dispatch({ type: 'toggleThinking' });
|
|
1774
|
-
// THE ACK IS MODE-AWARE FOR THE SAME REASON THE MARKER IS (D-16 / P0-2).
|
|
1775
|
-
// Inline mode cannot repaint what `<Static>` has already printed, so the
|
|
1776
|
-
// toast says what it DID rather than what the user hoped. The hide
|
|
1777
|
-
// direction needs no qualifier: hiding applies to everything drawn from
|
|
1778
|
-
// here on, and nothing is claimed about scrollback either way.
|
|
1779
1721
|
if (!revealing)
|
|
1780
1722
|
toast('info', 'Thinking hidden.');
|
|
1781
|
-
else if (fullscreen)
|
|
1782
|
-
toast('info', 'Thinking shown.');
|
|
1783
1723
|
else
|
|
1784
|
-
toast('info', 'Thinking shown
|
|
1724
|
+
toast('info', 'Thinking shown.');
|
|
1785
1725
|
return;
|
|
1786
1726
|
}
|
|
1787
1727
|
if (key.ctrl && (input === 'o' || input === 'O')) {
|
|
@@ -1927,40 +1867,25 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
1927
1867
|
// milliseconds because `ToolCard` is `React.memo`'d with the default
|
|
1928
1868
|
// comparator; `Transcript` passes it on only to running tool entries.
|
|
1929
1869
|
const nowSec = running || compactionLive ? Math.floor(Date.now() / 1000) : undefined;
|
|
1930
|
-
// Row budget for the viewport and everything sized against it (§4.2a). The
|
|
1931
|
-
// inline path has no fixed frame, hence no height to fit into: `Infinity`
|
|
1932
|
-
// tells `OverlayFrame` to render everything and show no position indicator.
|
|
1933
|
-
// `draftRows` is the number `PromptInput` just reported for the rows it is
|
|
1934
|
-
// ACTUALLY RENDERING (I-8). Defaulting it to 1 is what makes the inline path
|
|
1935
|
-
// and every test that never types byte-identical to before.
|
|
1936
1870
|
const viewportBudget = computeViewportRows(rows, draftRows);
|
|
1937
|
-
const overlayMaxRows =
|
|
1871
|
+
const overlayMaxRows = viewportBudget;
|
|
1938
1872
|
// Renderers consume these same projections. Team capacity does not depend on
|
|
1939
1873
|
// popup occupancy, so a menu cannot toggle the team's collapsed state.
|
|
1940
|
-
|
|
1941
|
-
|
|
1874
|
+
// THE ONE CONDITION for "the hint row exists": it sizes the footer below AND
|
|
1875
|
+
// decides whether the run status row may replace that hint row while running,
|
|
1876
|
+
// so starting a run can never add or remove a footer row.
|
|
1877
|
+
const hintRowEnabled = rows >= HINT_MIN_ROWS && cfg.hints;
|
|
1878
|
+
const composerBaseRows = 2 + clampDraftRows(rows, draftRows) + Number(hintRowEnabled);
|
|
1942
1879
|
const teamLayout = buildTeamPanelLayout({
|
|
1943
1880
|
snapshot: state.team, terminalRows: rows, availableRows: Math.max(0, viewportBudget - composerBaseRows),
|
|
1944
1881
|
});
|
|
1945
1882
|
const railLayout = buildTodoRailLayout({
|
|
1946
|
-
|
|
1883
|
+
cols: cols - 1, panelEnabled: cfg.todo.panel, overlayOpen: false,
|
|
1947
1884
|
itemCount: state.todos?.items.length ?? 0, viewportBudget,
|
|
1948
1885
|
teamRows: teamLayout.rowCount, popupRows, composerBaseRows,
|
|
1949
1886
|
});
|
|
1950
1887
|
// Transcript and composer share contentCols; fixed chrome and overlays use terminal width.
|
|
1951
1888
|
const { visible: showRail, width: railWidth, rows: railRows, contentCols } = railLayout;
|
|
1952
|
-
// The inline plan strip (todo-plan-followthrough §3.7 / W2).
|
|
1953
|
-
//
|
|
1954
|
-
// DELIBERATELY NOT `!showRail`, which is true for FOUR different reasons and
|
|
1955
|
-
// three of them mean "do not draw this". `cfg.todo.panel` is included because
|
|
1956
|
-
// `--no-todo-panel` means "keep the planning, drop the display" and a strip is
|
|
1957
|
-
// a display. Overlays are NOT consulted: inline mode's overlays are part of
|
|
1958
|
-
// the document flow, not a modal that owns the screen.
|
|
1959
|
-
//
|
|
1960
|
-
// `cfg` is the right read here and `controller.getTodoConfig()` would be
|
|
1961
|
-
// needless — this runs in RENDER scope, where `cfg` is re-read every frame.
|
|
1962
|
-
// The asymmetry with the `agent_end` handler above is the whole of P1-1.
|
|
1963
|
-
const showStrip = !fullscreen && cfg.todo.panel && state.todos !== null;
|
|
1964
1889
|
const overlayNode = overlay === 'help' ? (_jsx(OverlayFrame, { title: "Help", hint: `Esc close ${glyphs.midDot} PgUp/PgDn scroll`, maxRows: overlayMaxRows, cols: cols, rows: helpRows(theme, caps), scrollOffset: overlayScroll, onScrollClamp: setOverlayScroll, theme: theme, caps: caps })) : overlay === 'model' ? (_jsx(ModelPicker, { registry: controller.getModelRegistry(), currentProvider: cfg.provider, currentModel: cfg.model, maxRows: overlayMaxRows, cols: cols, theme: theme, caps: caps, onSelect: handleModelSelect })) : overlay === 'settings' ? (_jsx(SettingsScreen, { initial: {
|
|
1965
1890
|
provider: cfg.provider,
|
|
1966
1891
|
model: cfg.model,
|
|
@@ -1994,14 +1919,11 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
1994
1919
|
// Read from `cfg` (render scope, re-read every frame), so `/retry off`
|
|
1995
1920
|
// while the screen is open shows the new value on the next paint.
|
|
1996
1921
|
retrySummary: cfg.retry.enabled && cfg.retry.maxRetries > 0 ? String(cfg.retry.maxRetries) : 'off' })) : overlay === 'confirm' && confirmState ? (_jsx(ConfirmDialog, { state: confirmState, maxRows: overlayMaxRows, cols: cols, theme: theme, caps: caps, onClose: closeConfirm })) : overlay === 'question' && humanRequest?.kind === 'questions' ? (_jsx(QuestionOverlay, { questions: humanRequest.questions, maxRows: overlayMaxRows, cols: cols, theme: theme, caps: caps, onSubmit: (answers) => resolveHuman({ kind: 'answers', answers, cancelled: false }) })) : overlay === 'plan' && humanRequest?.kind === 'plan' ? (_jsx(PlanReviewOverlay, { plan: humanRequest.plan, maxRows: overlayMaxRows, cols: cols, scrollOffset: overlayScroll, onScrollClamp: setOverlayScroll, theme: theme, caps: caps, onVerdict: handlePlanVerdict })) : null;
|
|
1997
|
-
|
|
1998
|
-
// the viewport monotonic and the first submit jump-free (§4.2). Inline keeps
|
|
1999
|
-
// its v0.3.0 ternary and never calls `pickHeaderVariant`.
|
|
2000
|
-
const header = (_jsx(Header, { version: version, cwd: controller.getCwd(), provider: cfg.provider, model: cfg.model, hasKey: hasKey, variant: fullscreen ? pickHeaderVariant(cols) : empty ? 'banner' : 'bar', theme: theme, caps: caps }));
|
|
1922
|
+
const header = (_jsx(Header, { cwd: controller.getCwd(), provider: cfg.provider, model: cfg.model, hasKey: hasKey, variant: pickHeaderVariant(cols), theme: theme, caps: caps }));
|
|
2001
1923
|
// The wordmark + getting-started card now live INSIDE the viewport, as its
|
|
2002
1924
|
// first block of content, so they scroll away instead of being yanked out of
|
|
2003
1925
|
// the header the moment the first message lands.
|
|
2004
|
-
const opener = empty ? (_jsx(SessionOpener, { variant:
|
|
1926
|
+
const opener = empty ? (_jsx(SessionOpener, { variant: pickOpenerVariant(viewportBudget, contentCols, caps), version: version, cwd: controller.getCwd(), hasKey: hasKey, viewportRows: viewportBudget, theme: theme, caps: caps })) : null;
|
|
2005
1927
|
const density = cfg.density;
|
|
2006
1928
|
const transcriptWindow = cfg.transcriptWindow ?? DEFAULT_TRANSCRIPT_WINDOW;
|
|
2007
1929
|
// --- One spinner per run (single-spinner-while-running D-1 .. D-5). ------
|
|
@@ -2039,6 +1961,26 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
2039
1961
|
// threading the raw flag into a fifth consumer is the one silent way back to
|
|
2040
1962
|
// two spinners (R-2).
|
|
2041
1963
|
const viewReducedMotion = reducedMotion || activityVisible;
|
|
1964
|
+
// The run status row (spinner + phrase + steer/interrupt hint) replaces the idle
|
|
1965
|
+
// hint row ABOVE the input while running; it exists only when that hint row
|
|
1966
|
+
// would, so the footer height is identical with and without it. `running`, not
|
|
1967
|
+
// `activityVisible`: an overlay must not flip the Composer between two layouts.
|
|
1968
|
+
const runRowEnabled = running && hintRowEnabled;
|
|
1969
|
+
// The raw `reducedMotion` flag lives here and nowhere else (D-5).
|
|
1970
|
+
const runActivity = {
|
|
1971
|
+
startedAt: runStartedAt.current,
|
|
1972
|
+
elapsedMs,
|
|
1973
|
+
reducedMotion,
|
|
1974
|
+
runningTool,
|
|
1975
|
+
// THE ROW MUST NEVER SAY SOMETHING FALSE (§6.1). Compaction runs BEFORE
|
|
1976
|
+
// `turn_start`, so `status` is `'running'` with no tool in flight - precisely
|
|
1977
|
+
// the window in which the rotating phrase would claim the model is thinking
|
|
1978
|
+
// while it is actually waiting on a 30k-token summarization.
|
|
1979
|
+
compacting: state.compaction?.inFlight === true,
|
|
1980
|
+
};
|
|
1981
|
+
// The fixed bottom row takes over the life signal once the run row scrolls out
|
|
1982
|
+
// of the viewport; exactly one of the two ever animates.
|
|
1983
|
+
const runRowShown = runRowEnabled && activityRowVisible;
|
|
2042
1984
|
// Assigned during render so `/perf` reads the CURRENT frame (see `perfRef`).
|
|
2043
1985
|
// `mountedEntries` is the same number `render-budget.test.tsx` asserts (AC-9).
|
|
2044
1986
|
perfRef.current = {
|
|
@@ -2053,12 +1995,18 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
2053
1995
|
heightsMeasured: heights.stats().measured,
|
|
2054
1996
|
heightsEstimated: heights.stats().estimated,
|
|
2055
1997
|
cols: contentCols,
|
|
2056
|
-
mode:
|
|
2057
|
-
viewportRows:
|
|
1998
|
+
mode: 'fullscreen',
|
|
1999
|
+
viewportRows: viewportBudget,
|
|
2058
2000
|
offset: scrolledLines,
|
|
2001
|
+
// A getter: the bridge's `frameReady` and geometry change outside React renders.
|
|
2002
|
+
get scrollbar() {
|
|
2003
|
+
return readScrollbarPerf({
|
|
2004
|
+
bridge: terminal?.scrollbar, diffRender: cfg.diffRender, unicode: caps.unicode,
|
|
2005
|
+
});
|
|
2006
|
+
},
|
|
2059
2007
|
};
|
|
2060
2008
|
// Completion belongs to the document footer; cap it to keep the editor usable.
|
|
2061
|
-
const popupMaxRows =
|
|
2009
|
+
const popupMaxRows = Math.max(1, viewportBudget - 4);
|
|
2062
2010
|
// WHICH KEY THE HINT ROW NAMES (shift-tab-mode-toggle-still-dead-on-windows,
|
|
2063
2011
|
// C3-1). Derived from `vtInputWarning` rather than from a probe of its own,
|
|
2064
2012
|
// because that prop is already the single answer to "can this console deliver
|
|
@@ -2069,29 +2017,13 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
2069
2017
|
// Both keys always WORK (they share one branch in the input handler); this
|
|
2070
2018
|
// only decides which one the user is taught.
|
|
2071
2019
|
const modeToggleKey = vtInputWarning ? MODE_TOGGLE_KEYS.fallback : MODE_TOGGLE_KEYS.primary;
|
|
2072
|
-
const composer =
|
|
2073
|
-
|
|
2020
|
+
const composer = (_jsx(Composer, { measureRef: composerRef, runRow: runRowEnabled
|
|
2021
|
+
? { activity: runActivity, live: runRowShown && !overlayNode, rowRef: activityRef }
|
|
2022
|
+
: null, cols: contentCols, cursorVisible: cursorVisible, onInteraction: returnToComposer, reducedMotion: cfg.reducedMotion ?? false, onEscape: handleComposerEscape, onEscapeDismiss: cancelConfirmation, isActive: overlay === null && !tooSmall, running: state.status === 'running', history: promptHistory, commands: commandOptions, cwd: controller.getCwd(), showHint: rows >= HINT_MIN_ROWS, submitCount: cfg.submitCount, hintsEnabled: cfg.hints, agentMode: state.agentMode, services: liveServices, modeToggleKey: modeToggleKey, popupMaxRows: popupMaxRows, popupMaxHeight: railLayout.popupMaxHeight, onPopupRowsChange: onPopupRowsChange, scrolledLines: scrolledLines, onDraftRows: onDraftRows, onNotice: notify, theme: theme, caps: caps, onSubmit: (text) => void handleSubmit(text), onHelp: () => dispatch({ type: 'setOverlay', overlay: 'help' }) }));
|
|
2023
|
+
const team = state.team ? (_jsx(TeamPanel, { snapshot: state.team, layout: teamLayout, rows: rows, cols: contentCols, reducedMotion: viewReducedMotion, theme: theme, caps: caps })) : null;
|
|
2074
2024
|
const rail = showRail && state.todos ? (_jsx(TodoPanel, { snapshot: state.todos, width: railWidth, rows: railRows, running: running, reducedMotion: viewReducedMotion, theme: theme, caps: caps })) : null;
|
|
2075
|
-
const viewport =
|
|
2076
|
-
return (_jsx(AppShell, { inactive: tooSmall, placeholder: _jsxs(Text, { wrap: "truncate", color: theme.noticeWarn, children: [glyphs.warn, " Terminal too small - needs at least 40 columns and 12 rows."] }),
|
|
2077
|
-
// `!overlayNode` suppresses the line while an overlay owns the screen:
|
|
2078
|
-
// the full-screen branch replaces the whole viewport with the overlay,
|
|
2079
|
-
// and a working line under a settings screen is noise attached to a
|
|
2080
|
-
// surface the run is not visible on. (Inline overlays are part of the
|
|
2081
|
-
// document flow, so there the suppression is conservative rather than
|
|
2082
|
-
// necessary — P2-5.)
|
|
2083
|
-
//
|
|
2084
|
-
// THE CONDITION IS `activityVisible`, THE SAME CONST THE SUPPRESSION
|
|
2085
|
-
// SIGNAL IS DERIVED FROM (D-4): inlining it here again is how the two
|
|
2086
|
-
// drift apart, and the drift is silent in whichever branch is not
|
|
2087
|
-
// edited.
|
|
2088
|
-
activity: activityVisible ? (_jsx(ActivityLine, { startedAt: runStartedAt.current, elapsedMs: elapsedMs, reducedMotion: reducedMotion, runningTool: runningTool,
|
|
2089
|
-
// THE ROW MUST NEVER SAY SOMETHING FALSE (§6.1). Compaction runs
|
|
2090
|
-
// BEFORE `turn_start`, so `status` is `'running'` with no tool in
|
|
2091
|
-
// flight — precisely the window in which the rotating phrase
|
|
2092
|
-
// would claim the model is thinking while it is actually waiting
|
|
2093
|
-
// on a 30k-token summarization.
|
|
2094
|
-
compacting: state.compaction?.inFlight === true, theme: theme, caps: caps })) : null,
|
|
2025
|
+
const viewport = (_jsxs(ScrollViewport, { active: overlay === null && !tooSmall, overlay: tooSmall ? null : overlayNode, footer: _jsxs(_Fragment, { children: [team, composer] }), rail: rail, scrollbar: terminal?.scrollbar, composerRef: composerRef, onComposerVisibilityChange: setCursorVisible, activityRef: activityRef, onActivityVisibilityChange: setActivityRowVisible, resetKey: scrollResetKey, intent: scrollIntent, pinToBottomNonce: pinToBottomNonce, onScrolledLinesChange: setScrolledLines, onViewportShiftChange: onViewportShiftChange, tailRowsRef: tailSink, hold: selectionHold, resumeMs: cfg.scrollResumeMs, showScrollIndicator: true, cols: contentCols, theme: theme, caps: caps, children: [opener, _jsx(TranscriptList, { entries: state.entries, expandedToolIds: state.expandedToolIds, thinkingVisible: state.thinkingVisible, reducedMotion: viewReducedMotion, density: density, nowSec: nowSec, theme: theme, caps: caps, windowSize: transcriptWindow, cols: contentCols, heights: heights, mountedSink: mountedCount, tailSink: tailSink })] }));
|
|
2026
|
+
return (_jsx(AppShell, { inactive: tooSmall, placeholder: _jsxs(Text, { wrap: "truncate", color: theme.noticeWarn, children: [glyphs.warn, " Terminal too small - needs at least 40 columns and 12 rows."] }), rows: rows, cols: cols, header: header, viewport: viewport, toast: _jsx(BottomStatusRow, { toasts: state.toasts, activity: activityVisible && !runRowShown ? (_jsx(ActivityLine, { ...runActivity, theme: theme, caps: caps })) : null,
|
|
2095
2027
|
// THE FORM THE LIFE SIGNAL TAKES WHEN A TOAST HAS THE ROW
|
|
2096
2028
|
// (`activity-spinner-vanishes-behind-toast`). Passed UNCONDITIONALLY:
|
|
2097
2029
|
// `BottomStatusRow` reads it only when `activity` above is non-null,
|
|
@@ -2111,7 +2043,13 @@ export function App({ controller, version, mode, initialOverlay, initialPrompt,
|
|
|
2111
2043
|
// branch replaces the whole viewport with the overlay, and an update
|
|
2112
2044
|
// notice under a settings screen is noise attached to a surface it is
|
|
2113
2045
|
// not visible on.
|
|
2114
|
-
|
|
2046
|
+
//
|
|
2047
|
+
// `!running` (cli-auto-update D-2): the update notice stays silent for
|
|
2048
|
+
// the whole run. It used to be guaranteed by the activity line
|
|
2049
|
+
// out-ranking it inside this row; with the activity line moved above
|
|
2050
|
+
// the input, the row can be free during a run, so the silence is stated
|
|
2051
|
+
// here instead of inherited.
|
|
2052
|
+
update: updateSnapshot && !overlayNode && !running && shouldRenderUpdateLine(updateSnapshot) ? (_jsx(UpdateLine, { snapshot: updateSnapshot, compact: cols < UPDATE_LIMITS.statusCompactCols, theme: theme, caps: caps })) : null, theme: theme }), status: _jsx(StatusBar, { interruptHint: interruptGesture.current.phase === 'stopping'
|
|
2115
2053
|
? 'Esc force-stop' : 'Esc x2 interrupt', model: cfg.model, provider: cfg.provider, usageTotal: state.usageTotal,
|
|
2116
2054
|
// ONE OBJECT INSTEAD OF FOUR PARALLEL PROPS (§4.3). The four were a
|
|
2117
2055
|
// projection of "many sources of truth" into the props layer, and a
|
|
@@ -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
|
@@ -18,8 +18,24 @@ import type { Theme } from './theme.js';
|
|
|
18
18
|
import type { TermCapabilities } from './capabilities.js';
|
|
19
19
|
import { pickGlyphs } from './glyphs.js';
|
|
20
20
|
import { type CommandOption } from './PromptInput.js';
|
|
21
|
+
import { type RunActivity } from './run-status-row.js';
|
|
21
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
|
+
}
|
|
22
36
|
export interface ComposerProps {
|
|
37
|
+
/** Present only while the run status row replaces the hint row below the input. */
|
|
38
|
+
runRow?: RunRowProps | null;
|
|
23
39
|
cols?: number;
|
|
24
40
|
cursorVisible?: boolean;
|
|
25
41
|
onInteraction?: () => void;
|
|
@@ -96,6 +112,17 @@ export interface ComposerProps {
|
|
|
96
112
|
onSubmit: (text: string) => void;
|
|
97
113
|
onHelp?: () => void;
|
|
98
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[];
|
|
99
126
|
/**
|
|
100
127
|
* Build the hint row.
|
|
101
128
|
*
|
|
@@ -125,5 +152,5 @@ declare function hintText(opts: {
|
|
|
125
152
|
* than fishing the string out of a rendered frame.
|
|
126
153
|
*/
|
|
127
154
|
export declare const hintTextForTest: typeof hintText;
|
|
128
|
-
export declare function Composer({ 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;
|
|
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;
|
|
129
156
|
export {};
|