@treelocator/runtime 0.4.7 → 0.6.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 (166) hide show
  1. package/.eslintignore +1 -0
  2. package/dist/_generated_styles.d.ts +1 -1
  3. package/dist/_generated_styles.js +20 -0
  4. package/dist/_generated_tree_icon.d.ts +1 -1
  5. package/dist/adapters/HtmlElementTreeNode.d.ts +2 -2
  6. package/dist/adapters/HtmlElementTreeNode.js +4 -6
  7. package/dist/adapters/createTreeNode.js +17 -44
  8. package/dist/adapters/detectFramework.d.ts +8 -0
  9. package/dist/adapters/detectFramework.js +25 -0
  10. package/dist/adapters/detectFramework.test.d.ts +1 -0
  11. package/dist/adapters/detectFramework.test.js +60 -0
  12. package/dist/adapters/jsx/jsxAdapter.js +54 -89
  13. package/dist/adapters/jsx/jsxAdapter.test.d.ts +1 -0
  14. package/dist/adapters/jsx/jsxAdapter.test.js +273 -0
  15. package/dist/adapters/nextjs/parseNextjsDataAttributes.js +1 -1
  16. package/dist/adapters/nextjs/parseNextjsDataAttributes.test.d.ts +1 -0
  17. package/dist/adapters/nextjs/parseNextjsDataAttributes.test.js +158 -0
  18. package/dist/adapters/react/findFiberByHtmlElement.d.ts +1 -1
  19. package/dist/adapters/react/findFiberByHtmlElement.js +1 -1
  20. package/dist/adapters/react/getAllParentsElementsAndRootComponent.js +4 -0
  21. package/dist/adapters/resolveAdapter.d.ts +1 -1
  22. package/dist/adapters/resolveAdapter.js +4 -8
  23. package/dist/adapters/svelte/svelteAdapter.test.d.ts +1 -0
  24. package/dist/adapters/svelte/svelteAdapter.test.js +280 -0
  25. package/dist/adapters/vue/vueAdapter.test.d.ts +1 -0
  26. package/dist/adapters/vue/vueAdapter.test.js +222 -0
  27. package/dist/browserApi.d.ts +148 -0
  28. package/dist/browserApi.js +146 -5
  29. package/dist/browserApi.test.d.ts +1 -0
  30. package/dist/browserApi.test.js +287 -0
  31. package/dist/components/RecordingPillButton.d.ts +11 -0
  32. package/dist/components/RecordingPillButton.js +202 -0
  33. package/dist/components/RecordingResults.d.ts +2 -0
  34. package/dist/components/RecordingResults.js +213 -78
  35. package/dist/components/Runtime.js +161 -554
  36. package/dist/components/SettingsPanel.d.ts +5 -0
  37. package/dist/components/SettingsPanel.js +312 -0
  38. package/dist/consoleCapture.d.ts +9 -0
  39. package/dist/consoleCapture.js +95 -0
  40. package/dist/dejitter/recorder.d.ts +7 -1
  41. package/dist/dejitter/recorder.js +64 -1
  42. package/dist/functions/cssRuleInspector.d.ts +83 -0
  43. package/dist/functions/cssRuleInspector.js +608 -0
  44. package/dist/functions/cssRuleInspector.test.d.ts +1 -0
  45. package/dist/functions/cssRuleInspector.test.js +439 -0
  46. package/dist/functions/deduplicateLabels.test.d.ts +1 -0
  47. package/dist/functions/deduplicateLabels.test.js +178 -0
  48. package/dist/functions/enrichAncestrySourceMaps.js +0 -1
  49. package/dist/functions/extractComputedStyles.d.ts +51 -0
  50. package/dist/functions/extractComputedStyles.js +447 -0
  51. package/dist/functions/extractComputedStyles.test.d.ts +1 -0
  52. package/dist/functions/extractComputedStyles.test.js +549 -0
  53. package/dist/functions/formatAncestryChain.d.ts +8 -0
  54. package/dist/functions/formatAncestryChain.js +21 -1
  55. package/dist/functions/formatAncestryChain.test.js +18 -0
  56. package/dist/functions/getUsableName.test.d.ts +1 -0
  57. package/dist/functions/getUsableName.test.js +219 -0
  58. package/dist/functions/isCombinationModifiersPressed.test.d.ts +1 -0
  59. package/dist/functions/isCombinationModifiersPressed.test.js +192 -0
  60. package/dist/functions/mergeRects.test.js +210 -1
  61. package/dist/functions/namedSnapshots.d.ts +52 -0
  62. package/dist/functions/namedSnapshots.js +161 -0
  63. package/dist/functions/namedSnapshots.test.d.ts +1 -0
  64. package/dist/functions/namedSnapshots.test.js +85 -0
  65. package/dist/functions/normalizeFilePath.test.d.ts +1 -0
  66. package/dist/functions/normalizeFilePath.test.js +66 -0
  67. package/dist/functions/parseDataId.test.d.ts +1 -0
  68. package/dist/functions/parseDataId.test.js +101 -0
  69. package/dist/hooks/getStorage.d.ts +3 -0
  70. package/dist/hooks/getStorage.js +17 -0
  71. package/dist/hooks/useEventListeners.d.ts +15 -0
  72. package/dist/hooks/useEventListeners.js +56 -0
  73. package/dist/hooks/useLocatorStorage.d.ts +18 -0
  74. package/dist/hooks/useLocatorStorage.js +41 -0
  75. package/dist/hooks/useLocatorStorage.test.d.ts +1 -0
  76. package/dist/hooks/useLocatorStorage.test.js +124 -0
  77. package/dist/hooks/useRecordingState.d.ts +43 -0
  78. package/dist/hooks/useRecordingState.js +387 -0
  79. package/dist/hooks/useSettings.d.ts +13 -0
  80. package/dist/hooks/useSettings.js +66 -0
  81. package/dist/index.d.ts +5 -2
  82. package/dist/index.js +4 -2
  83. package/dist/initRuntime.d.ts +3 -1
  84. package/dist/initRuntime.js +4 -1
  85. package/dist/mcpBridge.d.ts +61 -0
  86. package/dist/mcpBridge.js +534 -0
  87. package/dist/mcpBridge.test.d.ts +1 -0
  88. package/dist/mcpBridge.test.js +248 -0
  89. package/dist/output.css +20 -0
  90. package/dist/visualDiff/diff.d.ts +9 -0
  91. package/dist/visualDiff/diff.js +209 -0
  92. package/dist/visualDiff/diff.test.d.ts +1 -0
  93. package/dist/visualDiff/diff.test.js +253 -0
  94. package/dist/visualDiff/settle.d.ts +3 -0
  95. package/dist/visualDiff/settle.js +50 -0
  96. package/dist/visualDiff/settle.test.d.ts +1 -0
  97. package/dist/visualDiff/settle.test.js +65 -0
  98. package/dist/visualDiff/snapshot.d.ts +4 -0
  99. package/dist/visualDiff/snapshot.js +84 -0
  100. package/dist/visualDiff/snapshot.test.d.ts +1 -0
  101. package/dist/visualDiff/snapshot.test.js +245 -0
  102. package/dist/visualDiff/types.d.ts +37 -0
  103. package/dist/visualDiff/types.js +1 -0
  104. package/package.json +2 -2
  105. package/scripts/wrapCSS.js +1 -1
  106. package/scripts/wrapImage.js +1 -1
  107. package/src/_generated_styles.ts +21 -1
  108. package/src/_generated_tree_icon.ts +1 -1
  109. package/src/adapters/HtmlElementTreeNode.ts +10 -7
  110. package/src/adapters/createTreeNode.ts +12 -51
  111. package/src/adapters/detectFramework.test.ts +73 -0
  112. package/src/adapters/detectFramework.ts +28 -0
  113. package/src/adapters/jsx/jsxAdapter.test.ts +240 -0
  114. package/src/adapters/jsx/jsxAdapter.ts +53 -106
  115. package/src/adapters/nextjs/parseNextjsDataAttributes.test.ts +212 -0
  116. package/src/adapters/nextjs/parseNextjsDataAttributes.ts +1 -1
  117. package/src/adapters/react/findDebugSource.ts +5 -6
  118. package/src/adapters/react/findFiberByHtmlElement.ts +3 -3
  119. package/src/adapters/react/getAllParentsElementsAndRootComponent.ts +3 -0
  120. package/src/adapters/react/reactAdapter.ts +1 -2
  121. package/src/adapters/resolveAdapter.ts +4 -14
  122. package/src/adapters/svelte/svelteAdapter.test.ts +334 -0
  123. package/src/adapters/vue/vueAdapter.test.ts +259 -0
  124. package/src/browserApi.test.ts +329 -0
  125. package/src/browserApi.ts +351 -4
  126. package/src/components/RecordingPillButton.tsx +301 -0
  127. package/src/components/RecordingResults.tsx +114 -13
  128. package/src/components/Runtime.tsx +176 -621
  129. package/src/components/SettingsPanel.tsx +339 -0
  130. package/src/consoleCapture.ts +113 -0
  131. package/src/dejitter/recorder.ts +67 -3
  132. package/src/functions/cssRuleInspector.test.ts +517 -0
  133. package/src/functions/cssRuleInspector.ts +708 -0
  134. package/src/functions/deduplicateLabels.test.ts +115 -0
  135. package/src/functions/enrichAncestrySourceMaps.ts +6 -3
  136. package/src/functions/extractComputedStyles.test.ts +681 -0
  137. package/src/functions/extractComputedStyles.ts +768 -0
  138. package/src/functions/formatAncestryChain.test.ts +23 -1
  139. package/src/functions/formatAncestryChain.ts +22 -1
  140. package/src/functions/getUsableName.test.ts +242 -0
  141. package/src/functions/isCombinationModifiersPressed.test.ts +156 -0
  142. package/src/functions/mergeRects.test.ts +111 -1
  143. package/src/functions/namedSnapshots.test.ts +106 -0
  144. package/src/functions/namedSnapshots.ts +232 -0
  145. package/src/functions/normalizeFilePath.test.ts +80 -0
  146. package/src/functions/parseDataId.test.ts +125 -0
  147. package/src/hooks/getStorage.ts +26 -0
  148. package/src/hooks/useEventListeners.ts +97 -0
  149. package/src/hooks/useLocatorStorage.test.ts +127 -0
  150. package/src/hooks/useLocatorStorage.ts +60 -0
  151. package/src/hooks/useRecordingState.ts +516 -0
  152. package/src/hooks/useSettings.ts +83 -0
  153. package/src/index.ts +10 -5
  154. package/src/initRuntime.ts +5 -0
  155. package/src/mcpBridge.test.ts +260 -0
  156. package/src/mcpBridge.ts +677 -0
  157. package/src/visualDiff/diff.test.ts +167 -0
  158. package/src/visualDiff/diff.ts +242 -0
  159. package/src/visualDiff/settle.test.ts +77 -0
  160. package/src/visualDiff/settle.ts +62 -0
  161. package/src/visualDiff/snapshot.test.ts +200 -0
  162. package/src/visualDiff/snapshot.ts +119 -0
  163. package/src/visualDiff/types.ts +40 -0
  164. package/tsconfig.json +3 -1
  165. package/vitest.config.ts +18 -0
  166. package/jest.config.ts +0 -195
