@reticlehq/browser 2.13.1 → 3.1.0
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/dist/actions/actions-dom.d.ts +8 -20
- package/dist/actions/actions-dom.js +70 -69
- package/dist/actions/actions-press.d.ts +14 -0
- package/dist/actions/actions-press.js +123 -0
- package/dist/actions/actions.d.ts +1 -89
- package/dist/actions/actions.js +64 -365
- package/dist/actions/appeared-text.d.ts +0 -20
- package/dist/actions/appeared-text.js +6 -36
- package/dist/actions/danger-context.d.ts +3 -0
- package/dist/actions/danger-context.js +39 -0
- package/dist/actions/scroll.d.ts +8 -10
- package/dist/actions/scroll.js +28 -17
- package/dist/actions/synthetic/synthetic-input.d.ts +2 -0
- package/dist/actions/synthetic/synthetic-input.js +13 -0
- package/dist/actions/value-input.d.ts +0 -30
- package/dist/actions/value-input.js +0 -33
- package/dist/commands/commands.d.ts +0 -2
- package/dist/commands/commands.js +10 -70
- package/dist/connect-options.d.ts +2 -96
- package/dist/dom/a11y.d.ts +0 -29
- package/dist/dom/a11y.js +3 -90
- package/dist/dom/addressing/auto-anchor.d.ts +25 -0
- package/dist/dom/addressing/auto-anchor.js +41 -0
- package/dist/dom/addressing/refs.d.ts +14 -0
- package/dist/dom/addressing/refs.js +114 -0
- package/dist/dom/addressing/source.d.ts +9 -0
- package/dist/dom/addressing/source.js +29 -0
- package/dist/dom/chart.d.ts +0 -61
- package/dist/dom/chart.js +0 -60
- package/dist/dom/desktop-capture.d.ts +0 -30
- package/dist/dom/desktop-capture.js +1 -73
- package/dist/dom/dom-ignore.d.ts +0 -30
- package/dist/dom/dom-ignore.js +1 -48
- package/dist/dom/occlusion.d.ts +0 -7
- package/dist/dom/occlusion.js +31 -12
- package/dist/dom/paint-context.d.ts +6 -0
- package/dist/dom/paint-context.js +38 -0
- package/dist/dom/query.d.ts +0 -14
- package/dist/dom/query.js +50 -216
- package/dist/dom/realm.d.ts +0 -20
- package/dist/dom/realm.js +0 -28
- package/dist/dom/shadow-registry.d.ts +0 -33
- package/dist/dom/shadow-registry.js +2 -72
- package/dist/dom/snapshot.d.ts +2 -43
- package/dist/dom/snapshot.js +27 -152
- package/dist/dom/theme.d.ts +0 -37
- package/dist/dom/theme.js +2 -52
- package/dist/dom/virtualized.d.ts +0 -17
- package/dist/dom/virtualized.js +0 -27
- package/dist/edit-epoch.d.ts +1 -23
- package/dist/edit-epoch.js +1 -53
- package/dist/index.d.ts +4 -5
- package/dist/index.js +4 -28
- package/dist/observers/animation.d.ts +0 -1
- package/dist/observers/animation.js +1 -4
- package/dist/observers/blind-spots.d.ts +0 -26
- package/dist/observers/blind-spots.js +3 -62
- package/dist/observers/console.d.ts +1 -2
- package/dist/observers/console.js +41 -30
- package/dist/observers/context-open.d.ts +1 -11
- package/dist/observers/context-open.js +10 -14
- package/dist/observers/departure.d.ts +3 -0
- package/dist/observers/departure.js +52 -0
- package/dist/observers/dialogs.d.ts +2 -0
- package/dist/observers/dialogs.js +39 -0
- package/dist/observers/dom.js +28 -52
- package/dist/observers/download.d.ts +1 -2
- package/dist/observers/download.js +39 -56
- package/dist/observers/focus.d.ts +0 -6
- package/dist/observers/focus.js +0 -7
- package/dist/observers/frames.d.ts +0 -9
- package/dist/observers/frames.js +1 -28
- package/dist/observers/health.d.ts +0 -5
- package/dist/observers/health.js +2 -34
- package/dist/observers/install-all.d.ts +2 -0
- package/dist/observers/install-all.js +26 -27
- package/dist/observers/ipc.d.ts +0 -25
- package/dist/observers/ipc.js +0 -65
- package/dist/observers/navigation.d.ts +2 -0
- package/dist/observers/navigation.js +33 -0
- package/dist/observers/net-detail/network-body.d.ts +12 -0
- package/dist/observers/net-detail/network-body.js +71 -0
- package/dist/observers/net-detail/network-redact.d.ts +1 -0
- package/dist/observers/net-detail/network-redact.js +1 -0
- package/dist/observers/network-stream.d.ts +0 -21
- package/dist/observers/network-stream.js +3 -46
- package/dist/observers/network.d.ts +3 -27
- package/dist/observers/network.js +178 -295
- package/dist/observers/perf.d.ts +0 -10
- package/dist/observers/perf.js +0 -16
- package/dist/observers/route.d.ts +1 -2
- package/dist/observers/route.js +13 -19
- package/dist/observers/scroll.d.ts +0 -1
- package/dist/observers/scroll.js +2 -11
- package/dist/observers/sdk-failure.d.ts +1 -9
- package/dist/observers/sdk-failure.js +1 -30
- package/dist/observers/state.d.ts +1 -11
- package/dist/observers/state.js +13 -41
- package/dist/observers/storage.d.ts +0 -14
- package/dist/observers/storage.js +10 -34
- package/dist/observers/types.d.ts +0 -15
- package/dist/observers/types.js +0 -14
- package/dist/patching/capture-method.d.ts +0 -24
- package/dist/patching/capture-method.js +0 -26
- package/dist/presenter/chrome/layers.d.ts +2 -0
- package/dist/presenter/chrome/layers.js +2 -0
- package/dist/presenter/chrome/log-kinds.d.ts +12 -0
- package/dist/presenter/chrome/log-kinds.js +7 -0
- package/dist/presenter/{overlay.d.ts → chrome/overlay.d.ts} +0 -1
- package/dist/presenter/{overlay.js → chrome/overlay.js} +0 -1
- package/dist/presenter/chrome/presenter-brand.d.ts +3 -0
- package/dist/presenter/{presenter-brand.js → chrome/presenter-brand.js} +0 -21
- package/dist/presenter/chrome/presenter-effects.d.ts +4 -0
- package/dist/presenter/{presenter-effects.js → chrome/presenter-effects.js} +1 -10
- package/dist/presenter/chrome/presenter-hud-chrome.d.ts +7 -0
- package/dist/presenter/{presenter-hud-chrome.js → chrome/presenter-hud-chrome.js} +0 -10
- package/dist/presenter/chrome/presenter-log.d.ts +17 -0
- package/dist/presenter/{presenter-log.js → chrome/presenter-log.js} +5 -54
- package/dist/presenter/chrome/presenter-report-copy.d.ts +51 -0
- package/dist/presenter/chrome/presenter-report-copy.js +95 -0
- package/dist/presenter/chrome/presenter-safe-html.d.ts +2 -0
- package/dist/presenter/chrome/presenter-safe-html.js +16 -0
- package/dist/presenter/{presenter-tally.d.ts → chrome/presenter-tally.d.ts} +0 -5
- package/dist/presenter/{presenter-tally.js → chrome/presenter-tally.js} +1 -5
- package/dist/presenter/{presenter-verbs.js → chrome/presenter-verbs.js} +0 -7
- package/dist/presenter/{presenter-heroicons-data.d.ts → icons/presenter-heroicons-data.d.ts} +1 -8
- package/dist/presenter/{presenter-heroicons-data.js → icons/presenter-heroicons-data.js} +1 -8
- package/dist/presenter/{presenter-icons.d.ts → icons/presenter-icons.d.ts} +1 -6
- package/dist/presenter/{presenter-icons.js → icons/presenter-icons.js} +1 -6
- package/dist/presenter/presenter-account-styles.d.ts +1 -0
- package/dist/presenter/presenter-account-styles.js +99 -0
- package/dist/presenter/presenter-account.d.ts +40 -0
- package/dist/presenter/presenter-account.js +178 -0
- package/dist/presenter/presenter-config.d.ts +10 -107
- package/dist/presenter/presenter-config.js +5 -92
- package/dist/presenter/presenter-controls.d.ts +1 -35
- package/dist/presenter/presenter-controls.js +10 -133
- package/dist/presenter/presenter-dock-layout.d.ts +0 -3
- package/dist/presenter/presenter-dock-layout.js +1 -5
- package/dist/presenter/presenter-drag.d.ts +2 -42
- package/dist/presenter/presenter-drag.js +3 -76
- package/dist/presenter/presenter-glow.d.ts +0 -21
- package/dist/presenter/presenter-glow.js +2 -28
- package/dist/presenter/presenter-hud-position.d.ts +18 -0
- package/dist/presenter/presenter-hud-position.js +50 -0
- package/dist/presenter/presenter-report-styles.d.ts +0 -6
- package/dist/presenter/presenter-report-styles.js +22 -7
- package/dist/presenter/presenter-report.d.ts +5 -7
- package/dist/presenter/presenter-report.js +44 -95
- package/dist/presenter/presenter-run-state.d.ts +1 -8
- package/dist/presenter/presenter-run-state.js +1 -6
- package/dist/presenter/presenter-settings-styles.d.ts +0 -1
- package/dist/presenter/presenter-settings-styles.js +2 -2
- package/dist/presenter/presenter-settings.d.ts +5 -23
- package/dist/presenter/presenter-settings.js +21 -33
- package/dist/presenter/presenter-shell-styles.js +23 -20
- package/dist/presenter/presenter-shell.d.ts +6 -18
- package/dist/presenter/presenter-shell.js +26 -77
- package/dist/presenter/presenter-styles.d.ts +2 -10
- package/dist/presenter/presenter-styles.js +6 -12
- package/dist/presenter/presenter-workspace.d.ts +4 -4
- package/dist/presenter/presenter-workspace.js +30 -10
- package/dist/presenter/presenter.d.ts +3 -63
- package/dist/presenter/presenter.js +29 -132
- package/dist/presenter/tour/tour-view.d.ts +33 -0
- package/dist/presenter/tour/tour-view.js +188 -0
- package/dist/presenter/tour/tour.d.ts +25 -0
- package/dist/presenter/tour/tour.js +245 -0
- package/dist/recorder/recorder-styles.d.ts +0 -6
- package/dist/recorder/recorder-styles.js +0 -6
- package/dist/recorder/recorder.d.ts +0 -34
- package/dist/recorder/recorder.js +6 -49
- package/dist/registry/auto-testids.d.ts +0 -19
- package/dist/registry/auto-testids.js +1 -21
- package/dist/registry/capabilities.d.ts +0 -31
- package/dist/registry/capabilities.js +1 -46
- package/dist/registry/commit-and-signal.d.ts +0 -6
- package/dist/registry/commit-and-signal.js +1 -7
- package/dist/registry/declared-channels.d.ts +2 -0
- package/dist/registry/declared-channels.js +14 -0
- package/dist/registry/declared-platform.d.ts +4 -0
- package/dist/registry/declared-platform.js +34 -0
- package/dist/registry/domains.d.ts +0 -8
- package/dist/registry/domains.js +0 -7
- package/dist/registry/emitter.d.ts +0 -14
- package/dist/registry/emitter.js +1 -11
- package/dist/registry/store-adapters.d.ts +0 -186
- package/dist/registry/store-adapters.js +1 -164
- package/dist/registry/{adapters.d.ts → stores/adapters.d.ts} +0 -10
- package/dist/registry/{adapters.js → stores/adapters.js} +0 -10
- package/dist/registry/stores.d.ts +0 -33
- package/dist/registry/stores.js +1 -60
- package/dist/reticle-presenter-helpers.d.ts +0 -13
- package/dist/reticle-presenter-helpers.js +10 -20
- package/dist/reticle-singleton.d.ts +2 -0
- package/dist/reticle-singleton.js +3 -0
- package/dist/reticle.d.ts +1 -52
- package/dist/reticle.js +115 -180
- package/dist/review/annotator-styles.js +1 -8
- package/dist/review/annotator.d.ts +2 -13
- package/dist/review/annotator.js +2 -42
- package/dist/review/mark-anchor.d.ts +0 -11
- package/dist/review/mark-anchor.js +3 -11
- package/dist/security/serialization.d.ts +0 -20
- package/dist/security/serialization.js +1 -66
- package/dist/session-continuity.d.ts +0 -16
- package/dist/session-continuity.js +1 -20
- package/dist/timers/clock.d.ts +1 -14
- package/dist/timers/clock.js +59 -60
- package/dist/timers/console/native-console.js +5 -0
- package/dist/timers/native/native-timers.d.ts +11 -0
- package/dist/timers/native/native-timers.js +48 -0
- package/dist/transport/transport.d.ts +0 -51
- package/dist/transport/transport.js +5 -131
- package/dist/transport/unreachable-message.d.ts +0 -17
- package/dist/transport/unreachable-message.js +0 -17
- package/dist/util/captured-method.d.ts +0 -35
- package/dist/util/captured-method.js +0 -35
- package/package.json +5 -4
- package/dist/actions/synthetic-input.d.ts +0 -9
- package/dist/actions/synthetic-input.js +0 -40
- package/dist/dom/auto-anchor.d.ts +0 -65
- package/dist/dom/auto-anchor.js +0 -74
- package/dist/dom/refs.d.ts +0 -90
- package/dist/dom/refs.js +0 -245
- package/dist/dom/source.d.ts +0 -53
- package/dist/dom/source.js +0 -74
- package/dist/observers/network-body.d.ts +0 -35
- package/dist/observers/network-body.js +0 -168
- package/dist/observers/network-redact.d.ts +0 -20
- package/dist/observers/network-redact.js +0 -80
- package/dist/presenter/presenter-brand.d.ts +0 -22
- package/dist/presenter/presenter-effects.d.ts +0 -13
- package/dist/presenter/presenter-hud-chrome.d.ts +0 -17
- package/dist/presenter/presenter-log.d.ts +0 -64
- package/dist/presenter/presenter-report-copy.d.ts +0 -112
- package/dist/presenter/presenter-report-copy.js +0 -156
- package/dist/presenter/presenter-test-helpers.d.ts +0 -16
- package/dist/presenter/presenter-test-helpers.js +0 -41
- package/dist/timers/native-console.js +0 -9
- package/dist/timers/native-timers.d.ts +0 -36
- package/dist/timers/native-timers.js +0 -78
- /package/dist/presenter/{presenter-verbs.d.ts → chrome/presenter-verbs.d.ts} +0 -0
- /package/dist/timers/{native-console.d.ts → console/native-console.d.ts} +0 -0
|
@@ -1,163 +1,76 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Presenter tunables + option surface. Split out of presenter.ts so that file is just the controller
|
|
3
|
-
* under the size cap; these are pure declarations (interfaces + named constants), no behavior. None
|
|
4
|
-
* cross the browser↔bridge↔agent wire, so they live here, not in @reticlehq/core.
|
|
5
|
-
*/
|
|
6
|
-
/**
|
|
7
|
-
* Border behavior.
|
|
8
|
-
* - 'session': base border persists connect→disconnect; the busy machine drives only the shimmer.
|
|
9
|
-
* - 'busy': back-compat - the busy machine toggles the base border on/off.
|
|
10
|
-
*/
|
|
11
1
|
export const BorderMode = { SESSION: 'session', BUSY: 'busy' };
|
|
12
2
|
export const DEFAULT_BORDER_MODE = BorderMode.SESSION;
|
|
13
3
|
export const DATA_BUSY = 'data-busy';
|
|
14
4
|
export const BUSY_ON = '1';
|
|
15
5
|
export const BUSY_OFF = '0';
|
|
16
6
|
export const DEFAULT_PACE = 450;
|
|
17
|
-
/**
|
|
18
|
-
* The pace to actually use: the default in a browser a human is watching, zero in an automated one.
|
|
19
|
-
*
|
|
20
|
-
* The HUD paces `beforeAct` so a person can see the cursor reach the element before it fires. In a
|
|
21
|
-
* headless browser that is 450ms of nothing, charged to the agent, on every action. Measured with
|
|
22
|
-
* RETICLE_TRACE across the e2e battery: 42 `act` round-trips, 40 of them in a 452–460ms band -
|
|
23
|
-
* a fixed cost, not app work - 19.7 SECONDS in total, 98.5% of all time the battery spent in the
|
|
24
|
-
* browser, while every other command ran in 1–4ms.
|
|
25
|
-
*
|
|
26
|
-
* `navigator.webdriver` is the discriminator because it is exactly the question being asked: true
|
|
27
|
-
* under automation (Playwright, CDP, the browser pool's leased tabs, CI), false in the dev browser
|
|
28
|
-
* a human has open - the case the HUD exists for, which keeps its pacing untouched.
|
|
29
|
-
*
|
|
30
|
-
* An EXPLICIT `paceMs` always wins: a recorded demo runs under automation and still wants the glide,
|
|
31
|
-
* and an option that quietly ignores what the caller asked for is worse than no option.
|
|
32
|
-
*/
|
|
33
7
|
export function effectivePaceMs(requested, nav = 'undefined' === typeof navigator ? undefined : navigator) {
|
|
34
8
|
if (requested !== undefined)
|
|
35
9
|
return requested;
|
|
36
10
|
return true === nav?.webdriver ? 0 : DEFAULT_PACE;
|
|
37
11
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Glow state machine phases (exposed via glowPhase for tests). A burst of activity flips the
|
|
40
|
-
* border IN once on the first activity, holds steady (the slow reticle-pulse breathing keeps running
|
|
41
|
-
* uninterrupted - no per-action restart/strobe), then fades OUT once after a quiet window.
|
|
42
|
-
*/
|
|
43
12
|
export const GlowPhase = {
|
|
44
13
|
IDLE: 'idle',
|
|
45
14
|
BUSY: 'busy',
|
|
46
15
|
FADING: 'fading',
|
|
47
16
|
};
|
|
48
|
-
/** Quiet window before busy -> fading. */
|
|
49
17
|
export const IDLE_AFTER_MS = 700;
|
|
50
|
-
/** Liveness heartbeat: how often the act strip refreshes its "idle · {duration}" clock. */
|
|
51
18
|
export const HEARTBEAT_MS = 1000;
|
|
52
|
-
/**
|
|
53
|
-
* After this much quiet, the act strip stops showing the last action and starts a LIVE, ticking
|
|
54
|
-
* "◌ idle · {duration} since last action" - so a watcher can tell a 3s think from a dead agent
|
|
55
|
-
* (the killer gap: a frozen panel used to look identical whether the agent paused or stopped).
|
|
56
|
-
*/
|
|
57
19
|
export const IDLE_NOTICE_MS = 4000;
|
|
58
|
-
/** Act-strip copy (presenter-only UI). */
|
|
59
20
|
export const ACT_STRIP = {
|
|
60
21
|
READY: 'Ready',
|
|
61
|
-
|
|
22
|
+
PLANNING_PREFIX: 'planning next action · ',
|
|
23
|
+
STOPPED: 'stopped',
|
|
24
|
+
STOPPED_PREFIX: 'stopped · ',
|
|
62
25
|
NOW: 'now',
|
|
63
26
|
SINCE_LAST: ' since last action',
|
|
64
27
|
};
|
|
65
|
-
/** Default session-idle-end: after this much quiet the session auto-ends (glow off, panel persists
|
|
66
|
-
* for analysis). Agent-tweakable via reticle_session { idleEndMs } for the app's needs. */
|
|
67
28
|
export const IDLE_END_MS = 300_000;
|
|
68
|
-
/** Floor for a tweaked idle-end so the agent can't set a uselessly tiny window. */
|
|
69
29
|
export const IDLE_END_MIN_MS = 5_000;
|
|
70
|
-
/** Must match the glow CSS opacity transition.25s) so phase reaches idle after the fade paints. */
|
|
71
30
|
export const GLOW_FADE_MS = 250;
|
|
72
31
|
export const GLOW_ON = '1';
|
|
73
32
|
export const GLOW_OFF = '0';
|
|
74
33
|
export const DATA_ON = 'data-on';
|
|
75
|
-
/** The overlay root's state attribute, which the shell styles resolve colour against. */
|
|
76
34
|
export const STATE_ATTR = 'data-reticle-state';
|
|
77
|
-
/**
|
|
78
|
-
* Not a SessionState: there is no session. It is the absence of one, said out loud.
|
|
79
|
-
*
|
|
80
|
-
* Kept off `SessionState` deliberately — that enum crosses the wire, and this state exists only
|
|
81
|
-
* because nothing crossed the wire.
|
|
82
|
-
*/
|
|
83
35
|
export const UNREACHABLE_STATE = 'unreachable';
|
|
84
|
-
/** Overlay-root attribute toggled when the HUD is collapsed to the FAB. */
|
|
85
36
|
export const MIN_ATTR = 'data-reticle-min';
|
|
86
|
-
/** Overlay-root attribute toggled when the agent chat panel is open. */
|
|
87
37
|
export const CHAT_ATTR = 'data-reticle-chat';
|
|
88
|
-
/** Fixed dock wrapper that stacks chat above the morphing HUD shell. */
|
|
89
38
|
export const DOCK_ATTR = 'data-reticle-dock';
|
|
90
|
-
/** Collapsed circular control that expands the toolbar. */
|
|
91
39
|
export const FAB_ATTR = 'data-reticle-fab';
|
|
92
|
-
/** Toolbar chat toggle button. */
|
|
93
40
|
export const CHAT_TOGGLE_ATTR = 'data-reticle-chat-toggle';
|
|
94
|
-
/** Agent chat panel anchored to the dock. */
|
|
95
41
|
export const CHAT_PANEL_ATTR = 'data-reticle-chat-panel';
|
|
96
|
-
/** Chat panel opens above or below the HUD bar depending on viewport space. */
|
|
97
42
|
export const CHAT_PLACEMENT_ATTR = 'data-reticle-chat-placement';
|
|
98
|
-
/** Settings card opens above or below the HUD bar depending on viewport space. */
|
|
99
43
|
export const SETTINGS_PLACEMENT_ATTR = 'data-reticle-settings-placement';
|
|
100
|
-
/** Horizontal alignment of dock-anchored panels (start = left, end = right). */
|
|
101
44
|
export const DOCK_ALIGN_ATTR = 'data-reticle-dock-align';
|
|
102
45
|
export const Placement = { ABOVE: 'above', BELOW: 'below' };
|
|
103
|
-
/** Show/hide annotation markers. */
|
|
104
46
|
export const MARKERS_BTN_ATTR = 'data-reticle-markers-btn';
|
|
105
|
-
/** Clear every annotation on the page. */
|
|
106
47
|
export const CLEAR_MARKS_ATTR = 'data-reticle-clear-marks';
|
|
107
|
-
/** Collapsed FAB annotation-count badge. */
|
|
108
48
|
export const MARK_COUNT_ATTR = 'data-reticle-mark-count';
|
|
109
|
-
/**
|
|
110
|
-
* Turn annotation on and off from the toolbar.
|
|
111
|
-
*
|
|
112
|
-
* Annotation used to be reachable only as a side effect of expanding the HUD, which meant there was
|
|
113
|
-
* no way to keep the HUD open and stop annotating, and nothing on screen said the mode existed at
|
|
114
|
-
* all. It is a mode the user chooses, so it gets a control they can see and press.
|
|
115
|
-
*/
|
|
116
49
|
export const ANNOTATE_BTN_ATTR = 'data-reticle-annotate-btn';
|
|
117
|
-
/** Minimise the agent chat back to the toolbar, without collapsing the whole HUD. */
|
|
118
50
|
export const CHAT_MIN_ATTR = 'data-reticle-chat-min';
|
|
119
|
-
/** Overlay flag: is the impact report on screen. */
|
|
120
51
|
export const REPORT_ATTR = 'data-reticle-report';
|
|
121
|
-
/** The report panel itself. */
|
|
122
52
|
export const REPORT_PANEL_ATTR = 'data-reticle-report-panel';
|
|
123
|
-
/** The report's close button. */
|
|
124
53
|
export const REPORT_CLOSE_ATTR = 'data-reticle-report-close';
|
|
125
|
-
/** The toolbar button that opens the report. */
|
|
126
54
|
export const REPORT_BTN_ATTR = 'data-reticle-report-btn';
|
|
127
|
-
/** The capsule that stands in for the chat while it is minimised - state dot + last action. */
|
|
128
55
|
export const CHAT_PILL_ATTR = 'data-reticle-chat-pill';
|
|
129
|
-
/** Settings gear is open - the card sits above the dock. */
|
|
130
56
|
export const SETTINGS_ATTR = 'data-reticle-settings';
|
|
131
57
|
export const SETTINGS_BTN_ATTR = 'data-reticle-settings-btn';
|
|
132
58
|
export const SETTINGS_PANEL_ATTR = 'data-reticle-settings-panel';
|
|
133
59
|
export const SETTINGS_CLOSE_ATTR = 'data-reticle-settings-close';
|
|
134
60
|
export const SETTING_KEY_ATTR = 'data-reticle-setting';
|
|
135
61
|
export const SETTINGS_STORAGE_KEY = 'reticle-presenter-settings';
|
|
136
|
-
/** Accent swatch applied to the dock chrome. */
|
|
137
|
-
/** Overlay flag: is the page-edge glow wanted at all (user setting). */
|
|
138
62
|
export const AMBIENT_GLOW_ATTR = 'data-reticle-ambient-glow';
|
|
139
|
-
/** Overlay mirror of the activity strip's liveness, so CSS can pick the ACTIVE status colour. */
|
|
140
63
|
export const LIVENESS_ATTR = 'data-reticle-live';
|
|
141
|
-
/** When 1, a transparent blocker captures page pointer events. */
|
|
142
64
|
export const BLOCK_ATTR = 'data-reticle-block';
|
|
143
|
-
/** When 1, the dock is hidden until the page reloads. */
|
|
144
65
|
export const HIDDEN_UNTIL_RESTART_ATTR = 'data-reticle-hidden';
|
|
145
|
-
/** When 0, activity-log timestamps are hidden. */
|
|
146
66
|
export const LOG_TIMESTAMPS_ATTR = 'data-reticle-log-ts';
|
|
147
|
-
/** When 1, the chat panel uses a narrower width. */
|
|
148
|
-
/** When 1, HUD motion is reduced for accessibility. */
|
|
149
67
|
export const REDUCE_MOTION_ATTR = 'data-reticle-reduce-motion';
|
|
150
|
-
/** Docs opened from the settings footer. */
|
|
151
68
|
export const MCP_DOCS_URL = 'https://github.com/reticlehq/reticle/blob/main/docs/getting-started.md';
|
|
152
|
-
/** HUD has been dragged off the default dock - positioned with explicit left/top. */
|
|
153
69
|
export const HUD_DRAGGED_ATTR = 'data-dragged';
|
|
154
|
-
/** CSS custom properties written when the HUD is dragged. */
|
|
155
70
|
export const HUD_POS_X_VAR = '--reticle-hud-x';
|
|
156
71
|
export const HUD_POS_Y_VAR = '--reticle-hud-y';
|
|
157
|
-
/** Pointer movement below this threshold is treated as a click, not a drag. */
|
|
158
72
|
export const HUD_DRAG_THRESHOLD_PX = 4;
|
|
159
|
-
/** Minimum gap between a dragged HUD and the viewport edge. */
|
|
160
73
|
export const HUD_DOCK_MARGIN_PX = 8;
|
|
161
|
-
|
|
162
|
-
export const HUD_DRAG_IGNORE_SEL = '[data-reticle-pause], [data-reticle-annotate-btn], [data-reticle-markers-btn], [data-reticle-clear-marks], [data-reticle-end], [data-reticle-min-btn], [data-reticle-settings-btn], [data-reticle-settings-panel], [data-reticle-report-btn], [data-reticle-report-panel], [data-reticle-chat-panel], [data-reticle-chat-toggle], [data-reticle-workspace-btn], [data-reticle-workspace-menu], [data-reticle-copy], [data-reticle-export], [data-reticle-send], input, textarea, select, a, .reticle-head-ctl, [data-reticle-tally], .reticle-maxhint';
|
|
74
|
+
export const HUD_DRAG_IGNORE_SEL = '[data-reticle-pause], [data-reticle-annotate-btn], [data-reticle-markers-btn], [data-reticle-clear-marks], [data-reticle-end], [data-reticle-min-btn], [data-reticle-settings-btn], [data-reticle-settings-panel], [data-reticle-report-btn], [data-reticle-report-panel], [data-reticle-chat-panel], [data-reticle-chat-toggle], [data-reticle-workspace-btn], [data-reticle-workspace-menu], [data-reticle-copy], [data-reticle-export], input, textarea, select, a, .reticle-head-ctl, [data-reticle-tally], .reticle-maxhint';
|
|
163
75
|
export const THROTTLED_ATTR = 'data-reticle-throttled';
|
|
76
|
+
export const Z_HUD_TOOLTIP = 100;
|
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
import { HumanControlKind, PresenterTone, SessionState } from '@reticlehq/core';
|
|
2
|
-
|
|
2
|
+
export type { ControlIntent, ControlHandler } from './presenter-config.js';
|
|
3
3
|
export declare const ENDED_FADE_MS = 4000;
|
|
4
|
-
/** Payload the panel hands to its host when the human drives a control. */
|
|
5
|
-
export interface ControlIntent {
|
|
6
|
-
kind: HumanControlKind;
|
|
7
|
-
text?: string;
|
|
8
|
-
}
|
|
9
|
-
export type ControlHandler = (intent: ControlIntent) => void;
|
|
10
|
-
/** CSS for the control surface (injected with the rest of the presenter stylesheet). */
|
|
11
4
|
export declare const CONTROLS_CSS: string;
|
|
12
|
-
/** Icon toolbar: pause, chat, markers, end, clear - copy/export appear after a run ends. */
|
|
13
5
|
export declare const CONTROLS_TOOLBAR_HTML: string;
|
|
14
|
-
/** Banner markup (between head and log, hidden unless ended). */
|
|
15
6
|
export declare const CONTROLS_BANNER_HTML = "<div data-reticle-banner class=\"reticle-banner\">Session ended</div>";
|
|
16
|
-
/** Replay-a-flow row (between log and footer); buttons are filled in by setFlows once flows arrive. */
|
|
17
7
|
export declare const CONTROLS_FLOWS_HTML = "<div data-reticle-flows class=\"reticle-flows\"><span class=\"reticle-flows-cap\">Replay a flow</span></div>";
|
|
18
|
-
/** Footer markup: composer only (export icons live in the toolbar). */
|
|
19
8
|
export declare const CONTROLS_FOOT_HTML: string;
|
|
20
9
|
interface ControlPanelHost {
|
|
21
10
|
emit: (kind: HumanControlKind, text?: string) => void;
|
|
@@ -25,36 +14,13 @@ interface ControlPanelHost {
|
|
|
25
14
|
clearRunLog?: () => void;
|
|
26
15
|
onStateChange?: (state: SessionState) => void;
|
|
27
16
|
}
|
|
28
|
-
/**
|
|
29
|
-
* The live-control panel: owns the control element refs, the SessionState, the ended-fade timer,
|
|
30
|
-
* the DOM wiring, and the data-reticle-state visual machine. Split out of Presenter to keep both files
|
|
31
|
-
* under the 500-line cap. A click handler both emits a control AND optimistically applies state; the
|
|
32
|
-
* server's PRESENTER echo re-syncs via setState only (never emits) so a control is delivered once.
|
|
33
|
-
*/
|
|
34
17
|
export declare class ControlPanel {
|
|
35
18
|
#private;
|
|
36
19
|
constructor(host: ControlPanelHost);
|
|
37
20
|
get state(): SessionState;
|
|
38
|
-
/** Query control refs out of the mounted root and bind the DOM listeners, then paint active. */
|
|
39
21
|
mount(root: HTMLElement, glow: HTMLElement | undefined): void;
|
|
40
|
-
/** Clear any pending ended-fade timer (called from Presenter.destroy). */
|
|
41
22
|
teardown(): void;
|
|
42
|
-
/** Render the replayable-flow chips from the server push. Each ▶ click re-runs that flow, no agent.
|
|
43
|
-
* Takes the raw wire value and narrows it here (the panel is the consumer of this push). */
|
|
44
23
|
setFlows(flows: unknown): void;
|
|
45
|
-
/**
|
|
46
|
-
* Re-render the replay chips for the CURRENT page. A flow "starts here" iff its first step's anchor
|
|
47
|
-
* (a testid `start` hint) is present in the live DOM; flows with no start hint (signal/role-first,
|
|
48
|
-
* un-checkable) always show. Called on connect and on every route change so the list tracks the page -
|
|
49
|
-
* so you never see (or click) a flow that can't replay from where you are. Existing flows benefit
|
|
50
|
-
* without re-recording, since the hint is derived from the first step, not stored on the flow.
|
|
51
|
-
*/
|
|
52
24
|
refilterFlows(): void;
|
|
53
|
-
/**
|
|
54
|
-
* Drive the panel's visual state. Idempotent; NEVER emits a control - the shared path for both the
|
|
55
|
-
* optimistic local click and the authoritative server PRESENTER echo. Only the ended-border fade
|
|
56
|
-
* touches a clock, via the injected native timer.
|
|
57
|
-
*/
|
|
58
25
|
setState(state: SessionState, text?: string, tone?: PresenterTone): void;
|
|
59
26
|
}
|
|
60
|
-
export {};
|
|
@@ -1,49 +1,31 @@
|
|
|
1
1
|
import { HumanControlKind, PresenterTone, SessionState } from '@reticlehq/core';
|
|
2
|
-
import { nativeSetTimeout, nativeClearTimeout } from '../timers/native-timers.js';
|
|
2
|
+
import { nativeSetTimeout, nativeClearTimeout } from '../timers/native/native-timers.js';
|
|
3
3
|
import { CHAT_TOGGLE_ATTR, CLEAR_MARKS_ATTR, MARKERS_BTN_ATTR } from './presenter-config.js';
|
|
4
|
-
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, hiIconHtml, hiToggleIconHtml, } from './presenter-icons.js';
|
|
4
|
+
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, hiIconHtml, hiToggleIconHtml, } from './icons/presenter-icons.js';
|
|
5
5
|
import { getPresenterSettings } from './presenter-settings.js';
|
|
6
6
|
import { mountWorkspaceSelector, workspaceRowHtml } from './presenter-workspace.js';
|
|
7
|
-
// Live-control panel: the two-way control surface inside the floating HUD - Pause/Resume + End
|
|
8
|
-
// (header), a message input + Send (footer), and the data-reticle-state visual machine. Split out of
|
|
9
|
-
// presenter.ts to keep both files under the 500-line cap (mirrors the presenter-log.ts split).
|
|
10
|
-
// All nodes carry data-reticle-* attrs so they're excluded from snapshots (see dom-ignore.ts). The
|
|
11
|
-
// strings here are presenter-only UI; the control kinds + state values reuse protocol constants.
|
|
12
|
-
/** data-reticle-state attribute on the overlay root; its value is always a SessionState. */
|
|
13
7
|
const DATA_RETICLE_STATE = 'data-reticle-state';
|
|
14
|
-
/** data-reticle-tone on the overlay root - waiting/ask/warn distinguishes how the agent handed back. */
|
|
15
8
|
const DATA_RETICLE_TONE = 'data-reticle-tone';
|
|
16
9
|
const DATA_ON = 'data-on';
|
|
17
10
|
const GLOW_OFF = '0';
|
|
18
|
-
/** Button copy (presenter-only UI; never a wire string). */
|
|
19
11
|
const CONTROL_LABEL = {
|
|
20
12
|
PAUSE: 'Pause',
|
|
21
13
|
RESUME: 'Resume',
|
|
22
14
|
END: 'End',
|
|
23
15
|
SEND: 'Send',
|
|
24
16
|
};
|
|
25
|
-
const INPUT_PLACEHOLDER = 'Tell the agent something…';
|
|
26
|
-
/** Accessible name for the composer (a placeholder is not an accessible name). */
|
|
27
|
-
const INPUT_ARIA_LABEL = 'Message to the agent';
|
|
28
17
|
const PAUSED_BADGE_TEXT = 'PAUSED';
|
|
29
18
|
const ENDED_BANNER_TEXT = 'Session ended';
|
|
30
19
|
const COPY_LABEL = 'Copy run';
|
|
31
20
|
const EXPORT_LABEL = 'Export';
|
|
32
21
|
const FLOWS_LABEL = 'Replay a flow';
|
|
33
22
|
const COPIED_TEXT = 'Copied ✓';
|
|
34
|
-
/** Download filename for the exported run state. */
|
|
35
23
|
const RUN_FILENAME = 'reticle-run.json';
|
|
36
|
-
/** Border fade-out delay after a session ends (native timer; presenter-only tunable). */
|
|
37
24
|
export const ENDED_FADE_MS = 4000;
|
|
38
|
-
/** Max composer height (px) before it scrolls. One source for both the CSS cap and the JS auto-grow
|
|
39
|
-
* clamp - they measure the same border-box, so the scrollbar appears exactly when growth stops. */
|
|
40
|
-
const MSG_MAX_H = 96;
|
|
41
|
-
/** CSS for the control surface (injected with the rest of the presenter stylesheet). */
|
|
42
25
|
export const CONTROLS_CSS = `
|
|
43
26
|
[data-reticle-chat-panel] [data-reticle-foot]{flex:none;padding:8px 10px 10px;border-top:1px solid rgba(255,255,255,.07);
|
|
44
27
|
background:linear-gradient(180deg,rgba(0,0,0,.35) 0%,rgba(0,0,0,.55) 100%);pointer-events:auto;}
|
|
45
28
|
[data-reticle-chat-panel] .reticle-hud-log-well{margin:0 0 4px;}
|
|
46
|
-
[data-reticle-chat-panel] .reticle-composer-stack{display:flex;flex-direction:column;gap:6px;}
|
|
47
29
|
[data-reticle-chat-panel] .reticle-workspace-wrap{position:relative;align-self:flex-start;max-width:100%;}
|
|
48
30
|
[data-reticle-chat-panel] .reticle-workspace{
|
|
49
31
|
display:inline-flex;align-items:center;gap:5px;max-width:100%;padding:3px 8px 3px 6px;border-radius:999px;cursor:pointer;
|
|
@@ -75,33 +57,14 @@ export const CONTROLS_CSS = `
|
|
|
75
57
|
background:rgba(255,255,255,.06);color:rgba(255,255,255,.7);cursor:pointer;line-height:0;transition:background .12s,color .12s;}
|
|
76
58
|
[data-reticle-chat-panel] .reticle-workspace-copy:hover:not(:disabled){background:rgba(255,255,255,.12);color:#fff;}
|
|
77
59
|
[data-reticle-chat-panel] .reticle-workspace-copy:disabled{opacity:.35;cursor:not-allowed;}
|
|
60
|
+
/* The account capsule, to the right of the menu title. Matches the local idiom in this block:
|
|
61
|
+
the workspace menu predates the design tokens used in the report panel and states its own
|
|
62
|
+
greys, so a token here would be the only one and would not match its neighbours. */
|
|
63
|
+
[data-reticle-chat-panel] .reticle-workspace-menu-actions{
|
|
64
|
+
display:flex;align-items:center;gap:6px;}
|
|
78
65
|
[data-reticle-chat-panel] .reticle-workspace-menu-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:4px 0;}
|
|
79
66
|
[data-reticle-chat-panel] .reticle-workspace-menu-k{flex:none;color:rgba(255,255,255,.42);}
|
|
80
67
|
[data-reticle-chat-panel] .reticle-workspace-menu-v{min-width:0;text-align:right;color:rgba(255,255,255,.9);font-weight:500;word-break:break-all;}
|
|
81
|
-
[data-reticle-chat-panel] .reticle-composer{display:flex;align-items:center;gap:6px;background:rgba(255,255,255,.03);
|
|
82
|
-
border:1px solid rgba(255,255,255,.1);border-radius:999px;padding:4px 4px 4px 14px;
|
|
83
|
-
box-shadow:inset 0 1px 0 rgba(255,255,255,.05);transition:border-color .2s,box-shadow .2s,background .2s;}
|
|
84
|
-
[data-reticle-chat-panel] .reticle-composer:focus-within{
|
|
85
|
-
border-color:color-mix(in srgb,var(--reticle-accent) 50%,transparent);background:rgba(255,255,255,.06);
|
|
86
|
-
box-shadow:inset 0 1px 0 rgba(255,255,255,.06),
|
|
87
|
-
0 0 0 1px color-mix(in srgb,var(--reticle-accent) 50%,transparent);}
|
|
88
|
-
[data-reticle-chat-panel] .reticle-msg{flex:1;min-width:0;pointer-events:auto;background:transparent;border:none;outline:none;resize:none;
|
|
89
|
-
box-sizing:border-box;color:var(--reticle-fg);font-family:var(--reticle-font);font-size:12.5px;line-height:18px;
|
|
90
|
-
height:28px;min-height:28px;max-height:${MSG_MAX_H}px;padding:5px 0;overflow-y:auto;
|
|
91
|
-
scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.14) transparent;}
|
|
92
|
-
[data-reticle-chat-panel] .reticle-msg::-webkit-scrollbar{width:9px;}
|
|
93
|
-
[data-reticle-chat-panel] .reticle-msg::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:9px;border:2px solid transparent;background-clip:content-box;}
|
|
94
|
-
[data-reticle-chat-panel] .reticle-msg::placeholder{color:var(--reticle-faint);}
|
|
95
|
-
[data-reticle-chat-panel] .reticle-msg:disabled{opacity:.5;}
|
|
96
|
-
[data-reticle-chat-panel] .reticle-send{flex:none;width:28px;height:28px;padding:0;border-radius:50%;border:none;cursor:pointer;pointer-events:auto;
|
|
97
|
-
background:var(--reticle-accent);color:#ffffff;display:inline-flex;align-items:center;justify-content:center;
|
|
98
|
-
transition:background .15s,transform .1s,box-shadow .15s;
|
|
99
|
-
box-shadow:0 1px 3px color-mix(in srgb,var(--reticle-accent) 30%,transparent);}
|
|
100
|
-
[data-reticle-chat-panel] .reticle-send:hover{
|
|
101
|
-
background:color-mix(in srgb,var(--reticle-accent) 85%,#000);
|
|
102
|
-
box-shadow:0 2px 6px color-mix(in srgb,var(--reticle-accent) 40%,transparent);}
|
|
103
|
-
[data-reticle-chat-panel] .reticle-send:active{transform:scale(.92);}
|
|
104
|
-
[data-reticle-chat-panel] .reticle-send:disabled{background:#374151;color:#9ca3af;box-shadow:none;opacity:.5;cursor:default;}
|
|
105
68
|
[data-reticle-chat-panel] .reticle-banner{display:none;flex:none;align-items:center;gap:8px;padding:10px 14px;color:var(--reticle-fg);
|
|
106
69
|
font-size:12px;font-weight:500;border-bottom:1px solid var(--reticle-line2);background:var(--reticle-surface);}
|
|
107
70
|
[data-reticle-overlay][data-reticle-state="ended"] [data-reticle-chat-panel] .reticle-banner{display:block;}
|
|
@@ -141,7 +104,6 @@ export const CONTROLS_CSS = `
|
|
|
141
104
|
[data-reticle-hud] .reticle-export-msg{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
|
|
142
105
|
overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;}
|
|
143
106
|
`;
|
|
144
|
-
/** Swap pause/resume icon + accessible label without visible text clutter in the toolbar. */
|
|
145
107
|
function paintPauseBtn(btn, paused) {
|
|
146
108
|
const iconName = paused ? PresenterIcon.PLAY : PresenterIcon.PAUSE;
|
|
147
109
|
const labelText = paused ? CONTROL_LABEL.RESUME : CONTROL_LABEL.PAUSE;
|
|
@@ -165,7 +127,6 @@ function pauseToolbarHtml() {
|
|
|
165
127
|
const tip = `<span class="reticle-tb-tip">${CONTROL_LABEL.PAUSE}</span>`;
|
|
166
128
|
return `<div class="reticle-tb-wrap reticle-tb-wrap--pause">${btn}${badge}${tip}</div>`;
|
|
167
129
|
}
|
|
168
|
-
/** Icon toolbar: pause, chat, markers, end, clear - copy/export appear after a run ends. */
|
|
169
130
|
export const CONTROLS_TOOLBAR_HTML = [
|
|
170
131
|
pauseToolbarHtml(),
|
|
171
132
|
tbWrap(`<button type="button" ${CHAT_TOGGLE_ATTR} class="reticle-tb-btn reticle-tb-btn--toggle" title="${CHAT_LABEL}" aria-label="${CHAT_LABEL}" aria-pressed="false" data-active="0">${hiToggleIconHtml(PresenterIcon.MESSAGE, TB)}</button>`, CHAT_LABEL),
|
|
@@ -176,18 +137,13 @@ export const CONTROLS_TOOLBAR_HTML = [
|
|
|
176
137
|
`<button type="button" data-reticle-export class="reticle-tb-btn reticle-tb-btn--export" title="${EXPORT_LABEL}" aria-label="${EXPORT_LABEL}" hidden>${hiIconHtml(PresenterIcon.DOWNLOAD, TB)}</button>`,
|
|
177
138
|
'<span data-reticle-export-msg class="reticle-export-msg" aria-live="polite"></span>',
|
|
178
139
|
].join('');
|
|
179
|
-
/** Banner markup (between head and log, hidden unless ended). */
|
|
180
140
|
export const CONTROLS_BANNER_HTML = `<div data-reticle-banner class="reticle-banner">${ENDED_BANNER_TEXT}</div>`;
|
|
181
|
-
/** Replay-a-flow row (between log and footer); buttons are filled in by setFlows once flows arrive. */
|
|
182
141
|
export const CONTROLS_FLOWS_HTML = `<div data-reticle-flows class="reticle-flows"><span class="reticle-flows-cap">${FLOWS_LABEL}</span></div>`;
|
|
183
|
-
|
|
184
|
-
export const CONTROLS_FOOT_HTML = `<div data-reticle-foot><div class="reticle-composer-stack">${workspaceRowHtml()}<div class="reticle-composer"><textarea data-reticle-input class="reticle-msg" rows="1" aria-label="${INPUT_ARIA_LABEL}" placeholder="${INPUT_PLACEHOLDER}"></textarea><button type="button" data-reticle-send class="reticle-send" aria-label="${CONTROL_LABEL.SEND}">${hiIconHtml(PresenterIcon.SEND, PRESENTER_ICON_SIZE.SEND)}</button></div></div></div>`;
|
|
142
|
+
export const CONTROLS_FOOT_HTML = `<div data-reticle-foot><div class="reticle-foot-stack">${workspaceRowHtml()}</div></div>`;
|
|
185
143
|
function queryControlRefs(root) {
|
|
186
144
|
return {
|
|
187
145
|
pauseBtn: root.querySelector('[data-reticle-pause]') ?? undefined,
|
|
188
146
|
endBtn: root.querySelector('[data-reticle-end]') ?? undefined,
|
|
189
|
-
input: root.querySelector('[data-reticle-input]') ?? undefined,
|
|
190
|
-
sendBtn: root.querySelector('[data-reticle-send]') ?? undefined,
|
|
191
147
|
banner: root.querySelector('[data-reticle-banner]') ?? undefined,
|
|
192
148
|
copyBtn: root.querySelector('[data-reticle-copy]') ?? undefined,
|
|
193
149
|
exportBtn: root.querySelector('[data-reticle-export]') ?? undefined,
|
|
@@ -195,18 +151,10 @@ function queryControlRefs(root) {
|
|
|
195
151
|
flows: root.querySelector('[data-reticle-flows]') ?? undefined,
|
|
196
152
|
};
|
|
197
153
|
}
|
|
198
|
-
/**
|
|
199
|
-
* The live-control panel: owns the control element refs, the SessionState, the ended-fade timer,
|
|
200
|
-
* the DOM wiring, and the data-reticle-state visual machine. Split out of Presenter to keep both files
|
|
201
|
-
* under the 500-line cap. A click handler both emits a control AND optimistically applies state; the
|
|
202
|
-
* server's PRESENTER echo re-syncs via setState only (never emits) so a control is delivered once.
|
|
203
|
-
*/
|
|
204
154
|
export class ControlPanel {
|
|
205
155
|
#refs = {
|
|
206
156
|
pauseBtn: undefined,
|
|
207
157
|
endBtn: undefined,
|
|
208
|
-
input: undefined,
|
|
209
|
-
sendBtn: undefined,
|
|
210
158
|
banner: undefined,
|
|
211
159
|
copyBtn: undefined,
|
|
212
160
|
exportBtn: undefined,
|
|
@@ -217,16 +165,8 @@ export class ControlPanel {
|
|
|
217
165
|
#fadeTimer;
|
|
218
166
|
#root;
|
|
219
167
|
#glow;
|
|
220
|
-
/** The full replayable-flow list from the last push; re-filtered per page on route change. */
|
|
221
168
|
#flowItems = [];
|
|
222
169
|
#workspaceTeardown;
|
|
223
|
-
/**
|
|
224
|
-
* One signal for every listener this controller registers.
|
|
225
|
-
*
|
|
226
|
-
* All eight are anonymous closures over `this`, so there was no reference to hand
|
|
227
|
-
* `removeEventListener` and teardown removed none of them — each kept the controller reachable
|
|
228
|
-
* for as long as its element lived, and a second mount stacked another set on top.
|
|
229
|
-
*/
|
|
230
170
|
#listeners;
|
|
231
171
|
#host;
|
|
232
172
|
constructor(host) {
|
|
@@ -235,7 +175,6 @@ export class ControlPanel {
|
|
|
235
175
|
get state() {
|
|
236
176
|
return this.#state;
|
|
237
177
|
}
|
|
238
|
-
/** Query control refs out of the mounted root and bind the DOM listeners, then paint active. */
|
|
239
178
|
mount(root, glow) {
|
|
240
179
|
this.#listeners = new AbortController();
|
|
241
180
|
const { signal } = this.#listeners;
|
|
@@ -248,16 +187,6 @@ export class ControlPanel {
|
|
|
248
187
|
}
|
|
249
188
|
this.#refs.pauseBtn?.addEventListener('click', () => this.#onPauseToggle(), { signal });
|
|
250
189
|
this.#refs.endBtn?.addEventListener('click', () => this.#onEnd(), { signal });
|
|
251
|
-
this.#refs.sendBtn?.addEventListener('click', () => this.#onSend(), { signal });
|
|
252
|
-
this.#refs.input?.addEventListener('keydown', (e) => {
|
|
253
|
-
// Enter sends; Shift+Enter inserts a newline (falls through to the textarea's default).
|
|
254
|
-
if (e instanceof KeyboardEvent && 'Enter' === e.key && !e.shiftKey) {
|
|
255
|
-
e.preventDefault();
|
|
256
|
-
this.#onSend();
|
|
257
|
-
}
|
|
258
|
-
}, { signal });
|
|
259
|
-
this.#refs.input?.addEventListener('input', () => this.#autosize(), { signal });
|
|
260
|
-
// Replay-a-flow: one ▶ click re-runs a saved flow (no agent). Delegated so it covers all chips.
|
|
261
190
|
this.#refs.flows?.addEventListener('click', (e) => {
|
|
262
191
|
const target = e.target;
|
|
263
192
|
if (!(target instanceof HTMLElement))
|
|
@@ -272,11 +201,9 @@ export class ControlPanel {
|
|
|
272
201
|
this.#workspaceTeardown = mountWorkspaceSelector(root);
|
|
273
202
|
this.setState(SessionState.ACTIVE);
|
|
274
203
|
}
|
|
275
|
-
/** Serialize the run state to pretty JSON for Copy/Export. */
|
|
276
204
|
#runJson() {
|
|
277
205
|
return JSON.stringify(this.#host.runState(), null, 2);
|
|
278
206
|
}
|
|
279
|
-
/** Copy the run state to the clipboard (with a brief "Copied ✓" flash). */
|
|
280
207
|
#onCopy() {
|
|
281
208
|
void navigator.clipboard?.writeText(this.#runJson());
|
|
282
209
|
if (getPresenterSettings().clearOnCopy) {
|
|
@@ -289,7 +216,6 @@ export class ControlPanel {
|
|
|
289
216
|
nativeSetTimeout(() => msg.setAttribute('data-show', '0'), 1600);
|
|
290
217
|
}
|
|
291
218
|
}
|
|
292
|
-
/** Download the run state as reticle-run.json. */
|
|
293
219
|
#onExport() {
|
|
294
220
|
const blob = new Blob([this.#runJson()], { type: 'application/json' });
|
|
295
221
|
const url = URL.createObjectURL(blob);
|
|
@@ -302,9 +228,7 @@ export class ControlPanel {
|
|
|
302
228
|
this.#host.clearRunLog?.();
|
|
303
229
|
}
|
|
304
230
|
}
|
|
305
|
-
/** Clear any pending ended-fade timer (called from Presenter.destroy). */
|
|
306
231
|
teardown() {
|
|
307
|
-
// All eight registrations, in one call that cannot drift from mount().
|
|
308
232
|
this.#listeners?.abort();
|
|
309
233
|
this.#listeners = undefined;
|
|
310
234
|
this.#workspaceTeardown?.();
|
|
@@ -329,32 +253,6 @@ export class ControlPanel {
|
|
|
329
253
|
this.#host.emit(HumanControlKind.END);
|
|
330
254
|
this.setState(SessionState.ENDED);
|
|
331
255
|
}
|
|
332
|
-
#onSend() {
|
|
333
|
-
if (this.#state === SessionState.ENDED)
|
|
334
|
-
return;
|
|
335
|
-
const text = (this.#refs.input?.value ?? '').trim();
|
|
336
|
-
if (0 === text.length)
|
|
337
|
-
return;
|
|
338
|
-
this.#host.emit(HumanControlKind.MESSAGE, text);
|
|
339
|
-
this.#host.logHuman(text);
|
|
340
|
-
if (getPresenterSettings().clearOnCopy) {
|
|
341
|
-
this.#host.clearRunLog?.();
|
|
342
|
-
}
|
|
343
|
-
if (this.#refs.input !== undefined)
|
|
344
|
-
this.#refs.input.value = '';
|
|
345
|
-
this.#autosize();
|
|
346
|
-
}
|
|
347
|
-
/** Grow the composer to fit its content (up to the CSS max-height), then shrink back - soothing,
|
|
348
|
-
* no scrollbar until it's genuinely long. Driven on input and after a send clears the field. */
|
|
349
|
-
#autosize() {
|
|
350
|
-
const el = this.#refs.input;
|
|
351
|
-
if (el === undefined)
|
|
352
|
-
return;
|
|
353
|
-
el.style.height = 'auto';
|
|
354
|
-
el.style.height = `${String(Math.min(el.scrollHeight, MSG_MAX_H))}px`;
|
|
355
|
-
}
|
|
356
|
-
/** Render the replayable-flow chips from the server push. Each ▶ click re-runs that flow, no agent.
|
|
357
|
-
* Takes the raw wire value and narrows it here (the panel is the consumer of this push). */
|
|
358
256
|
setFlows(flows) {
|
|
359
257
|
const list = Array.isArray(flows) ? flows : [];
|
|
360
258
|
this.#flowItems = list
|
|
@@ -374,13 +272,6 @@ export class ControlPanel {
|
|
|
374
272
|
.filter((c) => c !== null);
|
|
375
273
|
this.#renderFlows();
|
|
376
274
|
}
|
|
377
|
-
/**
|
|
378
|
-
* Re-render the replay chips for the CURRENT page. A flow "starts here" iff its first step's anchor
|
|
379
|
-
* (a testid `start` hint) is present in the live DOM; flows with no start hint (signal/role-first,
|
|
380
|
-
* un-checkable) always show. Called on connect and on every route change so the list tracks the page -
|
|
381
|
-
* so you never see (or click) a flow that can't replay from where you are. Existing flows benefit
|
|
382
|
-
* without re-recording, since the hint is derived from the first step, not stored on the flow.
|
|
383
|
-
*/
|
|
384
275
|
refilterFlows() {
|
|
385
276
|
this.#renderFlows();
|
|
386
277
|
}
|
|
@@ -391,26 +282,20 @@ export class ControlPanel {
|
|
|
391
282
|
const doc = el.ownerDocument;
|
|
392
283
|
const testids = new Set(Array.from(doc.querySelectorAll('[data-testid]')).map((n) => n.getAttribute('data-testid')));
|
|
393
284
|
const visible = this.#flowItems.filter((f) => f.start === undefined || testids.has(f.start));
|
|
394
|
-
el.querySelectorAll('[data-reticle-replay]').forEach((b) => b.remove());
|
|
285
|
+
el.querySelectorAll('[data-reticle-replay]').forEach((b) => b.remove());
|
|
395
286
|
for (const flow of visible) {
|
|
396
287
|
const btn = doc.createElement('button');
|
|
397
288
|
btn.type = 'button';
|
|
398
289
|
btn.className = 'reticle-flow';
|
|
399
|
-
btn.setAttribute('data-reticle-replay', flow.name);
|
|
290
|
+
btn.setAttribute('data-reticle-replay', flow.name);
|
|
400
291
|
btn.textContent = `▶ ${flow.name}`;
|
|
401
292
|
el.appendChild(btn);
|
|
402
293
|
}
|
|
403
294
|
el.setAttribute('data-has', visible.length > 0 ? '1' : '0');
|
|
404
295
|
}
|
|
405
|
-
/**
|
|
406
|
-
* Drive the panel's visual state. Idempotent; NEVER emits a control - the shared path for both the
|
|
407
|
-
* optimistic local click and the authoritative server PRESENTER echo. Only the ended-border fade
|
|
408
|
-
* touches a clock, via the injected native timer.
|
|
409
|
-
*/
|
|
410
296
|
setState(state, text, tone) {
|
|
411
297
|
this.#state = state;
|
|
412
298
|
this.#root?.setAttribute(DATA_RETICLE_STATE, state);
|
|
413
|
-
// A handoff tone (waiting/ask/warn) drives a distinct panel treatment; calm/undefined = a plain end.
|
|
414
299
|
const handoff = tone !== undefined && tone !== PresenterTone.CALM;
|
|
415
300
|
if (handoff)
|
|
416
301
|
this.#root?.setAttribute(DATA_RETICLE_TONE, tone);
|
|
@@ -440,12 +325,6 @@ export class ControlPanel {
|
|
|
440
325
|
else
|
|
441
326
|
refs.exportBtn.setAttribute('hidden', '');
|
|
442
327
|
}
|
|
443
|
-
if (refs.sendBtn !== undefined)
|
|
444
|
-
refs.sendBtn.disabled = ended;
|
|
445
|
-
if (refs.input !== undefined)
|
|
446
|
-
refs.input.disabled = ended;
|
|
447
|
-
// A calm end leads with "Session ended"; a handoff (waiting/ask/warn) leads with the notice itself,
|
|
448
|
-
// since the toned styling already conveys "ended" and the notice is the actionable headline.
|
|
449
328
|
if (refs.banner !== undefined) {
|
|
450
329
|
const summary = text !== undefined && text.trim().length > 0 ? text.trim() : '';
|
|
451
330
|
refs.banner.textContent =
|
|
@@ -454,8 +333,6 @@ export class ControlPanel {
|
|
|
454
333
|
: `${ENDED_BANNER_TEXT}${summary.length > 0 ? ` · ${summary}` : ''}`;
|
|
455
334
|
}
|
|
456
335
|
if (ended) {
|
|
457
|
-
// End the run: fade out the page BORDER (testing is over) but KEEP the panel so the human can
|
|
458
|
-
// read the result and Copy/Export the run state. The composer disables; the export row reveals.
|
|
459
336
|
const glow = this.#glow;
|
|
460
337
|
this.#fadeTimer = nativeSetTimeout(() => {
|
|
461
338
|
glow?.setAttribute(DATA_ON, GLOW_OFF);
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/** Flip floating panels and reclamp a dragged dock so everything stays on-screen. */
|
|
2
1
|
export declare function syncDockLayout(dock: HTMLElement, overlay: HTMLElement): void;
|
|
3
|
-
/** Batch dock layout work on the next frame (resize, panel open, drag end). */
|
|
4
2
|
export declare function scheduleSyncDockLayout(dock: HTMLElement, overlay: HTMLElement): void;
|
|
5
|
-
/** Resolve the dock node from an overlay root. */
|
|
6
3
|
export declare function findDock(overlay: HTMLElement): HTMLElement | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CHAT_ATTR, CHAT_PANEL_ATTR, CHAT_PLACEMENT_ATTR, DOCK_ALIGN_ATTR, DOCK_ATTR, HUD_DOCK_MARGIN_PX, Placement, SETTINGS_ATTR, SETTINGS_PANEL_ATTR, SETTINGS_PLACEMENT_ATTR, } from './presenter-config.js';
|
|
2
|
-
import { applyHudPosition, isHudDragged, readHudPosition, relayoutHudPosition, } from './presenter-
|
|
2
|
+
import { applyHudPosition, isHudDragged, readHudPosition, relayoutHudPosition, } from './presenter-hud-position.js';
|
|
3
3
|
const HUD_ATTR = 'data-reticle-hud';
|
|
4
4
|
const DOCK_PANEL_GAP_PX = 8;
|
|
5
5
|
const DOCK_SETTINGS_GAP_PX = 12;
|
|
@@ -49,7 +49,6 @@ function visiblePanel(el) {
|
|
|
49
49
|
const style = window.getComputedStyle(el);
|
|
50
50
|
return 'none' !== style.display && 'hidden' !== style.visibility;
|
|
51
51
|
}
|
|
52
|
-
/** Resolve the HUD bar inside a dock (or the node itself in minimal fixtures). */
|
|
53
52
|
function resolveHudBar(dock) {
|
|
54
53
|
const nested = dock.querySelector(`[${HUD_ATTR}]`);
|
|
55
54
|
if (nested instanceof HTMLElement)
|
|
@@ -60,7 +59,6 @@ function resolveHudBar(dock) {
|
|
|
60
59
|
return dock;
|
|
61
60
|
return undefined;
|
|
62
61
|
}
|
|
63
|
-
/** Flip floating panels and reclamp a dragged dock so everything stays on-screen. */
|
|
64
62
|
export function syncDockLayout(dock, overlay) {
|
|
65
63
|
const hud = resolveHudBar(dock);
|
|
66
64
|
if (hud === undefined)
|
|
@@ -153,7 +151,6 @@ export function syncDockLayout(dock, overlay) {
|
|
|
153
151
|
applyHudPosition(dock, base.left + dx, base.top + dy);
|
|
154
152
|
}
|
|
155
153
|
let syncRafId;
|
|
156
|
-
/** Batch dock layout work on the next frame (resize, panel open, drag end). */
|
|
157
154
|
export function scheduleSyncDockLayout(dock, overlay) {
|
|
158
155
|
if (syncRafId !== undefined)
|
|
159
156
|
cancelAnimationFrame(syncRafId);
|
|
@@ -163,7 +160,6 @@ export function scheduleSyncDockLayout(dock, overlay) {
|
|
|
163
160
|
requestAnimationFrame(() => syncDockLayout(dock, overlay));
|
|
164
161
|
});
|
|
165
162
|
}
|
|
166
|
-
/** Resolve the dock node from an overlay root. */
|
|
167
163
|
export function findDock(overlay) {
|
|
168
164
|
const dock = overlay.querySelector(`[${DOCK_ATTR}]`);
|
|
169
165
|
return dock instanceof HTMLElement ? dock : undefined;
|
|
@@ -1,48 +1,8 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { applyHudPosition, isHudDragged, readHudPosition, relayoutHudPosition, clampHudPosition, hudLayoutBox, resetHudDockPosition, } from './presenter-hud-position.js';
|
|
2
|
+
interface HudDragCallbacks {
|
|
2
3
|
onDragMove?: () => void;
|
|
3
|
-
/** Fires when a drag gesture ends; `moved` is true when the panel actually changed position. */
|
|
4
4
|
onDragEnd?: (moved: boolean) => void;
|
|
5
5
|
}
|
|
6
|
-
/** Whether the HUD is off the default bottom-right dock. */
|
|
7
|
-
export declare function isHudDragged(hud: HTMLElement): boolean;
|
|
8
|
-
/** Clamp a HUD position so the full panel stays inside the viewport. */
|
|
9
|
-
export declare function clampHudPosition(left: number, top: number, width: number, height: number, viewportWidth: number, viewportHeight: number, margin?: number): {
|
|
10
|
-
left: number;
|
|
11
|
-
top: number;
|
|
12
|
-
};
|
|
13
|
-
/** Paint an explicit left/top position (switches the HUD off the default bottom-right dock). */
|
|
14
|
-
export declare function applyHudPosition(hud: HTMLElement, left: number, top: number): void;
|
|
15
|
-
/** Return the HUD to the default bottom-right dock. */
|
|
16
|
-
export declare function resetHudDockPosition(hud: HTMLElement): void;
|
|
17
|
-
/** Return the HUD's laid-out box (rounded to whole pixels for stable clamping). */
|
|
18
|
-
export declare function hudLayoutBox(hud: HTMLElement): {
|
|
19
|
-
left: number;
|
|
20
|
-
top: number;
|
|
21
|
-
width: number;
|
|
22
|
-
height: number;
|
|
23
|
-
};
|
|
24
|
-
/** Read the dragged HUD's authored position (CSS vars), falling back to layout box. */
|
|
25
|
-
export declare function readHudPosition(hud: HTMLElement): {
|
|
26
|
-
left: number;
|
|
27
|
-
top: number;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Re-clamp a dragged HUD after resize, minimise/expand, or any layout-driven size change.
|
|
31
|
-
* No-op when the panel is still on the default dock.
|
|
32
|
-
*/
|
|
33
|
-
export declare function relayoutHudPosition(hud: HTMLElement): void;
|
|
34
|
-
/**
|
|
35
|
-
* Keep a dragged HUD inside the viewport when the window or panel size changes
|
|
36
|
-
* (minimise ↔ expand, tally appearing, viewport resize).
|
|
37
|
-
*/
|
|
38
6
|
export declare function installHudPositionGuards(hud: HTMLElement, overlay: HTMLElement): () => void;
|
|
39
|
-
/**
|
|
40
|
-
* Drag the presenter HUD by its header. Returns a teardown that removes listeners.
|
|
41
|
-
* Uses pointer capture so the drag stays smooth even when the cursor leaves the head.
|
|
42
|
-
*/
|
|
43
7
|
export declare function installHudDrag(hud: HTMLElement, head: HTMLElement, callbacks?: HudDragCallbacks): () => void;
|
|
44
|
-
/**
|
|
45
|
-
* Drag the presenter HUD from multiple handles (FAB when collapsed, toolbar brand when expanded).
|
|
46
|
-
* Returns a teardown that removes listeners from every handle.
|
|
47
|
-
*/
|
|
48
8
|
export declare function installHudDragHandles(hud: HTMLElement, handles: HTMLElement[], callbacks?: HudDragCallbacks): () => void;
|