@aragon-agent/cli 0.6.7 → 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/README.md +72 -43
- package/dist/agent/controller.d.ts +10 -6
- package/dist/agent/controller.js +35 -48
- package/dist/agent/headless.d.ts +2 -1
- package/dist/agent/prompt-options.d.ts +15 -0
- package/dist/agent/prompt-options.js +1 -0
- package/dist/cli.js +83 -53
- package/dist/commands/builtins.js +3 -1
- package/dist/commands/registry.d.ts +2 -3
- 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/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 +166 -128
- package/dist/ui/Composer.d.ts +11 -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 +9 -6
- package/dist/ui/PromptInput.js +34 -14
- package/dist/ui/StatusBar.d.ts +1 -0
- package/dist/ui/StatusBar.js +14 -8
- package/dist/ui/Transcript.js +1 -0
- package/dist/ui/composer-rows.js +50 -9
- 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/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/scroll-indicator.d.ts +1 -1
- package/dist/ui/layout/scroll-indicator.js +3 -2
- package/dist/ui/layout/scrollbar-geometry.d.ts +17 -0
- package/dist/ui/layout/scrollbar-geometry.js +13 -0
- package/dist/ui/layout/todo-layout.d.ts +1 -0
- package/dist/ui/layout/todo-layout.js +3 -2
- 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/use-wheel-routing.d.ts +2 -0
- package/dist/ui/use-wheel-routing.js +6 -0
- package/package.json +2 -2
|
@@ -1,29 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layout budget (spec §4.2a) — the ONE function that says how many rows the
|
|
3
|
-
* viewport gets.
|
|
4
|
-
*
|
|
5
|
-
* Before v0.4.0 this number was computed nowhere and assumed in three places,
|
|
6
|
-
* which is how the viewport ended up NON-MONOTONIC in `rows`: growing the
|
|
7
|
-
* terminal from 27 to 28 rows promoted the header to the 8-row wordmark tier and
|
|
8
|
-
* the visible content SHRANK from 18 rows to 13. Making the brand region a
|
|
9
|
-
* constant 1 row (§4.2) is what fixes that, and this module is where the fix is
|
|
10
|
-
* expressed as arithmetic so `budget.test.ts` can pin it.
|
|
11
|
-
*
|
|
12
|
-
* Deliberately NOT part of `frame.ts`: that file is the read-only foundation
|
|
13
|
-
* (invariant I-1) and this is content-layer policy.
|
|
14
|
-
*
|
|
15
|
-
* Scope: the STATIC chrome only. One consumer is dynamic and is handled by the
|
|
16
|
-
* degradation ladder in §5.2 instead of being averaged in here:
|
|
17
|
-
* - `AutocompletePopup` expands the bottom chrome by up to 9 rows.
|
|
18
|
-
*
|
|
19
|
-
* `ScrollViewport`'s "N new lines" hint used to be listed here as a second
|
|
20
|
-
* dynamic consumer. It no longer exists: the number renders as a chip on the
|
|
21
|
-
* composer's input row (tui-selection-and-scroll-follow §4.2), which costs
|
|
22
|
-
* COLUMNS rather than rows. The arithmetic below is unchanged — the hint was
|
|
23
|
-
* never in the budget in the first place — but leaving the bullet in place would
|
|
24
|
-
* make this comment a true-sounding statement about a row that is gone (AC-12,
|
|
25
|
-
* and the `wheel-scrolls-transcript-only` RV-3 lesson).
|
|
26
|
-
*/
|
|
1
|
+
/** Fullscreen reserves three fixed rows; composer height is a footer layout input. */
|
|
27
2
|
/** Below this many rows the composer's hint line is dropped to buy back a row. */
|
|
28
3
|
export declare const HINT_MIN_ROWS = 20;
|
|
29
4
|
export interface ChromeBudget {
|
|
@@ -50,28 +25,5 @@ export interface ChromeBudget {
|
|
|
50
25
|
*/
|
|
51
26
|
export declare function clampDraftRows(rows: number, draftRows: number): number;
|
|
52
27
|
export declare function chromeBudget(rows: number, draftRows?: number): ChromeBudget;
|
|
53
|
-
/**
|
|
54
|
-
|
|
55
|
-
*
|
|
56
|
-
* Note what is NOT a parameter: whether the transcript is empty. The absence of
|
|
57
|
-
* that argument is the guarantee that the first submitted message cannot make
|
|
58
|
-
* the layout jump (A-4). Do not add it back.
|
|
59
|
-
*
|
|
60
|
-
* `draftRows` IS NOT THAT ARGUMENT, and the distinction is the whole of D-12.
|
|
61
|
-
* A-4 forbids making the budget depend on TRANSCRIPT content, so that the first
|
|
62
|
-
* submitted message cannot move the layout. `draftRows` depends only on what the
|
|
63
|
-
* user is typing right now, and its whole purpose is that the jump is ALREADY
|
|
64
|
-
* HAPPENING -- Yoga takes the rows out of the transcript whether or not this
|
|
65
|
-
* function was told. `App` adds the hysteresis (grow now, shrink one tick later)
|
|
66
|
-
* that keeps it from becoming per-keystroke jitter.
|
|
67
|
-
*
|
|
68
|
-
* It DEFAULTS TO 1, i.e. to the constant this function used to assume, so every
|
|
69
|
-
* pre-existing caller and every pre-existing assertion is byte-identical -- and
|
|
70
|
-
* `viewportRows.length` stays 1, because a parameter with a default does not
|
|
71
|
-
* count toward it.
|
|
72
|
-
*
|
|
73
|
-
* Non-decreasing in `rows` at a fixed `draftRows = 1`: the one step where chrome
|
|
74
|
-
* grows (the hint row at `HINT_MIN_ROWS`) coincides with `rows` growing by one,
|
|
75
|
-
* so the net change is 0 rather than -1.
|
|
76
|
-
*/
|
|
77
|
-
export declare function viewportRows(rows: number, draftRows?: number): number;
|
|
28
|
+
/** Shared document height; the legacy draft argument remains source-compatible. */
|
|
29
|
+
export declare function viewportRows(rows: number, _draftRows?: number): number;
|
package/dist/ui/layout/budget.js
CHANGED
|
@@ -1,29 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layout budget (spec §4.2a) — the ONE function that says how many rows the
|
|
3
|
-
* viewport gets.
|
|
4
|
-
*
|
|
5
|
-
* Before v0.4.0 this number was computed nowhere and assumed in three places,
|
|
6
|
-
* which is how the viewport ended up NON-MONOTONIC in `rows`: growing the
|
|
7
|
-
* terminal from 27 to 28 rows promoted the header to the 8-row wordmark tier and
|
|
8
|
-
* the visible content SHRANK from 18 rows to 13. Making the brand region a
|
|
9
|
-
* constant 1 row (§4.2) is what fixes that, and this module is where the fix is
|
|
10
|
-
* expressed as arithmetic so `budget.test.ts` can pin it.
|
|
11
|
-
*
|
|
12
|
-
* Deliberately NOT part of `frame.ts`: that file is the read-only foundation
|
|
13
|
-
* (invariant I-1) and this is content-layer policy.
|
|
14
|
-
*
|
|
15
|
-
* Scope: the STATIC chrome only. One consumer is dynamic and is handled by the
|
|
16
|
-
* degradation ladder in §5.2 instead of being averaged in here:
|
|
17
|
-
* - `AutocompletePopup` expands the bottom chrome by up to 9 rows.
|
|
18
|
-
*
|
|
19
|
-
* `ScrollViewport`'s "N new lines" hint used to be listed here as a second
|
|
20
|
-
* dynamic consumer. It no longer exists: the number renders as a chip on the
|
|
21
|
-
* composer's input row (tui-selection-and-scroll-follow §4.2), which costs
|
|
22
|
-
* COLUMNS rather than rows. The arithmetic below is unchanged — the hint was
|
|
23
|
-
* never in the budget in the first place — but leaving the bullet in place would
|
|
24
|
-
* make this comment a true-sounding statement about a row that is gone (AC-12,
|
|
25
|
-
* and the `wheel-scrolls-transcript-only` RV-3 lesson).
|
|
26
|
-
*/
|
|
1
|
+
/** Fullscreen reserves three fixed rows; composer height is a footer layout input. */
|
|
27
2
|
import { draftMaxRows } from '../composer-limits.js';
|
|
28
3
|
import { frameHeight, MIN_FULLSCREEN_ROWS } from './frame.js';
|
|
29
4
|
/** Below this many rows the composer's hint line is dropped to buy back a row. */
|
|
@@ -47,33 +22,9 @@ export function chromeBudget(rows, draftRows = 1) {
|
|
|
47
22
|
status: 1,
|
|
48
23
|
};
|
|
49
24
|
}
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
*
|
|
53
|
-
* Note what is NOT a parameter: whether the transcript is empty. The absence of
|
|
54
|
-
* that argument is the guarantee that the first submitted message cannot make
|
|
55
|
-
* the layout jump (A-4). Do not add it back.
|
|
56
|
-
*
|
|
57
|
-
* `draftRows` IS NOT THAT ARGUMENT, and the distinction is the whole of D-12.
|
|
58
|
-
* A-4 forbids making the budget depend on TRANSCRIPT content, so that the first
|
|
59
|
-
* submitted message cannot move the layout. `draftRows` depends only on what the
|
|
60
|
-
* user is typing right now, and its whole purpose is that the jump is ALREADY
|
|
61
|
-
* HAPPENING -- Yoga takes the rows out of the transcript whether or not this
|
|
62
|
-
* function was told. `App` adds the hysteresis (grow now, shrink one tick later)
|
|
63
|
-
* that keeps it from becoming per-keystroke jitter.
|
|
64
|
-
*
|
|
65
|
-
* It DEFAULTS TO 1, i.e. to the constant this function used to assume, so every
|
|
66
|
-
* pre-existing caller and every pre-existing assertion is byte-identical -- and
|
|
67
|
-
* `viewportRows.length` stays 1, because a parameter with a default does not
|
|
68
|
-
* count toward it.
|
|
69
|
-
*
|
|
70
|
-
* Non-decreasing in `rows` at a fixed `draftRows = 1`: the one step where chrome
|
|
71
|
-
* grows (the hint row at `HINT_MIN_ROWS`) coincides with `rows` growing by one,
|
|
72
|
-
* so the net change is 0 rather than -1.
|
|
73
|
-
*/
|
|
74
|
-
export function viewportRows(rows, draftRows = 1) {
|
|
25
|
+
/** Shared document height; the legacy draft argument remains source-compatible. */
|
|
26
|
+
export function viewportRows(rows, _draftRows = 1) {
|
|
75
27
|
if (!Number.isFinite(rows) || rows < MIN_FULLSCREEN_ROWS)
|
|
76
28
|
return 0;
|
|
77
|
-
|
|
78
|
-
return Math.max(0, frameHeight(rows) - c.header - c.toast - c.composer - c.status);
|
|
29
|
+
return Math.max(0, frameHeight(rows) - 3);
|
|
79
30
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface DocumentLayoutInput {
|
|
2
|
+
rows: number;
|
|
3
|
+
bodyRows: number;
|
|
4
|
+
footerRows: number;
|
|
5
|
+
}
|
|
6
|
+
export interface DocumentLayout {
|
|
7
|
+
viewportRows: number;
|
|
8
|
+
paddingRows: number;
|
|
9
|
+
contentRows: number;
|
|
10
|
+
trailingContentRows: number;
|
|
11
|
+
}
|
|
12
|
+
/** Allocate the shared document, filling short sessions before the editor. */
|
|
13
|
+
export declare function buildDocumentLayout(input: DocumentLayoutInput): DocumentLayout;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { frameHeight } from './frame.js';
|
|
2
|
+
/** Allocate the shared document, filling short sessions before the editor. */
|
|
3
|
+
export function buildDocumentLayout(input) {
|
|
4
|
+
const count = (n) => Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0;
|
|
5
|
+
const viewportRows = count(input.rows) > 0 ? Math.max(0, frameHeight(count(input.rows)) - 3) : 0;
|
|
6
|
+
const body = count(input.bodyRows);
|
|
7
|
+
const footer = count(input.footerRows);
|
|
8
|
+
const paddingRows = Math.max(0, viewportRows - body - footer);
|
|
9
|
+
return { viewportRows, paddingRows, contentRows: body + paddingRows + footer,
|
|
10
|
+
trailingContentRows: paddingRows + footer };
|
|
11
|
+
}
|
|
@@ -58,6 +58,7 @@ export interface FollowInput {
|
|
|
58
58
|
overflowLines: number;
|
|
59
59
|
/** Signed: rows appended at (or removed from) the tail since the last frame. */
|
|
60
60
|
tailDelta: number;
|
|
61
|
+
layoutTailDelta?: number;
|
|
61
62
|
/** True while a selection drag holds the viewport (§4.4). */
|
|
62
63
|
hold: boolean;
|
|
63
64
|
newLinesWhilePaused: number;
|
|
@@ -74,9 +74,7 @@ export function reduceFollow(input) {
|
|
|
74
74
|
// of the drag (T-20).
|
|
75
75
|
if (offset === 0 && !hold)
|
|
76
76
|
return { offset, newLinesWhilePaused: 0 };
|
|
77
|
-
|
|
78
|
-
return { offset, newLinesWhilePaused: input.newLinesWhilePaused };
|
|
79
|
-
const next = clampScroll(offset + tailDelta, overflowLines);
|
|
77
|
+
const next = clampScroll(offset + tailDelta + (input.layoutTailDelta ?? 0), overflowLines);
|
|
80
78
|
return {
|
|
81
79
|
offset: next,
|
|
82
80
|
// Only real arrivals arm the resume timer, and reaching the bottom by ANY
|
|
@@ -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
|
+
}
|
|
@@ -8,9 +8,10 @@ export function buildTodoRailLayout(input) {
|
|
|
8
8
|
const cols = nonNegativeInt(input.cols);
|
|
9
9
|
const budget = nonNegativeInt(input.viewportBudget);
|
|
10
10
|
const teamRows = Math.min(budget, nonNegativeInt(input.teamRows));
|
|
11
|
-
const popupMaxHeight = Math.max(0, budget - teamRows - TODO_LIMITS.panelMinRows
|
|
11
|
+
const popupMaxHeight = Math.max(0, budget - teamRows - TODO_LIMITS.panelMinRows
|
|
12
|
+
- nonNegativeInt(input.composerBaseRows ?? 0));
|
|
12
13
|
const popupRows = popupMaxHeight < 3 ? 0 : Math.min(nonNegativeInt(input.popupRows), popupMaxHeight);
|
|
13
|
-
const rows = budget - teamRows - popupRows;
|
|
14
|
+
const rows = input.mode === 'fullscreen' ? budget : budget - teamRows - popupRows;
|
|
14
15
|
const width = todoRailWidth(cols);
|
|
15
16
|
const reason = input.mode !== 'fullscreen' ? 'inline'
|
|
16
17
|
: !input.panelEnabled ? 'disabled'
|
|
@@ -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
|
+
}
|
|
@@ -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",
|