browsertrack 0.1.2 → 0.2.1

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 (52) hide show
  1. package/AGENTS.md +6 -2
  2. package/dist/{chunk-ILRYKMME.js → chunk-3HOXPTM2.js} +97 -3
  3. package/dist/chunk-3HOXPTM2.js.map +1 -0
  4. package/dist/{chunk-G2Y3CXCY.js → chunk-464D4U2U.js} +86 -3
  5. package/dist/chunk-464D4U2U.js.map +1 -0
  6. package/dist/{chunk-SPCIROIU.js → chunk-7OCOQGDN.js} +24 -5
  7. package/dist/chunk-7OCOQGDN.js.map +1 -0
  8. package/dist/{chunk-SKCMT2DE.js → chunk-INXDWPJW.js} +806 -160
  9. package/dist/chunk-INXDWPJW.js.map +1 -0
  10. package/dist/cli/index.js +202 -6
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/client/index.cjs +809 -161
  13. package/dist/client/index.d.ts +62 -11
  14. package/dist/client/index.js +7 -3
  15. package/dist/client.iife.js +206 -27
  16. package/dist/{notes-CBvN91Wf.d.ts → commands-fjuqKzkm.d.ts} +125 -91
  17. package/dist/core/index.d.ts +2 -2
  18. package/dist/daemon/index.d.ts +3 -3
  19. package/dist/daemon/index.js +2 -2
  20. package/dist/{engine-CeT9URuN.d.ts → engine-CmchnMDq.d.ts} +13 -2
  21. package/dist/index.d.ts +4 -4
  22. package/dist/index.js +4 -4
  23. package/dist/mcp/index.d.ts +4 -4
  24. package/dist/mcp/index.js +2 -2
  25. package/dist/{projects-CY8ungMt.d.ts → projects-DB7S312i.d.ts} +1 -1
  26. package/dist/{server-Dd8NX2Mk.d.ts → server-DiVmTrIR.d.ts} +1 -1
  27. package/docs/component-resolver.md +108 -0
  28. package/docs/index.md +3 -1
  29. package/docs/mcp-reference.md +15 -2
  30. package/docs/scenarios-flows.md +86 -0
  31. package/docs/visual-notes.md +36 -0
  32. package/package.json +1 -1
  33. package/packages/client/src/client.ts +18 -1
  34. package/packages/client/src/config.ts +84 -1
  35. package/packages/client/src/index.ts +4 -1
  36. package/packages/client/src/interceptors/interaction.ts +3 -0
  37. package/packages/client/src/notes/inspector.ts +640 -166
  38. package/packages/client/src/source/resolver.ts +272 -0
  39. package/packages/core/src/types/events.ts +3 -0
  40. package/packages/core/src/types/notes.ts +36 -0
  41. package/packages/daemon/src/notes/engine.ts +6 -0
  42. package/packages/daemon/src/server/ws.ts +23 -1
  43. package/packages/daemon/src/storage/db.ts +106 -3
  44. package/packages/mcp/src/handlers.ts +59 -0
  45. package/packages/mcp/src/tools.ts +28 -0
  46. package/test/client/component-resolver.test.ts +141 -0
  47. package/test/client/interceptors.test.ts +120 -0
  48. package/test/daemon/scenario-storage.test.ts +158 -0
  49. package/dist/chunk-G2Y3CXCY.js.map +0 -1
  50. package/dist/chunk-ILRYKMME.js.map +0 -1
  51. package/dist/chunk-SKCMT2DE.js.map +0 -1
  52. package/dist/chunk-SPCIROIU.js.map +0 -1
@@ -28,7 +28,9 @@ __export(src_exports, {
28
28
  DEFAULT_OPTIONS: () => DEFAULT_OPTIONS,
29
29
  NoteInspector: () => NoteInspector,
30
30
  getClient: () => getClient,
31
- init: () => init
31
+ init: () => init,
32
+ resolveComponentSource: () => resolveComponentSource,
33
+ shouldHideUIFromUrl: () => shouldHideUIFromUrl
32
34
  });
33
35
  module.exports = __toCommonJS(src_exports);
34
36
 
@@ -167,12 +169,220 @@ function truncate(str, maxLength = 200) {
167
169
  return str.slice(0, maxLength) + "...";
168
170
  }
169
171
 
172
+ // packages/client/src/source/resolver.ts
173
+ function resolveComponentSource(el) {
174
+ if (!el || typeof el !== "object") return void 0;
175
+ const reactInfo = resolveReactComponent(el);
176
+ if (reactInfo) return reactInfo;
177
+ const vueInfo = resolveVueComponent(el);
178
+ if (vueInfo) return vueInfo;
179
+ const svelteInfo = resolveSvelteComponent(el);
180
+ if (svelteInfo) return svelteInfo;
181
+ if (el.tagName && el.tagName.includes("-")) {
182
+ return {
183
+ framework: "web-component",
184
+ componentName: el.tagName.toLowerCase(),
185
+ hierarchy: [el.tagName.toLowerCase()]
186
+ };
187
+ }
188
+ const attrInfo = resolveDataAttributeComponent(el);
189
+ if (attrInfo) return attrInfo;
190
+ return void 0;
191
+ }
192
+ function resolveReactComponent(el) {
193
+ try {
194
+ const fiberKey = Object.keys(el).find(
195
+ (key) => key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$")
196
+ );
197
+ if (!fiberKey) return void 0;
198
+ const hostFiber = el[fiberKey];
199
+ if (!hostFiber) return void 0;
200
+ let sourceFile;
201
+ let sourceLine;
202
+ let sourceColumn;
203
+ let componentName;
204
+ const hierarchy = [];
205
+ let props;
206
+ if (hostFiber._debugSource) {
207
+ sourceFile = hostFiber._debugSource.fileName;
208
+ sourceLine = hostFiber._debugSource.lineNumber;
209
+ sourceColumn = hostFiber._debugSource.columnNumber;
210
+ }
211
+ if (hostFiber._debugOwner) {
212
+ const ownerType = hostFiber._debugOwner.type;
213
+ componentName = getReactComponentName(ownerType);
214
+ if (!sourceFile && hostFiber._debugOwner._debugSource) {
215
+ sourceFile = hostFiber._debugOwner._debugSource.fileName;
216
+ sourceLine = hostFiber._debugOwner._debugSource.lineNumber;
217
+ sourceColumn = hostFiber._debugOwner._debugSource.columnNumber;
218
+ }
219
+ }
220
+ let curr = hostFiber;
221
+ while (curr) {
222
+ const type = curr.type;
223
+ const name = getReactComponentName(type);
224
+ if (name && !name.startsWith("html:") && name !== "Fragment") {
225
+ if (!componentName) {
226
+ componentName = name;
227
+ }
228
+ if (!hierarchy.includes(name)) {
229
+ hierarchy.unshift(name);
230
+ }
231
+ if (!sourceFile && curr._debugSource) {
232
+ sourceFile = curr._debugSource.fileName;
233
+ sourceLine = curr._debugSource.lineNumber;
234
+ sourceColumn = curr._debugSource.columnNumber;
235
+ }
236
+ if (!props && curr.memoizedProps && typeof curr.memoizedProps === "object") {
237
+ props = sanitizeProps(curr.memoizedProps);
238
+ }
239
+ }
240
+ curr = curr.return;
241
+ }
242
+ if (!componentName && !sourceFile) {
243
+ return void 0;
244
+ }
245
+ return {
246
+ framework: "react",
247
+ componentName,
248
+ sourceFile: normalizeFilePath(sourceFile),
249
+ sourceLine,
250
+ sourceColumn,
251
+ hierarchy: hierarchy.length > 0 ? hierarchy : componentName ? [componentName] : void 0,
252
+ props
253
+ };
254
+ } catch {
255
+ return void 0;
256
+ }
257
+ }
258
+ function getReactComponentName(type) {
259
+ if (!type) return void 0;
260
+ if (typeof type === "string") return void 0;
261
+ if (type.displayName) return type.displayName;
262
+ if (type.name) return type.name;
263
+ if (type.render?.displayName) return type.render.displayName;
264
+ if (type.render?.name) return type.render.name;
265
+ return void 0;
266
+ }
267
+ function resolveVueComponent(el) {
268
+ try {
269
+ const vueParent = el.__vueParentComponent || el.__vnode?.ctx;
270
+ if (vueParent) {
271
+ const type = vueParent.type || {};
272
+ const componentName = type.name || type.__name || type.displayName || "AnonymousComponent";
273
+ const sourceFile = type.__file;
274
+ const hierarchy = [];
275
+ let curr = vueParent;
276
+ while (curr) {
277
+ const cType = curr.type || {};
278
+ const cName = cType.name || cType.__name || cType.displayName;
279
+ if (cName && !hierarchy.includes(cName)) {
280
+ hierarchy.unshift(cName);
281
+ }
282
+ curr = curr.parent;
283
+ }
284
+ return {
285
+ framework: "vue",
286
+ componentName,
287
+ sourceFile: normalizeFilePath(sourceFile),
288
+ hierarchy: hierarchy.length > 0 ? hierarchy : [componentName],
289
+ props: vueParent.props ? sanitizeProps(vueParent.props) : void 0
290
+ };
291
+ }
292
+ const vue2Instance = el.__vue__;
293
+ if (vue2Instance) {
294
+ const options = vue2Instance.$options || {};
295
+ const componentName = options.name || options._componentTag || "VueComponent";
296
+ const sourceFile = options.__file;
297
+ return {
298
+ framework: "vue",
299
+ componentName,
300
+ sourceFile: normalizeFilePath(sourceFile),
301
+ hierarchy: [componentName],
302
+ props: vue2Instance.$props ? sanitizeProps(vue2Instance.$props) : void 0
303
+ };
304
+ }
305
+ return void 0;
306
+ } catch {
307
+ return void 0;
308
+ }
309
+ }
310
+ function resolveSvelteComponent(el) {
311
+ try {
312
+ let curr = el;
313
+ while (curr) {
314
+ const meta = curr.__svelte_meta;
315
+ if (meta && meta.loc) {
316
+ return {
317
+ framework: "svelte",
318
+ sourceFile: normalizeFilePath(meta.loc.file),
319
+ sourceLine: meta.loc.line,
320
+ sourceColumn: meta.loc.column,
321
+ componentName: meta.loc.file ? getBaseNameWithoutExt(meta.loc.file) : void 0,
322
+ hierarchy: meta.loc.file ? [getBaseNameWithoutExt(meta.loc.file)] : void 0
323
+ };
324
+ }
325
+ curr = curr.parentElement;
326
+ }
327
+ return void 0;
328
+ } catch {
329
+ return void 0;
330
+ }
331
+ }
332
+ function resolveDataAttributeComponent(el) {
333
+ try {
334
+ const compEl = el.closest("[data-component], [data-component-name], [data-source-file]");
335
+ if (!compEl) return void 0;
336
+ const componentName = compEl.getAttribute("data-component") || compEl.getAttribute("data-component-name") || void 0;
337
+ const sourceFile = compEl.getAttribute("data-source-file") || void 0;
338
+ const lineAttr = compEl.getAttribute("data-source-line");
339
+ const sourceLine = lineAttr ? parseInt(lineAttr, 10) : void 0;
340
+ if (!componentName && !sourceFile) return void 0;
341
+ return {
342
+ framework: "vanilla",
343
+ componentName,
344
+ sourceFile: normalizeFilePath(sourceFile),
345
+ sourceLine: isNaN(sourceLine) ? void 0 : sourceLine,
346
+ hierarchy: componentName ? [componentName] : void 0
347
+ };
348
+ } catch {
349
+ return void 0;
350
+ }
351
+ }
352
+ function normalizeFilePath(filePath) {
353
+ if (!filePath) return void 0;
354
+ const cleaned = filePath.split("?")[0];
355
+ return cleaned;
356
+ }
357
+ function getBaseNameWithoutExt(filePath) {
358
+ const parts = filePath.split("/");
359
+ const last = parts[parts.length - 1] || filePath;
360
+ return last.split(".")[0] || last;
361
+ }
362
+ function sanitizeProps(props) {
363
+ const result = {};
364
+ for (const [key, value] of Object.entries(props)) {
365
+ if (key.startsWith("__") || typeof value === "function") continue;
366
+ if (value === null || value === void 0) {
367
+ result[key] = value;
368
+ } else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
369
+ result[key] = value;
370
+ } else if (Array.isArray(value)) {
371
+ result[key] = `Array(${value.length})`;
372
+ } else if (typeof value === "object") {
373
+ result[key] = "[Object]";
374
+ }
375
+ }
376
+ return result;
377
+ }
378
+
170
379
  // packages/client/src/interceptors/interaction.ts
