@aragon-agent/cli 0.6.4 → 0.6.8
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 +37 -0
- package/README.md +163 -37
- package/dist/agent/controller.d.ts +49 -8
- package/dist/agent/controller.js +160 -48
- package/dist/agent/headless.d.ts +2 -1
- package/dist/agent/prompt-options.d.ts +15 -0
- package/dist/agent/prompt-options.js +1 -0
- package/dist/agent/reducer.d.ts +33 -19
- package/dist/agent/reducer.js +62 -71
- package/dist/agent/usage.d.ts +34 -0
- package/dist/agent/usage.js +43 -0
- package/dist/cli.js +131 -54
- package/dist/commands/builtins.js +12 -1
- package/dist/commands/registry.d.ts +2 -3
- package/dist/compaction/compactor.d.ts +60 -24
- package/dist/compaction/compactor.js +89 -61
- package/dist/compaction/context-command.d.ts +33 -0
- package/dist/compaction/context-command.js +166 -0
- package/dist/compaction/meter.d.ts +251 -0
- package/dist/compaction/meter.js +428 -0
- package/dist/compaction/pressure.d.ts +6 -0
- package/dist/compaction/pressure.js +4 -0
- package/dist/compaction/types.d.ts +41 -0
- package/dist/compaction/wiring.d.ts +47 -0
- package/dist/compaction/wiring.js +88 -27
- package/dist/config/cli-commands.js +5 -0
- package/dist/config/env.js +15 -1
- package/dist/config/load.d.ts +17 -0
- package/dist/config/load.js +45 -1
- package/dist/config/schema.d.ts +44 -0
- package/dist/config/schema.js +36 -0
- package/dist/config/store.js +27 -1
- package/dist/exec/events.d.ts +138 -1
- package/dist/exec/events.js +42 -0
- package/dist/exec/index.js +43 -6
- package/dist/exec/progress-reporter.d.ts +23 -0
- package/dist/exec/progress-reporter.js +88 -0
- package/dist/exec/runner.d.ts +34 -0
- package/dist/exec/runner.js +137 -25
- package/dist/fast/reviewer.d.ts +19 -1
- package/dist/fast/reviewer.js +22 -0
- package/dist/fast/types.d.ts +15 -0
- package/dist/fast/wiring.d.ts +10 -1
- package/dist/fast/wiring.js +18 -1
- package/dist/input/interrupt-gesture.d.ts +18 -0
- package/dist/input/interrupt-gesture.js +19 -0
- package/dist/input/pointer-router.d.ts +11 -0
- package/dist/input/pointer-router.js +14 -0
- package/dist/todo/limits.d.ts +4 -17
- package/dist/todo/limits.js +4 -17
- package/dist/todo/panel-rows.d.ts +8 -6
- package/dist/todo/panel-rows.js +19 -6
- package/dist/todo/store.d.ts +8 -13
- package/dist/todo/store.js +13 -14
- package/dist/todo/types.d.ts +2 -1
- package/dist/ui/App.d.ts +2 -0
- package/dist/ui/App.js +233 -183
- package/dist/ui/AutocompletePopup.d.ts +3 -1
- package/dist/ui/AutocompletePopup.js +9 -11
- package/dist/ui/BottomStatusRow.d.ts +1 -1
- package/dist/ui/Composer.d.ts +13 -5
- package/dist/ui/Composer.js +7 -11
- package/dist/ui/PromptCaret.d.ts +11 -0
- package/dist/ui/PromptCaret.js +31 -0
- package/dist/ui/PromptInput.d.ts +12 -6
- package/dist/ui/PromptInput.js +50 -16
- package/dist/ui/StatusBar.d.ts +22 -15
- package/dist/ui/StatusBar.js +61 -16
- package/dist/ui/TeamPanel.d.ts +3 -1
- package/dist/ui/TeamPanel.js +13 -9
- package/dist/ui/TodoPanel.d.ts +5 -5
- package/dist/ui/TodoPanel.js +56 -24
- package/dist/ui/Transcript.js +1 -0
- package/dist/ui/UpdateLine.d.ts +1 -1
- package/dist/ui/capabilities.js +5 -5
- package/dist/ui/composer-rows.js +50 -9
- package/dist/ui/exit-snapshot.d.ts +9 -0
- package/dist/ui/frame-differ.js +2 -19
- package/dist/ui/frame-observer.d.ts +17 -0
- package/dist/ui/frame-observer.js +81 -0
- package/dist/ui/frame-parser.d.ts +7 -0
- package/dist/ui/frame-parser.js +18 -0
- package/dist/ui/layout/AppShell.d.ts +7 -52
- package/dist/ui/layout/AppShell.js +2 -2
- package/dist/ui/layout/ScrollIndicator.d.ts +4 -3
- package/dist/ui/layout/ScrollIndicator.js +4 -4
- package/dist/ui/layout/ScrollViewport.d.ts +13 -66
- package/dist/ui/layout/ScrollViewport.js +183 -185
- package/dist/ui/layout/autocomplete.d.ts +18 -0
- package/dist/ui/layout/autocomplete.js +26 -0
- package/dist/ui/layout/budget.d.ts +3 -51
- package/dist/ui/layout/budget.js +4 -53
- package/dist/ui/layout/document-layout.d.ts +13 -0
- package/dist/ui/layout/document-layout.js +11 -0
- package/dist/ui/layout/follow-state.d.ts +1 -0
- package/dist/ui/layout/follow-state.js +1 -3
- package/dist/ui/layout/rail.d.ts +4 -53
- package/dist/ui/layout/rail.js +10 -62
- package/dist/ui/layout/scroll-indicator.d.ts +1 -1
- package/dist/ui/layout/scroll-indicator.js +3 -2
- package/dist/ui/layout/scrollbar-geometry.d.ts +17 -0
- package/dist/ui/layout/scrollbar-geometry.js +13 -0
- package/dist/ui/layout/team-panel.d.ts +18 -0
- package/dist/ui/layout/team-panel.js +22 -0
- package/dist/ui/layout/todo-layout.d.ts +25 -0
- package/dist/ui/layout/todo-layout.js +28 -0
- package/dist/ui/layout/viewport-geometry.d.ts +1 -0
- package/dist/ui/layout/viewport-geometry.js +1 -0
- package/dist/ui/layout/virtual-window.d.ts +1 -0
- package/dist/ui/layout/virtual-window.js +1 -1
- package/dist/ui/overlays/HelpOverlay.js +5 -2
- package/dist/ui/screen.d.ts +1 -1
- package/dist/ui/screen.js +4 -6
- package/dist/ui/scrollbar-controller.d.ts +30 -0
- package/dist/ui/scrollbar-controller.js +124 -0
- package/dist/ui/transcript-text.js +6 -2
- package/dist/ui/use-wheel-routing.d.ts +2 -0
- package/dist/ui/use-wheel-routing.js +6 -0
- package/package.json +2 -2
package/dist/ui/layout/rail.d.ts
CHANGED
|
@@ -1,56 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
* Rail geometry (todo-plan-execution §3.9) — the ONE place that says how wide
|
|
3
|
-
* and how tall the todo rail is allowed to be.
|
|
4
|
-
*
|
|
5
|
-
* Pure and React-free, mirroring `budget.ts`, so `todo-rail.test.ts` can pin the
|
|
6
|
-
* arithmetic without mounting anything. Deliberately NOT part of `budget.ts`:
|
|
7
|
-
* that module answers "how many ROWS does the viewport get" and stays a function
|
|
8
|
-
* of `rows` alone; the rail spends COLUMNS.
|
|
9
|
-
*
|
|
10
|
-
* NAMED FOR ITS POSITION, NOT ITS CONTENT, like `AppShellProps.rail` (D-18):
|
|
11
|
-
* this is a layout primitive and `viewportRows` is its neighbour, not
|
|
12
|
-
* `TodoPanel`.
|
|
13
|
-
*/
|
|
14
|
-
/** Below this the marker + a two-word title do not fit and the column is noise. */
|
|
15
|
-
export declare const TODO_RAIL_MIN_COLS = 18;
|
|
16
|
-
/**
|
|
17
|
-
* Above this, 20% is being spent on whitespace: at 36 columns a step title
|
|
18
|
-
* already fits on one line, and the transcript is the surface that benefits from
|
|
19
|
-
* a 200-column terminal. A deliberate, revertible deviation from a literal
|
|
20
|
-
* reading of R-e; it only binds above 180 columns.
|
|
21
|
-
*/
|
|
1
|
+
export declare const TODO_RAIL_MIN_COLS = 14;
|
|
22
2
|
export declare const TODO_RAIL_MAX_COLS = 36;
|
|
23
|
-
|
|
24
|
-
export declare const TODO_RAIL_MIN_TOTAL_COLS
|
|
25
|
-
/** Below this rail width the 2-column index prefix is dropped for content. */
|
|
3
|
+
export declare const TODO_TRANSCRIPT_MIN_COLS = 62;
|
|
4
|
+
export declare const TODO_RAIL_MIN_TOTAL_COLS: number;
|
|
26
5
|
export declare const TODO_RAIL_INDEX_MIN_COLS = 22;
|
|
27
|
-
/**
|
|
28
|
-
* Two properties are pinned by tests, for the reason `budget.ts` records about
|
|
29
|
-
* the viewport being non-monotonic before anyone measured it:
|
|
30
|
-
*
|
|
31
|
-
* - NON-DECREASING in `cols` across [40, 300]. A user dragging a window wider
|
|
32
|
-
* must never see the rail get narrower.
|
|
33
|
-
* - `cols - todoRailWidth(cols) >= 62` for every `cols >= 80`. Every existing
|
|
34
|
-
* width heuristic inside the viewport (`TERSE_HINT_COLS` 42,
|
|
35
|
-
* `MIN_INDICATOR_COLS` 50, the status bar's 60/72 steps) sits below that
|
|
36
|
-
* floor, so no existing behaviour changes because a rail appeared (R-3).
|
|
37
|
-
*/
|
|
6
|
+
/** Return a non-decreasing rail width; invalid or narrow terminals have no rail. */
|
|
38
7
|
export declare function todoRailWidth(cols: number): number;
|
|
39
|
-
/**
|
|
40
|
-
* How many rows the rail may actually draw.
|
|
41
|
-
*
|
|
42
|
-
* IT IS NOT `viewportBudget`, AND THE DIFFERENCE IS NOT COSMETIC (P1-3 / D-23).
|
|
43
|
-
* `viewportRows()` subtracts the STATIC chrome only, and says so; the live team
|
|
44
|
-
* roster is mounted inside `AppShell`'s measured bottom box, where it costs a
|
|
45
|
-
* header row, up to `TEAM_LIMITS.panelMaxRows` child rows, a `+N more` row and a
|
|
46
|
-
* mail row that `viewportBudget` knows nothing about. `ScrollViewport` is immune
|
|
47
|
-
* because it MEASURES itself; the rail does not measure - a second
|
|
48
|
-
* measure -> setState -> measure loop is exactly what §1.2 refuses - so it has
|
|
49
|
-
* to subtract.
|
|
50
|
-
*
|
|
51
|
-
* Ink clips overflow from the BOTTOM, so an over-request does not error: it
|
|
52
|
-
* silently eats the last row, which is the `+N below` marker - the one row whose
|
|
53
|
-
* absence is indistinguishable from "the list is short". That is why this is
|
|
54
|
-
* arithmetic rather than a shrug at `overflow: hidden`.
|
|
55
|
-
*/
|
|
56
|
-
export declare function todoRailRows(viewportBudget: number, teamActive: boolean): number;
|
package/dist/ui/layout/rail.js
CHANGED
|
@@ -1,68 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
* Pure and React-free, mirroring `budget.ts`, so `todo-rail.test.ts` can pin the
|
|
6
|
-
* arithmetic without mounting anything. Deliberately NOT part of `budget.ts`:
|
|
7
|
-
* that module answers "how many ROWS does the viewport get" and stays a function
|
|
8
|
-
* of `rows` alone; the rail spends COLUMNS.
|
|
9
|
-
*
|
|
10
|
-
* NAMED FOR ITS POSITION, NOT ITS CONTENT, like `AppShellProps.rail` (D-18):
|
|
11
|
-
* this is a layout primitive and `viewportRows` is its neighbour, not
|
|
12
|
-
* `TodoPanel`.
|
|
13
|
-
*/
|
|
14
|
-
import { TODO_LIMITS } from '../../todo/limits.js';
|
|
15
|
-
/** R-e, literally: the rail asks for a fifth of the terminal. */
|
|
16
|
-
const RAIL_FRACTION = 0.2;
|
|
17
|
-
/** Below this the marker + a two-word title do not fit and the column is noise. */
|
|
18
|
-
export const TODO_RAIL_MIN_COLS = 18;
|
|
19
|
-
/**
|
|
20
|
-
* Above this, 20% is being spent on whitespace: at 36 columns a step title
|
|
21
|
-
* already fits on one line, and the transcript is the surface that benefits from
|
|
22
|
-
* a 200-column terminal. A deliberate, revertible deviation from a literal
|
|
23
|
-
* reading of R-e; it only binds above 180 columns.
|
|
24
|
-
*/
|
|
1
|
+
/** Responsive TODO rail widths, measured in terminal columns including chrome. */
|
|
2
|
+
const RAIL_FRACTION = 0.15;
|
|
3
|
+
export const TODO_RAIL_MIN_COLS = 14;
|
|
25
4
|
export const TODO_RAIL_MAX_COLS = 36;
|
|
26
|
-
|
|
27
|
-
export const TODO_RAIL_MIN_TOTAL_COLS =
|
|
28
|
-
/** Below this rail width the 2-column index prefix is dropped for content. */
|
|
5
|
+
export const TODO_TRANSCRIPT_MIN_COLS = 62;
|
|
6
|
+
export const TODO_RAIL_MIN_TOTAL_COLS = TODO_TRANSCRIPT_MIN_COLS + TODO_RAIL_MIN_COLS;
|
|
29
7
|
export const TODO_RAIL_INDEX_MIN_COLS = 22;
|
|
30
|
-
/**
|
|
31
|
-
* Two properties are pinned by tests, for the reason `budget.ts` records about
|
|
32
|
-
* the viewport being non-monotonic before anyone measured it:
|
|
33
|
-
*
|
|
34
|
-
* - NON-DECREASING in `cols` across [40, 300]. A user dragging a window wider
|
|
35
|
-
* must never see the rail get narrower.
|
|
36
|
-
* - `cols - todoRailWidth(cols) >= 62` for every `cols >= 80`. Every existing
|
|
37
|
-
* width heuristic inside the viewport (`TERSE_HINT_COLS` 42,
|
|
38
|
-
* `MIN_INDICATOR_COLS` 50, the status bar's 60/72 steps) sits below that
|
|
39
|
-
* floor, so no existing behaviour changes because a rail appeared (R-3).
|
|
40
|
-
*/
|
|
8
|
+
/** Return a non-decreasing rail width; invalid or narrow terminals have no rail. */
|
|
41
9
|
export function todoRailWidth(cols) {
|
|
42
|
-
if (!Number.isFinite(cols)
|
|
10
|
+
if (!Number.isFinite(cols))
|
|
43
11
|
return 0;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* How many rows the rail may actually draw.
|
|
48
|
-
*
|
|
49
|
-
* IT IS NOT `viewportBudget`, AND THE DIFFERENCE IS NOT COSMETIC (P1-3 / D-23).
|
|
50
|
-
* `viewportRows()` subtracts the STATIC chrome only, and says so; the live team
|
|
51
|
-
* roster is mounted inside `AppShell`'s measured bottom box, where it costs a
|
|
52
|
-
* header row, up to `TEAM_LIMITS.panelMaxRows` child rows, a `+N more` row and a
|
|
53
|
-
* mail row that `viewportBudget` knows nothing about. `ScrollViewport` is immune
|
|
54
|
-
* because it MEASURES itself; the rail does not measure - a second
|
|
55
|
-
* measure -> setState -> measure loop is exactly what §1.2 refuses - so it has
|
|
56
|
-
* to subtract.
|
|
57
|
-
*
|
|
58
|
-
* Ink clips overflow from the BOTTOM, so an over-request does not error: it
|
|
59
|
-
* silently eats the last row, which is the `+N below` marker - the one row whose
|
|
60
|
-
* absence is indistinguishable from "the list is short". That is why this is
|
|
61
|
-
* arithmetic rather than a shrug at `overflow: hidden`.
|
|
62
|
-
*/
|
|
63
|
-
export function todoRailRows(viewportBudget, teamActive) {
|
|
64
|
-
if (!Number.isFinite(viewportBudget))
|
|
12
|
+
const columns = Math.floor(cols);
|
|
13
|
+
if (columns < TODO_RAIL_MIN_TOTAL_COLS)
|
|
65
14
|
return 0;
|
|
66
|
-
|
|
67
|
-
return Math.max(0, viewportBudget - reserved);
|
|
15
|
+
return Math.min(TODO_RAIL_MAX_COLS, Math.max(TODO_RAIL_MIN_COLS, Math.round(columns * RAIL_FRACTION)));
|
|
68
16
|
}
|
|
@@ -24,4 +24,4 @@ export interface ThumbRange {
|
|
|
24
24
|
* gets a one-row thumb, because an indicator that vanishes at exactly the
|
|
25
25
|
* moment the transcript is longest is worse than no indicator at all.
|
|
26
26
|
*/
|
|
27
|
-
export declare function thumbRange(viewportRows: number, contentRows: number, offsetFromBottom: number): ThumbRange | null;
|
|
27
|
+
export declare function thumbRange(viewportRows: number, contentRows: number, offsetFromBottom: number, reserveTravel?: boolean): ThumbRange | null;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* gets a one-row thumb, because an indicator that vanishes at exactly the
|
|
19
19
|
* moment the transcript is longest is worse than no indicator at all.
|
|
20
20
|
*/
|
|
21
|
-
export function thumbRange(viewportRows, contentRows, offsetFromBottom) {
|
|
21
|
+
export function thumbRange(viewportRows, contentRows, offsetFromBottom, reserveTravel = false) {
|
|
22
22
|
const viewport = Math.floor(viewportRows);
|
|
23
23
|
const content = Math.floor(contentRows);
|
|
24
24
|
if (!Number.isFinite(viewport) || !Number.isFinite(content))
|
|
@@ -27,7 +27,8 @@ export function thumbRange(viewportRows, contentRows, offsetFromBottom) {
|
|
|
27
27
|
return null;
|
|
28
28
|
const overflow = content - viewport;
|
|
29
29
|
const offset = Math.min(Math.max(Math.floor(offsetFromBottom) || 0, 0), overflow);
|
|
30
|
-
const
|
|
30
|
+
const maximum = reserveTravel && viewport >= 2 ? viewport - 1 : viewport;
|
|
31
|
+
const size = Math.min(maximum, Math.max(1, Math.round((viewport * viewport) / content)));
|
|
31
32
|
const travel = viewport - size;
|
|
32
33
|
const hiddenAbove = overflow - offset;
|
|
33
34
|
const start = travel <= 0 ? 0 : Math.round((hiddenAbove / overflow) * travel);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ThumbRange } from './scroll-indicator.js';
|
|
2
|
+
export interface ScrollbarGeometry {
|
|
3
|
+
trackTop: number;
|
|
4
|
+
trackCol: number;
|
|
5
|
+
trackRows: number;
|
|
6
|
+
contentRows: number;
|
|
7
|
+
offset: number;
|
|
8
|
+
thumb: ThumbRange | null;
|
|
9
|
+
revision: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ThumbOffsetInput {
|
|
12
|
+
geometry: ScrollbarGeometry;
|
|
13
|
+
pointerY: number;
|
|
14
|
+
grabRow: number;
|
|
15
|
+
}
|
|
16
|
+
/** Convert a captured pointer to a bottom-relative offset, with exact endpoints. */
|
|
17
|
+
export declare function offsetForThumb({ geometry: g, pointerY, grabRow }: ThumbOffsetInput): number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Convert a captured pointer to a bottom-relative offset, with exact endpoints. */
|
|
2
|
+
export function offsetForThumb({ geometry: g, pointerY, grabRow }) {
|
|
3
|
+
const values = [g.trackTop, g.trackRows, g.contentRows, pointerY, grabRow];
|
|
4
|
+
if (!values.every(Number.isFinite) || !g.thumb)
|
|
5
|
+
return g.offset;
|
|
6
|
+
const overflow = Math.max(0, g.contentRows - g.trackRows);
|
|
7
|
+
const travel = g.trackRows - g.thumb.size;
|
|
8
|
+
if (travel <= 0 || overflow === 0)
|
|
9
|
+
return g.offset;
|
|
10
|
+
const grab = Math.min(Math.max(0, Math.floor(grabRow)), g.thumb.size - 1);
|
|
11
|
+
const top = Math.min(travel, Math.max(0, Math.floor(pointerY) - g.trackTop - grab));
|
|
12
|
+
return overflow - Math.round(top / travel * overflow);
|
|
13
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SubagentRun, TeamSnapshot } from '../../team/types.js';
|
|
2
|
+
export interface TeamPanelLayout {
|
|
3
|
+
collapsed: boolean;
|
|
4
|
+
rowCount: number;
|
|
5
|
+
visible: SubagentRun[];
|
|
6
|
+
hiddenTotal: number;
|
|
7
|
+
hiddenRunning: number;
|
|
8
|
+
}
|
|
9
|
+
export interface TeamPanelLayoutInput {
|
|
10
|
+
snapshot: TeamSnapshot | null;
|
|
11
|
+
terminalRows: number;
|
|
12
|
+
availableRows: number;
|
|
13
|
+
}
|
|
14
|
+
/** Project actual roster rows, preserving three middle rows when expanded.
|
|
15
|
+
* Invalid geometry becomes zero; legacy callers may pass positive Infinity
|
|
16
|
+
* for an unlimited available-row budget. Does not throw or mutate the snapshot.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildTeamPanelLayout(input: TeamPanelLayoutInput): TeamPanelLayout;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { TEAM_LIMITS } from '../../team/limits.js';
|
|
2
|
+
import { selectPanelRows } from '../../team/panel-rows.js';
|
|
3
|
+
import { TODO_LIMITS } from '../../todo/limits.js';
|
|
4
|
+
/** Project actual roster rows, preserving three middle rows when expanded.
|
|
5
|
+
* Invalid geometry becomes zero; legacy callers may pass positive Infinity
|
|
6
|
+
* for an unlimited available-row budget. Does not throw or mutate the snapshot.
|
|
7
|
+
*/
|
|
8
|
+
export function buildTeamPanelLayout(input) {
|
|
9
|
+
const available = input.availableRows === Infinity ? Infinity : nonNegativeInt(input.availableRows);
|
|
10
|
+
if (!input.snapshot || available < 1) {
|
|
11
|
+
return { collapsed: true, rowCount: 0, visible: [], hiddenTotal: 0, hiddenRunning: 0 };
|
|
12
|
+
}
|
|
13
|
+
const selection = selectPanelRows(input.snapshot.runs, TEAM_LIMITS.panelMaxRows);
|
|
14
|
+
const natural = 1 + selection.visible.length + Number(selection.hiddenTotal > 0)
|
|
15
|
+
+ Number(Boolean(input.snapshot.lastMessage));
|
|
16
|
+
const collapsed = nonNegativeInt(input.terminalRows) < TEAM_LIMITS.panelCollapseRows
|
|
17
|
+
|| available - natural < TODO_LIMITS.panelMinRows;
|
|
18
|
+
return { ...selection, collapsed, rowCount: collapsed ? 1 : natural };
|
|
19
|
+
}
|
|
20
|
+
function nonNegativeInt(value) {
|
|
21
|
+
return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { RenderMode } from './frame.js';
|
|
2
|
+
export interface TodoRailLayoutInput {
|
|
3
|
+
mode: RenderMode;
|
|
4
|
+
cols: number;
|
|
5
|
+
panelEnabled: boolean;
|
|
6
|
+
overlayOpen: boolean;
|
|
7
|
+
itemCount: number;
|
|
8
|
+
viewportBudget: number;
|
|
9
|
+
teamRows: number;
|
|
10
|
+
popupRows: number;
|
|
11
|
+
composerBaseRows?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface TodoRailLayout {
|
|
14
|
+
visible: boolean;
|
|
15
|
+
reason: 'visible' | 'inline' | 'disabled' | 'overlay' | 'empty' | 'narrow' | 'short';
|
|
16
|
+
width: number;
|
|
17
|
+
rows: number;
|
|
18
|
+
contentCols: number;
|
|
19
|
+
popupMaxHeight: number;
|
|
20
|
+
}
|
|
21
|
+
/** Allocate columns and rows from the same team/menu projections as the renderers.
|
|
22
|
+
* Invalid geometry becomes zero, excess team/menu rows are clamped. Menu capacity
|
|
23
|
+
* never depends on menu occupancy or rail visibility, preventing feedback loops.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildTodoRailLayout(input: TodoRailLayoutInput): TodoRailLayout;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { TODO_LIMITS } from '../../todo/limits.js';
|
|
2
|
+
import { todoRailWidth } from './rail.js';
|
|
3
|
+
/** Allocate columns and rows from the same team/menu projections as the renderers.
|
|
4
|
+
* Invalid geometry becomes zero, excess team/menu rows are clamped. Menu capacity
|
|
5
|
+
* never depends on menu occupancy or rail visibility, preventing feedback loops.
|
|
6
|
+
*/
|
|
7
|
+
export function buildTodoRailLayout(input) {
|
|
8
|
+
const cols = nonNegativeInt(input.cols);
|
|
9
|
+
const budget = nonNegativeInt(input.viewportBudget);
|
|
10
|
+
const teamRows = Math.min(budget, nonNegativeInt(input.teamRows));
|
|
11
|
+
const popupMaxHeight = Math.max(0, budget - teamRows - TODO_LIMITS.panelMinRows
|
|
12
|
+
- nonNegativeInt(input.composerBaseRows ?? 0));
|
|
13
|
+
const popupRows = popupMaxHeight < 3 ? 0 : Math.min(nonNegativeInt(input.popupRows), popupMaxHeight);
|
|
14
|
+
const rows = input.mode === 'fullscreen' ? budget : budget - teamRows - popupRows;
|
|
15
|
+
const width = todoRailWidth(cols);
|
|
16
|
+
const reason = input.mode !== 'fullscreen' ? 'inline'
|
|
17
|
+
: !input.panelEnabled ? 'disabled'
|
|
18
|
+
: input.overlayOpen ? 'overlay'
|
|
19
|
+
: nonNegativeInt(input.itemCount) === 0 ? 'empty'
|
|
20
|
+
: width === 0 ? 'narrow'
|
|
21
|
+
: rows < TODO_LIMITS.panelMinRows ? 'short'
|
|
22
|
+
: 'visible';
|
|
23
|
+
const visible = reason === 'visible';
|
|
24
|
+
return { visible, reason, width, rows, contentCols: cols - (visible ? width : 0), popupMaxHeight };
|
|
25
|
+
}
|
|
26
|
+
function nonNegativeInt(value) {
|
|
27
|
+
return Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
28
|
+
}
|
|
@@ -17,6 +17,7 @@ export interface ViewportGeometry {
|
|
|
17
17
|
offset: number;
|
|
18
18
|
/** Natural (unclipped) height of the content, in rows. */
|
|
19
19
|
contentRows: number;
|
|
20
|
+
trailingContentRows?: number;
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
23
|
* The default is the UNMEASURED geometry, and consumers must treat it as such:
|
|
@@ -98,6 +98,7 @@ export interface WindowSelection {
|
|
|
98
98
|
totalRows: number;
|
|
99
99
|
}
|
|
100
100
|
export interface SelectWindowInput {
|
|
101
|
+
trailingContentRows?: number;
|
|
101
102
|
entries: readonly Entry[];
|
|
102
103
|
/** Measured, else estimated; never 0 for a real entry. */
|
|
103
104
|
heightOf: (index: number) => number;
|
|
@@ -352,7 +352,7 @@ export function selectWindow(input) {
|
|
|
352
352
|
const viewport = Math.max(1, Math.floor(input.viewportRows) || 0);
|
|
353
353
|
const offset = Math.max(0, Math.floor(input.offset) || 0);
|
|
354
354
|
const overscan = Math.max(VIRTUAL_LIMITS.overscan, Math.floor(input.overscan) || 0);
|
|
355
|
-
const bottom = totalRows - offset;
|
|
355
|
+
const bottom = totalRows + (input.trailingContentRows ?? 0) - offset;
|
|
356
356
|
const top = bottom - viewport;
|
|
357
357
|
let start = n - 1;
|
|
358
358
|
let acc = 0;
|
|
@@ -6,7 +6,9 @@ function keyRows(times) {
|
|
|
6
6
|
return [
|
|
7
7
|
['Enter', 'Submit (idle) / queue steering (running); re-pins to the newest output'],
|
|
8
8
|
['Alt+Enter / Shift+Enter', 'Insert newline'],
|
|
9
|
-
['Esc', '
|
|
9
|
+
['Esc', 'Close menu / overlay'],
|
|
10
|
+
['Esc twice', 'Interrupt run (within 1.5s)'],
|
|
11
|
+
['Esc again', 'Force-stop after requesting interruption'],
|
|
10
12
|
// BOTH KEYS ARE LISTED HERE, and this row is why (shift-tab-mode-toggle-
|
|
11
13
|
// still-dead-on-windows, review R-7). The hint row above the composer is the
|
|
12
14
|
// other place the fallback is named, and it has two off-switches - a short
|
|
@@ -24,7 +26,8 @@ function keyRows(times) {
|
|
|
24
26
|
// Drag-select is the one affordance here a user cannot discover by pressing
|
|
25
27
|
// a key, so it has to be named somewhere they can look it up. The one-shot
|
|
26
28
|
// startup notice is the other place, and it scrolls away.
|
|
27
|
-
['Drag
|
|
29
|
+
['Drag right edge', 'Scroll messages and input together; type to return'],
|
|
30
|
+
['Drag text (left button)', 'Select text on screen; releasing copies it'],
|
|
28
31
|
['Up / Down', 'Prompt history (empty input)'],
|
|
29
32
|
// Pasting is the one affordance here with no key of its own, so the only
|
|
30
33
|
// place a user can learn what happened to their 218 lines is this row.
|
package/dist/ui/screen.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export interface AltScreenOptions {
|
|
|
43
43
|
*
|
|
44
44
|
* SAME "ALREADY DECIDED ELSEWHERE" DISCIPLINE AS `mouse`: this module must
|
|
45
45
|
* never derive it from config. `cli.tsx` passes
|
|
46
|
-
* `
|
|
46
|
+
* `mouseOn`, after the filter and VT capability have been resolved.
|
|
47
47
|
*
|
|
48
48
|
* Optional, defaulting to `false`, so every pre-existing caller and every
|
|
49
49
|
* pre-existing test emits exactly the bytes it emitted before (AC-8 / T-34).
|
package/dist/ui/screen.js
CHANGED
|
@@ -23,18 +23,16 @@ const SHOW_CURSOR = '\x1b[?25h';
|
|
|
23
23
|
* text rather than raw bytes above 0x7F that `setEncoding('utf8')` would mangle.
|
|
24
24
|
*
|
|
25
25
|
* `?1002` is BUTTON-EVENT TRACKING: motion is reported ONLY WHILE A BUTTON IS
|
|
26
|
-
* HELD, which
|
|
26
|
+
* HELD, which both scrollbar dragging and text selection need and costs exactly zero reports while the
|
|
27
27
|
* pointer is idle. This comment used to lump it together with `?1003` and
|
|
28
28
|
* dismiss both as "floods stdin on every pointer move" — true of `?1003`
|
|
29
29
|
* (any-event), false of `?1002`, and the sentence had to go in the same commit
|
|
30
30
|
* that started asking for it or it would stand as a documented reason not to
|
|
31
31
|
* (AC-11 / D-10).
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* they replaced (AC-8 / D-15). `disableMouse` is the exact reverse of
|
|
37
|
-
* `enableMouse` for both values (I-1 / AC-7).
|
|
33
|
+
* Motion is enabled with mouse reporting, independently of text selection.
|
|
34
|
+
* Disabling selection must still allow scrollbar gestures. The enable/disable
|
|
35
|
+
* sequences remain paired for both values of this resolved capability.
|
|
38
36
|
*/
|
|
39
37
|
const enableMouse = (motion) => `\x1b[?1000h${motion ? '\x1b[?1002h' : ''}\x1b[?1006h`;
|
|
40
38
|
const disableMouse = (motion) => `\x1b[?1006l${motion ? '\x1b[?1002l' : ''}\x1b[?1000l`;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MouseEvent } from '../input/mouse-events.js';
|
|
2
|
+
import { type ScrollbarGeometry } from './layout/scrollbar-geometry.js';
|
|
3
|
+
export interface ScrollbarController {
|
|
4
|
+
handle(event: MouseEvent): boolean;
|
|
5
|
+
cancel(): void;
|
|
6
|
+
dispose(): void;
|
|
7
|
+
isCaptured(): boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ScrollbarOptions {
|
|
10
|
+
getGeometry(): ScrollbarGeometry | null;
|
|
11
|
+
setOffset(offset: number): void;
|
|
12
|
+
page(direction: 'pageUp' | 'pageDown'): void;
|
|
13
|
+
onHold(held: boolean): void;
|
|
14
|
+
onStart(): void;
|
|
15
|
+
isEnabled(): boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface ScrollbarBridge {
|
|
18
|
+
controller: ScrollbarController | null;
|
|
19
|
+
geometry: ScrollbarGeometry | null;
|
|
20
|
+
frameReady: boolean;
|
|
21
|
+
isEnabled(): boolean;
|
|
22
|
+
onStart?: () => void;
|
|
23
|
+
onGeometry?: () => void;
|
|
24
|
+
requestRedraw?: () => void;
|
|
25
|
+
invalidate(): void;
|
|
26
|
+
}
|
|
27
|
+
/** Build the late-bound terminal bridge; no scroll position is owned here. */
|
|
28
|
+
export declare function createScrollbarBridge(isEnabled: () => boolean): ScrollbarBridge;
|
|
29
|
+
/** Own one pointer gesture and bounded timers; positioning remains in the viewport. */
|
|
30
|
+
export declare function createScrollbarController(options: ScrollbarOptions): ScrollbarController;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { offsetForThumb } from './layout/scrollbar-geometry.js';
|
|
2
|
+
/** Build the late-bound terminal bridge; no scroll position is owned here. */
|
|
3
|
+
export function createScrollbarBridge(isEnabled) {
|
|
4
|
+
const bridge = {
|
|
5
|
+
controller: null, geometry: null, frameReady: false, isEnabled,
|
|
6
|
+
invalidate: () => { bridge.frameReady = false; bridge.controller?.cancel(); },
|
|
7
|
+
};
|
|
8
|
+
return bridge;
|
|
9
|
+
}
|
|
10
|
+
/** Own one pointer gesture and bounded timers; positioning remains in the viewport. */
|
|
11
|
+
export function createScrollbarController(options) {
|
|
12
|
+
return new ScrollbarGesture(options);
|
|
13
|
+
}
|
|
14
|
+
class ScrollbarGesture {
|
|
15
|
+
options;
|
|
16
|
+
capture = null;
|
|
17
|
+
timer = null;
|
|
18
|
+
watchdog = null;
|
|
19
|
+
swallowLate = false;
|
|
20
|
+
disposed = false;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
isCaptured() { return this.capture !== null; }
|
|
25
|
+
cancel() {
|
|
26
|
+
if (this.timer)
|
|
27
|
+
clearTimeout(this.timer);
|
|
28
|
+
if (this.watchdog)
|
|
29
|
+
clearTimeout(this.watchdog);
|
|
30
|
+
this.timer = this.watchdog = null;
|
|
31
|
+
const held = this.capture !== null;
|
|
32
|
+
this.capture = null;
|
|
33
|
+
if (held) {
|
|
34
|
+
this.swallowLate = true;
|
|
35
|
+
this.options.onHold(false);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
dispose() { this.cancel(); this.disposed = true; }
|
|
39
|
+
armWatchdog() {
|
|
40
|
+
if (this.watchdog)
|
|
41
|
+
clearTimeout(this.watchdog);
|
|
42
|
+
this.watchdog = setTimeout(() => this.cancel(), 30000);
|
|
43
|
+
this.watchdog.unref?.();
|
|
44
|
+
}
|
|
45
|
+
flush() {
|
|
46
|
+
if (this.timer)
|
|
47
|
+
clearTimeout(this.timer);
|
|
48
|
+
this.timer = null;
|
|
49
|
+
const g = this.options.getGeometry();
|
|
50
|
+
if (!this.capture || !g || !this.options.isEnabled() ||
|
|
51
|
+
g.revision !== this.capture.revision) {
|
|
52
|
+
this.cancel();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const y = this.capture.pendingY;
|
|
56
|
+
this.capture.pendingY = null;
|
|
57
|
+
if (y !== null)
|
|
58
|
+
this.options.setOffset(offsetForThumb({ geometry: g,
|
|
59
|
+
pointerY: y, grabRow: this.capture.grabRow }));
|
|
60
|
+
}
|
|
61
|
+
move(y) {
|
|
62
|
+
if (!this.capture || this.capture.kind !== 'thumb' || y === this.capture.lastPointerY)
|
|
63
|
+
return;
|
|
64
|
+
this.capture.lastPointerY = y;
|
|
65
|
+
this.capture.pendingY = y;
|
|
66
|
+
if (!this.timer) {
|
|
67
|
+
this.timer = setTimeout(() => this.flush(), 16);
|
|
68
|
+
this.timer.unref?.();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
press(event) {
|
|
72
|
+
this.cancel();
|
|
73
|
+
this.swallowLate = false;
|
|
74
|
+
const g = this.options.getGeometry();
|
|
75
|
+
if (!g || !this.canPress(event, g))
|
|
76
|
+
return false;
|
|
77
|
+
const row = event.y - g.trackTop;
|
|
78
|
+
const thumb = g.thumb;
|
|
79
|
+
const inside = thumb && row >= thumb.start && row < thumb.start + thumb.size;
|
|
80
|
+
this.capture = { kind: inside && g.trackRows > thumb.size ? 'thumb' : 'track',
|
|
81
|
+
grabRow: inside ? row - thumb.start : 0, revision: g.revision,
|
|
82
|
+
lastPointerY: event.y, pendingY: null };
|
|
83
|
+
this.options.onStart();
|
|
84
|
+
this.options.onHold(true);
|
|
85
|
+
this.armWatchdog();
|
|
86
|
+
if (thumb && !inside)
|
|
87
|
+
this.options.page(row < thumb.start ? 'pageUp' : 'pageDown');
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
canPress(event, g) {
|
|
91
|
+
const plainLeft = event.button === 0 && !event.shift && !event.alt && !event.ctrl;
|
|
92
|
+
const inTrack = event.x === g.trackCol && event.y >= g.trackTop &&
|
|
93
|
+
event.y < g.trackTop + g.trackRows;
|
|
94
|
+
return this.options.isEnabled() && plainLeft && inTrack;
|
|
95
|
+
}
|
|
96
|
+
handle(event) {
|
|
97
|
+
if (this.disposed)
|
|
98
|
+
return false;
|
|
99
|
+
if (event.kind === 'wheel')
|
|
100
|
+
return this.capture !== null;
|
|
101
|
+
if (![event.x, event.y].every(Number.isFinite))
|
|
102
|
+
return this.capture !== null;
|
|
103
|
+
if (event.kind === 'press')
|
|
104
|
+
return this.press(event);
|
|
105
|
+
if (!this.capture)
|
|
106
|
+
return this.swallowLate;
|
|
107
|
+
if (event.button !== 0) {
|
|
108
|
+
this.cancel();
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (!this.options.isEnabled()) {
|
|
112
|
+
this.cancel();
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
this.move(event.y);
|
|
116
|
+
if (event.kind === 'release') {
|
|
117
|
+
this.flush();
|
|
118
|
+
this.cancel();
|
|
119
|
+
}
|
|
120
|
+
else
|
|
121
|
+
this.armWatchdog();
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* transcript in the terminal's native scrollback, and replaying there would
|
|
7
7
|
* simply print everything twice.
|
|
8
8
|
*/
|
|
9
|
-
import { formatCost, formatDuration, formatTokens } from '../agent/usage.js';
|
|
9
|
+
import { formatCost, formatDuration, formatTokens, promptTokensOf } from '../agent/usage.js';
|
|
10
10
|
import { formatRetryLine } from '../agent/retry-view.js';
|
|
11
11
|
const DEFAULT_MAX_ENTRIES = 200;
|
|
12
12
|
const CONTINUATION_INDENT = ' ';
|
|
@@ -199,7 +199,11 @@ function renderEntry(entry, glyphs) {
|
|
|
199
199
|
export function renderTranscriptText(entries, opts) {
|
|
200
200
|
const turns = entries.filter((e) => e.kind === 'user').length;
|
|
201
201
|
const g = opts.glyphs;
|
|
202
|
-
|
|
202
|
+
// THE SAME PROMPT-SIDE TOTAL THE STATUS BAR SHOWS (context-usage-gauge-accuracy
|
|
203
|
+
// §3.6). This line is what a user compares against the bar they were watching a
|
|
204
|
+
// second earlier, so a different unit here would look like the exit summary
|
|
205
|
+
// losing tokens.
|
|
206
|
+
const tokens = `${formatTokens(promptTokensOf(opts.usageTotal))}${g.arrowUp} ${formatTokens(opts.usageTotal.outputTokens)}${g.arrowDown}`;
|
|
203
207
|
const summary = [
|
|
204
208
|
'aragon',
|
|
205
209
|
`${opts.provider}:${opts.model}`,
|
|
@@ -30,6 +30,8 @@ import type { ScrollIntent } from './layout/scroll.js';
|
|
|
30
30
|
*/
|
|
31
31
|
export declare const OVERLAY_PAGE = 8;
|
|
32
32
|
export interface WheelRoutingOptions {
|
|
33
|
+
isPointerCaptured?: () => boolean;
|
|
34
|
+
isActive?: () => boolean;
|
|
33
35
|
/** Absent when mouse support is off, in inline mode, or on a non-TTY. */
|
|
34
36
|
mouseSource?: MouseSource;
|
|
35
37
|
/** Read at EVENT time for the routing branch, and again at FLUSH time (P1-3). */
|
|
@@ -61,6 +61,8 @@ export function useWheelRouting(options) {
|
|
|
61
61
|
accumulator.current = null;
|
|
62
62
|
if (!pending)
|
|
63
63
|
return;
|
|
64
|
+
if (optionsRef.current.isPointerCaptured?.() || optionsRef.current.isActive?.() === false)
|
|
65
|
+
return;
|
|
64
66
|
// P1-3: THE DECISION BELONGS AT FLUSH TIME, not at event time, because the
|
|
65
67
|
// event-time decision is the one that goes stale. `App` swaps
|
|
66
68
|
// `ScrollViewport` out for an overlay, and 16 ms is more than wide enough
|
|
@@ -93,6 +95,10 @@ export function useWheelRouting(options) {
|
|
|
93
95
|
if (event.kind !== 'wheel')
|
|
94
96
|
return;
|
|
95
97
|
const current = optionsRef.current;
|
|
98
|
+
if (current.isPointerCaptured?.() || current.isActive?.() === false) {
|
|
99
|
+
clearCoalescer();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
96
102
|
const overlay = current.getOverlay();
|
|
97
103
|
// THE OVERLAY BRANCH IS EVALUATED FIRST AND RETURNS FOR EVERY OVERLAY IT
|
|
98
104
|
// RECOGNIZES — the same two ordering rules the keyboard handler follows,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aragon-agent/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "AragonAgent CLI — a Claude-Code / Codex-style interactive terminal UI (TUI) on top of the @aragon-agent/core engine. Installable and runnable via npm / npx.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AragonAgent contributors",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"prepublishOnly": "npm run build"
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@aragon-agent/core": "^0.2.
|
|
59
|
+
"@aragon-agent/core": "^0.2.20",
|
|
60
60
|
"chalk": "^5.6.2",
|
|
61
61
|
"cli-highlight": "^2.0.0",
|
|
62
62
|
"commander": "^12.1.0",
|