@reticlehq/browser 2.0.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/LICENSE +202 -0
- package/README.md +17 -0
- package/dist/actions/actions-dom.d.ts +29 -0
- package/dist/actions/actions-dom.d.ts.map +1 -0
- package/dist/actions/actions-dom.js +153 -0
- package/dist/actions/actions-dom.js.map +1 -0
- package/dist/actions/actions.d.ts +92 -0
- package/dist/actions/actions.d.ts.map +1 -0
- package/dist/actions/actions.js +348 -0
- package/dist/actions/actions.js.map +1 -0
- package/dist/actions/scroll.d.ts +19 -0
- package/dist/actions/scroll.d.ts.map +1 -0
- package/dist/actions/scroll.js +44 -0
- package/dist/actions/scroll.js.map +1 -0
- package/dist/commands/commands.d.ts +5 -0
- package/dist/commands/commands.d.ts.map +1 -0
- package/dist/commands/commands.js +246 -0
- package/dist/commands/commands.js.map +1 -0
- package/dist/dom/a11y.d.ts +20 -0
- package/dist/dom/a11y.d.ts.map +1 -0
- package/dist/dom/a11y.js +263 -0
- package/dist/dom/a11y.js.map +1 -0
- package/dist/dom/auto-anchor.d.ts +66 -0
- package/dist/dom/auto-anchor.d.ts.map +1 -0
- package/dist/dom/auto-anchor.js +75 -0
- package/dist/dom/auto-anchor.js.map +1 -0
- package/dist/dom/dom-ignore.d.ts +7 -0
- package/dist/dom/dom-ignore.d.ts.map +1 -0
- package/dist/dom/dom-ignore.js +21 -0
- package/dist/dom/dom-ignore.js.map +1 -0
- package/dist/dom/query.d.ts +6 -0
- package/dist/dom/query.d.ts.map +1 -0
- package/dist/dom/query.js +236 -0
- package/dist/dom/query.js.map +1 -0
- package/dist/dom/refs.d.ts +15 -0
- package/dist/dom/refs.d.ts.map +1 -0
- package/dist/dom/refs.js +36 -0
- package/dist/dom/refs.js.map +1 -0
- package/dist/dom/snapshot.d.ts +22 -0
- package/dist/dom/snapshot.d.ts.map +1 -0
- package/dist/dom/snapshot.js +171 -0
- package/dist/dom/snapshot.js.map +1 -0
- package/dist/dom/theme.d.ts +26 -0
- package/dist/dom/theme.d.ts.map +1 -0
- package/dist/dom/theme.js +90 -0
- package/dist/dom/theme.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/observers/animation.d.ts +4 -0
- package/dist/observers/animation.d.ts.map +1 -0
- package/dist/observers/animation.js +35 -0
- package/dist/observers/animation.js.map +1 -0
- package/dist/observers/console.d.ts +4 -0
- package/dist/observers/console.d.ts.map +1 -0
- package/dist/observers/console.js +58 -0
- package/dist/observers/console.js.map +1 -0
- package/dist/observers/dom.d.ts +4 -0
- package/dist/observers/dom.d.ts.map +1 -0
- package/dist/observers/dom.js +96 -0
- package/dist/observers/dom.js.map +1 -0
- package/dist/observers/health.d.ts +8 -0
- package/dist/observers/health.d.ts.map +1 -0
- package/dist/observers/health.js +33 -0
- package/dist/observers/health.js.map +1 -0
- package/dist/observers/network.d.ts +11 -0
- package/dist/observers/network.d.ts.map +1 -0
- package/dist/observers/network.js +128 -0
- package/dist/observers/network.js.map +1 -0
- package/dist/observers/route.d.ts +4 -0
- package/dist/observers/route.d.ts.map +1 -0
- package/dist/observers/route.js +56 -0
- package/dist/observers/route.js.map +1 -0
- package/dist/observers/scroll.d.ts +4 -0
- package/dist/observers/scroll.d.ts.map +1 -0
- package/dist/observers/scroll.js +42 -0
- package/dist/observers/scroll.js.map +1 -0
- package/dist/observers/types.d.ts +6 -0
- package/dist/observers/types.d.ts.map +1 -0
- package/dist/observers/types.js +2 -0
- package/dist/observers/types.js.map +1 -0
- package/dist/presenter/overlay.d.ts +12 -0
- package/dist/presenter/overlay.d.ts.map +1 -0
- package/dist/presenter/overlay.js +31 -0
- package/dist/presenter/overlay.js.map +1 -0
- package/dist/presenter/presenter-config.d.ts +86 -0
- package/dist/presenter/presenter-config.d.ts.map +1 -0
- package/dist/presenter/presenter-config.js +51 -0
- package/dist/presenter/presenter-config.js.map +1 -0
- package/dist/presenter/presenter-controls.d.ts +74 -0
- package/dist/presenter/presenter-controls.d.ts.map +1 -0
- package/dist/presenter/presenter-controls.js +360 -0
- package/dist/presenter/presenter-controls.js.map +1 -0
- package/dist/presenter/presenter-effects.d.ts +14 -0
- package/dist/presenter/presenter-effects.d.ts.map +1 -0
- package/dist/presenter/presenter-effects.js +40 -0
- package/dist/presenter/presenter-effects.js.map +1 -0
- package/dist/presenter/presenter-glow.d.ts +43 -0
- package/dist/presenter/presenter-glow.d.ts.map +1 -0
- package/dist/presenter/presenter-glow.js +126 -0
- package/dist/presenter/presenter-glow.js.map +1 -0
- package/dist/presenter/presenter-log.d.ts +43 -0
- package/dist/presenter/presenter-log.d.ts.map +1 -0
- package/dist/presenter/presenter-log.js +118 -0
- package/dist/presenter/presenter-log.js.map +1 -0
- package/dist/presenter/presenter-run-state.d.ts +48 -0
- package/dist/presenter/presenter-run-state.d.ts.map +1 -0
- package/dist/presenter/presenter-run-state.js +37 -0
- package/dist/presenter/presenter-run-state.js.map +1 -0
- package/dist/presenter/presenter-styles.d.ts +7 -0
- package/dist/presenter/presenter-styles.d.ts.map +1 -0
- package/dist/presenter/presenter-styles.js +118 -0
- package/dist/presenter/presenter-styles.js.map +1 -0
- package/dist/presenter/presenter-tally.d.ts +20 -0
- package/dist/presenter/presenter-tally.d.ts.map +1 -0
- package/dist/presenter/presenter-tally.js +38 -0
- package/dist/presenter/presenter-tally.js.map +1 -0
- package/dist/presenter/presenter-verbs.d.ts +2 -0
- package/dist/presenter/presenter-verbs.d.ts.map +1 -0
- package/dist/presenter/presenter-verbs.js +29 -0
- package/dist/presenter/presenter-verbs.js.map +1 -0
- package/dist/presenter/presenter.d.ts +89 -0
- package/dist/presenter/presenter.d.ts.map +1 -0
- package/dist/presenter/presenter.js +428 -0
- package/dist/presenter/presenter.js.map +1 -0
- package/dist/recorder/recorder-styles.d.ts +12 -0
- package/dist/recorder/recorder-styles.d.ts.map +1 -0
- package/dist/recorder/recorder-styles.js +45 -0
- package/dist/recorder/recorder-styles.js.map +1 -0
- package/dist/recorder/recorder.d.ts +60 -0
- package/dist/recorder/recorder.d.ts.map +1 -0
- package/dist/recorder/recorder.js +436 -0
- package/dist/recorder/recorder.js.map +1 -0
- package/dist/registry/adapters.d.ts +31 -0
- package/dist/registry/adapters.d.ts.map +1 -0
- package/dist/registry/adapters.js +47 -0
- package/dist/registry/adapters.js.map +1 -0
- package/dist/registry/capabilities.d.ts +25 -0
- package/dist/registry/capabilities.d.ts.map +1 -0
- package/dist/registry/capabilities.js +48 -0
- package/dist/registry/capabilities.js.map +1 -0
- package/dist/registry/commit-and-signal.d.ts +9 -0
- package/dist/registry/commit-and-signal.d.ts.map +1 -0
- package/dist/registry/commit-and-signal.js +12 -0
- package/dist/registry/commit-and-signal.js.map +1 -0
- package/dist/registry/domains.d.ts +15 -0
- package/dist/registry/domains.d.ts.map +1 -0
- package/dist/registry/domains.js +19 -0
- package/dist/registry/domains.js.map +1 -0
- package/dist/registry/emitter.d.ts +28 -0
- package/dist/registry/emitter.d.ts.map +1 -0
- package/dist/registry/emitter.js +27 -0
- package/dist/registry/emitter.js.map +1 -0
- package/dist/registry/stores.d.ts +9 -0
- package/dist/registry/stores.d.ts.map +1 -0
- package/dist/registry/stores.js +30 -0
- package/dist/registry/stores.js.map +1 -0
- package/dist/reticle-presenter-helpers.d.ts +19 -0
- package/dist/reticle-presenter-helpers.d.ts.map +1 -0
- package/dist/reticle-presenter-helpers.js +81 -0
- package/dist/reticle-presenter-helpers.js.map +1 -0
- package/dist/reticle.d.ts +113 -0
- package/dist/reticle.d.ts.map +1 -0
- package/dist/reticle.js +379 -0
- package/dist/reticle.js.map +1 -0
- package/dist/review/annotator.d.ts +36 -0
- package/dist/review/annotator.d.ts.map +1 -0
- package/dist/review/annotator.js +321 -0
- package/dist/review/annotator.js.map +1 -0
- package/dist/review/mark-anchor.d.ts +23 -0
- package/dist/review/mark-anchor.d.ts.map +1 -0
- package/dist/review/mark-anchor.js +74 -0
- package/dist/review/mark-anchor.js.map +1 -0
- package/dist/security/serialization.d.ts +6 -0
- package/dist/security/serialization.d.ts.map +1 -0
- package/dist/security/serialization.js +103 -0
- package/dist/security/serialization.js.map +1 -0
- package/dist/timers/clock.d.ts +6 -0
- package/dist/timers/clock.d.ts.map +1 -0
- package/dist/timers/clock.js +80 -0
- package/dist/timers/clock.js.map +1 -0
- package/dist/timers/native-timers.d.ts +37 -0
- package/dist/timers/native-timers.d.ts.map +1 -0
- package/dist/timers/native-timers.js +79 -0
- package/dist/timers/native-timers.js.map +1 -0
- package/dist/transport/transport.d.ts +54 -0
- package/dist/transport/transport.d.ts.map +1 -0
- package/dist/transport/transport.js +182 -0
- package/dist/transport/transport.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { HumanControlKind, PresenterTone, SessionState } from '@reticlehq/core';
|
|
2
|
+
import { nativeSetTimeout, nativeClearTimeout } from '../timers/native-timers.js';
|
|
3
|
+
// Live-control panel: the two-way control surface inside the floating HUD — Pause/Resume + End
|
|
4
|
+
// (header), a message input + Send (footer), and the data-reticle-state visual machine. Split out of
|
|
5
|
+
// presenter.ts to keep both files under the 500-line cap (mirrors the presenter-log.ts split).
|
|
6
|
+
// All nodes carry data-reticle-* attrs so they're excluded from snapshots (see dom-ignore.ts). The
|
|
7
|
+
// strings here are presenter-only UI; the control kinds + state values reuse protocol constants.
|
|
8
|
+
/** data-reticle-state attribute on the overlay root; its value is always a SessionState. */
|
|
9
|
+
const DATA_RETICLE_STATE = 'data-reticle-state';
|
|
10
|
+
/** data-reticle-tone on the overlay root — waiting/ask/warn distinguishes how the agent handed back. */
|
|
11
|
+
const DATA_RETICLE_TONE = 'data-reticle-tone';
|
|
12
|
+
const DATA_ON = 'data-on';
|
|
13
|
+
const GLOW_OFF = '0';
|
|
14
|
+
/** Button copy (presenter-only UI; never a wire string). */
|
|
15
|
+
const CONTROL_LABEL = {
|
|
16
|
+
PAUSE: 'Pause',
|
|
17
|
+
RESUME: 'Resume',
|
|
18
|
+
END: 'End',
|
|
19
|
+
SEND: 'Send',
|
|
20
|
+
};
|
|
21
|
+
const INPUT_PLACEHOLDER = 'Tell the agent something…';
|
|
22
|
+
const PAUSED_BADGE_TEXT = 'PAUSED';
|
|
23
|
+
const ENDED_BANNER_TEXT = 'Session ended';
|
|
24
|
+
const COPY_LABEL = 'Copy run';
|
|
25
|
+
const EXPORT_LABEL = 'Export';
|
|
26
|
+
const FLOWS_LABEL = 'Replay a flow';
|
|
27
|
+
const COPIED_TEXT = 'Copied ✓';
|
|
28
|
+
/** Download filename for the exported run state. */
|
|
29
|
+
const RUN_FILENAME = 'reticle-run.json';
|
|
30
|
+
/** Border fade-out delay after a session ends (native timer; presenter-only tunable). */
|
|
31
|
+
export const ENDED_FADE_MS = 4000;
|
|
32
|
+
/** CSS for the control surface (injected with the rest of the presenter stylesheet). */
|
|
33
|
+
export const CONTROLS_CSS = `
|
|
34
|
+
[data-reticle-hud] .reticle-ctl{pointer-events:auto;cursor:pointer;flex:none;display:inline-flex;align-items:center;justify-content:center;
|
|
35
|
+
height:26px;padding:0 11px;border-radius:8px;border:1px solid var(--reticle-line);background:rgba(255,255,255,.04);
|
|
36
|
+
color:var(--reticle-muted);font-family:var(--reticle-font);font-size:11px;font-weight:500;letter-spacing:.01em;line-height:1;
|
|
37
|
+
transition:background .15s,color .15s,border-color .15s,transform .1s;}
|
|
38
|
+
[data-reticle-hud] .reticle-ctl:hover{color:var(--reticle-fg);background:rgba(255,255,255,.09);}
|
|
39
|
+
[data-reticle-hud] .reticle-ctl:active{transform:scale(.95);}
|
|
40
|
+
[data-reticle-hud] .reticle-ctl:disabled{opacity:.35;cursor:default;}
|
|
41
|
+
[data-reticle-hud] [data-reticle-end]{color:#ff9aa2;border-color:rgba(255,107,107,.22);}
|
|
42
|
+
[data-reticle-hud] [data-reticle-end]:hover{color:#ff7a7a;border-color:rgba(255,107,107,.5);background:rgba(255,107,107,.1);}
|
|
43
|
+
[data-reticle-hud] .reticle-badge{display:none;align-items:center;flex:none;font-weight:600;letter-spacing:.1em;font-size:9px;
|
|
44
|
+
color:var(--reticle-accent);border:1px solid var(--reticle-accent);background:var(--reticle-accent-soft);padding:2px 8px;border-radius:999px;}
|
|
45
|
+
[data-reticle-overlay][data-reticle-state="paused"] [data-reticle-badge]{display:inline-flex;}
|
|
46
|
+
[data-reticle-hud] [data-reticle-foot]{flex:none;padding:10px 12px 12px;border-top:1px solid var(--reticle-line2);background:rgba(0,0,0,.16);}
|
|
47
|
+
[data-reticle-hud] .reticle-composer{display:flex;align-items:flex-end;gap:6px;background:rgba(255,255,255,.05);
|
|
48
|
+
border:1px solid var(--reticle-line);border-radius:14px;padding:5px 6px 5px 14px;transition:border-color .15s,box-shadow .15s;}
|
|
49
|
+
[data-reticle-hud] .reticle-composer:focus-within{border-color:var(--reticle-accent);box-shadow:0 0 0 3px var(--reticle-accent-soft);}
|
|
50
|
+
[data-reticle-hud] .reticle-msg{flex:1;min-width:0;pointer-events:auto;background:transparent;border:none;outline:none;resize:none;
|
|
51
|
+
color:var(--reticle-fg);font-family:var(--reticle-font);font-size:13px;line-height:18px;height:18px;min-height:18px;max-height:96px;
|
|
52
|
+
padding:5px 0;overflow-y:auto;}
|
|
53
|
+
[data-reticle-hud] .reticle-msg::placeholder{color:var(--reticle-faint);}
|
|
54
|
+
[data-reticle-hud] .reticle-msg:disabled{opacity:.5;}
|
|
55
|
+
[data-reticle-hud] .reticle-send{flex:none;width:30px;height:30px;padding:0;border-radius:10px;border:none;cursor:pointer;pointer-events:auto;
|
|
56
|
+
background:var(--reticle-accent);color:#0b0d14;display:inline-flex;align-items:center;justify-content:center;transition:filter .15s,transform .1s;}
|
|
57
|
+
[data-reticle-hud] .reticle-send svg{display:block;}
|
|
58
|
+
[data-reticle-hud] .reticle-send:hover{filter:brightness(1.12);}
|
|
59
|
+
[data-reticle-hud] .reticle-send:active{transform:scale(.9);}
|
|
60
|
+
[data-reticle-hud] .reticle-send:disabled{opacity:.4;cursor:default;}
|
|
61
|
+
[data-reticle-hud] .reticle-banner{display:none;flex:none;padding:8px 15px;color:var(--reticle-accent);
|
|
62
|
+
font-size:11.5px;font-weight:500;border-bottom:1px solid var(--reticle-line2);background:var(--reticle-accent-soft);}
|
|
63
|
+
[data-reticle-overlay][data-reticle-state="ended"] [data-reticle-banner]{display:block;}
|
|
64
|
+
/* Export row: hidden during a live session; revealed when ended so the run can be copied/saved. */
|
|
65
|
+
[data-reticle-hud] .reticle-export{display:none;align-items:center;gap:8px;margin-top:9px;}
|
|
66
|
+
[data-reticle-overlay][data-reticle-state="ended"] [data-reticle-hud] .reticle-export{display:flex;}
|
|
67
|
+
[data-reticle-hud] .reticle-export-msg{color:var(--reticle-ok);font-size:11px;opacity:0;transition:opacity .15s;}
|
|
68
|
+
[data-reticle-hud] .reticle-export-msg[data-show="1"]{opacity:1;}
|
|
69
|
+
[data-reticle-overlay][data-reticle-state="paused"] [data-reticle-glow][data-on="1"]{animation:none;
|
|
70
|
+
box-shadow:inset 0 0 0 3px rgba(246,180,76,.9),inset 0 0 30px 6px rgba(246,180,76,.4);}
|
|
71
|
+
[data-reticle-overlay][data-reticle-state="ended"] [data-reticle-glow][data-on="1"]{animation:none;
|
|
72
|
+
box-shadow:inset 0 0 0 2px rgba(61,215,166,.55);}
|
|
73
|
+
/* Handoff tones tell the human the agent's mode at a glance. waiting = calm teal "your turn" (no
|
|
74
|
+
alarm); ask = amber "answer me" with a pulse; warn = amber "agent crashed" with a pulse. Each leads
|
|
75
|
+
the banner with an icon and overrides the calm ended-green accent. */
|
|
76
|
+
[data-reticle-overlay][data-reticle-tone="waiting"] [data-reticle-hud]{--reticle-accent:#38bdf8;--reticle-accent-soft:rgba(56,189,248,.16);}
|
|
77
|
+
[data-reticle-overlay][data-reticle-tone="waiting"] [data-reticle-banner]{font-weight:600;color:#7dd3fc;}
|
|
78
|
+
[data-reticle-overlay][data-reticle-tone="waiting"] [data-reticle-banner]::before{content:"\\270B ";}
|
|
79
|
+
[data-reticle-overlay][data-reticle-tone="waiting"] [data-reticle-glow][data-on="1"]{animation:none;
|
|
80
|
+
box-shadow:inset 0 0 0 2px rgba(56,189,248,.5);}
|
|
81
|
+
[data-reticle-overlay][data-reticle-tone="ask"] [data-reticle-hud],
|
|
82
|
+
[data-reticle-overlay][data-reticle-tone="warn"] [data-reticle-hud]{--reticle-accent:#fb923c;--reticle-accent-soft:rgba(251,146,60,.18);}
|
|
83
|
+
[data-reticle-overlay][data-reticle-tone="ask"] [data-reticle-banner],
|
|
84
|
+
[data-reticle-overlay][data-reticle-tone="warn"] [data-reticle-banner]{font-weight:600;color:#fdba74;}
|
|
85
|
+
[data-reticle-overlay][data-reticle-tone="ask"] [data-reticle-banner]::before{content:"\\2753 ";}
|
|
86
|
+
[data-reticle-overlay][data-reticle-tone="warn"] [data-reticle-banner]::before{content:"\\26A0\\FE0F ";}
|
|
87
|
+
[data-reticle-overlay][data-reticle-tone="ask"] [data-reticle-glow][data-on="1"],
|
|
88
|
+
[data-reticle-overlay][data-reticle-tone="warn"] [data-reticle-glow][data-on="1"]{animation:reticle-warn-pulse 1.5s ease-in-out infinite;
|
|
89
|
+
box-shadow:inset 0 0 0 2px rgba(251,146,60,.7);}
|
|
90
|
+
@keyframes reticle-warn-pulse{0%,100%{box-shadow:inset 0 0 0 2px rgba(251,146,60,.32);}
|
|
91
|
+
50%{box-shadow:inset 0 0 0 3px rgba(251,146,60,.85),inset 0 0 26px 5px rgba(251,146,60,.34);}}
|
|
92
|
+
/* Replay-a-flow row: the human re-runs a saved flow with no agent. Hidden until flows are pushed.
|
|
93
|
+
Bounded + self-scrolling: it sits between the flex:1 log and the flex:none composer, so without a
|
|
94
|
+
height cap a long flow list would squeeze the log to nothing and push the message input past the
|
|
95
|
+
panel's overflow:hidden clip. flex:none + max-height + overflow-y keep the log and input always
|
|
96
|
+
visible; extra flow chips scroll inside this section instead of growing the panel. */
|
|
97
|
+
[data-reticle-hud] .reticle-flows{display:none;flex:none;flex-wrap:wrap;align-content:flex-start;gap:6px;
|
|
98
|
+
padding:9px 12px;border-top:1px solid var(--reticle-line2);max-height:88px;overflow-y:auto;overscroll-behavior:contain;}
|
|
99
|
+
[data-reticle-hud] .reticle-flows[data-has="1"]{display:flex;}
|
|
100
|
+
[data-reticle-hud] .reticle-flows::-webkit-scrollbar{width:9px;}
|
|
101
|
+
[data-reticle-hud] .reticle-flows::-webkit-scrollbar-thumb{background:rgba(255,255,255,.14);border-radius:9px;border:2px solid transparent;background-clip:content-box;}
|
|
102
|
+
[data-reticle-hud] .reticle-flows-cap{flex:0 0 100%;margin-bottom:1px;color:var(--reticle-faint);font-size:9.5px;letter-spacing:.09em;text-transform:uppercase;}
|
|
103
|
+
[data-reticle-hud] .reticle-flow{pointer-events:auto;cursor:pointer;display:inline-flex;align-items:center;gap:5px;height:24px;padding:0 10px;
|
|
104
|
+
border-radius:7px;border:1px solid var(--reticle-line);background:rgba(255,255,255,.04);color:var(--reticle-muted);
|
|
105
|
+
font-family:var(--reticle-font);font-size:11px;font-weight:500;transition:background .15s,color .15s,border-color .15s,transform .1s;}
|
|
106
|
+
[data-reticle-hud] .reticle-flow:hover{color:var(--reticle-fg);background:var(--reticle-accent-soft);border-color:var(--reticle-accent);}
|
|
107
|
+
[data-reticle-hud] .reticle-flow:active{transform:scale(.95);}
|
|
108
|
+
`;
|
|
109
|
+
/** Header markup (controls + badge) injected into .reticle-hud-head, after the expand button. */
|
|
110
|
+
export const CONTROLS_HEAD_HTML = `<button type="button" data-reticle-pause class="reticle-ctl">${CONTROL_LABEL.PAUSE}</button><button type="button" data-reticle-end class="reticle-ctl">${CONTROL_LABEL.END}</button><span data-reticle-badge class="reticle-badge">${PAUSED_BADGE_TEXT}</span>`;
|
|
111
|
+
/** Banner markup (between head and log, hidden unless ended). */
|
|
112
|
+
export const CONTROLS_BANNER_HTML = `<div data-reticle-banner class="reticle-banner">${ENDED_BANNER_TEXT}</div>`;
|
|
113
|
+
/** Replay-a-flow row (between log and footer); buttons are filled in by setFlows once flows arrive. */
|
|
114
|
+
export const CONTROLS_FLOWS_HTML = `<div data-reticle-flows class="reticle-flows"><span class="reticle-flows-cap">${FLOWS_LABEL}</span></div>`;
|
|
115
|
+
/** Aesthetic send glyph (Feather "send" paper-plane). Inline SVG so it's crisp at any DPI. */
|
|
116
|
+
const SEND_ICON = `<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>`;
|
|
117
|
+
/** Footer markup: a rounded composer pill (input + icon Send), appended after the log div. */
|
|
118
|
+
export const CONTROLS_FOOT_HTML = `<div data-reticle-foot><div class="reticle-composer"><textarea data-reticle-input class="reticle-msg" rows="1" placeholder="${INPUT_PLACEHOLDER}"></textarea><button type="button" data-reticle-send class="reticle-send" aria-label="${CONTROL_LABEL.SEND}">${SEND_ICON}</button></div><div class="reticle-export"><button type="button" data-reticle-copy class="reticle-ctl">${COPY_LABEL}</button><button type="button" data-reticle-export class="reticle-ctl">${EXPORT_LABEL}</button><span data-reticle-export-msg class="reticle-export-msg"></span></div></div>`;
|
|
119
|
+
function queryControlRefs(root) {
|
|
120
|
+
return {
|
|
121
|
+
pauseBtn: root.querySelector('[data-reticle-pause]') ?? undefined,
|
|
122
|
+
endBtn: root.querySelector('[data-reticle-end]') ?? undefined,
|
|
123
|
+
input: root.querySelector('[data-reticle-input]') ?? undefined,
|
|
124
|
+
sendBtn: root.querySelector('[data-reticle-send]') ?? undefined,
|
|
125
|
+
banner: root.querySelector('[data-reticle-banner]') ?? undefined,
|
|
126
|
+
copyBtn: root.querySelector('[data-reticle-copy]') ?? undefined,
|
|
127
|
+
exportBtn: root.querySelector('[data-reticle-export]') ?? undefined,
|
|
128
|
+
exportMsg: root.querySelector('[data-reticle-export-msg]') ?? undefined,
|
|
129
|
+
flows: root.querySelector('[data-reticle-flows]') ?? undefined,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The live-control panel: owns the control element refs, the SessionState, the ended-fade timer,
|
|
134
|
+
* the DOM wiring, and the data-reticle-state visual machine. Split out of Presenter to keep both files
|
|
135
|
+
* under the 500-line cap. A click handler both emits a control AND optimistically applies state; the
|
|
136
|
+
* server's PRESENTER echo re-syncs via setState only (never emits) so a control is delivered once.
|
|
137
|
+
*/
|
|
138
|
+
export class ControlPanel {
|
|
139
|
+
#refs = {
|
|
140
|
+
pauseBtn: undefined,
|
|
141
|
+
endBtn: undefined,
|
|
142
|
+
input: undefined,
|
|
143
|
+
sendBtn: undefined,
|
|
144
|
+
banner: undefined,
|
|
145
|
+
copyBtn: undefined,
|
|
146
|
+
exportBtn: undefined,
|
|
147
|
+
exportMsg: undefined,
|
|
148
|
+
flows: undefined,
|
|
149
|
+
};
|
|
150
|
+
#state = SessionState.ACTIVE;
|
|
151
|
+
#fadeTimer;
|
|
152
|
+
#root;
|
|
153
|
+
#glow;
|
|
154
|
+
/** The full replayable-flow list from the last push; re-filtered per page on route change. */
|
|
155
|
+
#flowItems = [];
|
|
156
|
+
#host;
|
|
157
|
+
constructor(host) {
|
|
158
|
+
this.#host = host;
|
|
159
|
+
}
|
|
160
|
+
get state() {
|
|
161
|
+
return this.#state;
|
|
162
|
+
}
|
|
163
|
+
/** Query control refs out of the mounted root and bind the DOM listeners, then paint active. */
|
|
164
|
+
mount(root, glow) {
|
|
165
|
+
this.#root = root;
|
|
166
|
+
this.#glow = glow;
|
|
167
|
+
this.#refs = queryControlRefs(root);
|
|
168
|
+
this.#refs.pauseBtn?.addEventListener('click', () => this.#onPauseToggle());
|
|
169
|
+
this.#refs.endBtn?.addEventListener('click', () => this.#onEnd());
|
|
170
|
+
this.#refs.sendBtn?.addEventListener('click', () => this.#onSend());
|
|
171
|
+
this.#refs.input?.addEventListener('keydown', (e) => {
|
|
172
|
+
// Enter sends; Shift+Enter inserts a newline (falls through to the textarea's default).
|
|
173
|
+
if (e instanceof KeyboardEvent && e.key === 'Enter' && !e.shiftKey) {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
this.#onSend();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
this.#refs.input?.addEventListener('input', () => this.#autosize());
|
|
179
|
+
// Replay-a-flow: one ▶ click re-runs a saved flow (no agent). Delegated so it covers all chips.
|
|
180
|
+
this.#refs.flows?.addEventListener('click', (e) => {
|
|
181
|
+
const target = e.target;
|
|
182
|
+
if (!(target instanceof HTMLElement))
|
|
183
|
+
return;
|
|
184
|
+
const name = target.closest('[data-reticle-replay]')?.getAttribute('data-reticle-replay');
|
|
185
|
+
if (name !== null && name !== undefined && name.length > 0) {
|
|
186
|
+
this.#host.emit(HumanControlKind.REPLAY, name);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
this.#refs.copyBtn?.addEventListener('click', () => this.#onCopy());
|
|
190
|
+
this.#refs.exportBtn?.addEventListener('click', () => this.#onExport());
|
|
191
|
+
this.setState(SessionState.ACTIVE);
|
|
192
|
+
}
|
|
193
|
+
/** Serialize the run state to pretty JSON for Copy/Export. */
|
|
194
|
+
#runJson() {
|
|
195
|
+
return JSON.stringify(this.#host.runState(), null, 2);
|
|
196
|
+
}
|
|
197
|
+
/** Copy the run state to the clipboard (with a brief "Copied ✓" flash). */
|
|
198
|
+
#onCopy() {
|
|
199
|
+
void navigator.clipboard?.writeText(this.#runJson());
|
|
200
|
+
const msg = this.#refs.exportMsg;
|
|
201
|
+
if (msg !== undefined) {
|
|
202
|
+
msg.textContent = COPIED_TEXT;
|
|
203
|
+
msg.setAttribute('data-show', '1');
|
|
204
|
+
nativeSetTimeout(() => msg.setAttribute('data-show', '0'), 1600);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/** Download the run state as reticle-run.json. */
|
|
208
|
+
#onExport() {
|
|
209
|
+
const blob = new Blob([this.#runJson()], { type: 'application/json' });
|
|
210
|
+
const url = URL.createObjectURL(blob);
|
|
211
|
+
const a = document.createElement('a');
|
|
212
|
+
a.href = url;
|
|
213
|
+
a.download = RUN_FILENAME;
|
|
214
|
+
a.click();
|
|
215
|
+
URL.revokeObjectURL(url);
|
|
216
|
+
}
|
|
217
|
+
/** Clear any pending ended-fade timer (called from Presenter.destroy). */
|
|
218
|
+
teardown() {
|
|
219
|
+
if (this.#fadeTimer !== undefined)
|
|
220
|
+
nativeClearTimeout(this.#fadeTimer);
|
|
221
|
+
this.#fadeTimer = undefined;
|
|
222
|
+
}
|
|
223
|
+
#onPauseToggle() {
|
|
224
|
+
if (this.#state === SessionState.PAUSED) {
|
|
225
|
+
this.#host.emit(HumanControlKind.RESUME);
|
|
226
|
+
this.setState(SessionState.ACTIVE);
|
|
227
|
+
}
|
|
228
|
+
else if (this.#state === SessionState.ACTIVE) {
|
|
229
|
+
this.#host.emit(HumanControlKind.PAUSE);
|
|
230
|
+
this.setState(SessionState.PAUSED);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
#onEnd() {
|
|
234
|
+
if (this.#state === SessionState.ENDED)
|
|
235
|
+
return;
|
|
236
|
+
this.#host.emit(HumanControlKind.END);
|
|
237
|
+
this.setState(SessionState.ENDED);
|
|
238
|
+
}
|
|
239
|
+
#onSend() {
|
|
240
|
+
if (this.#state === SessionState.ENDED)
|
|
241
|
+
return;
|
|
242
|
+
const text = (this.#refs.input?.value ?? '').trim();
|
|
243
|
+
if (text.length === 0)
|
|
244
|
+
return;
|
|
245
|
+
this.#host.emit(HumanControlKind.MESSAGE, text);
|
|
246
|
+
this.#host.logHuman(text);
|
|
247
|
+
if (this.#refs.input !== undefined)
|
|
248
|
+
this.#refs.input.value = '';
|
|
249
|
+
this.#autosize();
|
|
250
|
+
}
|
|
251
|
+
/** Grow the composer to fit its content (up to the CSS max-height), then shrink back — soothing,
|
|
252
|
+
* no scrollbar until it's genuinely long. Driven on input and after a send clears the field. */
|
|
253
|
+
#autosize() {
|
|
254
|
+
const el = this.#refs.input;
|
|
255
|
+
if (el === undefined)
|
|
256
|
+
return;
|
|
257
|
+
el.style.height = 'auto';
|
|
258
|
+
el.style.height = `${String(Math.min(el.scrollHeight, 96))}px`;
|
|
259
|
+
}
|
|
260
|
+
/** Render the replayable-flow chips from the server push. Each ▶ click re-runs that flow, no agent.
|
|
261
|
+
* Takes the raw wire value and narrows it here (the panel is the consumer of this push). */
|
|
262
|
+
setFlows(flows) {
|
|
263
|
+
const list = Array.isArray(flows) ? flows : [];
|
|
264
|
+
this.#flowItems = list
|
|
265
|
+
.map((f) => {
|
|
266
|
+
if (typeof f === 'string')
|
|
267
|
+
return f.length > 0 ? { name: f } : null;
|
|
268
|
+
if (typeof f === 'object' && f !== null) {
|
|
269
|
+
const rec = f;
|
|
270
|
+
const name = rec['name'];
|
|
271
|
+
if (typeof name !== 'string' || name.length === 0)
|
|
272
|
+
return null;
|
|
273
|
+
const start = rec['start'];
|
|
274
|
+
return typeof start === 'string' && start.length > 0 ? { name, start } : { name };
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
})
|
|
278
|
+
.filter((c) => c !== null);
|
|
279
|
+
this.#renderFlows();
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Re-render the replay chips for the CURRENT page. A flow "starts here" iff its first step's anchor
|
|
283
|
+
* (a testid `start` hint) is present in the live DOM; flows with no start hint (signal/role-first,
|
|
284
|
+
* un-checkable) always show. Called on connect and on every route change so the list tracks the page —
|
|
285
|
+
* so you never see (or click) a flow that can't replay from where you are. Existing flows benefit
|
|
286
|
+
* without re-recording, since the hint is derived from the first step, not stored on the flow.
|
|
287
|
+
*/
|
|
288
|
+
refilterFlows() {
|
|
289
|
+
this.#renderFlows();
|
|
290
|
+
}
|
|
291
|
+
#renderFlows() {
|
|
292
|
+
const el = this.#refs.flows;
|
|
293
|
+
if (el === undefined)
|
|
294
|
+
return;
|
|
295
|
+
const doc = el.ownerDocument;
|
|
296
|
+
const testids = new Set(Array.from(doc.querySelectorAll('[data-testid]')).map((n) => n.getAttribute('data-testid')));
|
|
297
|
+
const visible = this.#flowItems.filter((f) => f.start === undefined || testids.has(f.start));
|
|
298
|
+
el.querySelectorAll('[data-reticle-replay]').forEach((b) => b.remove()); // rebuild, keep the caption
|
|
299
|
+
for (const flow of visible) {
|
|
300
|
+
const btn = doc.createElement('button');
|
|
301
|
+
btn.type = 'button';
|
|
302
|
+
btn.className = 'reticle-flow';
|
|
303
|
+
btn.setAttribute('data-reticle-replay', flow.name); // setAttribute → no markup injection from a name
|
|
304
|
+
btn.textContent = `▶ ${flow.name}`;
|
|
305
|
+
el.appendChild(btn);
|
|
306
|
+
}
|
|
307
|
+
el.setAttribute('data-has', visible.length > 0 ? '1' : '0');
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Drive the panel's visual state. Idempotent; NEVER emits a control — the shared path for both the
|
|
311
|
+
* optimistic local click and the authoritative server PRESENTER echo. Only the ended-border fade
|
|
312
|
+
* touches a clock, via the injected native timer.
|
|
313
|
+
*/
|
|
314
|
+
setState(state, text, tone) {
|
|
315
|
+
this.#state = state;
|
|
316
|
+
this.#root?.setAttribute(DATA_RETICLE_STATE, state);
|
|
317
|
+
// A handoff tone (waiting/ask/warn) drives a distinct panel treatment; calm/undefined = a plain end.
|
|
318
|
+
const handoff = tone !== undefined && tone !== PresenterTone.CALM;
|
|
319
|
+
if (handoff)
|
|
320
|
+
this.#root?.setAttribute(DATA_RETICLE_TONE, tone);
|
|
321
|
+
else
|
|
322
|
+
this.#root?.removeAttribute(DATA_RETICLE_TONE);
|
|
323
|
+
if (this.#fadeTimer !== undefined) {
|
|
324
|
+
nativeClearTimeout(this.#fadeTimer);
|
|
325
|
+
this.#fadeTimer = undefined;
|
|
326
|
+
}
|
|
327
|
+
const refs = this.#refs;
|
|
328
|
+
const ended = state === SessionState.ENDED;
|
|
329
|
+
if (refs.pauseBtn !== undefined) {
|
|
330
|
+
refs.pauseBtn.textContent =
|
|
331
|
+
state === SessionState.PAUSED ? CONTROL_LABEL.RESUME : CONTROL_LABEL.PAUSE;
|
|
332
|
+
refs.pauseBtn.disabled = ended;
|
|
333
|
+
}
|
|
334
|
+
if (refs.endBtn !== undefined)
|
|
335
|
+
refs.endBtn.disabled = ended;
|
|
336
|
+
if (refs.sendBtn !== undefined)
|
|
337
|
+
refs.sendBtn.disabled = ended;
|
|
338
|
+
if (refs.input !== undefined)
|
|
339
|
+
refs.input.disabled = ended;
|
|
340
|
+
// A calm end leads with "Session ended"; a handoff (waiting/ask/warn) leads with the notice itself,
|
|
341
|
+
// since the toned styling already conveys "ended" and the notice is the actionable headline.
|
|
342
|
+
if (refs.banner !== undefined) {
|
|
343
|
+
const summary = text !== undefined && text.trim().length > 0 ? text.trim() : '';
|
|
344
|
+
refs.banner.textContent =
|
|
345
|
+
handoff && summary.length > 0
|
|
346
|
+
? summary
|
|
347
|
+
: `${ENDED_BANNER_TEXT}${summary.length > 0 ? ` · ${summary}` : ''}`;
|
|
348
|
+
}
|
|
349
|
+
if (ended) {
|
|
350
|
+
// End the run: fade out the page BORDER (testing is over) but KEEP the panel so the human can
|
|
351
|
+
// read the result and Copy/Export the run state. The composer disables; the export row reveals.
|
|
352
|
+
const glow = this.#glow;
|
|
353
|
+
this.#fadeTimer = nativeSetTimeout(() => {
|
|
354
|
+
glow?.setAttribute(DATA_ON, GLOW_OFF);
|
|
355
|
+
}, this.#host.endedFadeMs);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
export { DATA_RETICLE_STATE };
|
|
360
|
+
//# sourceMappingURL=presenter-controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presenter-controls.js","sourceRoot":"","sources":["../../src/presenter/presenter-controls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
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
|
+
export declare function ringAround(ring: HTMLElement | undefined, rect: DOMRect): void;
|
|
10
|
+
/** Spawn a one-shot click ripple at a viewport point (self-removing). */
|
|
11
|
+
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
|
+
export declare function pace(ms: number): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=presenter-effects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presenter-effects.d.ts","sourceRoot":"","sources":["../../src/presenter/presenter-effects.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAItF;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAQ7E;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAQrF;AAED,iEAAiE;AACjE,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9C"}
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
export function moveCursor(cursor, x, y) {
|
|
9
|
+
if (cursor === undefined)
|
|
10
|
+
return;
|
|
11
|
+
cursor.setAttribute('data-on', '1');
|
|
12
|
+
cursor.style.transform = `translate(${String(x)}px, ${String(y)}px)`;
|
|
13
|
+
}
|
|
14
|
+
/** Briefly draw the green focus ring around an element's box, then fade it. */
|
|
15
|
+
export function ringAround(ring, rect) {
|
|
16
|
+
if (ring === undefined)
|
|
17
|
+
return;
|
|
18
|
+
ring.style.left = `${String(rect.left - 4)}px`;
|
|
19
|
+
ring.style.top = `${String(rect.top - 4)}px`;
|
|
20
|
+
ring.style.width = `${String(rect.width + 8)}px`;
|
|
21
|
+
ring.style.height = `${String(rect.height + 8)}px`;
|
|
22
|
+
ring.setAttribute('data-on', '1');
|
|
23
|
+
nativeSetTimeout(() => ring.setAttribute('data-on', '0'), 700);
|
|
24
|
+
}
|
|
25
|
+
/** Spawn a one-shot click ripple at a viewport point (self-removing). */
|
|
26
|
+
export function spawnRipple(root, x, y) {
|
|
27
|
+
if (root === undefined)
|
|
28
|
+
return;
|
|
29
|
+
const r = document.createElement('div');
|
|
30
|
+
r.setAttribute('data-reticle-ripple', '');
|
|
31
|
+
r.style.left = `${String(x)}px`;
|
|
32
|
+
r.style.top = `${String(y)}px`;
|
|
33
|
+
root.appendChild(r);
|
|
34
|
+
nativeSetTimeout(() => r.remove(), 520);
|
|
35
|
+
}
|
|
36
|
+
/** Pace for the human so an action is watchable, not instant. */
|
|
37
|
+
export function pace(ms) {
|
|
38
|
+
return new Promise((res) => nativeSetTimeout(res, ms));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=presenter-effects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presenter-effects.js","sourceRoot":"","sources":["../../src/presenter/presenter-effects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAE9D;;;;GAIG;AAEH,gEAAgE;AAChE,MAAM,UAAU,UAAU,CAAC,MAA+B,EAAE,CAAS,EAAE,CAAS;IAC9E,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO;IACjC,MAAM,CAAC,YAAY,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IACpC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,aAAa,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;AACvE,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,UAAU,CAAC,IAA6B,EAAE,IAAa;IACrE,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC;IAC/C,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC;IAC7C,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC;IACjD,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC;IACnD,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAClC,gBAAgB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AACjE,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,WAAW,CAAC,IAA6B,EAAE,CAAS,EAAE,CAAS;IAC7E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO;IAC/B,MAAM,CAAC,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC,YAAY,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAChC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/B,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACpB,gBAAgB,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,IAAI,CAAC,EAAU;IAC7B,OAAO,IAAI,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { PresenterMode } from '@reticlehq/core';
|
|
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
|
+
interface GlowDeps {
|
|
5
|
+
now: () => number;
|
|
6
|
+
idleAfterMs: number;
|
|
7
|
+
glowFadeMs: number;
|
|
8
|
+
borderMode: BorderMode;
|
|
9
|
+
/** Called when the glow fades to idle, so the Presenter clears the READING/ACTING chip. */
|
|
10
|
+
setMode: (mode: PresenterMode) => void;
|
|
11
|
+
}
|
|
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
|
+
export declare class GlowController {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(deps: GlowDeps);
|
|
24
|
+
/** Wire the glow + cursor elements after the Presenter mounts the DOM. */
|
|
25
|
+
setElements(glow: HTMLElement | undefined, cursor: HTMLElement | undefined): void;
|
|
26
|
+
/** Current glow phase (test/diagnostic accessor). */
|
|
27
|
+
phase(): GlowPhase;
|
|
28
|
+
/** Last activity timestamp — read by the Presenter's liveness heartbeat. */
|
|
29
|
+
lastActivityMs(): number;
|
|
30
|
+
/** Set the activity baseline WITHOUT entering busy (sessionStart / revive). */
|
|
31
|
+
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
|
+
markActivity(ms?: number): void;
|
|
37
|
+
/** Re-arm the quiet-window idle check (kept for reticle.ts's finally block). */
|
|
38
|
+
scheduleIdle(): void;
|
|
39
|
+
/** Clear both timers (called from Presenter.destroy). */
|
|
40
|
+
teardown(): void;
|
|
41
|
+
}
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=presenter-glow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presenter-glow.d.ts","sourceRoot":"","sources":["../../src/presenter/presenter-glow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EACL,SAAS,EACT,UAAU,EAOX,MAAM,uBAAuB,CAAC;AAE/B,yGAAyG;AACzG,UAAU,QAAQ;IAChB,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC;IACvB,2FAA2F;IAC3F,OAAO,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CACxC;AAED;;;;;;;;GAQG;AACH,qBAAa,cAAc;;gBAab,IAAI,EAAE,QAAQ;IAQ1B,0EAA0E;IAC1E,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI;IAKjF,qDAAqD;IACrD,KAAK,IAAI,SAAS;IAIlB,4EAA4E;IAC5E,cAAc,IAAI,MAAM;IAIxB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACH,YAAY,CAAC,EAAE,GAAE,MAAoB,GAAG,IAAI;IAM5C,gFAAgF;IAChF,YAAY,IAAI,IAAI;IAIpB,yDAAyD;IACzD,QAAQ,IAAI,IAAI;CA6DjB"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { PresenterMode } from '@reticlehq/core';
|
|
2
|
+
import { nativeSetTimeout, nativeClearTimeout } from '../timers/native-timers.js';
|
|
3
|
+
import { GlowPhase, BorderMode, DATA_BUSY, BUSY_ON, BUSY_OFF, GLOW_ON, GLOW_OFF, DATA_ON, } from './presenter-config.js';
|
|
4
|
+
/**
|
|
5
|
+
* The glow / activity state machine — drives the border shimmer + synthetic-cursor visibility from
|
|
6
|
+
* agent-activity timing. Extracted from presenter.ts so the controller file stays under the size cap;
|
|
7
|
+
* behavior is byte-for-byte the same. It holds references to the same `#glow`/`#cursor` nodes the
|
|
8
|
+
* Presenter does (both call setAttribute on those elements — the Presenter owns the SESSION border via
|
|
9
|
+
* data-on; this owns the BUSY shimmer via data-busy). A burst flips the glow IN once, holds it (the
|
|
10
|
+
* slow reticle-pulse keeps breathing — no per-action restart/strobe), then fades it OUT once after a quiet
|
|
11
|
+
* window.
|
|
12
|
+
*/
|
|
13
|
+
export class GlowController {
|
|
14
|
+
#phase = GlowPhase.IDLE;
|
|
15
|
+
#lastActivityMs = 0;
|
|
16
|
+
#idleCheckTimer;
|
|
17
|
+
#fadeTimer;
|
|
18
|
+
#glow;
|
|
19
|
+
#cursor;
|
|
20
|
+
#now;
|
|
21
|
+
#idleAfterMs;
|
|
22
|
+
#glowFadeMs;
|
|
23
|
+
#borderMode;
|
|
24
|
+
#setMode;
|
|
25
|
+
constructor(deps) {
|
|
26
|
+
this.#now = deps.now;
|
|
27
|
+
this.#idleAfterMs = deps.idleAfterMs;
|
|
28
|
+
this.#glowFadeMs = deps.glowFadeMs;
|
|
29
|
+
this.#borderMode = deps.borderMode;
|
|
30
|
+
this.#setMode = deps.setMode;
|
|
31
|
+
}
|
|
32
|
+
/** Wire the glow + cursor elements after the Presenter mounts the DOM. */
|
|
33
|
+
setElements(glow, cursor) {
|
|
34
|
+
this.#glow = glow;
|
|
35
|
+
this.#cursor = cursor;
|
|
36
|
+
}
|
|
37
|
+
/** Current glow phase (test/diagnostic accessor). */
|
|
38
|
+
phase() {
|
|
39
|
+
return this.#phase;
|
|
40
|
+
}
|
|
41
|
+
/** Last activity timestamp — read by the Presenter's liveness heartbeat. */
|
|
42
|
+
lastActivityMs() {
|
|
43
|
+
return this.#lastActivityMs;
|
|
44
|
+
}
|
|
45
|
+
/** Set the activity baseline WITHOUT entering busy (sessionStart / revive). */
|
|
46
|
+
resetActivity(ms) {
|
|
47
|
+
this.#lastActivityMs = ms;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Record agent activity. Idempotent while busy — only the first activity from idle/fading flips the
|
|
51
|
+
* glow on (no strobe). `ms` lets log() read the clock exactly once per row.
|
|
52
|
+
*/
|
|
53
|
+
markActivity(ms = this.#now()) {
|
|
54
|
+
this.#lastActivityMs = ms;
|
|
55
|
+
if (this.#phase === GlowPhase.IDLE || this.#phase === GlowPhase.FADING)
|
|
56
|
+
this.#enterBusy();
|
|
57
|
+
this.#armIdleCheck();
|
|
58
|
+
}
|
|
59
|
+
/** Re-arm the quiet-window idle check (kept for reticle.ts's finally block). */
|
|
60
|
+
scheduleIdle() {
|
|
61
|
+
this.#armIdleCheck();
|
|
62
|
+
}
|
|
63
|
+
/** Clear both timers (called from Presenter.destroy). */
|
|
64
|
+
teardown() {
|
|
65
|
+
if (this.#idleCheckTimer !== undefined)
|
|
66
|
+
nativeClearTimeout(this.#idleCheckTimer);
|
|
67
|
+
if (this.#fadeTimer !== undefined)
|
|
68
|
+
nativeClearTimeout(this.#fadeTimer);
|
|
69
|
+
this.#idleCheckTimer = undefined;
|
|
70
|
+
this.#fadeTimer = undefined;
|
|
71
|
+
}
|
|
72
|
+
#enterBusy() {
|
|
73
|
+
if (this.#fadeTimer !== undefined) {
|
|
74
|
+
nativeClearTimeout(this.#fadeTimer);
|
|
75
|
+
this.#fadeTimer = undefined;
|
|
76
|
+
}
|
|
77
|
+
this.#phase = GlowPhase.BUSY;
|
|
78
|
+
if (this.#borderMode === BorderMode.SESSION) {
|
|
79
|
+
// Session mode: modulate only the shimmer; the base border (data-on) stays session-owned.
|
|
80
|
+
this.#glow?.setAttribute(DATA_BUSY, BUSY_ON);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// back-compat: the busy machine owns the base border.
|
|
84
|
+
this.#glow?.setAttribute(DATA_ON, GLOW_ON);
|
|
85
|
+
}
|
|
86
|
+
// The HUD/log is session-owned (sessionStart/End), NOT toggled here — it stays put on idle.
|
|
87
|
+
this.#cursor?.setAttribute(DATA_ON, GLOW_ON);
|
|
88
|
+
}
|
|
89
|
+
#armIdleCheck() {
|
|
90
|
+
if (this.#idleCheckTimer !== undefined)
|
|
91
|
+
nativeClearTimeout(this.#idleCheckTimer);
|
|
92
|
+
this.#idleCheckTimer = nativeSetTimeout(() => this.#checkIdle(), this.#idleAfterMs);
|
|
93
|
+
}
|
|
94
|
+
#checkIdle() {
|
|
95
|
+
this.#idleCheckTimer = undefined;
|
|
96
|
+
if (this.#phase !== GlowPhase.BUSY)
|
|
97
|
+
return;
|
|
98
|
+
const quietFor = this.#now() - this.#lastActivityMs;
|
|
99
|
+
if (quietFor < this.#idleAfterMs) {
|
|
100
|
+
// Activity landed during the wait; re-arm for only the remaining quiet time.
|
|
101
|
+
this.#idleCheckTimer = nativeSetTimeout(() => this.#checkIdle(), this.#idleAfterMs - quietFor);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this.#beginFade();
|
|
105
|
+
}
|
|
106
|
+
#beginFade() {
|
|
107
|
+
this.#phase = GlowPhase.FADING;
|
|
108
|
+
if (this.#borderMode === BorderMode.SESSION) {
|
|
109
|
+
// Session mode: relax the shimmer only; the base border stays on.
|
|
110
|
+
this.#glow?.setAttribute(DATA_BUSY, BUSY_OFF);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// back-compat: fade the base border out.
|
|
114
|
+
this.#glow?.setAttribute(DATA_ON, GLOW_OFF);
|
|
115
|
+
}
|
|
116
|
+
// Do NOT hide the HUD/log on idle — it persists for the whole session (sessionStart/End).
|
|
117
|
+
this.#cursor?.setAttribute(DATA_ON, GLOW_OFF);
|
|
118
|
+
this.#setMode(PresenterMode.IDLE); // clear the READING/ACTING chip when going quiet
|
|
119
|
+
this.#fadeTimer = nativeSetTimeout(() => {
|
|
120
|
+
this.#fadeTimer = undefined;
|
|
121
|
+
if (this.#phase === GlowPhase.FADING)
|
|
122
|
+
this.#phase = GlowPhase.IDLE;
|
|
123
|
+
}, this.#glowFadeMs);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=presenter-glow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presenter-glow.js","sourceRoot":"","sources":["../../src/presenter/presenter-glow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAClF,OAAO,EACL,SAAS,EACT,UAAU,EACV,SAAS,EACT,OAAO,EACP,QAAQ,EACR,OAAO,EACP,QAAQ,EACR,OAAO,GACR,MAAM,uBAAuB,CAAC;AAY/B;;;;;;;;GAQG;AACH,MAAM,OAAO,cAAc;IACzB,MAAM,GAAc,SAAS,CAAC,IAAI,CAAC;IACnC,eAAe,GAAG,CAAC,CAAC;IACpB,eAAe,CAAqB;IACpC,UAAU,CAAqB;IAC/B,KAAK,CAA0B;IAC/B,OAAO,CAA0B;IACxB,IAAI,CAAe;IACnB,YAAY,CAAS;IACrB,WAAW,CAAS;IACpB,WAAW,CAAa;IACxB,QAAQ,CAAgC;IAEjD,YAAY,IAAc;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACnC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACnC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC;IAC/B,CAAC;IAED,0EAA0E;IAC1E,WAAW,CAAC,IAA6B,EAAE,MAA+B;QACxE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED,qDAAqD;IACrD,KAAK;QACH,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,4EAA4E;IAC5E,cAAc;QACZ,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;IAED,+EAA+E;IAC/E,aAAa,CAAC,EAAU;QACtB,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,KAAa,IAAI,CAAC,IAAI,EAAE;QACnC,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM;YAAE,IAAI,CAAC,UAAU,EAAE,CAAC;QAC1F,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAED,gFAAgF;IAChF,YAAY;QACV,IAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC;IAED,yDAAyD;IACzD,QAAQ;QACN,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS;YAAE,kBAAkB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACjF,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS;YAAE,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvE,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAClC,kBAAkB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACpC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC;QAC7B,IAAI,IAAI,CAAC,WAAW,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;YAC5C,0FAA0F;YAC1F,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,sDAAsD;YACtD,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC7C,CAAC;QACD,4FAA4F;QAC5F,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,aAAa;QACX,IAAI,IAAI,CAAC,eAAe,KAAK,SAAS;YAAE,kBAAkB,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACjF,IAAI,CAAC,eAAe,GAAG,gBAAgB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IACtF,CAAC;IAED,UAAU;QACR,IAAI,CAAC,eAAe,GAAG,SAAS,CAAC;QACjC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,IAAI;YAAE,OAAO;QAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC;QACpD,IAAI,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YACjC,6EAA6E;YAC7E,IAAI,CAAC,eAAe,GAAG,gBAAgB,CACrC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,EACvB,IAAI,CAAC,YAAY,GAAG,QAAQ,CAC7B,CAAC;YACF,OAAO;QACT,CAAC;QACD,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,UAAU;QACR,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QAC/B,IAAI,IAAI,CAAC,WAAW,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;YAC5C,kEAAkE;YAClE,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,yCAAyC;YACzC,IAAI,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;QACD,0FAA0F;QAC1F,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,iDAAiD;QACpF,IAAI,CAAC,UAAU,GAAG,gBAAgB,CAAC,GAAG,EAAE;YACtC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM;gBAAE,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC;QACrE,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACvB,CAAC;CACF"}
|