@@ -1,85 +1,35 @@
1
1
  import { template as _$template } from "solid-js/web";
2
- import { delegateEvents as _$delegateEvents } from "solid-js/web";
3
- import { createComponent as _$createComponent } from "solid-js/web";
4
2
  import { effect as _$effect } from "solid-js/web";
5
- import { insert as _$insert } from "solid-js/web";
6
- import { setAttribute as _$setAttribute } from "solid-js/web";
7
3
  import { setStyleProperty as _$setStyleProperty } from "solid-js/web";
4
+ import { createComponent as _$createComponent } from "solid-js/web";
8
5
  import { memo as _$memo } from "solid-js/web";
9
- var _tmpl$ = /*#__PURE__*/_$template(`<div class="fixed pointer-events-auto"title="TreeLocatorJS - Component Ancestry Tracker"data-treelocator-api=window.__treelocator__ data-treelocator-help=window.__treelocator__.help() style=z-index:2147483646><style>\n @keyframes treelocator-rec-pulse \{\n 0%, 100% \{ opacity: 1; }\n 50% \{ opacity: 0.3; }\n }\n </style><div style=align-items:stretch;border-radius:27px><div aria-label="TreeLocatorJS: Get component paths using window.__treelocator__.getPath(selector)"role=button style="align-items:center;justify-content:center;border-right:1px solid rgba(0, 0, 0, 0.1)"><img alt=TreeLocatorJS width=44 height=44></div><div role=button style=align-items:center;justify-content:center></div></div><div role=note aria-label="TreeLocatorJS Developer Tool API — Run: window.__treelocator__.help() for full usage instructions. Quick start: window.__treelocator__.getPath('button.submit') returns component ancestry. window.__treelocator__.replayWithRecord('.my-element') replays recorded interactions while tracking visual changes."style=white-space:nowrap>TreeLocatorJS: Run window.__treelocator__.help() for API docs`),
10
- _tmpl$2 = /*#__PURE__*/_$template(`<div style="z-index:2147483645;border-radius:50%;pointer-events:none;box-shadow:0 0 12px rgba(59, 130, 246, 0.5)">`),
11
- _tmpl$3 = /*#__PURE__*/_$template(`<div style=border-radius:3px>`),
12
- _tmpl$4 = /*#__PURE__*/_$template(`<div style=border-radius:50%>`);
13
- import { createEffect, createSignal, onCleanup } from "solid-js";
6
+ var _tmpl$ = /*#__PURE__*/_$template(`<div style="z-index:2147483645;border-radius:50%;pointer-events:none;box-shadow:0 0 12px rgba(59, 130, 246, 0.5)">`);
7
+ import { createEffect, createSignal } from "solid-js";
14
8
  import { render } from "solid-js/web";
