@reticlehq/browser 2.13.1 → 3.1.0

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