@reticlehq/browser 2.13.1 → 2.14.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 +0 -20
- package/dist/actions/actions-dom.js +0 -66
- package/dist/actions/actions-press.d.ts +10 -0
- package/dist/actions/actions-press.js +77 -0
- package/dist/actions/actions.d.ts +0 -89
- package/dist/actions/actions.js +24 -358
- package/dist/actions/appeared-text.d.ts +0 -20
- package/dist/actions/appeared-text.js +0 -36
- package/dist/actions/danger-context.d.ts +3 -0
- package/dist/actions/danger-context.js +39 -0
- package/dist/actions/scroll.d.ts +0 -9
- package/dist/actions/scroll.js +0 -7
- package/dist/actions/synthetic-input.d.ts +0 -7
- package/dist/actions/synthetic-input.js +0 -27
- 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 +0 -67
- package/dist/connect-options.d.ts +2 -96
- package/dist/dom/a11y.d.ts +0 -29
- package/dist/dom/a11y.js +1 -88
- package/dist/dom/auto-anchor.d.ts +0 -40
- package/dist/dom/auto-anchor.js +0 -33
- 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 +0 -72
- package/dist/dom/dom-ignore.d.ts +0 -30
- package/dist/dom/dom-ignore.js +0 -47
- package/dist/dom/occlusion.d.ts +0 -7
- package/dist/dom/occlusion.js +0 -7
- package/dist/dom/query.d.ts +0 -14
- package/dist/dom/query.js +48 -214
- package/dist/dom/realm.d.ts +0 -20
- package/dist/dom/realm.js +0 -28
- package/dist/dom/refs.d.ts +0 -76
- package/dist/dom/refs.js +2 -133
- 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 +26 -151
- package/dist/dom/source.d.ts +0 -44
- package/dist/dom/source.js +0 -45
- 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 +0 -22
- package/dist/edit-epoch.js +0 -52
- package/dist/index.js +1 -23
- package/dist/observers/animation.d.ts +0 -1
- package/dist/observers/animation.js +0 -3
- package/dist/observers/blind-spots.d.ts +0 -26
- package/dist/observers/blind-spots.js +2 -61
- 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 +0 -44
- package/dist/observers/download.d.ts +1 -2
- package/dist/observers/download.js +38 -55
- 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 +1 -33
- 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/network-body.d.ts +5 -28
- package/dist/observers/network-body.js +11 -108
- package/dist/observers/network-redact.d.ts +1 -20
- package/dist/observers/network-redact.js +1 -80
- package/dist/observers/network-stream.d.ts +0 -21
- package/dist/observers/network-stream.js +1 -44
- package/dist/observers/network.d.ts +2 -26
- package/dist/observers/network.js +177 -294
- 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 +0 -9
- 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/overlay.d.ts +0 -1
- package/dist/presenter/overlay.js +0 -1
- package/dist/presenter/presenter-brand.d.ts +0 -19
- package/dist/presenter/presenter-brand.js +0 -21
- package/dist/presenter/presenter-config.d.ts +1 -105
- package/dist/presenter/presenter-config.js +2 -91
- package/dist/presenter/presenter-controls.d.ts +0 -29
- package/dist/presenter/presenter-controls.js +3 -131
- package/dist/presenter/presenter-dock-layout.d.ts +0 -3
- package/dist/presenter/presenter-dock-layout.js +0 -4
- package/dist/presenter/presenter-drag.d.ts +2 -24
- package/dist/presenter/presenter-drag.js +0 -26
- package/dist/presenter/presenter-effects.d.ts +0 -9
- package/dist/presenter/presenter-effects.js +0 -9
- package/dist/presenter/presenter-glow.d.ts +0 -21
- package/dist/presenter/presenter-glow.js +1 -27
- package/dist/presenter/presenter-heroicons-data.d.ts +0 -8
- package/dist/presenter/presenter-heroicons-data.js +0 -8
- package/dist/presenter/presenter-hud-chrome.d.ts +0 -10
- package/dist/presenter/presenter-hud-chrome.js +0 -10
- package/dist/presenter/presenter-icons.d.ts +0 -6
- package/dist/presenter/presenter-icons.js +0 -6
- package/dist/presenter/presenter-log.d.ts +0 -38
- package/dist/presenter/presenter-log.js +1 -45
- package/dist/presenter/presenter-report-copy.d.ts +5 -66
- package/dist/presenter/presenter-report-copy.js +5 -66
- package/dist/presenter/presenter-report-styles.d.ts +0 -6
- package/dist/presenter/presenter-report-styles.js +6 -6
- package/dist/presenter/presenter-report.d.ts +4 -7
- package/dist/presenter/presenter-report.js +18 -76
- package/dist/presenter/presenter-run-state.d.ts +0 -7
- package/dist/presenter/presenter-run-state.js +0 -5
- package/dist/presenter/presenter-settings-styles.d.ts +0 -1
- package/dist/presenter/presenter-settings-styles.js +0 -1
- package/dist/presenter/presenter-settings.d.ts +1 -23
- package/dist/presenter/presenter-settings.js +1 -31
- package/dist/presenter/presenter-shell-styles.js +2 -5
- package/dist/presenter/presenter-shell.d.ts +2 -18
- package/dist/presenter/presenter-shell.js +1 -73
- package/dist/presenter/presenter-styles.d.ts +0 -9
- package/dist/presenter/presenter-styles.js +0 -9
- package/dist/presenter/presenter-tally.d.ts +0 -5
- package/dist/presenter/presenter-tally.js +0 -4
- package/dist/presenter/presenter-test-helpers.d.ts +0 -2
- package/dist/presenter/presenter-test-helpers.js +0 -2
- package/dist/presenter/presenter-verbs.js +0 -7
- package/dist/presenter/presenter-workspace.d.ts +0 -4
- package/dist/presenter/presenter-workspace.js +1 -8
- package/dist/presenter/presenter.d.ts +0 -60
- package/dist/presenter/presenter.js +4 -118
- 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 +0 -49
- package/dist/registry/adapters.d.ts +0 -10
- package/dist/registry/adapters.js +0 -10
- 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/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 +0 -10
- package/dist/registry/store-adapters.d.ts +0 -186
- package/dist/registry/store-adapters.js +0 -163
- package/dist/registry/stores.d.ts +0 -33
- package/dist/registry/stores.js +0 -59
- package/dist/reticle-presenter-helpers.d.ts +0 -13
- package/dist/reticle-presenter-helpers.js +0 -14
- package/dist/reticle.d.ts +1 -52
- package/dist/reticle.js +20 -145
- package/dist/review/annotator-styles.js +0 -7
- package/dist/review/annotator.d.ts +2 -13
- package/dist/review/annotator.js +0 -40
- package/dist/review/mark-anchor.d.ts +0 -11
- package/dist/review/mark-anchor.js +0 -8
- 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/native-console.js +0 -4
- package/dist/timers/native-timers.d.ts +0 -25
- package/dist/timers/native-timers.js +3 -33
- package/dist/transport/transport.d.ts +0 -51
- package/dist/transport/transport.js +3 -129
- 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 +2 -2
|
@@ -4,46 +4,28 @@ import { CHAT_TOGGLE_ATTR, CLEAR_MARKS_ATTR, MARKERS_BTN_ATTR } from './presente
|
|
|
4
4
|
import { PresenterIcon, PRESENTER_ICON_SIZE, hiIcon, hiIconHtml, hiToggleIconHtml, } from './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;
|
|
@@ -78,30 +60,6 @@ export const CONTROLS_CSS = `
|
|
|
78
60
|
[data-reticle-chat-panel] .reticle-workspace-menu-row{display:flex;align-items:flex-start;justify-content:space-between;gap:10px;padding:4px 0;}
|
|
79
61
|
[data-reticle-chat-panel] .reticle-workspace-menu-k{flex:none;color:rgba(255,255,255,.42);}
|
|
80
62
|
[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
63
|
[data-reticle-chat-panel] .reticle-banner{display:none;flex:none;align-items:center;gap:8px;padding:10px 14px;color:var(--reticle-fg);
|
|
106
64
|
font-size:12px;font-weight:500;border-bottom:1px solid var(--reticle-line2);background:var(--reticle-surface);}
|
|
107
65
|
[data-reticle-overlay][data-reticle-state="ended"] [data-reticle-chat-panel] .reticle-banner{display:block;}
|
|
@@ -141,7 +99,6 @@ export const CONTROLS_CSS = `
|
|
|
141
99
|
[data-reticle-hud] .reticle-export-msg{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
|
|
142
100
|
overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;}
|
|
143
101
|
`;
|
|
144
|
-
/** Swap pause/resume icon + accessible label without visible text clutter in the toolbar. */
|
|
145
102
|
function paintPauseBtn(btn, paused) {
|
|
146
103
|
const iconName = paused ? PresenterIcon.PLAY : PresenterIcon.PAUSE;
|
|
147
104
|
const labelText = paused ? CONTROL_LABEL.RESUME : CONTROL_LABEL.PAUSE;
|
|
@@ -165,7 +122,6 @@ function pauseToolbarHtml() {
|
|
|
165
122
|
const tip = `<span class="reticle-tb-tip">${CONTROL_LABEL.PAUSE}</span>`;
|
|
166
123
|
return `<div class="reticle-tb-wrap reticle-tb-wrap--pause">${btn}${badge}${tip}</div>`;
|
|
167
124
|
}
|
|
168
|
-
/** Icon toolbar: pause, chat, markers, end, clear - copy/export appear after a run ends. */
|
|
169
125
|
export const CONTROLS_TOOLBAR_HTML = [
|
|
170
126
|
pauseToolbarHtml(),
|
|
171
127
|
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 +132,13 @@ export const CONTROLS_TOOLBAR_HTML = [
|
|
|
176
132
|
`<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
133
|
'<span data-reticle-export-msg class="reticle-export-msg" aria-live="polite"></span>',
|
|
178
134
|
].join('');
|
|
179
|
-
/** Banner markup (between head and log, hidden unless ended). */
|
|
180
135
|
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
136
|
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>`;
|
|
137
|
+
export const CONTROLS_FOOT_HTML = `<div data-reticle-foot><div class="reticle-foot-stack">${workspaceRowHtml()}</div></div>`;
|
|
185
138
|
function queryControlRefs(root) {
|
|
186
139
|
return {
|
|
187
140
|
pauseBtn: root.querySelector('[data-reticle-pause]') ?? undefined,
|
|
188
141
|
endBtn: root.querySelector('[data-reticle-end]') ?? undefined,
|
|
189
|
-
input: root.querySelector('[data-reticle-input]') ?? undefined,
|
|
190
|
-
sendBtn: root.querySelector('[data-reticle-send]') ?? undefined,
|
|
191
142
|
banner: root.querySelector('[data-reticle-banner]') ?? undefined,
|
|
192
143
|
copyBtn: root.querySelector('[data-reticle-copy]') ?? undefined,
|
|
193
144
|
exportBtn: root.querySelector('[data-reticle-export]') ?? undefined,
|
|
@@ -195,18 +146,10 @@ function queryControlRefs(root) {
|
|
|
195
146
|
flows: root.querySelector('[data-reticle-flows]') ?? undefined,
|
|
196
147
|
};
|
|
197
148
|
}
|
|
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
149
|
export class ControlPanel {
|
|
205
150
|
#refs = {
|
|
206
151
|
pauseBtn: undefined,
|
|
207
152
|
endBtn: undefined,
|
|
208
|
-
input: undefined,
|
|
209
|
-
sendBtn: undefined,
|
|
210
153
|
banner: undefined,
|
|
211
154
|
copyBtn: undefined,
|
|
212
155
|
exportBtn: undefined,
|
|
@@ -217,16 +160,8 @@ export class ControlPanel {
|
|
|
217
160
|
#fadeTimer;
|
|
218
161
|
#root;
|
|
219
162
|
#glow;
|
|
220
|
-
/** The full replayable-flow list from the last push; re-filtered per page on route change. */
|
|
221
163
|
#flowItems = [];
|
|
222
164
|
#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
165
|
#listeners;
|
|
231
166
|
#host;
|
|
232
167
|
constructor(host) {
|
|
@@ -235,7 +170,6 @@ export class ControlPanel {
|
|
|
235
170
|
get state() {
|
|
236
171
|
return this.#state;
|
|
237
172
|
}
|
|
238
|
-
/** Query control refs out of the mounted root and bind the DOM listeners, then paint active. */
|
|
239
173
|
mount(root, glow) {
|
|
240
174
|
this.#listeners = new AbortController();
|
|
241
175
|
const { signal } = this.#listeners;
|
|
@@ -248,16 +182,6 @@ export class ControlPanel {
|
|
|
248
182
|
}
|
|
249
183
|
this.#refs.pauseBtn?.addEventListener('click', () => this.#onPauseToggle(), { signal });
|
|
250
184
|
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
185
|
this.#refs.flows?.addEventListener('click', (e) => {
|
|
262
186
|
const target = e.target;
|
|
263
187
|
if (!(target instanceof HTMLElement))
|
|
@@ -272,11 +196,9 @@ export class ControlPanel {
|
|
|
272
196
|
this.#workspaceTeardown = mountWorkspaceSelector(root);
|
|
273
197
|
this.setState(SessionState.ACTIVE);
|
|
274
198
|
}
|
|
275
|
-
/** Serialize the run state to pretty JSON for Copy/Export. */
|
|
276
199
|
#runJson() {
|
|
277
200
|
return JSON.stringify(this.#host.runState(), null, 2);
|
|
278
201
|
}
|
|
279
|
-
/** Copy the run state to the clipboard (with a brief "Copied ✓" flash). */
|
|
280
202
|
#onCopy() {
|
|
281
203
|
void navigator.clipboard?.writeText(this.#runJson());
|
|
282
204
|
if (getPresenterSettings().clearOnCopy) {
|
|
@@ -289,7 +211,6 @@ export class ControlPanel {
|
|
|
289
211
|
nativeSetTimeout(() => msg.setAttribute('data-show', '0'), 1600);
|
|
290
212
|
}
|
|
291
213
|
}
|
|
292
|
-
/** Download the run state as reticle-run.json. */
|
|
293
214
|
#onExport() {
|
|
294
215
|
const blob = new Blob([this.#runJson()], { type: 'application/json' });
|
|
295
216
|
const url = URL.createObjectURL(blob);
|
|
@@ -302,9 +223,7 @@ export class ControlPanel {
|
|
|
302
223
|
this.#host.clearRunLog?.();
|
|
303
224
|
}
|
|
304
225
|
}
|
|
305
|
-
/** Clear any pending ended-fade timer (called from Presenter.destroy). */
|
|
306
226
|
teardown() {
|
|
307
|
-
// All eight registrations, in one call that cannot drift from mount().
|
|
308
227
|
this.#listeners?.abort();
|
|
309
228
|
this.#listeners = undefined;
|
|
310
229
|
this.#workspaceTeardown?.();
|
|
@@ -329,32 +248,6 @@ export class ControlPanel {
|
|
|
329
248
|
this.#host.emit(HumanControlKind.END);
|
|
330
249
|
this.setState(SessionState.ENDED);
|
|
331
250
|
}
|
|
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
251
|
setFlows(flows) {
|
|
359
252
|
const list = Array.isArray(flows) ? flows : [];
|
|
360
253
|
this.#flowItems = list
|
|
@@ -374,13 +267,6 @@ export class ControlPanel {
|
|
|
374
267
|
.filter((c) => c !== null);
|
|
375
268
|
this.#renderFlows();
|
|
376
269
|
}
|
|
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
270
|
refilterFlows() {
|
|
385
271
|
this.#renderFlows();
|
|
386
272
|
}
|
|
@@ -391,26 +277,20 @@ export class ControlPanel {
|
|
|
391
277
|
const doc = el.ownerDocument;
|
|
392
278
|
const testids = new Set(Array.from(doc.querySelectorAll('[data-testid]')).map((n) => n.getAttribute('data-testid')));
|
|
393
279
|
const visible = this.#flowItems.filter((f) => f.start === undefined || testids.has(f.start));
|
|
394
|
-
el.querySelectorAll('[data-reticle-replay]').forEach((b) => b.remove());
|
|
280
|
+
el.querySelectorAll('[data-reticle-replay]').forEach((b) => b.remove());
|
|
395
281
|
for (const flow of visible) {
|
|
396
282
|
const btn = doc.createElement('button');
|
|
397
283
|
btn.type = 'button';
|
|
398
284
|
btn.className = 'reticle-flow';
|
|
399
|
-
btn.setAttribute('data-reticle-replay', flow.name);
|
|
285
|
+
btn.setAttribute('data-reticle-replay', flow.name);
|
|
400
286
|
btn.textContent = `▶ ${flow.name}`;
|
|
401
287
|
el.appendChild(btn);
|
|
402
288
|
}
|
|
403
289
|
el.setAttribute('data-has', visible.length > 0 ? '1' : '0');
|
|
404
290
|
}
|
|
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
291
|
setState(state, text, tone) {
|
|
411
292
|
this.#state = state;
|
|
412
293
|
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
294
|
const handoff = tone !== undefined && tone !== PresenterTone.CALM;
|
|
415
295
|
if (handoff)
|
|
416
296
|
this.#root?.setAttribute(DATA_RETICLE_TONE, tone);
|
|
@@ -440,12 +320,6 @@ export class ControlPanel {
|
|
|
440
320
|
else
|
|
441
321
|
refs.exportBtn.setAttribute('hidden', '');
|
|
442
322
|
}
|
|
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
323
|
if (refs.banner !== undefined) {
|
|
450
324
|
const summary = text !== undefined && text.trim().length > 0 ? text.trim() : '';
|
|
451
325
|
refs.banner.textContent =
|
|
@@ -454,8 +328,6 @@ export class ControlPanel {
|
|
|
454
328
|
: `${ENDED_BANNER_TEXT}${summary.length > 0 ? ` · ${summary}` : ''}`;
|
|
455
329
|
}
|
|
456
330
|
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
331
|
const glow = this.#glow;
|
|
460
332
|
this.#fadeTimer = nativeSetTimeout(() => {
|
|
461
333
|
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;
|
|
@@ -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,26 @@
|
|
|
1
|
-
|
|
1
|
+
interface HudDragCallbacks {
|
|
2
2
|
onDragMove?: () => void;
|
|
3
|
-
/** Fires when a drag gesture ends; `moved` is true when the panel actually changed position. */
|
|
4
3
|
onDragEnd?: (moved: boolean) => void;
|
|
5
4
|
}
|
|
6
|
-
/** Whether the HUD is off the default bottom-right dock. */
|
|
7
5
|
export declare function isHudDragged(hud: HTMLElement): boolean;
|
|
8
|
-
/** Clamp a HUD position so the full panel stays inside the viewport. */
|
|
9
6
|
export declare function clampHudPosition(left: number, top: number, width: number, height: number, viewportWidth: number, viewportHeight: number, margin?: number): {
|
|
10
7
|
left: number;
|
|
11
8
|
top: number;
|
|
12
9
|
};
|
|
13
|
-
/** Paint an explicit left/top position (switches the HUD off the default bottom-right dock). */
|
|
14
10
|
export declare function applyHudPosition(hud: HTMLElement, left: number, top: number): void;
|
|
15
|
-
/** Return the HUD to the default bottom-right dock. */
|
|
16
11
|
export declare function resetHudDockPosition(hud: HTMLElement): void;
|
|
17
|
-
/** Return the HUD's laid-out box (rounded to whole pixels for stable clamping). */
|
|
18
12
|
export declare function hudLayoutBox(hud: HTMLElement): {
|
|
19
13
|
left: number;
|
|
20
14
|
top: number;
|
|
21
15
|
width: number;
|
|
22
16
|
height: number;
|
|
23
17
|
};
|
|
24
|
-
/** Read the dragged HUD's authored position (CSS vars), falling back to layout box. */
|
|
25
18
|
export declare function readHudPosition(hud: HTMLElement): {
|
|
26
19
|
left: number;
|
|
27
20
|
top: number;
|
|
28
21
|
};
|
|
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
22
|
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
23
|
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
24
|
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
25
|
export declare function installHudDragHandles(hud: HTMLElement, handles: HTMLElement[], callbacks?: HudDragCallbacks): () => void;
|
|
26
|
+
export {};
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { HUD_DOCK_MARGIN_PX, HUD_DRAG_IGNORE_SEL, HUD_DRAG_THRESHOLD_PX, HUD_DRAGGED_ATTR, HUD_POS_X_VAR, HUD_POS_Y_VAR, MIN_ATTR, CHAT_ATTR, SETTINGS_ATTR, } from './presenter-config.js';
|
|
2
2
|
import { scheduleSyncDockLayout } from './presenter-dock-layout.js';
|
|
3
3
|
const DRAG_HANDLE_DRAGGING_CLASS = 'reticle-drag-handle--dragging';
|
|
4
|
-
/** Whether the HUD is off the default bottom-right dock. */
|
|
5
4
|
export function isHudDragged(hud) {
|
|
6
5
|
return '1' === hud.getAttribute(HUD_DRAGGED_ATTR);
|
|
7
6
|
}
|
|
8
|
-
/** Clamp a HUD position so the full panel stays inside the viewport. */
|
|
9
7
|
export function clampHudPosition(left, top, width, height, viewportWidth, viewportHeight, margin = HUD_DOCK_MARGIN_PX) {
|
|
10
8
|
const minLeft = margin;
|
|
11
9
|
const minTop = margin;
|
|
@@ -16,19 +14,16 @@ export function clampHudPosition(left, top, width, height, viewportWidth, viewpo
|
|
|
16
14
|
top: Math.max(minTop, Math.min(top, maxTop)),
|
|
17
15
|
};
|
|
18
16
|
}
|
|
19
|
-
/** Paint an explicit left/top position (switches the HUD off the default bottom-right dock). */
|
|
20
17
|
export function applyHudPosition(hud, left, top) {
|
|
21
18
|
hud.setAttribute(HUD_DRAGGED_ATTR, '1');
|
|
22
19
|
hud.style.setProperty(HUD_POS_X_VAR, `${String(left)}px`);
|
|
23
20
|
hud.style.setProperty(HUD_POS_Y_VAR, `${String(top)}px`);
|
|
24
21
|
}
|
|
25
|
-
/** Return the HUD to the default bottom-right dock. */
|
|
26
22
|
export function resetHudDockPosition(hud) {
|
|
27
23
|
hud.removeAttribute(HUD_DRAGGED_ATTR);
|
|
28
24
|
hud.style.removeProperty(HUD_POS_X_VAR);
|
|
29
25
|
hud.style.removeProperty(HUD_POS_Y_VAR);
|
|
30
26
|
}
|
|
31
|
-
/** Return the HUD's laid-out box (rounded to whole pixels for stable clamping). */
|
|
32
27
|
export function hudLayoutBox(hud) {
|
|
33
28
|
const rect = hud.getBoundingClientRect();
|
|
34
29
|
return {
|
|
@@ -38,7 +33,6 @@ export function hudLayoutBox(hud) {
|
|
|
38
33
|
height: rect.height,
|
|
39
34
|
};
|
|
40
35
|
}
|
|
41
|
-
/** Read the dragged HUD's authored position (CSS vars), falling back to layout box. */
|
|
42
36
|
export function readHudPosition(hud) {
|
|
43
37
|
const x = hud.style.getPropertyValue(HUD_POS_X_VAR);
|
|
44
38
|
const y = hud.style.getPropertyValue(HUD_POS_Y_VAR);
|
|
@@ -48,10 +42,6 @@ export function readHudPosition(hud) {
|
|
|
48
42
|
const rect = hud.getBoundingClientRect();
|
|
49
43
|
return { left: rect.left, top: rect.top };
|
|
50
44
|
}
|
|
51
|
-
/**
|
|
52
|
-
* Re-clamp a dragged HUD after resize, minimise/expand, or any layout-driven size change.
|
|
53
|
-
* No-op when the panel is still on the default dock.
|
|
54
|
-
*/
|
|
55
45
|
export function relayoutHudPosition(hud) {
|
|
56
46
|
if (!isHudDragged(hud))
|
|
57
47
|
return;
|
|
@@ -60,16 +50,10 @@ export function relayoutHudPosition(hud) {
|
|
|
60
50
|
const next = clampHudPosition(left, top, width, height, window.innerWidth, window.innerHeight);
|
|
61
51
|
applyHudPosition(hud, next.left, next.top);
|
|
62
52
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Keep a dragged HUD inside the viewport when the window or panel size changes
|
|
65
|
-
* (minimise ↔ expand, tally appearing, viewport resize).
|
|
66
|
-
*/
|
|
67
53
|
export function installHudPositionGuards(hud, overlay) {
|
|
68
54
|
const scheduleRelayout = () => {
|
|
69
55
|
scheduleSyncDockLayout(hud, overlay);
|
|
70
56
|
};
|
|
71
|
-
// One signal for all three listeners here. The observers below are NOT covered — neither
|
|
72
|
-
// ResizeObserver nor MutationObserver accepts one — so they keep their explicit disconnects.
|
|
73
57
|
const listeners = new AbortController();
|
|
74
58
|
const { signal } = listeners;
|
|
75
59
|
const onResize = () => scheduleRelayout();
|
|
@@ -93,10 +77,6 @@ export function installHudPositionGuards(hud, overlay) {
|
|
|
93
77
|
minObserver.disconnect();
|
|
94
78
|
};
|
|
95
79
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Drag the presenter HUD by its header. Returns a teardown that removes listeners.
|
|
98
|
-
* Uses pointer capture so the drag stays smooth even when the cursor leaves the head.
|
|
99
|
-
*/
|
|
100
80
|
export function installHudDrag(hud, head, callbacks = {}) {
|
|
101
81
|
let dragging = false;
|
|
102
82
|
let startX = 0;
|
|
@@ -173,8 +153,6 @@ export function installHudDrag(hud, head, callbacks = {}) {
|
|
|
173
153
|
releaseCapture(e.pointerId);
|
|
174
154
|
finishDrag();
|
|
175
155
|
};
|
|
176
|
-
// All four pointer phases share one signal: they are added together and must come off
|
|
177
|
-
// together, and a partial removal would leave a half-live drag.
|
|
178
156
|
const listeners = new AbortController();
|
|
179
157
|
const { signal } = listeners;
|
|
180
158
|
head.addEventListener('pointerdown', onPointerDown, { signal });
|
|
@@ -188,10 +166,6 @@ export function installHudDrag(hud, head, callbacks = {}) {
|
|
|
188
166
|
finishDrag();
|
|
189
167
|
};
|
|
190
168
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Drag the presenter HUD from multiple handles (FAB when collapsed, toolbar brand when expanded).
|
|
193
|
-
* Returns a teardown that removes listeners from every handle.
|
|
194
|
-
*/
|
|
195
169
|
export function installHudDragHandles(hud, handles, callbacks = {}) {
|
|
196
170
|
const teardowns = handles.map((handle) => installHudDrag(hud, handle, callbacks));
|
|
197
171
|
return () => {
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Synthetic-cursor + click effects - the visible "the agent is acting here" motion. Pure DOM helpers
|
|
3
|
-
* extracted from presenter.ts so the controller stays under the size cap; each is a no-op when its
|
|
4
|
-
* element is absent (the panel may not be mounted). Behavior is unchanged from the inlined methods.
|
|
5
|
-
*/
|
|
6
|
-
/** Fly the synthetic cursor to a viewport point and show it. */
|
|
7
1
|
export declare function moveCursor(cursor: HTMLElement | undefined, x: number, y: number): void;
|
|
8
|
-
/** Briefly draw the green focus ring around an element's box, then fade it. */
|
|
9
2
|
export declare function ringAround(ring: HTMLElement | undefined, rect: DOMRect): void;
|
|
10
|
-
/** Spawn a one-shot click ripple at a viewport point (self-removing). */
|
|
11
3
|
export declare function spawnRipple(root: HTMLElement | undefined, x: number, y: number): void;
|
|
12
|
-
/** Pace for the human so an action is watchable, not instant. */
|
|
13
4
|
export declare function pace(ms: number): Promise<void>;
|
|
@@ -1,17 +1,10 @@
|
|
|
1
1
|
import { nativeSetTimeout } from '../timers/native-timers.js';
|
|
2
|
-
/**
|
|
3
|
-
* Synthetic-cursor + click effects - the visible "the agent is acting here" motion. Pure DOM helpers
|
|
4
|
-
* extracted from presenter.ts so the controller stays under the size cap; each is a no-op when its
|
|
5
|
-
* element is absent (the panel may not be mounted). Behavior is unchanged from the inlined methods.
|
|
6
|
-
*/
|
|
7
|
-
/** Fly the synthetic cursor to a viewport point and show it. */
|
|
8
2
|
export function moveCursor(cursor, x, y) {
|
|
9
3
|
if (cursor === undefined)
|
|
10
4
|
return;
|
|
11
5
|
cursor.setAttribute('data-on', '1');
|
|
12
6
|
cursor.style.transform = `translate(${String(x)}px, ${String(y)}px)`;
|
|
13
7
|
}
|
|
14
|
-
/** Briefly draw the green focus ring around an element's box, then fade it. */
|
|
15
8
|
export function ringAround(ring, rect) {
|
|
16
9
|
if (ring === undefined)
|
|
17
10
|
return;
|
|
@@ -22,7 +15,6 @@ export function ringAround(ring, rect) {
|
|
|
22
15
|
ring.setAttribute('data-on', '1');
|
|
23
16
|
nativeSetTimeout(() => ring.setAttribute('data-on', '0'), 700);
|
|
24
17
|
}
|
|
25
|
-
/** Spawn a one-shot click ripple at a viewport point (self-removing). */
|
|
26
18
|
export function spawnRipple(root, x, y) {
|
|
27
19
|
if (root === undefined)
|
|
28
20
|
return;
|
|
@@ -33,7 +25,6 @@ export function spawnRipple(root, x, y) {
|
|
|
33
25
|
root.appendChild(r);
|
|
34
26
|
nativeSetTimeout(() => r.remove(), 520);
|
|
35
27
|
}
|
|
36
|
-
/** Pace for the human so an action is watchable, not instant. */
|
|
37
28
|
export function pace(ms) {
|
|
38
29
|
return new Promise((res) => nativeSetTimeout(res, ms));
|
|
39
30
|
}
|
|
@@ -1,42 +1,21 @@
|
|
|
1
1
|
import { PresenterMode } from '@reticlehq/core';
|
|
2
2
|
import { GlowPhase, BorderMode } from './presenter-config.js';
|
|
3
|
-
/** What the controller needs from the Presenter: the clock, the timing knobs, and a setMode callback. */
|
|
4
3
|
interface GlowDeps {
|
|
5
4
|
now: () => number;
|
|
6
5
|
idleAfterMs: number;
|
|
7
6
|
glowFadeMs: number;
|
|
8
7
|
borderMode: BorderMode;
|
|
9
|
-
/** Called when the glow fades to idle, so the Presenter clears the READING/ACTING chip. */
|
|
10
8
|
setMode: (mode: PresenterMode) => void;
|
|
11
9
|
}
|
|
12
|
-
/**
|
|
13
|
-
* The glow / activity state machine - drives the border shimmer + synthetic-cursor visibility from
|
|
14
|
-
* agent-activity timing. Extracted from presenter.ts so the controller file stays under the size cap;
|
|
15
|
-
* behavior is byte-for-byte the same. It holds references to the same `#glow`/`#cursor` nodes the
|
|
16
|
-
* Presenter does (both call setAttribute on those elements - the Presenter owns the SESSION border via
|
|
17
|
-
* data-on; this owns the BUSY shimmer via data-busy). A burst flips the glow IN once, holds it (the
|
|
18
|
-
* slow reticle-pulse keeps breathing - no per-action restart/strobe), then fades it OUT once after a quiet
|
|
19
|
-
* window.
|
|
20
|
-
*/
|
|
21
10
|
export declare class GlowController {
|
|
22
11
|
#private;
|
|
23
12
|
constructor(deps: GlowDeps);
|
|
24
|
-
/** Wire the glow + cursor elements after the Presenter mounts the DOM. */
|
|
25
13
|
setElements(glow: HTMLElement | undefined, cursor: HTMLElement | undefined): void;
|
|
26
|
-
/** Current glow phase (test/diagnostic accessor). */
|
|
27
14
|
phase(): GlowPhase;
|
|
28
|
-
/** Last activity timestamp - read by the Presenter's liveness heartbeat. */
|
|
29
15
|
lastActivityMs(): number;
|
|
30
|
-
/** Set the activity baseline WITHOUT entering busy (sessionStart / revive). */
|
|
31
16
|
resetActivity(ms: number): void;
|
|
32
|
-
/**
|
|
33
|
-
* Record agent activity. Idempotent while busy - only the first activity from idle/fading flips the
|
|
34
|
-
* glow on (no strobe). `ms` lets log read the clock exactly once per row.
|
|
35
|
-
*/
|
|
36
17
|
markActivity(ms?: number): void;
|
|
37
|
-
/** Re-arm the quiet-window idle check (kept for reticle.ts's finally block). */
|
|
38
18
|
scheduleIdle(): void;
|
|
39
|
-
/** Clear both timers (called from Presenter.destroy). */
|
|
40
19
|
teardown(): void;
|
|
41
20
|
}
|
|
42
21
|
export {};
|