15
9
  import { isCombinationModifiersPressed } from "../functions/isCombinationModifiersPressed";
16
10
  import { MaybeOutline } from "./MaybeOutline";
17
11
  import { isLocatorsOwnElement } from "../functions/isLocatorsOwnElement";
18
12
  import { Toast } from "./Toast";
19
- import { collectAncestry, formatAncestryChain, truncateAtFirstFile } from "../functions/formatAncestryChain";
13
+ import { collectAncestry, formatAncestryChain, truncateAtFirstFile, getElementLabel } from "../functions/formatAncestryChain";
20
14
  import { enrichAncestryWithSourceMaps } from "../functions/enrichAncestrySourceMaps";
15
+ import { extractComputedStyles } from "../functions/extractComputedStyles";
21
16
  import { createTreeNode } from "../adapters/createTreeNode";
22
- import treeIconUrl from "../_generated_tree_icon";
23
- import { createDejitterRecorder } from "../dejitter/recorder";
24
17
  import { RecordingOutline } from "./RecordingOutline";
25
18
  import { RecordingResults } from "./RecordingResults";
26
- const DEJITTER_CONFIG = {
27
- selector: '[data-treelocator-recording]',
28
- props: ['opacity', 'transform', 'boundingRect', 'width', 'height'],
29
- sampleRate: 15,
30
- maxDuration: 30000,
31
- idleTimeout: 0,
32
- mutations: true
33
- };
19
+ import { RecordingPillButton } from "./RecordingPillButton";
20
+ import { SettingsPanel } from "./SettingsPanel";
21
+ import { useRecordingState } from "../hooks/useRecordingState";
22
+ import { useEventListeners } from "../hooks/useEventListeners";
23
+ import { settings } from "../hooks/useSettings";
34
24
  function Runtime(props) {
35
25
  const [holdingModKey, setHoldingModKey] = createSignal(false);
36
26
  const [holdingShift, setHoldingShift] = createSignal(false);
37
27
  const [currentElement, setCurrentElement] = createSignal(null);
38
28
  const [toastMessage, setToastMessage] = createSignal(null);
39
29
  const [locatorActive, setLocatorActive] = createSignal(false);
40
-
41
- // Recording state machine: idle -> selecting -> recording -> results -> idle
42
-
43
- // --- localStorage persistence ---
44
- const STORAGE_KEY = '__treelocator_recording__';
45
- function loadFromStorage() {
46
- try {
47
- const raw = localStorage.getItem(STORAGE_KEY);
48
- if (raw) return JSON.parse(raw);
49
- } catch {}
50
- return {
51
- last: null,
52
- previous: null
53
- };
54
- }
55
- function saveToStorage(current) {
56
- try {
57
- const stored = loadFromStorage();
58
- localStorage.setItem(STORAGE_KEY, JSON.stringify({
59
- last: current,
60
- previous: stored.last
61
- }));
62
- } catch {}
63
- }
64
-
65
- // Restore last results on mount
66
- const restored = loadFromStorage();
67
- const restoredLast = restored.last;
68
- const [recordingState, setRecordingState] = createSignal(restoredLast ? 'results' : 'idle');
69
- const [recordedElement, setRecordedElement] = createSignal(null);
70
- const [recordingFindings, setRecordingFindings] = createSignal(restoredLast?.findings ?? []);
71
- const [recordingSummary, setRecordingSummary] = createSignal(restoredLast?.summary ?? null);
72
- const [interactionLog, setInteractionLog] = createSignal(restoredLast?.interactions ?? []);
73
- const [recordingData, setRecordingData] = createSignal(restoredLast?.data ?? null);
74
- const [recordingElementPath, setRecordingElementPath] = createSignal(restoredLast?.elementPath ?? "");
75
- const [replayBox, setReplayBox] = createSignal(null);
76
- const [replaying, setReplaying] = createSignal(false);
77
- const [viewingPrevious, setViewingPrevious] = createSignal(false);
78
- let dejitterInstance = null;
79
- let interactionClickHandler = null;
80
- let recordingStartTime = 0;
81
- let replayTimerId = null;
82
- const isActive = () => (holdingModKey() || locatorActive() || recordingState() === 'selecting') && currentElement();
30
+ const [settingsOpen, setSettingsOpen] = createSignal(false);
31
+ const recording = useRecordingState(props.adapterId);
32
+ const isActive = () => (holdingModKey() || locatorActive() || recording.recordingState() === "selecting") && currentElement();
83
33
  createEffect(() => {
84
34
  if (isActive()) {
85
35
  document.body.classList.add("locatorjs-active-pointer");
@@ -90,372 +40,61 @@ function Runtime(props) {
90
40
 
91
41
  // Expose replay functions on the browser API
92
42
  if (typeof window !== "undefined" && window.__treelocator__) {
93
- window.__treelocator__.replay = () => replayRecording();
94
- window.__treelocator__.replayWithRecord = elementOrSelector => replayWithRecord(elementOrSelector);
43
+ window.__treelocator__.replay = () => recording.replayRecording();
44
+ window.__treelocator__.replayWithRecord = elementOrSelector => recording.replayWithRecord(elementOrSelector);
95
45
  }
96
- function keyUpListener(e) {
97
- setHoldingModKey(isCombinationModifiersPressed(e));
98
- setHoldingShift(e.shiftKey);
99
- }
100
- function keyDownListener(e) {
101
- setHoldingModKey(isCombinationModifiersPressed(e, true));
102
- setHoldingShift(e.shiftKey);
103
- }
104
- function mouseMoveListener(e) {
105
- // Update modifier state from mouse events - more reliable than keydown/keyup
106
- setHoldingModKey(e.altKey);
107
- setHoldingShift(e.shiftKey);
46
+
47
+ // --- Event handlers ---
48
+
49
+ function eventPathHasAttribute(e, attr) {
50
+ const path = typeof e.composedPath === "function" ? e.composedPath() : e.target ? [e.target] : [];
51
+ return path.some(node => node instanceof Element && node.hasAttribute(attr));
108
52
  }
109
53
  function findElementAtPoint(e) {
110
54
  const elementsAtPoint = document.elementsFromPoint(e.clientX, e.clientY);
111
55
  for (const el of elementsAtPoint) {
112
56
  if (isLocatorsOwnElement(el)) continue;
113
57
  if (el instanceof HTMLElement || el instanceof SVGElement) {
114
- const withLocator = el.closest('[data-locatorjs-id], [data-locatorjs]');
58
+ const withLocator = el.closest("[data-locatorjs-id], [data-locatorjs]");
115
59
  if (withLocator) return withLocator;
116
60
  }
117
61
  }
118
- // Fallback to e.target
119
62
  const target = e.target;
120
63
  if (target && (target instanceof HTMLElement || target instanceof SVGElement)) {
121
- const el = target instanceof SVGElement ? target.closest('[data-locatorjs-id], [data-locatorjs]') ?? target.closest('svg') ?? target : target;
64
+ const el = target instanceof SVGElement ? target.closest("[data-locatorjs-id], [data-locatorjs]") ?? target.closest("svg") ?? target : target;
122
65
  if (el && !isLocatorsOwnElement(el)) return el;
123
66
  }
124
67
  return null;
125
68
  }
126
-
127
- // --- Recording lifecycle ---
128
-
129
- function handleRecordClick() {
130
- switch (recordingState()) {
131
- case 'idle':
132
- setRecordingState('selecting');
133
- break;
134
- case 'selecting':
135
- setRecordingState('idle');
136
- break;
137
- case 'recording':
138
- stopRecording();
139
- break;
140
- case 'results':
141
- dismissResults();
142
- break;
143
- }
144
- }
145
- function startRecording(element) {
146
- element.setAttribute('data-treelocator-recording', 'true');
147
- setRecordedElement(element);
148
- dejitterInstance = createDejitterRecorder();
149
- dejitterInstance.configure(DEJITTER_CONFIG);
150
- dejitterInstance.start();
151
- startInteractionTracker();
152
- setRecordingState('recording');
153
- }
154
- function stopRecording() {
155
- if (!dejitterInstance) return;
156
- dejitterInstance.stop();
157
- const findings = dejitterInstance.findings(true);
158
- const summary = dejitterInstance.summary(true);
159
- const data = dejitterInstance.getData();
160
-
161
- // Collect ancestry path from treelocator before clearing
162
- const el = recordedElement();
163
- let elementPath = "";
164
- if (el) {
165
- const treeNode = createTreeNode(el, props.adapterId);
166
- if (treeNode) {
167
- const ancestry = collectAncestry(treeNode);
168
- elementPath = formatAncestryChain(ancestry);
169
- }
170
- }
171
- setRecordingFindings(findings);
172
- setRecordingSummary(summary);
173
- setRecordingData(data);
174
- setRecordingElementPath(elementPath);
175
- stopInteractionTracker();
176
-
177
- // Persist to localStorage (moves previous "last" to "previous")
178
- saveToStorage({
179
- findings,
180
- summary,
181
- data,
182
- elementPath,
183
- interactions: interactionLog()
184
- });
185
- el?.removeAttribute('data-treelocator-recording');
186
- setRecordingState('results');
187
- dejitterInstance = null;
188
- }
189
- function replayRecording() {
190
- const events = interactionLog();
191
- if (events.length === 0) return;
192
- stopReplay();
193
- setReplaying(true);
194
- let eventIdx = 0;
195
- function scheduleNext() {
196
- if (eventIdx >= events.length) {
197
- stopReplay();
198
- return;
199
- }
200
- const evt = events[eventIdx];
201
- const delay = eventIdx === 0 ? 100 : evt.t - events[eventIdx - 1].t;
202
- replayTimerId = window.setTimeout(() => {
203
- // Show click indicator
204
- setReplayBox({
205
- x: evt.x - 12,
206
- y: evt.y - 12,
207
- w: 24,
208
- h: 24
209
- });
210
-
211
- // Dispatch a real click at the recorded position
212
- const target = document.elementFromPoint(evt.x, evt.y);
213
- if (target) {
214
- target.dispatchEvent(new MouseEvent('click', {
215
- bubbles: true,
216
- cancelable: true,
217
- clientX: evt.x,
218
- clientY: evt.y,
219
- view: window
220
- }));
221
- }
222
-
223
- // Clear click indicator after a short flash
224
- window.setTimeout(() => setReplayBox(null), 200);
225
- eventIdx++;
226
- scheduleNext();
227
- }, Math.max(delay, 50));
228
- }
229
- scheduleNext();
230
- }
231
- function stopReplay() {
232
- if (replayTimerId) {
233
- clearTimeout(replayTimerId);
234
- replayTimerId = null;
235
- }
236
- setReplaying(false);
237
- setReplayBox(null);
238
- }
239
- function replayWithRecord(elementOrSelector) {
240
- // Resolve element
241
- let element;
242
- if (typeof elementOrSelector === 'string') {
243
- const found = document.querySelector(elementOrSelector);
244
- element = found instanceof HTMLElement ? found : null;
245
- } else {
246
- element = elementOrSelector;
247
- }
248
- if (!element) return Promise.resolve(null);
249
-
250
- // Get stored interactions to replay
251
- const stored = loadFromStorage();
252
- const events = stored.last?.interactions ?? interactionLog();
253
- if (events.length === 0) return Promise.resolve(null);
254
- return new Promise(resolve => {
255
- // Start recording on the element
256
- element.setAttribute('data-treelocator-recording', 'true');
257
- setRecordedElement(element);
258
- dejitterInstance = createDejitterRecorder();
259
- dejitterInstance.configure(DEJITTER_CONFIG);
260
- dejitterInstance.start();
261
- setRecordingState('recording');
262
- setReplaying(true);
263
- let eventIdx = 0;
264
- function finishRecording() {
265
- setReplaying(false);
266
- setReplayBox(null);
267
- if (!dejitterInstance) {
268
- resolve(null);
269
- return;
270
- }
271
- dejitterInstance.stop();
272
- const findings = dejitterInstance.findings(true);
273
- const summary = dejitterInstance.summary(true);
274
- const data = dejitterInstance.getData();
275
- const el = recordedElement();
276
- let elementPath = "";
277
- if (el) {
278
- const treeNode = createTreeNode(el, props.adapterId);
279
- if (treeNode) {
280
- const ancestry = collectAncestry(treeNode);
281
- elementPath = formatAncestryChain(ancestry);
282
- }
283
- }
284
- setRecordingFindings(findings);
285
- setRecordingSummary(summary);
286
- setRecordingData(data);
287
- setRecordingElementPath(elementPath);
288
- setInteractionLog(events);
289
- saveToStorage({
290
- findings,
291
- summary,
292
- data,
293
- elementPath,
294
- interactions: events
295
- });
296
- el?.removeAttribute('data-treelocator-recording');
297
- setRecordingState('results');
298
- dejitterInstance = null;
299
- resolve({
300
- path: elementPath,
301
- findings,
302
- summary,
303
- data,
304
- interactions: events
305
- });
306
- }
307
- function scheduleNext() {
308
- if (eventIdx >= events.length) {
309
- // Wait for CSS transitions to settle before stopping recording
310
- replayTimerId = window.setTimeout(finishRecording, 500);
311
- return;
312
- }
313
- const evt = events[eventIdx];
314
- const delay = eventIdx === 0 ? 100 : evt.t - events[eventIdx - 1].t;
315
- replayTimerId = window.setTimeout(() => {
316
- setReplayBox({
317
- x: evt.x - 12,
318
- y: evt.y - 12,
319
- w: 24,
320
- h: 24
321
- });
322
- const target = document.elementFromPoint(evt.x, evt.y);
323
- if (target) {
324
- target.dispatchEvent(new MouseEvent('click', {
325
- bubbles: true,
326
- cancelable: true,
327
- clientX: evt.x,
328
- clientY: evt.y,
329
- view: window
330
- }));
331
- }
332
- window.setTimeout(() => setReplayBox(null), 200);
333
- eventIdx++;
334
- scheduleNext();
335
- }, Math.max(delay, 50));
336
- }
337
- scheduleNext();
338
- });
339
- }
340
- function dismissResults() {
341
- stopReplay();
342
- setRecordingFindings([]);
343
- setRecordingSummary(null);
344
- setRecordingData(null);
345
- setRecordingElementPath("");
346
- setInteractionLog([]);
347
- setRecordedElement(null);
348
- setViewingPrevious(false);
349
- setRecordingState('idle');
350
- try {
351
- localStorage.removeItem(STORAGE_KEY);
352
- } catch {}
353
- }
354
- function hasPreviousRecording() {
355
- return loadFromStorage().previous !== null;
356
- }
357
- function loadPreviousRecording() {
358
- const stored = loadFromStorage();
359
- if (!stored.previous) return;
360
- const prev = stored.previous;
361
- setRecordingFindings(prev.findings);
362
- setRecordingSummary(prev.summary);
363
- setRecordingData(prev.data);
364
- setRecordingElementPath(prev.elementPath);
365
- setInteractionLog(prev.interactions);
366
- setViewingPrevious(true);
367
- setRecordingState('results');
368
- }
369
- function loadLatestRecording() {
370
- const stored = loadFromStorage();
371
- if (!stored.last) return;
372
- const last = stored.last;
373
- setRecordingFindings(last.findings);
374
- setRecordingSummary(last.summary);
375
- setRecordingData(last.data);
376
- setRecordingElementPath(last.elementPath);
377
- setInteractionLog(last.interactions);
378
- setViewingPrevious(false);
379
- setRecordingState('results');
380
- }
381
- function startInteractionTracker() {
382
- recordingStartTime = performance.now();
383
- setInteractionLog([]);
384
- interactionClickHandler = e => {
385
- if (isLocatorsOwnElement(e.target)) return;
386
- const el = e.target;
387
- const tag = el.tagName?.toLowerCase() || 'unknown';
388
- const id = el.id ? '#' + el.id : '';
389
- const cls = el.className && typeof el.className === 'string' ? '.' + el.className.split(' ')[0] : '';
390
- setInteractionLog(prev => [...prev, {
391
- t: Math.round(performance.now() - recordingStartTime),
392
- type: 'click',
393
- target: `${tag}${id}${cls}`,
394
- x: e.clientX,
395
- y: e.clientY
396
- }]);
397
- };
398
- document.addEventListener('click', interactionClickHandler, {
399
- capture: true
400
- });
401
- }
402
- function stopInteractionTracker() {
403
- if (interactionClickHandler) {
404
- document.removeEventListener('click', interactionClickHandler, {
405
- capture: true
406
- });
407
- interactionClickHandler = null;
69
+ function clickListener(e) {
70
+ if (settingsOpen() && !eventPathHasAttribute(e, "data-treelocator-settings-panel") && !eventPathHasAttribute(e, "data-treelocator-settings-toggle")) {
71
+ setSettingsOpen(false);
408
72
  }
409
- }
410
- function mouseOverListener(e) {
411
- setHoldingModKey(e.altKey);
412
- setHoldingShift(e.shiftKey);
413
73
 
414
- // Don't update hovered element while recording -- highlight is sticky
415
- if (recordingState() === 'recording') return;
416
- const element = findElementAtPoint(e);
417
- if (element) {
418
- setCurrentElement(element);
419
- }
420
- }
421
- function mouseDownUpListener(e) {
422
- setHoldingModKey(e.altKey);
423
- if (e.altKey || locatorActive() || recordingState() === 'selecting') {
424
- e.preventDefault();
425
- e.stopPropagation();
426
- }
427
- }
428
- function clickListener(e) {
429
74
  // Handle recording element selection
430
- if (recordingState() === 'selecting') {
75
+ if (recording.recordingState() === "selecting") {
431
76
  e.preventDefault();
432
77
  e.stopPropagation();
433
78
  const element = findElementAtPoint(e);
434
79
  if (element && !isLocatorsOwnElement(element)) {
435
- startRecording(element);
80
+ recording.startRecording(element);
436
81
  }
437
82
  return;
438
83
  }
439
84
 
440
85
  // During recording, let clicks pass through (tracked by interaction logger)
441
- if (recordingState() === 'recording') return;
86
+ if (recording.recordingState() === "recording") return;
442
87
  if (!e.altKey && !isCombinationModifiersPressed(e) && !locatorActive()) {
443
88
  return;
444
89
  }
445
90
  const element = findElementAtPoint(e);
446
- if (!element) {
447
- return;
448
- }
449
- if (element instanceof HTMLElement && element.shadowRoot) {
450
- return;
451
- }
452
- if (isLocatorsOwnElement(element)) {
453
- return;
454
- }
91
+ if (!element) return;
92
+ if (element instanceof HTMLElement && element.shadowRoot) return;
93
+ if (isLocatorsOwnElement(element)) return;
455
94
  e.preventDefault();
456
95
  e.stopPropagation();
457
96
 
458
- // Copy ancestry to clipboard on alt+click
97
+ // Copy ancestry + computed styles to clipboard on alt+click
459
98
  const treeNode = createTreeNode(element, props.adapterId);
460
99
  if (treeNode) {
461
100
  let ancestry = collectAncestry(treeNode);
@@ -465,17 +104,37 @@ function Runtime(props) {
465
104
  ancestry = truncateAtFirstFile(ancestry);
466
105
  }
467
106
 
107
+ // Extract computed styles for the clicked element (if enabled)
108
+ const stylesEnabled = settings().computedStyles;
109
+ const elementLabel = getElementLabel(ancestry);
110
+ const stylesResult = stylesEnabled ? extractComputedStyles(element, elementLabel, {
111
+ includeDefaults: settings().computedStylesIncludeDefaults
112
+ }) : null;
113
+
468
114
  // Write immediately with component names (preserves user gesture for clipboard API)
469
115
  const formatted = formatAncestryChain(ancestry);
470
- navigator.clipboard.writeText(formatted).then(() => {
116
+ const fullOutput = stylesResult ? formatted + "\n\n" + stylesResult.formatted : formatted;
117
+ navigator.clipboard.writeText(fullOutput).then(() => {
471
118
  setToastMessage("Copied to clipboard");
472
119
  });
473
120
 
474
- // For React 19+: try to enrich with source map file paths and re-copy
121
+ // For React 19+: try to enrich with source map file paths and re-copy.
122
+ // If the enriched label differs, re-extract with forceFull:true so the
123
+ // diff-mode fast path doesn't collapse the second extraction (for the
124
+ // same element within the diff window) into "No changes detected".
475
125
  enrichAncestryWithSourceMaps(ancestry, element).then(enriched => {
476
126
  const enrichedFormatted = formatAncestryChain(enriched);
477
127
  if (enrichedFormatted !== formatted) {
478
- navigator.clipboard.writeText(enrichedFormatted).then(() => {
128
+ let enrichedFull = enrichedFormatted;
129
+ if (stylesResult) {
130
+ const enrichedLabel = getElementLabel(enriched);
131
+ const enrichedStyles = enrichedLabel !== elementLabel ? extractComputedStyles(element, enrichedLabel, {
132
+ forceFull: true,
133
+ includeDefaults: settings().computedStylesIncludeDefaults
134
+ }).formatted : stylesResult.formatted;
135
+ enrichedFull = enrichedFormatted + "\n\n" + enrichedStyles;
136
+ }
137
+ navigator.clipboard.writeText(enrichedFull).then(() => {
479
138
  setToastMessage("Copied to clipboard");
480
139
  });
481
140
  }
@@ -487,58 +146,54 @@ function Runtime(props) {
487
146
  setLocatorActive(false);
488
147
  }
489
148
  }
490
- function scrollListener() {
491
- setCurrentElement(null);
492
- }
493
- const roots = [document];
494
- document.querySelectorAll("*").forEach(node => {
495
- if (node.id === "locatorjs-wrapper") {
496
- return;
497
- }
498
- if (node.shadowRoot) {
499
- roots.push(node.shadowRoot);
149
+ function mouseOverListener(e) {
150
+ setHoldingModKey(e.altKey);
151
+ setHoldingShift(e.shiftKey);
152
+
153
+ // Don't update hovered element while recording — highlight is sticky
154
+ if (recording.recordingState() === "recording") return;
155
+ const element = findElementAtPoint(e);
156
+ if (element) {
157
+ setCurrentElement(element);
500
158
  }
501
- });
502
- for (const root of roots) {
503
- root.addEventListener("mouseover", mouseOverListener, {
504
- capture: true
505
- });
506
- root.addEventListener("mousemove", mouseMoveListener, {
507
- capture: true
508
- });
509
- root.addEventListener("keydown", keyDownListener);
510
- root.addEventListener("keyup", keyUpListener);
511
- root.addEventListener("click", clickListener, {
512
- capture: true
513
- });
514
- root.addEventListener("mousedown", mouseDownUpListener, {
515
- capture: true
516
- });
517
- root.addEventListener("mouseup", mouseDownUpListener, {
518
- capture: true
519
- });
520
- root.addEventListener("scroll", scrollListener);
521
159
  }
522
- onCleanup(() => {
523
- stopReplay();
524
- stopInteractionTracker();
525
- for (const root of roots) {
526
- root.removeEventListener("keyup", keyUpListener);
527
- root.removeEventListener("keydown", keyDownListener);
528
- root.removeEventListener("mouseover", mouseOverListener, {
529
- capture: true
530
- });
531
- root.removeEventListener("click", clickListener, {
532
- capture: true
533
- });
534
- root.removeEventListener("mousedown", mouseDownUpListener, {
535
- capture: true
536
- });
537
- root.removeEventListener("mouseup", mouseDownUpListener, {
538
- capture: true
539
- });
540
- root.removeEventListener("scroll", scrollListener);
541
- }
160
+ useEventListeners({
161
+ mouseOverListener,
162
+ mouseMoveListener(e) {
163
+ setHoldingModKey(e.altKey);
164
+ setHoldingShift(e.shiftKey);
165
+ },
166
+ keyDownListener(e) {
167
+ setHoldingModKey(isCombinationModifiersPressed(e, true));
168
+ setHoldingShift(e.shiftKey);
169
+ if (e.key === "Escape") {
170
+ const wasSelecting = recording.recordingState() === "selecting";
171
+ const wasLocatorActive = locatorActive();
172
+ if (wasSelecting || wasLocatorActive) {
173
+ e.preventDefault();
174
+ e.stopPropagation();
175
+ if (wasLocatorActive) setLocatorActive(false);
176
+ if (wasSelecting) recording.handleRecordClick();
177
+ setCurrentElement(null);
178
+ }
179
+ }
180
+ },
181
+ keyUpListener(e) {
182
+ setHoldingModKey(isCombinationModifiersPressed(e));
183
+ setHoldingShift(e.shiftKey);
184
+ },
185
+ clickListener,
186
+ mouseDownUpListener(e) {
187
+ setHoldingModKey(e.altKey);
188
+ if (e.altKey || locatorActive() || recording.recordingState() === "selecting") {
189
+ e.preventDefault();
190
+ e.stopPropagation();
191
+ }
192
+ },
193
+ scrollListener() {
194
+ setCurrentElement(null);
195
+ },
196
+ onCleanup: recording.cleanup
542
197
  });
543
198
  return [_$memo(() => _$memo(() => !!isActive())() ? _$createComponent(MaybeOutline, {
544
199
  get currentElement() {
@@ -553,24 +208,24 @@ function Runtime(props) {
553
208
  get dashed() {
554
209
  return holdingShift();
555
210
  }
556
- }) : null), _$memo(() => _$memo(() => !!(recordingState() === 'recording' && recordedElement()))() ? _$createComponent(RecordingOutline, {
211
+ }) : null), _$memo(() => _$memo(() => !!(recording.recordingState() === "recording" && recording.recordedElement()))() ? _$createComponent(RecordingOutline, {
557
212
  get element() {
558
- return recordedElement();
213
+ return recording.recordedElement();
559
214
  }
560
- }) : null), _$memo(() => _$memo(() => !!replayBox())() ? (() => {
561
- var _el$8 = _tmpl$2();
562
- _$setStyleProperty(_el$8, "position", "fixed");
563
- _$setStyleProperty(_el$8, "background", "rgba(59, 130, 246, 0.4)");
564
- _$setStyleProperty(_el$8, "border", "2px solid #3b82f6");
215
+ }) : null), _$memo(() => _$memo(() => !!recording.replayBox())() ? (() => {
216
+ var _el$ = _tmpl$();
217
+ _$setStyleProperty(_el$, "position", "fixed");
218
+ _$setStyleProperty(_el$, "background", "rgba(59, 130, 246, 0.4)");
219
+ _$setStyleProperty(_el$, "border", "2px solid #3b82f6");
565
220
  _$effect(_p$ => {
566
- var _v$4 = replayBox().x + "px",
567
- _v$5 = replayBox().y + "px",
568
- _v$6 = replayBox().w + "px",
569
- _v$7 = replayBox().h + "px";
570
- _v$4 !== _p$.e && _$setStyleProperty(_el$8, "left", _p$.e = _v$4);
571
- _v$5 !== _p$.t && _$setStyleProperty(_el$8, "top", _p$.t = _v$5);
572
- _v$6 !== _p$.a && _$setStyleProperty(_el$8, "width", _p$.a = _v$6);
573
- _v$7 !== _p$.o && _$setStyleProperty(_el$8, "height", _p$.o = _v$7);
221
+ var _v$ = recording.replayBox().x + "px",
222
+ _v$2 = recording.replayBox().y + "px",
223
+ _v$3 = recording.replayBox().w + "px",
224
+ _v$4 = recording.replayBox().h + "px";
225
+ _v$ !== _p$.e && _$setStyleProperty(_el$, "left", _p$.e = _v$);
226
+ _v$2 !== _p$.t && _$setStyleProperty(_el$, "top", _p$.t = _v$2);
227
+ _v$3 !== _p$.a && _$setStyleProperty(_el$, "width", _p$.a = _v$3);
228
+ _v$4 !== _p$.o && _$setStyleProperty(_el$, "height", _p$.o = _v$4);
574
229
  return _p$;
575
230
  }, {
576
231
  e: undefined,
@@ -578,118 +233,71 @@ function Runtime(props) {
578
233
  a: undefined,
579
234
  o: undefined
580
235
  });
581
- return _el$8;
582
- })() : null), _$memo(() => _$memo(() => recordingState() === 'results')() ? _$createComponent(RecordingResults, {
236
+ return _el$;
237
+ })() : null), _$memo(() => _$memo(() => recording.recordingState() === "results")() ? _$createComponent(RecordingResults, {
583
238
  get findings() {
584
- return recordingFindings();
239
+ return recording.recordingFindings();
585
240
  },
586
241
  get summary() {
587
- return recordingSummary();
242
+ return recording.recordingSummary();
588
243
  },
589
244
  get data() {
590
- return recordingData();
245
+ return recording.recordingData();
591
246
  },
592
247
  get elementPath() {
593
- return recordingElementPath();
248
+ return recording.recordingElementPath();
594
249
  },
595
250
  get interactions() {
596
- return interactionLog();
251
+ return recording.interactionLog();
252
+ },
253
+ get visualDiff() {
254
+ return recording.visualDiff();
255
+ },
256
+ get onDismiss() {
257
+ return recording.dismissResults;
258
+ },
259
+ get onReplay() {
260
+ return recording.replayRecording;
597
261
  },
598
- onDismiss: dismissResults,
599
- onReplay: replayRecording,
600
262
  get replaying() {
601
- return replaying();
263
+ return recording.replaying();
602
264
  },
603
265
  onToast: setToastMessage,
604
266
  get hasPrevious() {
605
- return _$memo(() => !!!viewingPrevious())() && hasPreviousRecording();
267
+ return _$memo(() => !!!recording.viewingPrevious())() && recording.hasPreviousRecording();
268
+ },
269
+ get onLoadPrevious() {
270
+ return recording.loadPreviousRecording;
606
271
  },
607
- onLoadPrevious: loadPreviousRecording,
608
272
  get hasNext() {
609
- return viewingPrevious();
273
+ return recording.viewingPrevious();
610
274
  },
611
- onLoadNext: loadLatestRecording
275
+ get onLoadNext() {
276
+ return recording.loadLatestRecording;
277
+ }
278
+ }) : null), _$memo(() => _$memo(() => !!settingsOpen())() ? _$createComponent(SettingsPanel, {
279
+ onDismiss: () => setSettingsOpen(false)
612
280
  }) : null), _$memo(() => _$memo(() => !!toastMessage())() && _$createComponent(Toast, {
613
281
  get message() {
614
282
  return toastMessage();
615
283
  },
616
284
  onClose: () => setToastMessage(null)
617
- })), (() => {
618
- var _el$ = _tmpl$(),
619
- _el$2 = _el$.firstChild,
620
- _el$3 = _el$2.nextSibling,
621
- _el$4 = _el$3.firstChild,
622
- _el$5 = _el$4.firstChild,
623
- _el$6 = _el$4.nextSibling,
624
- _el$7 = _el$3.nextSibling;
625
- _$setStyleProperty(_el$, "bottom", "20px");
626
- _$setStyleProperty(_el$, "right", "20px");
627
- _$setStyleProperty(_el$3, "display", "flex");
628
- _$setStyleProperty(_el$3, "overflow", "hidden");
629
- _$setStyleProperty(_el$3, "transition", "box-shadow 0.2s ease-in-out");
630
- _el$4.$$click = () => setLocatorActive(!locatorActive());
631
- _el$4.addEventListener("mouseleave", e => e.currentTarget.style.background = "#ffffff");
632
- _el$4.addEventListener("mouseenter", e => e.currentTarget.style.background = "#f0f0f0");
633
- _$setStyleProperty(_el$4, "width", "54px");
634
- _$setStyleProperty(_el$4, "height", "54px");
635
- _$setStyleProperty(_el$4, "background", "#ffffff");
636
- _$setStyleProperty(_el$4, "display", "flex");
637
- _$setStyleProperty(_el$4, "cursor", "pointer");
638
- _$setStyleProperty(_el$4, "overflow", "hidden");
639
- _$setStyleProperty(_el$4, "transition", "background 0.15s ease-in-out");
640
- _$setAttribute(_el$5, "src", treeIconUrl);
641
- _el$6.$$click = handleRecordClick;
642
- _el$6.addEventListener("mouseleave", e => {
643
- if (recordingState() !== 'recording') e.currentTarget.style.background = "#ffffff";
644
- });
645
- _el$6.addEventListener("mouseenter", e => {
646
- if (recordingState() !== 'recording') e.currentTarget.style.background = "#f0f0f0";
647
- });
648
- _$setStyleProperty(_el$6, "width", "54px");
649
- _$setStyleProperty(_el$6, "height", "54px");
650
- _$setStyleProperty(_el$6, "display", "flex");
651
- _$setStyleProperty(_el$6, "cursor", "pointer");
652
- _$setStyleProperty(_el$6, "transition", "background 0.15s ease-in-out");
653
- _$insert(_el$6, (() => {
654
- var _c$ = _$memo(() => recordingState() === 'recording');
655
- return () => _c$() ? (() => {
656
- var _el$9 = _tmpl$3();
657
- _$setStyleProperty(_el$9, "width", "18px");
658
- _$setStyleProperty(_el$9, "height", "18px");
659
- _$setStyleProperty(_el$9, "background", "#fff");
660
- return _el$9;
661
- })() : (() => {
662
- var _el$0 = _tmpl$4();
663
- _$setStyleProperty(_el$0, "width", "18px");
664
- _$setStyleProperty(_el$0, "height", "18px");
665
- _$setStyleProperty(_el$0, "background", "#ef4444");
666
- _$effect(_$p => _$setStyleProperty(_el$0, "animation", recordingState() === 'selecting' ? "treelocator-rec-pulse 1s ease-in-out infinite" : "none"));
667
- return _el$0;
668
- })();
669
- })());
670
- _$setStyleProperty(_el$7, "position", "absolute");
671
- _$setStyleProperty(_el$7, "width", "1px");
672
- _$setStyleProperty(_el$7, "height", "1px");
673
- _$setStyleProperty(_el$7, "padding", "0");
674
- _$setStyleProperty(_el$7, "margin", "-1px");
675
- _$setStyleProperty(_el$7, "overflow", "hidden");
676
- _$setStyleProperty(_el$7, "clip", "rect(0,0,0,0)");
677
- _$setStyleProperty(_el$7, "border", "0");
678
- _$effect(_p$ => {
679
- var _v$ = locatorActive() ? "0 0 0 3px #3b82f6, 0 4px 14px rgba(0, 0, 0, 0.25)" : recordingState() === 'selecting' ? "0 0 0 3px #3b82f6, 0 4px 14px rgba(0, 0, 0, 0.25)" : recordingState() === 'recording' ? "0 0 0 3px #ef4444, 0 4px 14px rgba(0, 0, 0, 0.25)" : "0 4px 14px rgba(0, 0, 0, 0.25)",
680
- _v$2 = recordingState() === 'recording' ? "#ef4444" : "#ffffff",
681
- _v$3 = recordingState() === 'idle' ? "Record element changes. API: window.__treelocator__.replayWithRecord(selector)" : recordingState() === 'selecting' ? "Cancel recording selection" : recordingState() === 'recording' ? "Stop recording" : "Dismiss results";
682
- _v$ !== _p$.e && _$setStyleProperty(_el$3, "box-shadow", _p$.e = _v$);
683
- _v$2 !== _p$.t && _$setStyleProperty(_el$6, "background", _p$.t = _v$2);
684
- _v$3 !== _p$.a && _$setAttribute(_el$6, "aria-label", _p$.a = _v$3);
685
- return _p$;
686
- }, {
687
- e: undefined,
688
- t: undefined,
689
- a: undefined
690
- });
691
- return _el$;
692
- })()];
285
+ })), _$createComponent(RecordingPillButton, {
286
+ get locatorActive() {
287
+ return locatorActive();
288
+ },
289
+ get recordingState() {
290
+ return recording.recordingState();
291
+ },
292
+ get settingsOpen() {
293
+ return settingsOpen();
294
+ },
295
+ onLocatorToggle: () => setLocatorActive(!locatorActive()),
296
+ get onRecordClick() {
297
+ return recording.handleRecordClick;
298
+ },
299
+ onSettingsClick: () => setSettingsOpen(!settingsOpen())
300
+ })];
693
301
  }
694
302
  export function initRender(solidLayer, adapter, targets) {
695
303
  render(() => _$createComponent(Runtime, {
@@ -703,5 +311,4 @@ export function initRender(solidLayer, adapter, targets) {
703
311
  },
704
312
  adapterId: adapter
705
313
  }), solidLayer);
706
- }
707
- _$delegateEvents(["click"]);
314
+ }