@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.
Files changed (196) hide show
  1. package/dist/actions/actions-dom.d.ts +0 -20
  2. package/dist/actions/actions-dom.js +0 -66
  3. package/dist/actions/actions-press.d.ts +10 -0
  4. package/dist/actions/actions-press.js +77 -0
  5. package/dist/actions/actions.d.ts +0 -89
  6. package/dist/actions/actions.js +24 -358
  7. package/dist/actions/appeared-text.d.ts +0 -20
  8. package/dist/actions/appeared-text.js +0 -36
  9. package/dist/actions/danger-context.d.ts +3 -0
  10. package/dist/actions/danger-context.js +39 -0
  11. package/dist/actions/scroll.d.ts +0 -9
  12. package/dist/actions/scroll.js +0 -7
  13. package/dist/actions/synthetic-input.d.ts +0 -7
  14. package/dist/actions/synthetic-input.js +0 -27
  15. package/dist/actions/value-input.d.ts +0 -30
  16. package/dist/actions/value-input.js +0 -33
  17. package/dist/commands/commands.d.ts +0 -2
  18. package/dist/commands/commands.js +0 -67
  19. package/dist/connect-options.d.ts +2 -96
  20. package/dist/dom/a11y.d.ts +0 -29
  21. package/dist/dom/a11y.js +1 -88
  22. package/dist/dom/auto-anchor.d.ts +0 -40
  23. package/dist/dom/auto-anchor.js +0 -33
  24. package/dist/dom/chart.d.ts +0 -61
  25. package/dist/dom/chart.js +0 -60
  26. package/dist/dom/desktop-capture.d.ts +0 -30
  27. package/dist/dom/desktop-capture.js +0 -72
  28. package/dist/dom/dom-ignore.d.ts +0 -30
  29. package/dist/dom/dom-ignore.js +0 -47
  30. package/dist/dom/occlusion.d.ts +0 -7
  31. package/dist/dom/occlusion.js +0 -7
  32. package/dist/dom/query.d.ts +0 -14
  33. package/dist/dom/query.js +48 -214
  34. package/dist/dom/realm.d.ts +0 -20
  35. package/dist/dom/realm.js +0 -28
  36. package/dist/dom/refs.d.ts +0 -76
  37. package/dist/dom/refs.js +2 -133
  38. package/dist/dom/shadow-registry.d.ts +0 -33
  39. package/dist/dom/shadow-registry.js +2 -72
  40. package/dist/dom/snapshot.d.ts +2 -43
  41. package/dist/dom/snapshot.js +26 -151
  42. package/dist/dom/source.d.ts +0 -44
  43. package/dist/dom/source.js +0 -45
  44. package/dist/dom/theme.d.ts +0 -37
  45. package/dist/dom/theme.js +2 -52
  46. package/dist/dom/virtualized.d.ts +0 -17
  47. package/dist/dom/virtualized.js +0 -27
  48. package/dist/edit-epoch.d.ts +0 -22
  49. package/dist/edit-epoch.js +0 -52
  50. package/dist/index.js +1 -23
  51. package/dist/observers/animation.d.ts +0 -1
  52. package/dist/observers/animation.js +0 -3
  53. package/dist/observers/blind-spots.d.ts +0 -26
  54. package/dist/observers/blind-spots.js +2 -61
  55. package/dist/observers/console.d.ts +1 -2
  56. package/dist/observers/console.js +41 -30
  57. package/dist/observers/context-open.d.ts +1 -11
  58. package/dist/observers/context-open.js +10 -14
  59. package/dist/observers/departure.d.ts +3 -0
  60. package/dist/observers/departure.js +52 -0
  61. package/dist/observers/dialogs.d.ts +2 -0
  62. package/dist/observers/dialogs.js +39 -0
  63. package/dist/observers/dom.js +0 -44
  64. package/dist/observers/download.d.ts +1 -2
  65. package/dist/observers/download.js +38 -55
  66. package/dist/observers/focus.d.ts +0 -6
  67. package/dist/observers/focus.js +0 -7
  68. package/dist/observers/frames.d.ts +0 -9
  69. package/dist/observers/frames.js +1 -28
  70. package/dist/observers/health.d.ts +0 -5
  71. package/dist/observers/health.js +1 -33
  72. package/dist/observers/install-all.d.ts +2 -0
  73. package/dist/observers/install-all.js +26 -27
  74. package/dist/observers/ipc.d.ts +0 -25
  75. package/dist/observers/ipc.js +0 -65
  76. package/dist/observers/navigation.d.ts +2 -0
  77. package/dist/observers/navigation.js +33 -0
  78. package/dist/observers/network-body.d.ts +5 -28
  79. package/dist/observers/network-body.js +11 -108
  80. package/dist/observers/network-redact.d.ts +1 -20
  81. package/dist/observers/network-redact.js +1 -80
  82. package/dist/observers/network-stream.d.ts +0 -21
  83. package/dist/observers/network-stream.js +1 -44
  84. package/dist/observers/network.d.ts +2 -26
  85. package/dist/observers/network.js +177 -294
  86. package/dist/observers/perf.d.ts +0 -10
  87. package/dist/observers/perf.js +0 -16
  88. package/dist/observers/route.d.ts +1 -2
  89. package/dist/observers/route.js +13 -19
  90. package/dist/observers/scroll.d.ts +0 -1
  91. package/dist/observers/scroll.js +0 -9
  92. package/dist/observers/sdk-failure.d.ts +1 -9
  93. package/dist/observers/sdk-failure.js +1 -30
  94. package/dist/observers/state.d.ts +1 -11
  95. package/dist/observers/state.js +13 -41
  96. package/dist/observers/storage.d.ts +0 -14
  97. package/dist/observers/storage.js +10 -34
  98. package/dist/observers/types.d.ts +0 -15
  99. package/dist/observers/types.js +0 -14
  100. package/dist/patching/capture-method.d.ts +0 -24
  101. package/dist/patching/capture-method.js +0 -26
  102. package/dist/presenter/overlay.d.ts +0 -1
  103. package/dist/presenter/overlay.js +0 -1
  104. package/dist/presenter/presenter-brand.d.ts +0 -19
  105. package/dist/presenter/presenter-brand.js +0 -21
  106. package/dist/presenter/presenter-config.d.ts +1 -105
  107. package/dist/presenter/presenter-config.js +2 -91
  108. package/dist/presenter/presenter-controls.d.ts +0 -29
  109. package/dist/presenter/presenter-controls.js +3 -131
  110. package/dist/presenter/presenter-dock-layout.d.ts +0 -3
  111. package/dist/presenter/presenter-dock-layout.js +0 -4
  112. package/dist/presenter/presenter-drag.d.ts +2 -24
  113. package/dist/presenter/presenter-drag.js +0 -26
  114. package/dist/presenter/presenter-effects.d.ts +0 -9
  115. package/dist/presenter/presenter-effects.js +0 -9
  116. package/dist/presenter/presenter-glow.d.ts +0 -21
  117. package/dist/presenter/presenter-glow.js +1 -27
  118. package/dist/presenter/presenter-heroicons-data.d.ts +0 -8
  119. package/dist/presenter/presenter-heroicons-data.js +0 -8
  120. package/dist/presenter/presenter-hud-chrome.d.ts +0 -10
  121. package/dist/presenter/presenter-hud-chrome.js +0 -10
  122. package/dist/presenter/presenter-icons.d.ts +0 -6
  123. package/dist/presenter/presenter-icons.js +0 -6
  124. package/dist/presenter/presenter-log.d.ts +0 -38
  125. package/dist/presenter/presenter-log.js +1 -45
  126. package/dist/presenter/presenter-report-copy.d.ts +5 -66
  127. package/dist/presenter/presenter-report-copy.js +5 -66
  128. package/dist/presenter/presenter-report-styles.d.ts +0 -6
  129. package/dist/presenter/presenter-report-styles.js +6 -6
  130. package/dist/presenter/presenter-report.d.ts +4 -7
  131. package/dist/presenter/presenter-report.js +18 -76
  132. package/dist/presenter/presenter-run-state.d.ts +0 -7
  133. package/dist/presenter/presenter-run-state.js +0 -5
  134. package/dist/presenter/presenter-settings-styles.d.ts +0 -1
  135. package/dist/presenter/presenter-settings-styles.js +0 -1
  136. package/dist/presenter/presenter-settings.d.ts +1 -23
  137. package/dist/presenter/presenter-settings.js +1 -31
  138. package/dist/presenter/presenter-shell-styles.js +2 -5
  139. package/dist/presenter/presenter-shell.d.ts +2 -18
  140. package/dist/presenter/presenter-shell.js +1 -73
  141. package/dist/presenter/presenter-styles.d.ts +0 -9
  142. package/dist/presenter/presenter-styles.js +0 -9
  143. package/dist/presenter/presenter-tally.d.ts +0 -5
  144. package/dist/presenter/presenter-tally.js +0 -4
  145. package/dist/presenter/presenter-test-helpers.d.ts +0 -2
  146. package/dist/presenter/presenter-test-helpers.js +0 -2
  147. package/dist/presenter/presenter-verbs.js +0 -7
  148. package/dist/presenter/presenter-workspace.d.ts +0 -4
  149. package/dist/presenter/presenter-workspace.js +1 -8
  150. package/dist/presenter/presenter.d.ts +0 -60
  151. package/dist/presenter/presenter.js +4 -118
  152. package/dist/recorder/recorder-styles.d.ts +0 -6
  153. package/dist/recorder/recorder-styles.js +0 -6
  154. package/dist/recorder/recorder.d.ts +0 -34
  155. package/dist/recorder/recorder.js +0 -49
  156. package/dist/registry/adapters.d.ts +0 -10
  157. package/dist/registry/adapters.js +0 -10
  158. package/dist/registry/auto-testids.d.ts +0 -19
  159. package/dist/registry/auto-testids.js +1 -21
  160. package/dist/registry/capabilities.d.ts +0 -31
  161. package/dist/registry/capabilities.js +1 -46
  162. package/dist/registry/commit-and-signal.d.ts +0 -6
  163. package/dist/registry/commit-and-signal.js +1 -7
  164. package/dist/registry/domains.d.ts +0 -8
  165. package/dist/registry/domains.js +0 -7
  166. package/dist/registry/emitter.d.ts +0 -14
  167. package/dist/registry/emitter.js +0 -10
  168. package/dist/registry/store-adapters.d.ts +0 -186
  169. package/dist/registry/store-adapters.js +0 -163
  170. package/dist/registry/stores.d.ts +0 -33
  171. package/dist/registry/stores.js +0 -59
  172. package/dist/reticle-presenter-helpers.d.ts +0 -13
  173. package/dist/reticle-presenter-helpers.js +0 -14
  174. package/dist/reticle.d.ts +1 -52
  175. package/dist/reticle.js +20 -145
  176. package/dist/review/annotator-styles.js +0 -7
  177. package/dist/review/annotator.d.ts +2 -13
  178. package/dist/review/annotator.js +0 -40
  179. package/dist/review/mark-anchor.d.ts +0 -11
  180. package/dist/review/mark-anchor.js +0 -8
  181. package/dist/security/serialization.d.ts +0 -20
  182. package/dist/security/serialization.js +1 -66
  183. package/dist/session-continuity.d.ts +0 -16
  184. package/dist/session-continuity.js +1 -20
  185. package/dist/timers/clock.d.ts +1 -14
  186. package/dist/timers/clock.js +59 -60
  187. package/dist/timers/native-console.js +0 -4
  188. package/dist/timers/native-timers.d.ts +0 -25
  189. package/dist/timers/native-timers.js +3 -33
  190. package/dist/transport/transport.d.ts +0 -51
  191. package/dist/transport/transport.js +3 -129
  192. package/dist/transport/unreachable-message.d.ts +0 -17
  193. package/dist/transport/unreachable-message.js +0 -17
  194. package/dist/util/captured-method.d.ts +0 -35
  195. package/dist/util/captured-method.js +0 -35
  196. 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
- /** Footer markup: composer only (export icons live in the toolbar). */
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()); // rebuild, keep the caption
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); // setAttribute → no markup injection from a 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
- export interface HudDragCallbacks {
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 {};