171
380
  function extractElementSummary(el) {
172
381
  const selector = getSemanticSelector(el);
173
382
  const tag = el.tagName.toLowerCase();
174
383
  const id = el.id || void 0;
175
384
  const classes = Array.from(el.classList || []);
385
+ const componentSource = resolveComponentSource(el);
176
386
  let boundingRect = void 0;
177
387
  let visible = true;
178
388
  try {
@@ -204,7 +414,8 @@ function extractElementSummary(el) {
204
414
  boundingRect,
205
415
  visible,
206
416
  innerText,
207
- outerHTML
417
+ outerHTML,
418
+ componentSource
208
419
  };
209
420
  }
210
421
  function setupInteractionInterceptors(onInteraction) {
@@ -522,10 +733,62 @@ var DEFAULT_OPTIONS = {
522
733
  enabled: true,
523
734
  shortcut: "Alt+Click",
524
735
  showBadges: true,
736
+ showToolbar: true,
525
737
  maskSelectors: ['input[type="password"]', "[data-sensitive]"]
526
738
  },
739
+ hidden: false,
740
+ hideQueryParam: void 0,
527
741
  debug: false
528
742
  };
743
+ var HIDE_VALUES = /* @__PURE__ */ new Set(["0", "false", "hidden", "hide", "off", "none", "disabled", "ui_off", "silent"]);
744
+ function shouldHideUIFromUrl(customParam, searchString) {
745
+ let search = searchString;
746
+ if (search === void 0) {
747
+ if (typeof window === "undefined" || !window.location) return false;
748
+ search = window.location.search;
749
+ }
750
+ if (!search) return false;
751
+ try {
752
+ const params = new URLSearchParams(search);
753
+ if (customParam) {
754
+ const customKeys = Array.isArray(customParam) ? customParam : [customParam];
755
+ for (const key of customKeys) {
756
+ if (params.has(key)) {
757
+ const val = (params.get(key) || "").toLowerCase().trim();
758
+ if (val === "" || val === "1" || val === "true" || HIDE_VALUES.has(val)) {
759
+ return true;
760
+ }
761
+ }
762
+ }
763
+ }
764
+ for (const flag of ["no_bt", "no_browsertrack", "hide_bt", "hide_browsertrack"]) {
765
+ if (params.has(flag)) {
766
+ const val = (params.get(flag) || "").toLowerCase().trim();
767
+ if (val === "" || val === "1" || val === "true" || val === "yes") {
768
+ return true;
769
+ }
770
+ }
771
+ }
772
+ if (params.has("bt")) {
773
+ const val = (params.get("bt") || "").toLowerCase().trim();
774
+ if (HIDE_VALUES.has(val)) return true;
775
+ }
776
+ if (params.has("browsertrack")) {
777
+ const val = (params.get("browsertrack") || "").toLowerCase().trim();
778
+ if (HIDE_VALUES.has(val)) return true;
779
+ }
780
+ if (params.has("bt_ui")) {
781
+ const val = (params.get("bt_ui") || "").toLowerCase().trim();
782
+ if (HIDE_VALUES.has(val) || val === "0" || val === "false") return true;
783
+ }
784
+ if (params.has("bt_hide")) {
785
+ const val = (params.get("bt_hide") || "").toLowerCase().trim();
786
+ if (val === "" || val === "1" || val === "true") return true;
787
+ }
788
+ } catch {
789
+ }
790
+ return false;
791
+ }
529
792
 
530
793
  // packages/client/src/interceptors/console.ts
531
794
  function setupConsoleInterceptors(onConsole) {
@@ -1184,7 +1447,9 @@ var NoteInspector = class {
1184
1447
  __publicField(this, "toolbarElement", null);
1185
1448
  __publicField(this, "modalOverlay", null);
1186
1449
  __publicField(this, "cardOverlay", null);
1450
+ __publicField(this, "toastContainer", null);
1187
1451
  __publicField(this, "activeMode", "idle");
1452
+ __publicField(this, "activeScenario", null);
1188
1453
  __publicField(this, "hoveredElement", null);
1189
1454
  __publicField(this, "selectedElement", null);
1190
1455
  __publicField(this, "selectedRegion", null);
@@ -1193,13 +1458,18 @@ var NoteInspector = class {
1193
1458
  __publicField(this, "dragStartY", 0);
1194
1459
  __publicField(this, "savedNotes", []);
1195
1460
  __publicField(this, "showMarkers", true);
1461
+ __publicField(this, "isHidden", false);
1196
1462
  __publicField(this, "cleanups", []);
1197
1463
  this.transport = transport;
1198
1464
  this.screenshotDriver = screenshotDriver;
1465
+ const hiddenByQuery = shouldHideUIFromUrl(options.hideQueryParam);
1466
+ this.isHidden = options.hidden === true || hiddenByQuery;
1467
+ this.showMarkers = options.showBadges !== false && !this.isHidden;
1199
1468
  this.options = {
1200
1469
  shortcut: "Alt+Click",
1201
1470
  maskSelectors: ['input[type="password"]', "[data-sensitive]"],
1202
1471
  showToolbar: true,
1472
+ showBadges: true,
1203
1473
  ...options
1204
1474
  };
1205
1475
  }
@@ -1225,6 +1495,41 @@ var NoteInspector = class {
1225
1495
  this.updateToolbarCount();
1226
1496
  this.renderMarkers();
1227
1497
  }
1498
+ showToast(message, icon = "\u2728", durationMs = 2500) {
1499
+ if (typeof document === "undefined") return;
1500
+ const root = this.ensureContainer();
1501
+ if (!root || !this.toastContainer) return;
1502
+ const toast = document.createElement("div");
1503
+ toast.className = "bt-toast";
1504
+ toast.innerHTML = `<span>${icon}</span> <span>${message}</span>`;
1505
+ this.toastContainer.appendChild(toast);
1506
+ setTimeout(() => {
1507
+ toast.classList.add("bt-toast-fadeout");
1508
+ setTimeout(() => {
1509
+ if (toast.parentElement) {
1510
+ toast.parentElement.removeChild(toast);
1511
+ }
1512
+ }, 250);
1513
+ }, durationMs);
1514
+ }
1515
+ startScenario(title) {
1516
+ const defaultTitle = title || `Scenario ${(/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
1517
+ this.activeScenario = {
1518
+ id: `scen_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`,
1519
+ title: defaultTitle,
1520
+ stepNumber: 1
1521
+ };
1522
+ this.updateToolbarState();
1523
+ this.setMode("element");
1524
+ this.showToast(`Started flow: "${defaultTitle}"`, "\u{1F3AC}");
1525
+ }
1526
+ finishScenario() {
1527
+ const title = this.activeScenario?.title;
1528
+ this.activeScenario = null;
1529
+ this.updateToolbarState();
1530
+ this.setMode("idle");
1531
+ this.showToast(title ? `Finished flow: "${title}"` : "Flow recording finished", "\u2713");
1532
+ }
1228
1533
  setMode(mode) {
1229
1534
  this.activeMode = mode;
1230
1535
  this.updateToolbarState();
@@ -1242,12 +1547,48 @@ var NoteInspector = class {
1242
1547
  this.hideHighlight();
1243
1548
  }
1244
1549
  }
1550
+ isVisible() {
1551
+ return !this.isHidden;
1552
+ }
1553
+ setVisible(visible) {
1554
+ this.isHidden = !visible;
1555
+ this.showMarkers = this.options.showBadges !== false && !this.isHidden;
1556
+ if (this.container) {
1557
+ this.container.style.display = this.isHidden ? "none" : "block";
1558
+ }
1559
+ if (this.toolbarElement) {
1560
+ this.toolbarElement.style.display = this.isHidden ? "none" : "flex";
1561
+ } else if (!this.isHidden && this.options.showToolbar !== false) {
1562
+ this.createToolbar();
1563
+ }
1564
+ if (this.isHidden) {
1565
+ this.hideHighlight();
1566
+ this.hideRegionOverlay();
1567
+ if (this.modalOverlay && this.shadowRoot) {
1568
+ this.shadowRoot.removeChild(this.modalOverlay);
1569
+ this.modalOverlay = null;
1570
+ }
1571
+ if (this.cardOverlay && this.shadowRoot) {
1572
+ this.shadowRoot.removeChild(this.cardOverlay);
1573
+ this.cardOverlay = null;
1574
+ }
1575
+ if (this.markersContainer) {
1576
+ this.markersContainer.innerHTML = "";
1577
+ }
1578
+ } else {
1579
+ this.renderMarkers();
1580
+ this.updateToolbarCount();
1581
+ }
1582
+ }
1245
1583
  ensureContainer() {
1246
1584
  if (typeof document === "undefined") return null;
1247
1585
  if (!this.container) {
1248
1586
  this.container = document.createElement("div");
1249
1587
  this.container.id = "browsertrack-inspector-host";
1250
1588
  this.container.style.cssText = "all: initial; position: fixed; top: 0; left: 0; width: 0; height: 0; z-index: 2147483647; pointer-events: none;";
1589
+ if (this.isHidden) {
1590
+ this.container.style.display = "none";
1591
+ }
1251
1592
  this.shadowRoot = this.container.attachShadow({ mode: "open" });
1252
1593
  const style = document.createElement("style");
1253
1594
  style.textContent = `
@@ -1404,6 +1745,12 @@ var NoteInspector = class {
1404
1745
  z-index: 2147483641;
1405
1746
  }
1406
1747
 
1748
+ .bt-note-marker-step {
1749
+ background: linear-gradient(135deg, #f59e0b, #d97706);
1750
+ border-color: #fef3c7;
1751
+ box-shadow: 0 4px 14px rgba(245, 158, 11, 0.4), 0 0 0 1px rgba(217, 119, 6, 0.5);
1752
+ }
1753
+
1407
1754
  @keyframes bt-pop-in {
1408
1755
  0% { transform: scale(0.6); opacity: 0; }
1409
1756
  100% { transform: scale(1); opacity: 1; }
@@ -1414,6 +1761,10 @@ var NoteInspector = class {
1414
1761
  box-shadow: 0 8px 20px rgba(37,99,235,0.6), 0 0 0 2px #60a5fa;
1415
1762
  }
1416
1763
 
1764
+ .bt-note-marker-step:hover {
1765
+ box-shadow: 0 8px 20px rgba(245, 158, 11, 0.7), 0 0 0 2px #fde68a;
1766
+ }
1767
+
1417
1768
  .bt-marker-resolved {
1418
1769
  background: linear-gradient(135deg, #475569, #334155);
1419
1770
  border-color: #94a3b8;
@@ -1510,6 +1861,19 @@ var NoteInspector = class {
1510
1861
  box-shadow: 0 2px 6px rgba(37, 99, 235, 0.35);
1511
1862
  }
1512
1863
 
1864
+ .bt-toolbar-btn.active-scenario {
1865
+ background: linear-gradient(135deg, #f59e0b, #d97706);
1866
+ color: #ffffff;
1867
+ font-weight: 700;
1868
+ box-shadow: 0 2px 8px rgba(245, 158, 11, 0.4);
1869
+ animation: bt-pulse 2s infinite;
1870
+ }
1871
+
1872
+ @keyframes bt-pulse {
1873
+ 0%, 100% { opacity: 1; }
1874
+ 50% { opacity: 0.85; }
1875
+ }
1876
+
1513
1877
  .bt-count-pill {
1514
1878
  background: rgba(255, 255, 255, 0.2);
1515
1879
  color: #ffffff;
@@ -1557,7 +1921,7 @@ var NoteInspector = class {
1557
1921
  border: 1px solid #334155;
1558
1922
  border-radius: 14px;
1559
1923
  padding: 20px;
1560
- width: 450px;
1924
+ width: 470px;
1561
1925
  max-width: 92vw;
1562
1926
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.05);
1563
1927
  display: flex;
@@ -1580,6 +1944,7 @@ var NoteInspector = class {
1580
1944
  align-items: center;
1581
1945
  gap: 8px;
1582
1946
  color: #f8fafc;
1947
+ flex-wrap: wrap;
1583
1948
  }
1584
1949
 
1585
1950
  .bt-mode-badge {
@@ -1609,6 +1974,12 @@ var NoteInspector = class {
1609
1974
  border: 1px solid rgba(168, 85, 247, 0.4);
1610
1975
  }
1611
1976
 
1977
+ .bt-badge-step {
1978
+ background: rgba(245, 158, 11, 0.15);
1979
+ color: #fbbf24;
1980
+ border: 1px solid rgba(245, 158, 11, 0.4);
1981
+ }
1982
+
1612
1983
  .bt-badge-status-open {
1613
1984
  background: rgba(16, 185, 129, 0.15);
1614
1985
  color: #34d399;
@@ -1660,6 +2031,59 @@ var NoteInspector = class {
1660
2031
  color: #cbd5e1;
1661
2032
  }
1662
2033
 
2034
+ .bt-component-pill {
2035
+ display: flex;
2036
+ align-items: center;
2037
+ gap: 6px;
2038
+ background: rgba(15, 23, 42, 0.95);
2039
+ border: 1px solid rgba(99, 102, 241, 0.35);
2040
+ border-radius: 6px;
2041
+ padding: 6px 10px;
2042
+ font-size: 11.5px;
2043
+ color: #c7d2fe;
2044
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
2045
+ overflow: hidden;
2046
+ text-overflow: ellipsis;
2047
+ white-space: nowrap;
2048
+ }
2049
+
2050
+ .bt-scenario-stepper {
2051
+ display: flex;
2052
+ align-items: center;
2053
+ justify-content: space-between;
2054
+ background: #090d16;
2055
+ border: 1px solid #1e293b;
2056
+ border-radius: 8px;
2057
+ padding: 6px 10px;
2058
+ font-size: 12px;
2059
+ font-weight: 600;
2060
+ color: #f59e0b;
2061
+ }
2062
+
2063
+ .bt-step-nav-btn {
2064
+ background: #1e293b;
2065
+ color: #f8fafc;
2066
+ border: 1px solid #334155;
2067
+ padding: 4px 10px;
2068
+ border-radius: 6px;
2069
+ cursor: pointer;
2070
+ font-size: 11px;
2071
+ font-weight: 600;
2072
+ transition: all 0.12s ease;
2073
+ font-family: inherit;
2074
+ }
2075
+
2076
+ .bt-step-nav-btn:hover:not(:disabled) {
2077
+ background: #2563eb;
2078
+ border-color: #3b82f6;
2079
+ color: #ffffff;
2080
+ }
2081
+
2082
+ .bt-step-nav-btn:disabled {
2083
+ opacity: 0.3;
2084
+ cursor: not-allowed;
2085
+ }
2086
+
1663
2087
  .bt-note-message-box {
1664
2088
  background: #090d16;
1665
2089
  border: 1px solid #1e293b;
@@ -1704,8 +2128,9 @@ var NoteInspector = class {
1704
2128
  display: flex;
1705
2129
  justify-content: space-between;
1706
2130
  align-items: center;
1707
- gap: 12px;
2131
+ gap: 10px;
1708
2132
  margin-top: 2px;
2133
+ flex-wrap: wrap;
1709
2134
  }
1710
2135
 
1711
2136
  .bt-kbd-hint {
@@ -1733,12 +2158,13 @@ var NoteInspector = class {
1733
2158
  justify-content: flex-end;
1734
2159
  align-items: center;
1735
2160
  gap: 8px;
2161
+ flex-wrap: wrap;
1736
2162
  }
1737
2163
 
1738
2164
  .bt-btn {
1739
- padding: 8px 16px;
2165
+ padding: 7px 14px;
1740
2166
  border-radius: 7px;
1741
- font-size: 12.5px;
2167
+ font-size: 12px;
1742
2168
  font-weight: 600;
1743
2169
  cursor: pointer;
1744
2170
  border: 1px solid transparent;
@@ -1747,7 +2173,7 @@ var NoteInspector = class {
1747
2173
  display: inline-flex;
1748
2174
  align-items: center;
1749
2175
  justify-content: center;
1750
- gap: 6px;
2176
+ gap: 5px;
1751
2177
  }
1752
2178
 
1753
2179
  .bt-btn-cancel {
@@ -1769,6 +2195,25 @@ var NoteInspector = class {
1769
2195
  box-shadow: 0 4px 10px rgba(37, 99, 235, 0.45);
1770
2196
  }
1771
2197
 
2198
+ .bt-btn-next-step {
2199
+ background: linear-gradient(135deg, #6366f1, #4f46e5);
2200
+ color: #ffffff;
2201
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.4);
2202
+ }
2203
+ .bt-btn-next-step:hover {
2204
+ background: linear-gradient(135deg, #4f46e5, #4338ca);
2205
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.55);
2206
+ }
2207
+
2208
+ .bt-btn-finish-flow {
2209
+ background: linear-gradient(135deg, #10b981, #059669);
2210
+ color: #ffffff;
2211
+ box-shadow: 0 2px 8px rgba(16, 185, 129, 0.35);
2212
+ }
2213
+ .bt-btn-finish-flow:hover {
2214
+ background: linear-gradient(135deg, #059669, #047857);
2215
+ }
2216
+
1772
2217
  .bt-btn-resolve {
1773
2218
  background: rgba(16, 185, 129, 0.15);
1774
2219
  color: #6ee7b7;
@@ -1788,6 +2233,47 @@ var NoteInspector = class {
1788
2233
  background: rgba(239, 68, 68, 0.3);
1789
2234
  color: #ffffff;
1790
2235
  }
2236
+
2237
+ /* 6. Toast Notifications */
2238
+ .bt-toast-container {
2239
+ position: fixed;
2240
+ bottom: 74px;
2241
+ right: 24px;
2242
+ display: flex;
2243
+ flex-direction: column;
2244
+ gap: 8px;
2245
+ pointer-events: none;
2246
+ z-index: 2147483647;
2247
+ align-items: flex-end;
2248
+ }
2249
+
2250
+ .bt-toast {
2251
+ background: #0f172a;
2252
+ color: #f8fafc;
2253
+ border: 1px solid #334155;
2254
+ border-radius: 30px;
2255
+ padding: 8px 16px;
2256
+ font-size: 12.5px;
2257
+ font-weight: 500;
2258
+ display: inline-flex;
2259
+ align-items: center;
2260
+ gap: 8px;
2261
+ box-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.6), 0 4px 6px -4px rgba(0, 0, 0, 0.4);
2262
+ pointer-events: auto;
2263
+ animation: bt-toast-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
2264
+ transition: opacity 0.25s ease, transform 0.25s ease;
2265
+ user-select: none;
2266
+ }
2267
+
2268
+ .bt-toast-fadeout {
2269
+ opacity: 0;
2270
+ transform: translateY(6px);
2271
+ }
2272
+
2273
+ @keyframes bt-toast-in {
2274
+ from { opacity: 0; transform: translateY(8px) scale(0.95); }
2275
+ to { opacity: 1; transform: translateY(0) scale(1); }
2276
+ }
1791
2277
  `;
1792
2278
  this.shadowRoot.appendChild(style);
1793
2279
  this.highlightOverlay = document.createElement("div");
@@ -1797,7 +2283,10 @@ var NoteInspector = class {
1797
2283
  this.markersContainer = document.createElement("div");
1798
2284
  this.markersContainer.className = "bt-markers-layer";
1799
2285
  this.shadowRoot.appendChild(this.markersContainer);
1800
- if (this.options.showToolbar) {
2286
+ this.toastContainer = document.createElement("div");
2287
+ this.toastContainer.className = "bt-toast-container";
2288
+ this.shadowRoot.appendChild(this.toastContainer);
2289
+ if (this.options.showToolbar && !this.isHidden) {
1801
2290
  this.createToolbar();
1802
2291
  }
1803
2292
  }
@@ -1824,6 +2313,10 @@ var NoteInspector = class {
1824
2313
  <span>\u{1F4C4}</span> Page
1825
2314
  </button>
1826
2315
  <div class="bt-toolbar-divider"></div>
2316
+ <button class="bt-toolbar-btn" id="bt-mode-flow" title="Record multi-step reproduction flow">
2317
+ <span>\u{1F3AC}</span> Flow
2318
+ </button>
2319
+ <div class="bt-toolbar-divider"></div>
1827
2320
  <button class="bt-toolbar-btn active" id="bt-toggle-notes" title="Toggle visible note markers on screen">
1828
2321
  <span>\u{1F4CC}</span> Notes <span class="bt-count-pill" id="bt-notes-count">0</span>
1829
2322
  </button>
@@ -1831,6 +2324,7 @@ var NoteInspector = class {
1831
2324
  const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
1832
2325
  const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
1833
2326
  const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
2327
+ const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
1834
2328
  const btnToggleNotes = this.toolbarElement.querySelector("#bt-toggle-notes");
1835
2329
  btnElement.onclick = (e) => {
1836
2330
  e.stopPropagation();
@@ -1844,6 +2338,14 @@ var NoteInspector = class {
1844
2338
  e.stopPropagation();
1845
2339
  this.setMode("page");
1846
2340
  };
2341
+ btnFlow.onclick = (e) => {
2342
+ e.stopPropagation();
2343
+ if (this.activeScenario) {
2344
+ this.finishScenario();
2345
+ } else {
2346
+ this.startScenario();
2347
+ }
2348
+ };
1847
2349
  btnToggleNotes.onclick = (e) => {
1848
2350
  e.stopPropagation();
1849
2351
  this.showMarkers = !this.showMarkers;
@@ -1857,9 +2359,21 @@ var NoteInspector = class {
1857
2359
  const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
1858
2360
  const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
1859
2361
  const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
2362
+ const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
1860
2363
  btnElement?.classList.toggle("active", this.activeMode === "element");
1861
2364
  btnRegion?.classList.toggle("active", this.activeMode === "region");
1862
2365
  btnPage?.classList.toggle("active", this.activeMode === "page");
2366
+ if (btnFlow) {
2367
+ if (this.activeScenario) {
2368
+ btnFlow.className = "bt-toolbar-btn active-scenario";
2369
+ btnFlow.innerHTML = `<span>\u{1F3AC}</span> Step ${this.activeScenario.stepNumber} (Finish)`;
2370
+ btnFlow.setAttribute("title", `Click to finish recording "${this.activeScenario.title}"`);
2371
+ } else {
2372
+ btnFlow.className = "bt-toolbar-btn";
2373
+ btnFlow.innerHTML = `<span>\u{1F3AC}</span> Flow`;
2374
+ btnFlow.setAttribute("title", "Record multi-step reproduction flow");
2375
+ }
2376
+ }
1863
2377
  }
1864
2378
  updateToolbarCount() {
1865
2379
  if (!this.toolbarElement) return;
@@ -1873,7 +2387,7 @@ var NoteInspector = class {
1873
2387
  const root = this.ensureContainer();
1874
2388
  if (!root || !this.markersContainer) return;
1875
2389
  this.markersContainer.innerHTML = "";
1876
- if (!this.showMarkers) return;
2390
+ if (!this.showMarkers || this.isHidden) return;
1877
2391
  const currentPath = window.location.pathname;
1878
2392
  const activeNotes = this.savedNotes.filter(
1879
2393
  (n) => n.status === "OPEN" && (n.route === currentPath || !n.route || n.route === "/" || window.location.href.includes(n.route))
@@ -1884,6 +2398,9 @@ var NoteInspector = class {
1884
2398
  pageNotes.push(note);
1885
2399
  return;
1886
2400
  }
2401
+ const isStep = !!note.scenarioId && note.stepNumber != null;
2402
+ const stepLabel = isStep ? `\u{1F3AC} Step ${note.stepNumber}` : `#${index + 1}`;
2403
+ const markerClass = `bt-note-marker ${isStep ? "bt-note-marker-step" : ""}`;
1887
2404
  if (note.type === "region" && note.region) {
1888
2405
  const regBox = document.createElement("div");
1889
2406
  regBox.className = "bt-region-marker-box";
@@ -1893,11 +2410,11 @@ var NoteInspector = class {
1893
2410
  regBox.style.height = `${note.region.height}px`;
1894
2411
  this.markersContainer.appendChild(regBox);
1895
2412
  const pin = document.createElement("div");
1896
- pin.className = "bt-note-marker";
2413
+ pin.className = markerClass;
1897
2414
  pin.style.left = `${Math.max(4, note.region.x - 12)}px`;
1898
2415
  pin.style.top = `${Math.max(4, note.region.y - 12)}px`;
1899
- pin.title = note.message;
1900
- pin.innerHTML = `<span>\u{1F4D0}</span> <span>#${index + 1}</span>`;
2416
+ pin.title = isStep ? `[${note.scenarioTitle || "Scenario"}] Step ${note.stepNumber}: ${note.message}` : note.message;
2417
+ pin.innerHTML = `<span>${isStep ? "\u{1F3AC}" : "\u{1F4D0}"}</span> <span>${stepLabel}</span>`;
1901
2418
  pin.onclick = (e) => {
1902
2419
  e.stopPropagation();
1903
2420
  this.openNoteCard(note);
@@ -1915,12 +2432,12 @@ var NoteInspector = class {
1915
2432
  const rect = targetEl ? targetEl.getBoundingClientRect() : note.target?.boundingRect;
1916
2433
  if (rect) {
1917
2434
  const pin = document.createElement("div");
1918
- pin.className = "bt-note-marker";
2435
+ pin.className = markerClass;
1919
2436
  pin.setAttribute("data-note-id", note.id);
1920
- pin.title = note.message;
2437
+ pin.title = isStep ? `[${note.scenarioTitle || "Scenario"}] Step ${note.stepNumber}: ${note.message}` : note.message;
1921
2438
  pin.style.left = `${Math.max(4, rect.left - 10)}px`;
1922
2439
  pin.style.top = `${Math.max(4, rect.top - 12)}px`;
1923
- pin.innerHTML = `<span>\u{1F4DD}</span> <span>#${index + 1}</span>`;
2440
+ pin.innerHTML = `<span>${isStep ? "\u{1F3AC}" : "\u{1F4DD}"}</span> <span>${stepLabel}</span>`;
1924
2441
  pin.onclick = (e) => {
1925
2442
  e.stopPropagation();
1926
2443
  this.openNoteCard(note);
@@ -1935,10 +2452,11 @@ var NoteInspector = class {
1935
2452
  if (pageNotes.length > 0) {
1936
2453
  const pageDock = document.createElement("div");
1937
2454
  pageDock.className = "bt-page-notes-dock";
1938
- pageNotes.forEach((pNote, idx) => {
2455
+ pageNotes.forEach((pNote) => {
1939
2456
  const pill = document.createElement("div");
1940
2457
  pill.className = "bt-page-note-pill";
1941
- pill.innerHTML = `<span>\u{1F4C4}</span> <span>Page Note (${truncate(pNote.message, 25)})</span>`;
2458
+ const label = pNote.scenarioId && pNote.stepNumber ? `Step ${pNote.stepNumber}: ` : "";
2459
+ pill.innerHTML = `<span>\u{1F4C4}</span> <span>${label}${truncate(pNote.message, 25)}</span>`;
1942
2460
  pill.onclick = (e) => {
1943
2461
  e.stopPropagation();
1944
2462
  this.openNoteCard(pNote);
@@ -1967,17 +2485,46 @@ var NoteInspector = class {
1967
2485
  const icon = note.type === "region" ? "\u{1F4D0}" : note.type === "page" ? "\u{1F4C4}" : "\u{1F3AF}";
1968
2486
  const contextText = note.type === "region" && note.region ? `Region: ${note.region.width} \xD7 ${note.region.height} px \xB7 Route: ${note.route}` : note.type === "page" ? `Page Note \xB7 Route: ${note.route}` : `${note.target?.selector || "Element"} (${note.target?.boundingRect?.width || 0}\xD7${note.target?.boundingRect?.height || 0}px) \xB7 ${note.route}`;
1969
2487
  const formattedDate = new Date(note.createdAt).toLocaleString();
2488
+ let scenarioStepsHtml = "";
2489
+ let scenarioSteps = [];
2490
+ if (note.scenarioId) {
2491
+ scenarioSteps = this.savedNotes.filter((n) => n.scenarioId === note.scenarioId).sort((a, b) => (a.stepNumber || 0) - (b.stepNumber || 0));
2492
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2493
+ const prevStep = currentIndex > 0 ? scenarioSteps[currentIndex - 1] : null;
2494
+ const nextStep = currentIndex >= 0 && currentIndex < scenarioSteps.length - 1 ? scenarioSteps[currentIndex + 1] : null;
2495
+ scenarioStepsHtml = `
2496
+ <div class="bt-scenario-stepper">
2497
+ <button class="bt-step-nav-btn" id="btn-prev-step" ${!prevStep ? "disabled" : ""}>
2498
+ \u25C0 Step ${prevStep ? prevStep.stepNumber : ""}
2499
+ </button>
2500
+ <span>\u{1F3AC} Step ${note.stepNumber || 1} of ${scenarioSteps.length}</span>
2501
+ <button class="bt-step-nav-btn" id="btn-next-step-card" ${!nextStep ? "disabled" : ""}>
2502
+ Step ${nextStep ? nextStep.stepNumber : ""} \u25B6
2503
+ </button>
2504
+ </div>
2505
+ `;
2506
+ }
1970
2507
  backdrop.innerHTML = `
1971
2508
  <div class="bt-modal">
1972
2509
  <div class="bt-modal-header">
1973
2510
  <div class="bt-modal-title">
1974
- <span>${icon} Visual Note Details</span>
2511
+ <span>${note.scenarioId ? "\u{1F3AC} " + (note.scenarioTitle || "Scenario Flow") : icon + " Visual Note Details"}</span>
1975
2512
  <span class="bt-mode-badge ${badgeClass}">${note.type.toUpperCase()}</span>
2513
+ ${note.scenarioId && note.stepNumber ? `<span class="bt-mode-badge bt-badge-step">STEP ${note.stepNumber}</span>` : ""}
1976
2514
  <span class="bt-mode-badge ${statusClass}">${note.status}</span>
1977
2515
  </div>
1978
2516
  <button class="bt-close-btn" id="btn-card-close" title="Close (Esc)">\u2715</button>
1979
2517
  </div>
1980
2518
 
2519
+ ${scenarioStepsHtml}
2520
+
2521
+ ${note.elementContext?.componentSource?.componentName ? `<div class="bt-component-pill" title="${note.elementContext.componentSource.sourceFile ? note.elementContext.componentSource.sourceFile + (note.elementContext.componentSource.sourceLine ? ":" + note.elementContext.componentSource.sourceLine : "") : note.elementContext.componentSource.componentName}">
2522
+ <span>\u{1F9EC}</span>
2523
+ <span style="font-weight: 600; color: #a5b4fc;">&lt;${note.elementContext.componentSource.componentName}&gt;</span>
2524
+ ${note.elementContext.componentSource.sourceFile ? `<span style="color: #64748b; font-size: 11px; margin-left: 4px;">${note.elementContext.componentSource.sourceFile}${note.elementContext.componentSource.sourceLine ? ":" + note.elementContext.componentSource.sourceLine : ""}</span>` : ""}
2525
+ ${note.elementContext.componentSource.framework ? `<span class="bt-mode-badge" style="background: rgba(99, 102, 241, 0.15); color: #818cf8; margin-left: auto; font-size: 10px;">${note.elementContext.componentSource.framework.toUpperCase()}</span>` : ""}
2526
+ </div>` : ""}
2527
+
1981
2528
  <div class="bt-target-pill" title="${contextText}">
1982
2529
  <span>\u{1F3F7}\uFE0F</span>
1983
2530
  <span class="bt-pill-content">${contextText}</span>
@@ -1991,9 +2538,14 @@ var NoteInspector = class {
1991
2538
  </div>
1992
2539
 
1993
2540
  <div class="bt-modal-footer">
1994
- <button class="bt-btn bt-btn-delete" id="btn-card-delete" title="Delete this note">
1995
- <span>\u{1F5D1}\uFE0F</span> Delete
1996
- </button>
2541
+ <div style="display: flex; gap: 6px;">
2542
+ <button class="bt-btn bt-btn-delete" id="btn-card-delete" title="Delete this note">
2543
+ <span>\u{1F5D1}\uFE0F</span> Delete
2544
+ </button>
2545
+ ${note.scenarioId ? `<button class="bt-btn bt-btn-delete" id="btn-card-delete-flow" title="Delete entire scenario flow">
2546
+ <span>\u{1F5D1}\uFE0F</span> Delete Flow
2547
+ </button>` : ""}
2548
+ </div>
1997
2549
  <div class="bt-modal-actions">
1998
2550
  <button class="bt-btn bt-btn-cancel" id="btn-card-dismiss">Close</button>
1999
2551
  <button class="bt-btn ${note.status === "OPEN" ? "bt-btn-resolve" : "bt-btn-save"}" id="btn-card-resolve">
@@ -2007,6 +2559,9 @@ var NoteInspector = class {
2007
2559
  const btnDismiss = backdrop.querySelector("#btn-card-dismiss");
2008
2560
  const btnResolve = backdrop.querySelector("#btn-card-resolve");
2009
2561
  const btnDelete = backdrop.querySelector("#btn-card-delete");
2562
+ const btnDeleteFlow = backdrop.querySelector("#btn-card-delete-flow");
2563
+ const btnPrevStep = backdrop.querySelector("#btn-prev-step");
2564
+ const btnNextStepCard = backdrop.querySelector("#btn-next-step-card");
2010
2565
  const closeCard = () => {
2011
2566
  if (this.cardOverlay) {
2012
2567
  root.removeChild(this.cardOverlay);
@@ -2019,23 +2574,49 @@ var NoteInspector = class {
2019
2574
  backdrop.onclick = (e) => {
2020
2575
  if (e.target === backdrop) closeCard();
2021
2576
  };
2577
+ if (btnPrevStep && scenarioSteps.length > 0) {
2578
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2579
+ if (currentIndex > 0) {
2580
+ btnPrevStep.onclick = () => {
2581
+ this.openNoteCard(scenarioSteps[currentIndex - 1]);
2582
+ };
2583
+ }
2584
+ }
2585
+ if (btnNextStepCard && scenarioSteps.length > 0) {
2586
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2587
+ if (currentIndex >= 0 && currentIndex < scenarioSteps.length - 1) {
2588
+ btnNextStepCard.onclick = () => {
2589
+ this.openNoteCard(scenarioSteps[currentIndex + 1]);
2590
+ };
2591
+ }
2592
+ }
2022
2593
  btnResolve.onclick = () => {
2023
2594
  if (note.status === "OPEN") {
2024
2595
  this.transport.send({ type: "resolve_note", noteId: note.id });
2596
+ this.showToast("Note marked as resolved", "\u2705");
2025
2597
  } else {
2026
2598
  this.transport.send({ type: "reopen_note", noteId: note.id });
2599
+ this.showToast("Note reopened", "\u21BA");
2027
2600
  }
2028
2601
  closeCard();
2029
2602
  };
2030
2603
  btnDelete.onclick = () => {
2031
2604
  this.transport.send({ type: "delete_note", noteId: note.id });
2605
+ this.showToast("Note deleted", "\u{1F5D1}\uFE0F");
2032
2606
  closeCard();
2033
2607
  };
2608
+ if (btnDeleteFlow && note.scenarioId) {
2609
+ btnDeleteFlow.onclick = () => {
2610
+ this.transport.send({ type: "delete_scenario", scenarioId: note.scenarioId });
2611
+ this.showToast("Scenario flow deleted", "\u{1F5D1}\uFE0F");
2612
+ closeCard();
2613
+ };
2614
+ }
2034
2615
  root.appendChild(backdrop);
2035
2616
  }
2036
2617
  setupListeners() {
2037
2618
  const onMouseMove = (e) => {
2038
- if (this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
2619
+ if (this.isHidden || this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
2039
2620
  const path = e.composedPath ? e.composedPath() : [];
2040
2621
  if (this.container && path.includes(this.container)) {
2041
2622
  this.hideHighlight();
@@ -2054,7 +2635,7 @@ var NoteInspector = class {
2054
2635
  }
2055
2636
  };
2056
2637
  const onClick = (e) => {
2057
- if (this.modalOverlay || this.cardOverlay) return;
2638
+ if (this.isHidden || this.modalOverlay || this.cardOverlay) return;
2058
2639
  const path = e.composedPath ? e.composedPath() : [];
2059
2640
  if (this.container && path.includes(this.container)) {
2060
2641
  return;
@@ -2067,7 +2648,7 @@ var NoteInspector = class {
2067
2648
  if (target && target !== this.container && !this.container?.contains(target)) {
2068
2649
  this.selectedElement = target;
2069
2650
  this.openNoteEditor(target, "element");
2070
- if (this.activeMode === "element") {
2651
+ if (this.activeMode === "element" && !this.activeScenario) {
2071
2652
  this.setMode("idle");
2072
2653
  }
2073
2654
  }
@@ -2079,7 +2660,9 @@ var NoteInspector = class {
2079
2660
  this.shadowRoot.removeChild(this.cardOverlay);
2080
2661
  this.cardOverlay = null;
2081
2662
  } else if (this.activeMode === "region" || this.activeMode === "element") {
2082
- this.setMode("idle");
2663
+ if (!this.activeScenario) {
2664
+ this.setMode("idle");
2665
+ }
2083
2666
  }
2084
2667
  }
2085
2668
  };
@@ -2185,28 +2768,27 @@ var NoteInspector = class {
2185
2768
  width: Math.round(width),
2186
2769
  height: Math.round(height)
2187
2770
  };
2188
- this.hideRegionOverlay();
2189
- this.setMode("idle");
2190
2771
  this.openRegionNoteEditor(this.selectedRegion);
2191
- } else {
2192
- this.hideRegionOverlay();
2772
+ }
2773
+ if (this.regionBox) {
2774
+ this.regionBox.style.display = "none";
2775
+ }
2776
+ if (!this.activeScenario) {
2193
2777
  this.setMode("idle");
2194
2778
  }
2195
2779
  };
2196
- }
2197
- if (this.toolbarElement && this.toolbarElement.parentNode === root) {
2198
- root.insertBefore(this.regionOverlay, this.toolbarElement);
2199
- } else {
2200
2780
  root.appendChild(this.regionOverlay);
2781
+ } else {
2782
+ this.regionOverlay.style.display = "block";
2201
2783
  }
2202
2784
  }
2203
2785
  hideRegionOverlay() {
2204
- this.isDraggingRegion = false;
2205
- if (this.regionOverlay && this.regionOverlay.parentNode) {
2206
- this.regionOverlay.parentNode.removeChild(this.regionOverlay);
2207
- }
2208
- if (this.regionBox) {
2209
- this.regionBox.style.display = "none";
2786
+ if (this.regionOverlay) {
2787
+ this.regionOverlay.style.display = "none";
2788
+ this.isDraggingRegion = false;
2789
+ if (this.regionBox) {
2790
+ this.regionBox.style.display = "none";
2791
+ }
2210
2792
  }
2211
2793
  }
2212
2794
  updateHighlight(el) {
@@ -2214,19 +2796,19 @@ var NoteInspector = class {
2214
2796
  if (!root || !this.highlightOverlay) return;
2215
2797
  const rect = el.getBoundingClientRect();
2216
2798
  this.highlightOverlay.style.display = "block";
2217
- this.highlightOverlay.style.top = `${rect.top}px`;
2218
2799
  this.highlightOverlay.style.left = `${rect.left}px`;
2800
+ this.highlightOverlay.style.top = `${rect.top}px`;
2219
2801
  this.highlightOverlay.style.width = `${rect.width}px`;
2220
2802
  this.highlightOverlay.style.height = `${rect.height}px`;
2221
- const selector = getSemanticSelector(el);
2222
- const badgeText = `${selector} \xB7 ${Math.round(rect.width)}\xD7${Math.round(rect.height)}`;
2223
2803
  let badge = this.highlightOverlay.querySelector(".bt-badge");
2224
2804
  if (!badge) {
2225
2805
  badge = document.createElement("div");
2226
2806
  badge.className = "bt-badge";
2227
2807
  this.highlightOverlay.appendChild(badge);
2228
2808
  }
2229
- badge.textContent = badgeText;
2809
+ const selector = getSemanticSelector(el);
2810
+ const label = this.activeScenario ? `\u{1F3AC} Step ${this.activeScenario.stepNumber} \xB7 ${selector}` : selector;
2811
+ badge.textContent = `${label} (${Math.round(rect.width)} \xD7 ${Math.round(rect.height)} px)`;
2230
2812
  }
2231
2813
  hideHighlight() {
2232
2814
  if (this.highlightOverlay) {
@@ -2241,22 +2823,60 @@ var NoteInspector = class {
2241
2823
  if (noteType === "element") {
2242
2824
  this.updateHighlight(targetEl);
2243
2825
  }
2826
+ const comp = noteType === "element" ? resolveComponentSource(targetEl) : void 0;
2827
+ const compPrefix = comp?.componentName ? `\u{1F9EC} <${comp.componentName}>${comp.sourceFile ? " (" + comp.sourceFile + (comp.sourceLine ? ":" + comp.sourceLine : "") + ")" : ""} \xB7 ` : "";
2244
2828
  this.renderNoteModal({
2245
- title: "Add Visual Note",
2246
- modeBadge: noteType.toUpperCase(),
2247
- pillText: noteType === "page" ? `Page Viewport: ${window.innerWidth} \xD7 ${window.innerHeight} px` : `${getSemanticSelector(targetEl)} (${Math.round(targetEl.getBoundingClientRect().width)}\xD7${Math.round(targetEl.getBoundingClientRect().height)}) \xB7 Viewport: ${window.innerWidth}\xD7${window.innerHeight}`,
2248
- onSave: async (message) => {
2249
- await this.saveVisualNote(targetEl, message, noteType);
2829
+ title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Visual Note",
2830
+ modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : noteType.toUpperCase(),
2831
+ pillText: noteType === "page" ? `Page Viewport: ${window.innerWidth} \xD7 ${window.innerHeight} px` : `${compPrefix}${getSemanticSelector(targetEl)} (${Math.round(targetEl.getBoundingClientRect().width)}\xD7${Math.round(targetEl.getBoundingClientRect().height)}) \xB7 Viewport: ${window.innerWidth}\xD7${window.innerHeight}`,
2832
+ onSave: async (message, action) => {
2833
+ let scenarioParam;
2834
+ if (action === "next_step" && !this.activeScenario) {
2835
+ this.startScenario();
2836
+ }
2837
+ if (this.activeScenario) {
2838
+ scenarioParam = {
2839
+ scenarioId: this.activeScenario.id,
2840
+ stepNumber: this.activeScenario.stepNumber,
2841
+ scenarioTitle: this.activeScenario.title
2842
+ };
2843
+ }
2844
+ await this.saveVisualNote(targetEl, message, noteType, scenarioParam);
2845
+ if (action === "next_step" && this.activeScenario) {
2846
+ this.activeScenario.stepNumber++;
2847
+ this.updateToolbarState();
2848
+ this.setMode("element");
2849
+ } else if (action === "finish_flow" && this.activeScenario) {
2850
+ this.finishScenario();
2851
+ }
2250
2852
  }
2251
2853
  });
2252
2854
  }
2253
2855
  openRegionNoteEditor(region) {
2254
2856
  this.renderNoteModal({
2255
- title: "Add Region Note",
2256
- modeBadge: "REGION",
2857
+ title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Region Note",
2858
+ modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : "REGION",
2257
2859
  pillText: `Selected Area: x:${region.x}, y:${region.y} (${region.width} \xD7 ${region.height} px)`,
2258
- onSave: async (message) => {
2259
- await this.saveRegionVisualNote(region, message);
2860
+ onSave: async (message, action) => {
2861
+ let scenarioParam;
2862
+ if (action === "next_step" && !this.activeScenario) {
2863
+ this.startScenario();
2864
+ }
2865
+ if (this.activeScenario) {
2866
+ scenarioParam = {
2867
+ scenarioId: this.activeScenario.id,
2868
+ stepNumber: this.activeScenario.stepNumber,
2869
+ scenarioTitle: this.activeScenario.title
2870
+ };
2871
+ }
2872
+ await this.saveRegionVisualNote(region, message, scenarioParam);
2873
+ if (action === "next_step" && this.activeScenario) {
2874
+ this.activeScenario.stepNumber++;
2875
+ this.updateToolbarState();
2876
+ this.setMode("element");
2877
+ } else if (action === "finish_flow" && this.activeScenario) {
2878
+ this.finishScenario();
2879
+ }
2260
2880
  }
2261
2881
  });
2262
2882
  }
@@ -2270,14 +2890,15 @@ var NoteInspector = class {
2270
2890
  const backdrop = document.createElement("div");
2271
2891
  backdrop.className = "bt-modal-backdrop";
2272
2892
  this.modalOverlay = backdrop;
2273
- const badgeClass = `bt-badge-${options.modeBadge.toLowerCase()}`;
2274
- const icon = options.modeBadge === "REGION" ? "\u{1F4D0}" : options.modeBadge === "PAGE" ? "\u{1F4C4}" : "\u{1F3AF}";
2893
+ const isStep = options.modeBadge.startsWith("STEP");
2894
+ const badgeClass = isStep ? "bt-badge-step" : `bt-badge-${options.modeBadge.toLowerCase()}`;
2895
+ const icon = isStep ? "\u{1F3AC}" : options.modeBadge === "REGION" ? "\u{1F4D0}" : options.modeBadge === "PAGE" ? "\u{1F4C4}" : "\u{1F3AF}";
2275
2896
  const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
2276
2897
  backdrop.innerHTML = `
2277
2898
  <div class="bt-modal">
2278
2899
  <div class="bt-modal-header">
2279
2900
  <div class="bt-modal-title">
2280
- <span>\u{1F4DD} ${options.title}</span>
2901
+ <span>${icon} ${options.title}</span>
2281
2902
  <span class="bt-mode-badge ${badgeClass}">${options.modeBadge}</span>
2282
2903
  </div>
2283
2904
  <button class="bt-close-btn" id="btn-close" title="Close (Esc)">\u2715</button>
@@ -2286,14 +2907,19 @@ var NoteInspector = class {
2286
2907
  <span>${icon}</span>
2287
2908
  <span class="bt-pill-content">${options.pillText}</span>
2288
2909
  </div>
2289
- <textarea class="bt-textarea" placeholder="Describe the layout issue, styling bug, or note for AI agent..." autofocus></textarea>
2910
+ <textarea class="bt-textarea" placeholder="Describe the layout issue, user action, or note for AI agent..." autofocus></textarea>
2290
2911
  <div class="bt-modal-footer">
2291
2912
  <div class="bt-kbd-hint">
2292
2913
  <kbd>${isMac ? "\u2318" : "Ctrl"}+Enter</kbd> save \xB7 <kbd>Esc</kbd> cancel
2293
2914
  </div>
2294
2915
  <div class="bt-modal-actions">
2295
2916
  <button class="bt-btn bt-btn-cancel" id="btn-cancel">Cancel</button>
2296
- <button class="bt-btn bt-btn-save" id="btn-save">Save Note</button>
2917
+ <button class="bt-btn bt-btn-next-step" id="btn-next-step" title="Save this step and immediately select the next element">
2918
+ <span>\u27A1\uFE0F</span> ${this.activeScenario ? "Save & Next Step" : "Save as Step 1 (Flow)"}
2919
+ </button>
2920
+ ${this.activeScenario ? `<button class="bt-btn bt-btn-finish-flow" id="btn-finish-flow" title="Save final step and complete scenario">
2921
+ <span>\u2713</span> Save & Finish Flow
2922
+ </button>` : `<button class="bt-btn bt-btn-save" id="btn-save">Save Note</button>`}
2297
2923
  </div>
2298
2924
  </div>
2299
2925
  </div>
@@ -2302,6 +2928,8 @@ var NoteInspector = class {
2302
2928
  const btnClose = backdrop.querySelector("#btn-close");
2303
2929
  const btnCancel = backdrop.querySelector("#btn-cancel");
2304
2930
  const btnSave = backdrop.querySelector("#btn-save");
2931
+ const btnNextStep = backdrop.querySelector("#btn-next-step");
2932
+ const btnFinishFlow = backdrop.querySelector("#btn-finish-flow");
2305
2933
  const closeModal = () => {
2306
2934
  if (this.modalOverlay) {
2307
2935
  root.removeChild(this.modalOverlay);
@@ -2317,22 +2945,29 @@ var NoteInspector = class {
2317
2945
  closeModal();
2318
2946
  }
2319
2947
  };
2320
- const submitNote = async () => {
2948
+ const handleAction = async (action) => {
2321
2949
  const message = textarea.value.trim();
2322
2950
  if (!message) return;
2323
- btnSave.textContent = "Saving...";
2324
- btnSave.disabled = true;
2951
+ btnNextStep.disabled = true;
2952
+ if (btnSave) btnSave.disabled = true;
2953
+ if (btnFinishFlow) btnFinishFlow.disabled = true;
2325
2954
  try {
2326
- await options.onSave(message);
2955
+ await options.onSave(message, action);
2327
2956
  } finally {
2328
2957
  closeModal();
2329
2958
  }
2330
2959
  };
2331
- btnSave.onclick = submitNote;
2960
+ if (btnSave) {
2961
+ btnSave.onclick = () => handleAction("save");
2962
+ }
2963
+ btnNextStep.onclick = () => handleAction("next_step");
2964
+ if (btnFinishFlow) {
2965
+ btnFinishFlow.onclick = () => handleAction("finish_flow");
2966
+ }
2332
2967
  textarea.onkeydown = (e) => {
2333
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey || !e.shiftKey)) {
2968
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2334
2969
  e.preventDefault();
2335
- submitNote();
2970
+ handleAction(this.activeScenario ? "next_step" : "save");
2336
2971
  }
2337
2972
  if (e.key === "Escape") {
2338
2973
  closeModal();
@@ -2341,7 +2976,7 @@ var NoteInspector = class {
2341
2976
  root.appendChild(backdrop);
2342
2977
  setTimeout(() => textarea?.focus(), 50);
2343
2978
  }
2344
- async saveVisualNote(targetEl, message, noteType = "element") {
2979
+ async saveVisualNote(targetEl, message, noteType = "element", scenario) {
2345
2980
  const rect = targetEl.getBoundingClientRect();
2346
2981
  const selector = noteType === "page" ? "body" : getSemanticSelector(targetEl);
2347
2982
  let screenshotDataUrl;
@@ -2368,33 +3003,38 @@ var NoteInspector = class {
2368
3003
  visible: rect.width > 0 && rect.height > 0,
2369
3004
  confidence: selector.startsWith("[data-test") ? "high" : selector.startsWith("#") ? "medium" : "low"
2370
3005
  };
3006
+ const route = typeof window !== "undefined" ? window.location.pathname + window.location.search : "/";
3007
+ const url = typeof window !== "undefined" ? window.location.href : "http://localhost/";
3008
+ const viewport = typeof window !== "undefined" ? { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio || 1 } : { width: 1280, height: 800, devicePixelRatio: 1 };
3009
+ const scroll = typeof window !== "undefined" ? { scrollX: window.scrollX, scrollY: window.scrollY } : { scrollX: 0, scrollY: 0 };
2371
3010
  const notePayload = {
2372
3011
  type: "create_note",
2373
3012
  sessionId: this.transport.getSessionId() || "",
2374
3013
  noteType,
2375
3014
  message,
2376
- route: window.location.pathname + window.location.search,
2377
- url: window.location.href,
2378
- viewport: {
2379
- width: window.innerWidth,
2380
- height: window.innerHeight,
2381
- devicePixelRatio: window.devicePixelRatio || 1
2382
- },
2383
- scroll: {
2384
- scrollX: window.scrollX,
2385
- scrollY: window.scrollY
2386
- },
3015
+ route,
3016
+ url,
3017
+ viewport,
3018
+ scroll,
2387
3019
  target,
2388
3020
  elementContext,
2389
3021
  screenshot: screenshotDataUrl,
3022
+ scenarioId: scenario?.scenarioId,
3023
+ stepNumber: scenario?.stepNumber,
3024
+ scenarioTitle: scenario?.scenarioTitle,
2390
3025
  timestamp: Date.now()
2391
3026
  };
2392
3027
  this.transport.send(notePayload);
2393
3028
  if (this.options.onNoteCreated) {
2394
3029
  this.options.onNoteCreated(notePayload);
2395
3030
  }
3031
+ if (scenario?.scenarioId) {
3032
+ this.showToast(`Step ${scenario.stepNumber || 1} recorded`, "\u{1F3AC}");
3033
+ } else {
3034
+ this.showToast("Visual note saved", "\u2728");
3035
+ }
2396
3036
  }
2397
- async saveRegionVisualNote(region, message) {
3037
+ async saveRegionVisualNote(region, message, scenario) {
2398
3038
  let screenshotDataUrl;
2399
3039
  try {
2400
3040
  const snap = await this.screenshotDriver.captureElement(document.body || document.documentElement);
@@ -2403,123 +3043,115 @@ var NoteInspector = class {
2403
3043
  }
2404
3044
  } catch {
2405
3045
  }
3046
+ const route = typeof window !== "undefined" ? window.location.pathname + window.location.search : "/";
3047
+ const url = typeof window !== "undefined" ? window.location.href : "http://localhost/";
3048
+ const viewport = typeof window !== "undefined" ? { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio || 1 } : { width: 1280, height: 800, devicePixelRatio: 1 };
3049
+ const scroll = typeof window !== "undefined" ? { scrollX: window.scrollX, scrollY: window.scrollY } : { scrollX: 0, scrollY: 0 };
2406
3050
  const notePayload = {
2407
3051
  type: "create_note",
2408
3052
  sessionId: this.transport.getSessionId() || "",
2409
3053
  noteType: "region",
2410
3054
  message,
2411
- route: window.location.pathname + window.location.search,
2412
- url: window.location.href,
2413
- viewport: {
2414
- width: window.innerWidth,
2415
- height: window.innerHeight,
2416
- devicePixelRatio: window.devicePixelRatio || 1
2417
- },
2418
- scroll: {
2419
- scrollX: window.scrollX,
2420
- scrollY: window.scrollY
2421
- },
3055
+ route,
3056
+ url,
3057
+ viewport,
3058
+ scroll,
2422
3059
  region,
2423
3060
  screenshot: screenshotDataUrl,
3061
+ scenarioId: scenario?.scenarioId,
3062
+ stepNumber: scenario?.stepNumber,
3063
+ scenarioTitle: scenario?.scenarioTitle,
2424
3064
  timestamp: Date.now()
2425
3065
  };
2426
3066
  this.transport.send(notePayload);
2427
3067
  if (this.options.onNoteCreated) {
2428
3068
  this.options.onNoteCreated(notePayload);
2429
3069
  }
2430
- }
2431
- async cropDataUrl(dataUrl, region) {
2432
- return new Promise((resolve) => {
2433
- const img = new Image();
2434
- img.onload = () => {
2435
- try {
2436
- const canvas = document.createElement("canvas");
2437
- canvas.width = region.width;
2438
- canvas.height = region.height;
2439
- const ctx = canvas.getContext("2d");
2440
- if (!ctx) {
2441
- resolve(dataUrl);
2442
- return;
2443
- }
2444
- const dpr = window.devicePixelRatio || 1;
2445
- ctx.drawImage(
2446
- img,
2447
- region.x * dpr,
2448
- region.y * dpr,
2449
- region.width * dpr,
2450
- region.height * dpr,
2451
- 0,
2452
- 0,
2453
- region.width,
2454
- region.height
2455
- );
2456
- resolve(canvas.toDataURL("image/webp", 0.9));
2457
- } catch {
2458
- resolve(dataUrl);
2459
- }
2460
- };
2461
- img.onerror = () => resolve(dataUrl);
2462
- img.src = dataUrl;
2463
- });
3070
+ if (scenario?.scenarioId) {
3071
+ this.showToast(`Step ${scenario.stepNumber || 1} region recorded`, "\u{1F3AC}");
3072
+ } else {
3073
+ this.showToast("Region note saved", "\u{1F4D0}");
3074
+ }
2464
3075
  }
2465
3076
  extractElementContext(el) {
2466
3077
  const selector = getSemanticSelector(el);
2467
- const tag = el.tagName.toLowerCase();
2468
3078
  const attributes = {};
2469
3079
  for (let i = 0; i < el.attributes.length; i++) {
2470
3080
  const attr = el.attributes[i];
2471
- if (attr.name === "value" && el.type === "password") {
3081
+ if (this.options.maskSelectors?.some((mask) => {
3082
+ try {
3083
+ return el.matches(mask);
3084
+ } catch {
3085
+ return false;
3086
+ }
3087
+ }) && (attr.name === "value" || attr.name === "data-secret")) {
2472
3088
  attributes[attr.name] = "[REDACTED]";
2473
3089
  } else {
2474
3090
  attributes[attr.name] = attr.value;
2475
3091
  }
2476
3092
  }
2477
- let outerHTML = "";
2478
- try {
2479
- const clone = el.cloneNode(true);
2480
- for (const passInput of Array.from(clone.querySelectorAll('input[type="password"]'))) {
2481
- passInput.setAttribute("value", "[REDACTED]");
2482
- }
2483
- outerHTML = truncate(clone.outerHTML, 10240);
2484
- } catch {
2485
- outerHTML = truncate(el.outerHTML, 10240);
3093
+ let outerHTML = el.outerHTML;
3094
+ if (outerHTML && outerHTML.length > 1e3) {
3095
+ outerHTML = truncate(outerHTML, 1e3);
2486
3096
  }
2487
- let parent;
2488
- if (el.parentElement && el.parentElement !== document.body) {
2489
- parent = {
2490
- selector: getSemanticSelector(el.parentElement),
2491
- tag: el.parentElement.tagName.toLowerCase()
2492
- };
3097
+ let innerText = el.innerText || el.textContent || "";
3098
+ if (innerText && innerText.length > 200) {
3099
+ innerText = truncate(innerText, 200);
2493
3100
  }
3101
+ const componentSource = resolveComponentSource(el);
2494
3102
  return {
2495
3103
  selector,
2496
- tag,
3104
+ tag: el.tagName.toLowerCase(),
2497
3105
  attributes,
2498
3106
  outerHTML,
2499
- innerText: truncate(el.textContent?.trim(), 200),
2500
- parent
3107
+ innerText,
3108
+ componentSource,
3109
+ parent: el.parentElement ? {
3110
+ selector: getSemanticSelector(el.parentElement),
3111
+ tag: el.parentElement.tagName.toLowerCase()
3112
+ } : void 0
2501
3113
  };
2502
3114
  }
3115
+ async cropDataUrl(dataUrl, region) {
3116
+ return new Promise((resolve) => {
3117
+ const img = new Image();
3118
+ img.onload = () => {
3119
+ const canvas = document.createElement("canvas");
3120
+ canvas.width = region.width;
3121
+ canvas.height = region.height;
3122
+ const ctx = canvas.getContext("2d");
3123
+ if (!ctx) {
3124
+ resolve(dataUrl);
3125
+ return;
3126
+ }
3127
+ const dpr = window.devicePixelRatio || 1;
3128
+ ctx.drawImage(
3129
+ img,
3130
+ region.x * dpr,
3131
+ region.y * dpr,
3132
+ region.width * dpr,
3133
+ region.height * dpr,
3134
+ 0,
3135
+ 0,
3136
+ region.width,
3137
+ region.height
3138
+ );
3139
+ resolve(canvas.toDataURL("image/png"));
3140
+ };
3141
+ img.onerror = () => resolve(dataUrl);
3142
+ img.src = dataUrl;
3143
+ });
3144
+ }
2503
3145
  destroy() {
2504
3146
  for (const cleanup of this.cleanups) {
2505
- try {
2506
- cleanup();
2507
- } catch {
2508
- }
3147
+ cleanup();
2509
3148
  }
2510
3149
  this.cleanups = [];
2511
- if (this.container && this.container.parentElement) {
2512
- this.container.parentElement.removeChild(this.container);
2513
- }
2514
- this.container = null;
2515
- this.shadowRoot = null;
2516
- this.toolbarElement = null;
2517
- this.regionOverlay = null;
2518
- this.regionBox = null;
2519
- this.regionBanner = null;
2520
- this.markersContainer = null;
2521
- this.modalOverlay = null;
2522
- this.cardOverlay = null;
3150
+ if (this.container && this.container.parentNode) {
3151
+ this.container.parentNode.removeChild(this.container);
3152
+ this.container = null;
3153
+ this.shadowRoot = null;
3154
+ }
2523
3155
  }
2524
3156
  };
2525
3157
 
@@ -2556,7 +3188,11 @@ var BrowserTrackClient = class {
2556
3188
  if (this.options.notes.enabled) {
2557
3189
  this.inspector = new NoteInspector(this.transport, this.screenshotDriver, {
2558
3190
  shortcut: this.options.notes.shortcut,
2559
- maskSelectors: this.options.notes.maskSelectors
3191
+ maskSelectors: this.options.notes.maskSelectors,
3192
+ showToolbar: this.options.notes.showToolbar,
3193
+ showBadges: this.options.notes.showBadges,
3194
+ hidden: this.options.hidden,
3195
+ hideQueryParam: this.options.hideQueryParam
2560
3196
  });
2561
3197
  }
2562
3198
  }
@@ -2622,6 +3258,14 @@ var BrowserTrackClient = class {
2622
3258
  this.inspector.setMode("element");
2623
3259
  }
2624
3260
  }
3261
+ isUIVisible() {
3262
+ return this.inspector ? this.inspector.isVisible() : false;
3263
+ }
3264
+ setUIVisible(visible) {
3265
+ if (this.inspector) {
3266
+ this.inspector.setVisible(visible);
3267
+ }
3268
+ }
2625
3269
  setInspectMode(mode) {
2626
3270
  if (this.inspector) {
2627
3271
  this.inspector.setMode(mode);
@@ -2762,8 +3406,10 @@ if (typeof window !== "undefined") {
2762
3406
  const autoInit = currentScript?.getAttribute("data-auto-init") !== "false";
2763
3407
  const daemonUrl = currentScript?.getAttribute("data-daemon-url") || void 0;
2764
3408
  const projectId = currentScript?.getAttribute("data-project-id") || void 0;
3409
+ const hidden = currentScript?.getAttribute("data-hidden") === "true";
3410
+ const hideQueryParam = currentScript?.getAttribute("data-hide-query-param") || void 0;
2765
3411
  if (autoInit) {
2766
- init({ daemonUrl, projectId });
3412
+ init({ daemonUrl, projectId, hidden, hideQueryParam });
2767
3413
  }
2768
3414
  } catch {
2769
3415
  }
@@ -2776,5 +3422,7 @@ if (typeof window !== "undefined") {
2776
3422
  DEFAULT_OPTIONS,
2777
3423
  NoteInspector,
2778
3424
  getClient,
2779
- init
3425
+ init,
3426
+ resolveComponentSource,
3427
+ shouldHideUIFromUrl
2780
3428
  });