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
@@ -29,12 +29,220 @@ var BreadcrumbBuffer = class {
29
29
  }
30
30
  };
31
31
 
32
+ // packages/client/src/source/resolver.ts
33
+ function resolveComponentSource(el) {
34
+ if (!el || typeof el !== "object") return void 0;
35
+ const reactInfo = resolveReactComponent(el);
36
+ if (reactInfo) return reactInfo;
37
+ const vueInfo = resolveVueComponent(el);
38
+ if (vueInfo) return vueInfo;
39
+ const svelteInfo = resolveSvelteComponent(el);
40
+ if (svelteInfo) return svelteInfo;
41
+ if (el.tagName && el.tagName.includes("-")) {
42
+ return {
43
+ framework: "web-component",
44
+ componentName: el.tagName.toLowerCase(),
45
+ hierarchy: [el.tagName.toLowerCase()]
46
+ };
47
+ }
48
+ const attrInfo = resolveDataAttributeComponent(el);
49
+ if (attrInfo) return attrInfo;
50
+ return void 0;
51
+ }
52
+ function resolveReactComponent(el) {
53
+ try {
54
+ const fiberKey = Object.keys(el).find(
55
+ (key) => key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$")
56
+ );
57
+ if (!fiberKey) return void 0;
58
+ const hostFiber = el[fiberKey];
59
+ if (!hostFiber) return void 0;
60
+ let sourceFile;
61
+ let sourceLine;
62
+ let sourceColumn;
63
+ let componentName;
64
+ const hierarchy = [];
65
+ let props;
66
+ if (hostFiber._debugSource) {
67
+ sourceFile = hostFiber._debugSource.fileName;
68
+ sourceLine = hostFiber._debugSource.lineNumber;
69
+ sourceColumn = hostFiber._debugSource.columnNumber;
70
+ }
71
+ if (hostFiber._debugOwner) {
72
+ const ownerType = hostFiber._debugOwner.type;
73
+ componentName = getReactComponentName(ownerType);
74
+ if (!sourceFile && hostFiber._debugOwner._debugSource) {
75
+ sourceFile = hostFiber._debugOwner._debugSource.fileName;
76
+ sourceLine = hostFiber._debugOwner._debugSource.lineNumber;
77
+ sourceColumn = hostFiber._debugOwner._debugSource.columnNumber;
78
+ }
79
+ }
80
+ let curr = hostFiber;
81
+ while (curr) {
82
+ const type = curr.type;
83
+ const name = getReactComponentName(type);
84
+ if (name && !name.startsWith("html:") && name !== "Fragment") {
85
+ if (!componentName) {
86
+ componentName = name;
87
+ }
88
+ if (!hierarchy.includes(name)) {
89
+ hierarchy.unshift(name);
90
+ }
91
+ if (!sourceFile && curr._debugSource) {
92
+ sourceFile = curr._debugSource.fileName;
93
+ sourceLine = curr._debugSource.lineNumber;
94
+ sourceColumn = curr._debugSource.columnNumber;
95
+ }
96
+ if (!props && curr.memoizedProps && typeof curr.memoizedProps === "object") {
97
+ props = sanitizeProps(curr.memoizedProps);
98
+ }
99
+ }
100
+ curr = curr.return;
101
+ }
102
+ if (!componentName && !sourceFile) {
103
+ return void 0;
104
+ }
105
+ return {
106
+ framework: "react",
107
+ componentName,
108
+ sourceFile: normalizeFilePath(sourceFile),
109
+ sourceLine,
110
+ sourceColumn,
111
+ hierarchy: hierarchy.length > 0 ? hierarchy : componentName ? [componentName] : void 0,
112
+ props
113
+ };
114
+ } catch {
115
+ return void 0;
116
+ }
117
+ }
118
+ function getReactComponentName(type) {
119
+ if (!type) return void 0;
120
+ if (typeof type === "string") return void 0;
121
+ if (type.displayName) return type.displayName;
122
+ if (type.name) return type.name;
123
+ if (type.render?.displayName) return type.render.displayName;
124
+ if (type.render?.name) return type.render.name;
125
+ return void 0;
126
+ }
127
+ function resolveVueComponent(el) {
128
+ try {
129
+ const vueParent = el.__vueParentComponent || el.__vnode?.ctx;
130
+ if (vueParent) {
131
+ const type = vueParent.type || {};
132
+ const componentName = type.name || type.__name || type.displayName || "AnonymousComponent";
133
+ const sourceFile = type.__file;
134
+ const hierarchy = [];
135
+ let curr = vueParent;
136
+ while (curr) {
137
+ const cType = curr.type || {};
138
+ const cName = cType.name || cType.__name || cType.displayName;
139
+ if (cName && !hierarchy.includes(cName)) {
140
+ hierarchy.unshift(cName);
141
+ }
142
+ curr = curr.parent;
143
+ }
144
+ return {
145
+ framework: "vue",
146
+ componentName,
147
+ sourceFile: normalizeFilePath(sourceFile),
148
+ hierarchy: hierarchy.length > 0 ? hierarchy : [componentName],
149
+ props: vueParent.props ? sanitizeProps(vueParent.props) : void 0
150
+ };
151
+ }
152
+ const vue2Instance = el.__vue__;
153
+ if (vue2Instance) {
154
+ const options = vue2Instance.$options || {};
155
+ const componentName = options.name || options._componentTag || "VueComponent";
156
+ const sourceFile = options.__file;
157
+ return {
158
+ framework: "vue",
159
+ componentName,
160
+ sourceFile: normalizeFilePath(sourceFile),
161
+ hierarchy: [componentName],
162
+ props: vue2Instance.$props ? sanitizeProps(vue2Instance.$props) : void 0
163
+ };
164
+ }
165
+ return void 0;
166
+ } catch {
167
+ return void 0;
168
+ }
169
+ }
170
+ function resolveSvelteComponent(el) {
171
+ try {
172
+ let curr = el;
173
+ while (curr) {
174
+ const meta = curr.__svelte_meta;
175
+ if (meta && meta.loc) {
176
+ return {
177
+ framework: "svelte",
178
+ sourceFile: normalizeFilePath(meta.loc.file),
179
+ sourceLine: meta.loc.line,
180
+ sourceColumn: meta.loc.column,
181
+ componentName: meta.loc.file ? getBaseNameWithoutExt(meta.loc.file) : void 0,
182
+ hierarchy: meta.loc.file ? [getBaseNameWithoutExt(meta.loc.file)] : void 0
183
+ };
184
+ }
185
+ curr = curr.parentElement;
186
+ }
187
+ return void 0;
188
+ } catch {
189
+ return void 0;
190
+ }
191
+ }
192
+ function resolveDataAttributeComponent(el) {
193
+ try {
194
+ const compEl = el.closest("[data-component], [data-component-name], [data-source-file]");
195
+ if (!compEl) return void 0;
196
+ const componentName = compEl.getAttribute("data-component") || compEl.getAttribute("data-component-name") || void 0;
197
+ const sourceFile = compEl.getAttribute("data-source-file") || void 0;
198
+ const lineAttr = compEl.getAttribute("data-source-line");
199
+ const sourceLine = lineAttr ? parseInt(lineAttr, 10) : void 0;
200
+ if (!componentName && !sourceFile) return void 0;
201
+ return {
202
+ framework: "vanilla",
203
+ componentName,
204
+ sourceFile: normalizeFilePath(sourceFile),
205
+ sourceLine: isNaN(sourceLine) ? void 0 : sourceLine,
206
+ hierarchy: componentName ? [componentName] : void 0
207
+ };
208
+ } catch {
209
+ return void 0;
210
+ }
211
+ }
212
+ function normalizeFilePath(filePath) {
213
+ if (!filePath) return void 0;
214
+ const cleaned = filePath.split("?")[0];
215
+ return cleaned;
216
+ }
217
+ function getBaseNameWithoutExt(filePath) {
218
+ const parts = filePath.split("/");
219
+ const last = parts[parts.length - 1] || filePath;
220
+ return last.split(".")[0] || last;
221
+ }
222
+ function sanitizeProps(props) {
223
+ const result = {};
224
+ for (const [key, value] of Object.entries(props)) {
225
+ if (key.startsWith("__") || typeof value === "function") continue;
226
+ if (value === null || value === void 0) {
227
+ result[key] = value;
228
+ } else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
229
+ result[key] = value;
230
+ } else if (Array.isArray(value)) {
231
+ result[key] = `Array(${value.length})`;
232
+ } else if (typeof value === "object") {
233
+ result[key] = "[Object]";
234
+ }
235
+ }
236
+ return result;
237
+ }
238
+
32
239
  // packages/client/src/interceptors/interaction.ts
33
240
  function extractElementSummary(el) {
34
241
  const selector = getSemanticSelector(el);
35
242
  const tag = el.tagName.toLowerCase();
36
243
  const id = el.id || void 0;
37
244
  const classes = Array.from(el.classList || []);
245
+ const componentSource = resolveComponentSource(el);
38
246
  let boundingRect = void 0;
39
247
  let visible = true;
40
248
  try {
@@ -66,7 +274,8 @@ function extractElementSummary(el) {
66
274
  boundingRect,
67
275
  visible,
68
276
  innerText,
69
- outerHTML
277
+ outerHTML,
278
+ componentSource
70
279
  };
71
280
  }
72
281
  function setupInteractionInterceptors(onInteraction) {
@@ -384,10 +593,62 @@ var DEFAULT_OPTIONS = {
384
593
  enabled: true,
385
594
  shortcut: "Alt+Click",
386
595
  showBadges: true,
596
+ showToolbar: true,
387
597
  maskSelectors: ['input[type="password"]', "[data-sensitive]"]
388
598
  },
599
+ hidden: false,
600
+ hideQueryParam: void 0,
389
601
  debug: false
390
602
  };
603
+ var HIDE_VALUES = /* @__PURE__ */ new Set(["0", "false", "hidden", "hide", "off", "none", "disabled", "ui_off", "silent"]);
604
+ function shouldHideUIFromUrl(customParam, searchString) {
605
+ let search = searchString;
606
+ if (search === void 0) {
607
+ if (typeof window === "undefined" || !window.location) return false;
608
+ search = window.location.search;
609
+ }
610
+ if (!search) return false;
611
+ try {
612
+ const params = new URLSearchParams(search);
613
+ if (customParam) {
614
+ const customKeys = Array.isArray(customParam) ? customParam : [customParam];
615
+ for (const key of customKeys) {
616
+ if (params.has(key)) {
617
+ const val = (params.get(key) || "").toLowerCase().trim();
618
+ if (val === "" || val === "1" || val === "true" || HIDE_VALUES.has(val)) {
619
+ return true;
620
+ }
621
+ }
622
+ }
623
+ }
624
+ for (const flag of ["no_bt", "no_browsertrack", "hide_bt", "hide_browsertrack"]) {
625
+ if (params.has(flag)) {
626
+ const val = (params.get(flag) || "").toLowerCase().trim();
627
+ if (val === "" || val === "1" || val === "true" || val === "yes") {
628
+ return true;
629
+ }
630
+ }
631
+ }
632
+ if (params.has("bt")) {
633
+ const val = (params.get("bt") || "").toLowerCase().trim();
634
+ if (HIDE_VALUES.has(val)) return true;
635
+ }
636
+ if (params.has("browsertrack")) {
637
+ const val = (params.get("browsertrack") || "").toLowerCase().trim();
638
+ if (HIDE_VALUES.has(val)) return true;
639
+ }
640
+ if (params.has("bt_ui")) {
641
+ const val = (params.get("bt_ui") || "").toLowerCase().trim();
642
+ if (HIDE_VALUES.has(val) || val === "0" || val === "false") return true;
643
+ }
644
+ if (params.has("bt_hide")) {
645
+ const val = (params.get("bt_hide") || "").toLowerCase().trim();
646
+ if (val === "" || val === "1" || val === "true") return true;
647
+ }
648
+ } catch {
649
+ }
650
+ return false;
651
+ }
391
652
 
392
653
  // packages/client/src/interceptors/console.ts
393
654
  function setupConsoleInterceptors(onConsole) {
@@ -1043,7 +1304,9 @@ var NoteInspector = class {
1043
1304
  toolbarElement = null;
1044
1305
  modalOverlay = null;
1045
1306
  cardOverlay = null;
1307
+ toastContainer = null;
1046
1308
  activeMode = "idle";
1309
+ activeScenario = null;
1047
1310
  hoveredElement = null;
1048
1311
  selectedElement = null;
1049
1312
  selectedRegion = null;
@@ -1052,14 +1315,19 @@ var NoteInspector = class {
1052
1315
  dragStartY = 0;
1053
1316
  savedNotes = [];
1054
1317
  showMarkers = true;
1318
+ isHidden = false;
1055
1319
  cleanups = [];
1056
1320
  constructor(transport, screenshotDriver, options = {}) {
1057
1321
  this.transport = transport;
1058
1322
  this.screenshotDriver = screenshotDriver;
1323
+ const hiddenByQuery = shouldHideUIFromUrl(options.hideQueryParam);
1324
+ this.isHidden = options.hidden === true || hiddenByQuery;
1325
+ this.showMarkers = options.showBadges !== false && !this.isHidden;
1059
1326
  this.options = {
1060
1327
  shortcut: "Alt+Click",
1061
1328
  maskSelectors: ['input[type="password"]', "[data-sensitive]"],
1062
1329
  showToolbar: true,
1330
+ showBadges: true,
1063
1331
  ...options
1064
1332
  };
1065
1333
  }
@@ -1085,6 +1353,41 @@ var NoteInspector = class {
1085
1353
  this.updateToolbarCount();
1086
1354
  this.renderMarkers();
1087
1355
  }
1356
+ showToast(message, icon = "\u2728", durationMs = 2500) {
1357
+ if (typeof document === "undefined") return;
1358
+ const root = this.ensureContainer();
1359
+ if (!root || !this.toastContainer) return;
1360
+ const toast = document.createElement("div");
1361
+ toast.className = "bt-toast";
1362
+ toast.innerHTML = `<span>${icon}</span> <span>${message}</span>`;
1363
+ this.toastContainer.appendChild(toast);
1364
+ setTimeout(() => {
1365
+ toast.classList.add("bt-toast-fadeout");
1366
+ setTimeout(() => {
1367
+ if (toast.parentElement) {
1368
+ toast.parentElement.removeChild(toast);
1369
+ }
1370
+ }, 250);
1371
+ }, durationMs);
1372
+ }
1373
+ startScenario(title) {
1374
+ const defaultTitle = title || `Scenario ${(/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
1375
+ this.activeScenario = {
1376
+ id: `scen_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`,
1377
+ title: defaultTitle,
1378
+ stepNumber: 1
1379
+ };
1380
+ this.updateToolbarState();
1381
+ this.setMode("element");
1382
+ this.showToast(`Started flow: "${defaultTitle}"`, "\u{1F3AC}");
1383
+ }
1384
+ finishScenario() {
1385
+ const title = this.activeScenario?.title;
1386
+ this.activeScenario = null;
1387
+ this.updateToolbarState();
1388
+ this.setMode("idle");
1389
+ this.showToast(title ? `Finished flow: "${title}"` : "Flow recording finished", "\u2713");
1390
+ }
1088
1391
  setMode(mode) {
1089
1392
  this.activeMode = mode;
1090
1393
  this.updateToolbarState();
@@ -1102,12 +1405,48 @@ var NoteInspector = class {
1102
1405
  this.hideHighlight();
1103
1406
  }
1104
1407
  }
1408
+ isVisible() {
1409
+ return !this.isHidden;
1410
+ }
1411
+ setVisible(visible) {
1412
+ this.isHidden = !visible;
1413
+ this.showMarkers = this.options.showBadges !== false && !this.isHidden;
1414
+ if (this.container) {
1415
+ this.container.style.display = this.isHidden ? "none" : "block";
1416
+ }
1417
+ if (this.toolbarElement) {
1418
+ this.toolbarElement.style.display = this.isHidden ? "none" : "flex";
1419
+ } else if (!this.isHidden && this.options.showToolbar !== false) {
1420
+ this.createToolbar();
1421
+ }
1422
+ if (this.isHidden) {
1423
+ this.hideHighlight();
1424
+ this.hideRegionOverlay();
1425
+ if (this.modalOverlay && this.shadowRoot) {
1426
+ this.shadowRoot.removeChild(this.modalOverlay);
1427
+ this.modalOverlay = null;
1428
+ }
1429
+ if (this.cardOverlay && this.shadowRoot) {
1430
+ this.shadowRoot.removeChild(this.cardOverlay);
1431
+ this.cardOverlay = null;
1432
+ }
1433
+ if (this.markersContainer) {
1434
+ this.markersContainer.innerHTML = "";
1435
+ }
1436
+ } else {
1437
+ this.renderMarkers();
1438
+ this.updateToolbarCount();
1439
+ }
1440
+ }
1105
1441
  ensureContainer() {
1106
1442
  if (typeof document === "undefined") return null;
1107
1443
  if (!this.container) {
1108
1444
  this.container = document.createElement("div");
1109
1445
  this.container.id = "browsertrack-inspector-host";
1110
1446
  this.container.style.cssText = "all: initial; position: fixed; top: 0; left: 0; width: 0; height: 0; z-index: 2147483647; pointer-events: none;";
1447
+ if (this.isHidden) {
1448
+ this.container.style.display = "none";
1449
+ }
1111
1450
  this.shadowRoot = this.container.attachShadow({ mode: "open" });
1112
1451
  const style = document.createElement("style");
1113
1452
  style.textContent = `
@@ -1264,6 +1603,12 @@ var NoteInspector = class {
1264
1603
  z-index: 2147483641;
1265
1604
  }
1266
1605
 
1606
+ .bt-note-marker-step {
1607
+ background: linear-gradient(135deg, #f59e0b, #d97706);
1608
+ border-color: #fef3c7;
1609
+ box-shadow: 0 4px 14px rgba(245, 158, 11, 0.4), 0 0 0 1px rgba(217, 119, 6, 0.5);
1610
+ }
1611
+
1267
1612
  @keyframes bt-pop-in {
1268
1613
  0% { transform: scale(0.6); opacity: 0; }
1269
1614
  100% { transform: scale(1); opacity: 1; }
@@ -1274,6 +1619,10 @@ var NoteInspector = class {
1274
1619
  box-shadow: 0 8px 20px rgba(37,99,235,0.6), 0 0 0 2px #60a5fa;
1275
1620
  }
1276
1621
 
1622
+ .bt-note-marker-step:hover {
1623
+ box-shadow: 0 8px 20px rgba(245, 158, 11, 0.7), 0 0 0 2px #fde68a;
1624
+ }
1625
+
1277
1626
  .bt-marker-resolved {
1278
1627
  background: linear-gradient(135deg, #475569, #334155);
1279
1628
  border-color: #94a3b8;
@@ -1370,6 +1719,19 @@ var NoteInspector = class {
1370
1719
  box-shadow: 0 2px 6px rgba(37, 99, 235, 0.35);
1371
1720
  }
1372
1721
 
1722
+ .bt-toolbar-btn.active-scenario {
1723
+ background: linear-gradient(135deg, #f59e0b, #d97706);
1724
+ color: #ffffff;
1725
+ font-weight: 700;
1726
+ box-shadow: 0 2px 8px rgba(245, 158, 11, 0.4);
1727
+ animation: bt-pulse 2s infinite;
1728
+ }
1729
+
1730
+ @keyframes bt-pulse {
1731
+ 0%, 100% { opacity: 1; }
1732
+ 50% { opacity: 0.85; }
1733
+ }
1734
+
1373
1735
  .bt-count-pill {
1374
1736
  background: rgba(255, 255, 255, 0.2);
1375
1737
  color: #ffffff;
@@ -1417,7 +1779,7 @@ var NoteInspector = class {
1417
1779
  border: 1px solid #334155;
1418
1780
  border-radius: 14px;
1419
1781
  padding: 20px;
1420
- width: 450px;
1782
+ width: 470px;
1421
1783
  max-width: 92vw;
1422
1784
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.05);
1423
1785
  display: flex;
@@ -1440,6 +1802,7 @@ var NoteInspector = class {
1440
1802
  align-items: center;
1441
1803
  gap: 8px;
1442
1804
  color: #f8fafc;
1805
+ flex-wrap: wrap;
1443
1806
  }
1444
1807
 
1445
1808
  .bt-mode-badge {
@@ -1469,6 +1832,12 @@ var NoteInspector = class {
1469
1832
  border: 1px solid rgba(168, 85, 247, 0.4);
1470
1833
  }
1471
1834
 
1835
+ .bt-badge-step {
1836
+ background: rgba(245, 158, 11, 0.15);
1837
+ color: #fbbf24;
1838
+ border: 1px solid rgba(245, 158, 11, 0.4);
1839
+ }
1840
+
1472
1841
  .bt-badge-status-open {
1473
1842
  background: rgba(16, 185, 129, 0.15);
1474
1843
  color: #34d399;
@@ -1520,6 +1889,59 @@ var NoteInspector = class {
1520
1889
  color: #cbd5e1;
1521
1890
  }
1522
1891
 
1892
+ .bt-component-pill {
1893
+ display: flex;
1894
+ align-items: center;
1895
+ gap: 6px;
1896
+ background: rgba(15, 23, 42, 0.95);
1897
+ border: 1px solid rgba(99, 102, 241, 0.35);
1898
+ border-radius: 6px;
1899
+ padding: 6px 10px;
1900
+ font-size: 11.5px;
1901
+ color: #c7d2fe;
1902
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1903
+ overflow: hidden;
1904
+ text-overflow: ellipsis;
1905
+ white-space: nowrap;
1906
+ }
1907
+
1908
+ .bt-scenario-stepper {
1909
+ display: flex;
1910
+ align-items: center;
1911
+ justify-content: space-between;
1912
+ background: #090d16;
1913
+ border: 1px solid #1e293b;
1914
+ border-radius: 8px;
1915
+ padding: 6px 10px;
1916
+ font-size: 12px;
1917
+ font-weight: 600;
1918
+ color: #f59e0b;
1919
+ }
1920
+
1921
+ .bt-step-nav-btn {
1922
+ background: #1e293b;
1923
+ color: #f8fafc;
1924
+ border: 1px solid #334155;
1925
+ padding: 4px 10px;
1926
+ border-radius: 6px;
1927
+ cursor: pointer;
1928
+ font-size: 11px;
1929
+ font-weight: 600;
1930
+ transition: all 0.12s ease;
1931
+ font-family: inherit;
1932
+ }
1933
+
1934
+ .bt-step-nav-btn:hover:not(:disabled) {
1935
+ background: #2563eb;
1936
+ border-color: #3b82f6;
1937
+ color: #ffffff;
1938
+ }
1939
+
1940
+ .bt-step-nav-btn:disabled {
1941
+ opacity: 0.3;
1942
+ cursor: not-allowed;
1943
+ }
1944
+
1523
1945
  .bt-note-message-box {
1524
1946
  background: #090d16;
1525
1947
  border: 1px solid #1e293b;
@@ -1564,8 +1986,9 @@ var NoteInspector = class {
1564
1986
  display: flex;
1565
1987
  justify-content: space-between;
1566
1988
  align-items: center;
1567
- gap: 12px;
1989
+ gap: 10px;
1568
1990
  margin-top: 2px;
1991
+ flex-wrap: wrap;
1569
1992
  }
1570
1993
 
1571
1994
  .bt-kbd-hint {
@@ -1593,12 +2016,13 @@ var NoteInspector = class {
1593
2016
  justify-content: flex-end;
1594
2017
  align-items: center;
1595
2018
  gap: 8px;
2019
+ flex-wrap: wrap;
1596
2020
  }
1597
2021
 
1598
2022
  .bt-btn {
1599
- padding: 8px 16px;
2023
+ padding: 7px 14px;
1600
2024
  border-radius: 7px;
1601
- font-size: 12.5px;
2025
+ font-size: 12px;
1602
2026
  font-weight: 600;
1603
2027
  cursor: pointer;
1604
2028
  border: 1px solid transparent;
@@ -1607,7 +2031,7 @@ var NoteInspector = class {
1607
2031
  display: inline-flex;
1608
2032
  align-items: center;
1609
2033
  justify-content: center;
1610
- gap: 6px;
2034
+ gap: 5px;
1611
2035
  }
1612
2036
 
1613
2037
  .bt-btn-cancel {
@@ -1629,6 +2053,25 @@ var NoteInspector = class {
1629
2053
  box-shadow: 0 4px 10px rgba(37, 99, 235, 0.45);
1630
2054
  }
1631
2055
 
2056
+ .bt-btn-next-step {
2057
+ background: linear-gradient(135deg, #6366f1, #4f46e5);
2058
+ color: #ffffff;
2059
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.4);
2060
+ }
2061
+ .bt-btn-next-step:hover {
2062
+ background: linear-gradient(135deg, #4f46e5, #4338ca);
2063
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.55);
2064
+ }
2065
+
2066
+ .bt-btn-finish-flow {
2067
+ background: linear-gradient(135deg, #10b981, #059669);
2068
+ color: #ffffff;
2069
+ box-shadow: 0 2px 8px rgba(16, 185, 129, 0.35);
2070
+ }
2071
+ .bt-btn-finish-flow:hover {
2072
+ background: linear-gradient(135deg, #059669, #047857);
2073
+ }
2074
+
1632
2075
  .bt-btn-resolve {
1633
2076
  background: rgba(16, 185, 129, 0.15);
1634
2077
  color: #6ee7b7;
@@ -1648,6 +2091,47 @@ var NoteInspector = class {
1648
2091
  background: rgba(239, 68, 68, 0.3);
1649
2092
  color: #ffffff;
1650
2093
  }
2094
+
2095
+ /* 6. Toast Notifications */
2096
+ .bt-toast-container {
2097
+ position: fixed;
2098
+ bottom: 74px;
2099
+ right: 24px;
2100
+ display: flex;
2101
+ flex-direction: column;
2102
+ gap: 8px;
2103
+ pointer-events: none;
2104
+ z-index: 2147483647;
2105
+ align-items: flex-end;
2106
+ }
2107
+
2108
+ .bt-toast {
2109
+ background: #0f172a;
2110
+ color: #f8fafc;
2111
+ border: 1px solid #334155;
2112
+ border-radius: 30px;
2113
+ padding: 8px 16px;
2114
+ font-size: 12.5px;
2115
+ font-weight: 500;
2116
+ display: inline-flex;
2117
+ align-items: center;
2118
+ gap: 8px;
2119
+ box-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.6), 0 4px 6px -4px rgba(0, 0, 0, 0.4);
2120
+ pointer-events: auto;
2121
+ animation: bt-toast-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
2122
+ transition: opacity 0.25s ease, transform 0.25s ease;
2123
+ user-select: none;
2124
+ }
2125
+
2126
+ .bt-toast-fadeout {
2127
+ opacity: 0;
2128
+ transform: translateY(6px);
2129
+ }
2130
+
2131
+ @keyframes bt-toast-in {
2132
+ from { opacity: 0; transform: translateY(8px) scale(0.95); }
2133
+ to { opacity: 1; transform: translateY(0) scale(1); }
2134
+ }
1651
2135
  `;
1652
2136
  this.shadowRoot.appendChild(style);
1653
2137
  this.highlightOverlay = document.createElement("div");
@@ -1657,7 +2141,10 @@ var NoteInspector = class {
1657
2141
  this.markersContainer = document.createElement("div");
1658
2142
  this.markersContainer.className = "bt-markers-layer";
1659
2143
  this.shadowRoot.appendChild(this.markersContainer);
1660
- if (this.options.showToolbar) {
2144
+ this.toastContainer = document.createElement("div");
2145
+ this.toastContainer.className = "bt-toast-container";
2146
+ this.shadowRoot.appendChild(this.toastContainer);
2147
+ if (this.options.showToolbar && !this.isHidden) {
1661
2148
  this.createToolbar();
1662
2149
  }
1663
2150
  }
@@ -1684,6 +2171,10 @@ var NoteInspector = class {
1684
2171
  <span>\u{1F4C4}</span> Page
1685
2172
  </button>
1686
2173
  <div class="bt-toolbar-divider"></div>
2174
+ <button class="bt-toolbar-btn" id="bt-mode-flow" title="Record multi-step reproduction flow">
2175
+ <span>\u{1F3AC}</span> Flow
2176
+ </button>
2177
+ <div class="bt-toolbar-divider"></div>
1687
2178
  <button class="bt-toolbar-btn active" id="bt-toggle-notes" title="Toggle visible note markers on screen">
1688
2179
  <span>\u{1F4CC}</span> Notes <span class="bt-count-pill" id="bt-notes-count">0</span>
1689
2180
  </button>
@@ -1691,6 +2182,7 @@ var NoteInspector = class {
1691
2182
  const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
1692
2183
  const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
1693
2184
  const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
2185
+ const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
1694
2186
  const btnToggleNotes = this.toolbarElement.querySelector("#bt-toggle-notes");
1695
2187
  btnElement.onclick = (e) => {
1696
2188
  e.stopPropagation();
@@ -1704,6 +2196,14 @@ var NoteInspector = class {
1704
2196
  e.stopPropagation();
1705
2197
  this.setMode("page");
1706
2198
  };
2199
+ btnFlow.onclick = (e) => {
2200
+ e.stopPropagation();
2201
+ if (this.activeScenario) {
2202
+ this.finishScenario();
2203
+ } else {
2204
+ this.startScenario();
2205
+ }
2206
+ };
1707
2207
  btnToggleNotes.onclick = (e) => {
1708
2208
  e.stopPropagation();
1709
2209
  this.showMarkers = !this.showMarkers;
@@ -1717,9 +2217,21 @@ var NoteInspector = class {
1717
2217
  const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
1718
2218
  const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
1719
2219
  const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
2220
+ const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
1720
2221
  btnElement?.classList.toggle("active", this.activeMode === "element");
1721
2222
  btnRegion?.classList.toggle("active", this.activeMode === "region");
1722
2223
  btnPage?.classList.toggle("active", this.activeMode === "page");
2224
+ if (btnFlow) {
2225
+ if (this.activeScenario) {
2226
+ btnFlow.className = "bt-toolbar-btn active-scenario";
2227
+ btnFlow.innerHTML = `<span>\u{1F3AC}</span> Step ${this.activeScenario.stepNumber} (Finish)`;
2228
+ btnFlow.setAttribute("title", `Click to finish recording "${this.activeScenario.title}"`);
2229
+ } else {
2230
+ btnFlow.className = "bt-toolbar-btn";
2231
+ btnFlow.innerHTML = `<span>\u{1F3AC}</span> Flow`;
2232
+ btnFlow.setAttribute("title", "Record multi-step reproduction flow");
2233
+ }
2234
+ }
1723
2235
  }
1724
2236
  updateToolbarCount() {
1725
2237
  if (!this.toolbarElement) return;
@@ -1733,7 +2245,7 @@ var NoteInspector = class {
1733
2245
  const root = this.ensureContainer();
1734
2246
  if (!root || !this.markersContainer) return;
1735
2247
  this.markersContainer.innerHTML = "";
1736
- if (!this.showMarkers) return;
2248
+ if (!this.showMarkers || this.isHidden) return;
1737
2249
  const currentPath = window.location.pathname;
1738
2250
  const activeNotes = this.savedNotes.filter(
1739
2251
  (n) => n.status === "OPEN" && (n.route === currentPath || !n.route || n.route === "/" || window.location.href.includes(n.route))
@@ -1744,6 +2256,9 @@ var NoteInspector = class {
1744
2256
  pageNotes.push(note);
1745
2257
  return;
1746
2258
  }
2259
+ const isStep = !!note.scenarioId && note.stepNumber != null;
2260
+ const stepLabel = isStep ? `\u{1F3AC} Step ${note.stepNumber}` : `#${index + 1}`;
2261
+ const markerClass = `bt-note-marker ${isStep ? "bt-note-marker-step" : ""}`;
1747
2262
  if (note.type === "region" && note.region) {
1748
2263
  const regBox = document.createElement("div");
1749
2264
  regBox.className = "bt-region-marker-box";
@@ -1753,11 +2268,11 @@ var NoteInspector = class {
1753
2268
  regBox.style.height = `${note.region.height}px`;
1754
2269
  this.markersContainer.appendChild(regBox);
1755
2270
  const pin = document.createElement("div");
1756
- pin.className = "bt-note-marker";
2271
+ pin.className = markerClass;
1757
2272
  pin.style.left = `${Math.max(4, note.region.x - 12)}px`;
1758
2273
  pin.style.top = `${Math.max(4, note.region.y - 12)}px`;
1759
- pin.title = note.message;
1760
- pin.innerHTML = `<span>\u{1F4D0}</span> <span>#${index + 1}</span>`;
2274
+ pin.title = isStep ? `[${note.scenarioTitle || "Scenario"}] Step ${note.stepNumber}: ${note.message}` : note.message;
2275
+ pin.innerHTML = `<span>${isStep ? "\u{1F3AC}" : "\u{1F4D0}"}</span> <span>${stepLabel}</span>`;
1761
2276
  pin.onclick = (e) => {
1762
2277
  e.stopPropagation();
1763
2278
  this.openNoteCard(note);
@@ -1775,12 +2290,12 @@ var NoteInspector = class {
1775
2290
  const rect = targetEl ? targetEl.getBoundingClientRect() : note.target?.boundingRect;
1776
2291
  if (rect) {
1777
2292
  const pin = document.createElement("div");
1778
- pin.className = "bt-note-marker";
2293
+ pin.className = markerClass;
1779
2294
  pin.setAttribute("data-note-id", note.id);
1780
- pin.title = note.message;
2295
+ pin.title = isStep ? `[${note.scenarioTitle || "Scenario"}] Step ${note.stepNumber}: ${note.message}` : note.message;
1781
2296
  pin.style.left = `${Math.max(4, rect.left - 10)}px`;
1782
2297
  pin.style.top = `${Math.max(4, rect.top - 12)}px`;
1783
- pin.innerHTML = `<span>\u{1F4DD}</span> <span>#${index + 1}</span>`;
2298
+ pin.innerHTML = `<span>${isStep ? "\u{1F3AC}" : "\u{1F4DD}"}</span> <span>${stepLabel}</span>`;
1784
2299
  pin.onclick = (e) => {
1785
2300
  e.stopPropagation();
1786
2301
  this.openNoteCard(note);
@@ -1795,10 +2310,11 @@ var NoteInspector = class {
1795
2310
  if (pageNotes.length > 0) {
1796
2311
  const pageDock = document.createElement("div");
1797
2312
  pageDock.className = "bt-page-notes-dock";
1798
- pageNotes.forEach((pNote, idx) => {
2313
+ pageNotes.forEach((pNote) => {
1799
2314
  const pill = document.createElement("div");
1800
2315
  pill.className = "bt-page-note-pill";
1801
- pill.innerHTML = `<span>\u{1F4C4}</span> <span>Page Note (${truncate(pNote.message, 25)})</span>`;
2316
+ const label = pNote.scenarioId && pNote.stepNumber ? `Step ${pNote.stepNumber}: ` : "";
2317
+ pill.innerHTML = `<span>\u{1F4C4}</span> <span>${label}${truncate(pNote.message, 25)}</span>`;
1802
2318
  pill.onclick = (e) => {
1803
2319
  e.stopPropagation();
1804
2320
  this.openNoteCard(pNote);
@@ -1827,17 +2343,46 @@ var NoteInspector = class {
1827
2343
  const icon = note.type === "region" ? "\u{1F4D0}" : note.type === "page" ? "\u{1F4C4}" : "\u{1F3AF}";
1828
2344
  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}`;
1829
2345
  const formattedDate = new Date(note.createdAt).toLocaleString();
2346
+ let scenarioStepsHtml = "";
2347
+ let scenarioSteps = [];
2348
+ if (note.scenarioId) {
2349
+ scenarioSteps = this.savedNotes.filter((n) => n.scenarioId === note.scenarioId).sort((a, b) => (a.stepNumber || 0) - (b.stepNumber || 0));
2350
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2351
+ const prevStep = currentIndex > 0 ? scenarioSteps[currentIndex - 1] : null;
2352
+ const nextStep = currentIndex >= 0 && currentIndex < scenarioSteps.length - 1 ? scenarioSteps[currentIndex + 1] : null;
2353
+ scenarioStepsHtml = `
2354
+ <div class="bt-scenario-stepper">
2355
+ <button class="bt-step-nav-btn" id="btn-prev-step" ${!prevStep ? "disabled" : ""}>
2356
+ \u25C0 Step ${prevStep ? prevStep.stepNumber : ""}
2357
+ </button>
2358
+ <span>\u{1F3AC} Step ${note.stepNumber || 1} of ${scenarioSteps.length}</span>
2359
+ <button class="bt-step-nav-btn" id="btn-next-step-card" ${!nextStep ? "disabled" : ""}>
2360
+ Step ${nextStep ? nextStep.stepNumber : ""} \u25B6
2361
+ </button>
2362
+ </div>
2363
+ `;
2364
+ }
1830
2365
  backdrop.innerHTML = `
1831
2366
  <div class="bt-modal">
1832
2367
  <div class="bt-modal-header">
1833
2368
  <div class="bt-modal-title">
1834
- <span>${icon} Visual Note Details</span>
2369
+ <span>${note.scenarioId ? "\u{1F3AC} " + (note.scenarioTitle || "Scenario Flow") : icon + " Visual Note Details"}</span>
1835
2370
  <span class="bt-mode-badge ${badgeClass}">${note.type.toUpperCase()}</span>
2371
+ ${note.scenarioId && note.stepNumber ? `<span class="bt-mode-badge bt-badge-step">STEP ${note.stepNumber}</span>` : ""}
1836
2372
  <span class="bt-mode-badge ${statusClass}">${note.status}</span>
1837
2373
  </div>
1838
2374
  <button class="bt-close-btn" id="btn-card-close" title="Close (Esc)">\u2715</button>
1839
2375
  </div>
1840
2376
 
2377
+ ${scenarioStepsHtml}
2378
+
2379
+ ${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}">
2380
+ <span>\u{1F9EC}</span>
2381
+ <span style="font-weight: 600; color: #a5b4fc;">&lt;${note.elementContext.componentSource.componentName}&gt;</span>
2382
+ ${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>` : ""}
2383
+ ${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>` : ""}
2384
+ </div>` : ""}
2385
+
1841
2386
  <div class="bt-target-pill" title="${contextText}">
1842
2387
  <span>\u{1F3F7}\uFE0F</span>
1843
2388
  <span class="bt-pill-content">${contextText}</span>
@@ -1851,9 +2396,14 @@ var NoteInspector = class {
1851
2396
  </div>
1852
2397
 
1853
2398
  <div class="bt-modal-footer">
1854
- <button class="bt-btn bt-btn-delete" id="btn-card-delete" title="Delete this note">
1855
- <span>\u{1F5D1}\uFE0F</span> Delete
1856
- </button>
2399
+ <div style="display: flex; gap: 6px;">
2400
+ <button class="bt-btn bt-btn-delete" id="btn-card-delete" title="Delete this note">
2401
+ <span>\u{1F5D1}\uFE0F</span> Delete
2402
+ </button>
2403
+ ${note.scenarioId ? `<button class="bt-btn bt-btn-delete" id="btn-card-delete-flow" title="Delete entire scenario flow">
2404
+ <span>\u{1F5D1}\uFE0F</span> Delete Flow
2405
+ </button>` : ""}
2406
+ </div>
1857
2407
  <div class="bt-modal-actions">
1858
2408
  <button class="bt-btn bt-btn-cancel" id="btn-card-dismiss">Close</button>
1859
2409
  <button class="bt-btn ${note.status === "OPEN" ? "bt-btn-resolve" : "bt-btn-save"}" id="btn-card-resolve">
@@ -1867,6 +2417,9 @@ var NoteInspector = class {
1867
2417
  const btnDismiss = backdrop.querySelector("#btn-card-dismiss");
1868
2418
  const btnResolve = backdrop.querySelector("#btn-card-resolve");
1869
2419
  const btnDelete = backdrop.querySelector("#btn-card-delete");
2420
+ const btnDeleteFlow = backdrop.querySelector("#btn-card-delete-flow");
2421
+ const btnPrevStep = backdrop.querySelector("#btn-prev-step");
2422
+ const btnNextStepCard = backdrop.querySelector("#btn-next-step-card");
1870
2423
  const closeCard = () => {
1871
2424
  if (this.cardOverlay) {
1872
2425
  root.removeChild(this.cardOverlay);
@@ -1879,23 +2432,49 @@ var NoteInspector = class {
1879
2432
  backdrop.onclick = (e) => {
1880
2433
  if (e.target === backdrop) closeCard();
1881
2434
  };
2435
+ if (btnPrevStep && scenarioSteps.length > 0) {
2436
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2437
+ if (currentIndex > 0) {
2438
+ btnPrevStep.onclick = () => {
2439
+ this.openNoteCard(scenarioSteps[currentIndex - 1]);
2440
+ };
2441
+ }
2442
+ }
2443
+ if (btnNextStepCard && scenarioSteps.length > 0) {
2444
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2445
+ if (currentIndex >= 0 && currentIndex < scenarioSteps.length - 1) {
2446
+ btnNextStepCard.onclick = () => {
2447
+ this.openNoteCard(scenarioSteps[currentIndex + 1]);
2448
+ };
2449
+ }
2450
+ }
1882
2451
  btnResolve.onclick = () => {
1883
2452
  if (note.status === "OPEN") {
1884
2453
  this.transport.send({ type: "resolve_note", noteId: note.id });
2454
+ this.showToast("Note marked as resolved", "\u2705");
1885
2455
  } else {
1886
2456
  this.transport.send({ type: "reopen_note", noteId: note.id });
2457
+ this.showToast("Note reopened", "\u21BA");
1887
2458
  }
1888
2459
  closeCard();
1889
2460
  };
1890
2461
  btnDelete.onclick = () => {
1891
2462
  this.transport.send({ type: "delete_note", noteId: note.id });
2463
+ this.showToast("Note deleted", "\u{1F5D1}\uFE0F");
1892
2464
  closeCard();
1893
2465
  };
2466
+ if (btnDeleteFlow && note.scenarioId) {
2467
+ btnDeleteFlow.onclick = () => {
2468
+ this.transport.send({ type: "delete_scenario", scenarioId: note.scenarioId });
2469
+ this.showToast("Scenario flow deleted", "\u{1F5D1}\uFE0F");
2470
+ closeCard();
2471
+ };
2472
+ }
1894
2473
  root.appendChild(backdrop);
1895
2474
  }
1896
2475
  setupListeners() {
1897
2476
  const onMouseMove = (e) => {
1898
- if (this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
2477
+ if (this.isHidden || this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
1899
2478
  const path = e.composedPath ? e.composedPath() : [];
1900
2479
  if (this.container && path.includes(this.container)) {
1901
2480
  this.hideHighlight();
@@ -1914,7 +2493,7 @@ var NoteInspector = class {
1914
2493
  }
1915
2494
  };
1916
2495
  const onClick = (e) => {
1917
- if (this.modalOverlay || this.cardOverlay) return;
2496
+ if (this.isHidden || this.modalOverlay || this.cardOverlay) return;
1918
2497
  const path = e.composedPath ? e.composedPath() : [];
1919
2498
  if (this.container && path.includes(this.container)) {
1920
2499
  return;
@@ -1927,7 +2506,7 @@ var NoteInspector = class {
1927
2506
  if (target && target !== this.container && !this.container?.contains(target)) {
1928
2507
  this.selectedElement = target;
1929
2508
  this.openNoteEditor(target, "element");
1930
- if (this.activeMode === "element") {
2509
+ if (this.activeMode === "element" && !this.activeScenario) {
1931
2510
  this.setMode("idle");
1932
2511
  }
1933
2512
  }
@@ -1939,7 +2518,9 @@ var NoteInspector = class {
1939
2518
  this.shadowRoot.removeChild(this.cardOverlay);
1940
2519
  this.cardOverlay = null;
1941
2520
  } else if (this.activeMode === "region" || this.activeMode === "element") {
1942
- this.setMode("idle");
2521
+ if (!this.activeScenario) {
2522
+ this.setMode("idle");
2523
+ }
1943
2524
  }
1944
2525
  }
1945
2526
  };
@@ -2045,28 +2626,27 @@ var NoteInspector = class {
2045
2626
  width: Math.round(width),
2046
2627
  height: Math.round(height)
2047
2628
  };
2048
- this.hideRegionOverlay();
2049
- this.setMode("idle");
2050
2629
  this.openRegionNoteEditor(this.selectedRegion);
2051
- } else {
2052
- this.hideRegionOverlay();
2630
+ }
2631
+ if (this.regionBox) {
2632
+ this.regionBox.style.display = "none";
2633
+ }
2634
+ if (!this.activeScenario) {
2053
2635
  this.setMode("idle");
2054
2636
  }
2055
2637
  };
2056
- }
2057
- if (this.toolbarElement && this.toolbarElement.parentNode === root) {
2058
- root.insertBefore(this.regionOverlay, this.toolbarElement);
2059
- } else {
2060
2638
  root.appendChild(this.regionOverlay);
2639
+ } else {
2640
+ this.regionOverlay.style.display = "block";
2061
2641
  }
2062
2642
  }
2063
2643
  hideRegionOverlay() {
2064
- this.isDraggingRegion = false;
2065
- if (this.regionOverlay && this.regionOverlay.parentNode) {
2066
- this.regionOverlay.parentNode.removeChild(this.regionOverlay);
2067
- }
2068
- if (this.regionBox) {
2069
- this.regionBox.style.display = "none";
2644
+ if (this.regionOverlay) {
2645
+ this.regionOverlay.style.display = "none";
2646
+ this.isDraggingRegion = false;
2647
+ if (this.regionBox) {
2648
+ this.regionBox.style.display = "none";
2649
+ }
2070
2650
  }
2071
2651
  }
2072
2652
  updateHighlight(el) {
@@ -2074,19 +2654,19 @@ var NoteInspector = class {
2074
2654
  if (!root || !this.highlightOverlay) return;
2075
2655
  const rect = el.getBoundingClientRect();
2076
2656
  this.highlightOverlay.style.display = "block";
2077
- this.highlightOverlay.style.top = `${rect.top}px`;
2078
2657
  this.highlightOverlay.style.left = `${rect.left}px`;
2658
+ this.highlightOverlay.style.top = `${rect.top}px`;
2079
2659
  this.highlightOverlay.style.width = `${rect.width}px`;
2080
2660
  this.highlightOverlay.style.height = `${rect.height}px`;
2081
- const selector = getSemanticSelector(el);
2082
- const badgeText = `${selector} \xB7 ${Math.round(rect.width)}\xD7${Math.round(rect.height)}`;
2083
2661
  let badge = this.highlightOverlay.querySelector(".bt-badge");
2084
2662
  if (!badge) {
2085
2663
  badge = document.createElement("div");
2086
2664
  badge.className = "bt-badge";
2087
2665
  this.highlightOverlay.appendChild(badge);
2088
2666
  }
2089
- badge.textContent = badgeText;
2667
+ const selector = getSemanticSelector(el);
2668
+ const label = this.activeScenario ? `\u{1F3AC} Step ${this.activeScenario.stepNumber} \xB7 ${selector}` : selector;
2669
+ badge.textContent = `${label} (${Math.round(rect.width)} \xD7 ${Math.round(rect.height)} px)`;
2090
2670
  }
2091
2671
  hideHighlight() {
2092
2672
  if (this.highlightOverlay) {
@@ -2101,22 +2681,60 @@ var NoteInspector = class {
2101
2681
  if (noteType === "element") {
2102
2682
  this.updateHighlight(targetEl);
2103
2683
  }
2684
+ const comp = noteType === "element" ? resolveComponentSource(targetEl) : void 0;
2685
+ const compPrefix = comp?.componentName ? `\u{1F9EC} <${comp.componentName}>${comp.sourceFile ? " (" + comp.sourceFile + (comp.sourceLine ? ":" + comp.sourceLine : "") + ")" : ""} \xB7 ` : "";
2104
2686
  this.renderNoteModal({
2105
- title: "Add Visual Note",
2106
- modeBadge: noteType.toUpperCase(),
2107
- 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}`,
2108
- onSave: async (message) => {
2109
- await this.saveVisualNote(targetEl, message, noteType);
2687
+ title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Visual Note",
2688
+ modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : noteType.toUpperCase(),
2689
+ 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}`,
2690
+ onSave: async (message, action) => {
2691
+ let scenarioParam;
2692
+ if (action === "next_step" && !this.activeScenario) {
2693
+ this.startScenario();
2694
+ }
2695
+ if (this.activeScenario) {
2696
+ scenarioParam = {
2697
+ scenarioId: this.activeScenario.id,
2698
+ stepNumber: this.activeScenario.stepNumber,
2699
+ scenarioTitle: this.activeScenario.title
2700
+ };
2701
+ }
2702
+ await this.saveVisualNote(targetEl, message, noteType, scenarioParam);
2703
+ if (action === "next_step" && this.activeScenario) {
2704
+ this.activeScenario.stepNumber++;
2705
+ this.updateToolbarState();
2706
+ this.setMode("element");
2707
+ } else if (action === "finish_flow" && this.activeScenario) {
2708
+ this.finishScenario();
2709
+ }
2110
2710
  }
2111
2711
  });
2112
2712
  }
2113
2713
  openRegionNoteEditor(region) {
2114
2714
  this.renderNoteModal({
2115
- title: "Add Region Note",
2116
- modeBadge: "REGION",
2715
+ title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Region Note",
2716
+ modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : "REGION",
2117
2717
  pillText: `Selected Area: x:${region.x}, y:${region.y} (${region.width} \xD7 ${region.height} px)`,
2118
- onSave: async (message) => {
2119
- await this.saveRegionVisualNote(region, message);
2718
+ onSave: async (message, action) => {
2719
+ let scenarioParam;
2720
+ if (action === "next_step" && !this.activeScenario) {
2721
+ this.startScenario();
2722
+ }
2723
+ if (this.activeScenario) {
2724
+ scenarioParam = {
2725
+ scenarioId: this.activeScenario.id,
2726
+ stepNumber: this.activeScenario.stepNumber,
2727
+ scenarioTitle: this.activeScenario.title
2728
+ };
2729
+ }
2730
+ await this.saveRegionVisualNote(region, message, scenarioParam);
2731
+ if (action === "next_step" && this.activeScenario) {
2732
+ this.activeScenario.stepNumber++;
2733
+ this.updateToolbarState();
2734
+ this.setMode("element");
2735
+ } else if (action === "finish_flow" && this.activeScenario) {
2736
+ this.finishScenario();
2737
+ }
2120
2738
  }
2121
2739
  });
2122
2740
  }
@@ -2130,14 +2748,15 @@ var NoteInspector = class {
2130
2748
  const backdrop = document.createElement("div");
2131
2749
  backdrop.className = "bt-modal-backdrop";
2132
2750
  this.modalOverlay = backdrop;
2133
- const badgeClass = `bt-badge-${options.modeBadge.toLowerCase()}`;
2134
- const icon = options.modeBadge === "REGION" ? "\u{1F4D0}" : options.modeBadge === "PAGE" ? "\u{1F4C4}" : "\u{1F3AF}";
2751
+ const isStep = options.modeBadge.startsWith("STEP");
2752
+ const badgeClass = isStep ? "bt-badge-step" : `bt-badge-${options.modeBadge.toLowerCase()}`;
2753
+ const icon = isStep ? "\u{1F3AC}" : options.modeBadge === "REGION" ? "\u{1F4D0}" : options.modeBadge === "PAGE" ? "\u{1F4C4}" : "\u{1F3AF}";
2135
2754
  const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
2136
2755
  backdrop.innerHTML = `
2137
2756
  <div class="bt-modal">
2138
2757
  <div class="bt-modal-header">
2139
2758
  <div class="bt-modal-title">
2140
- <span>\u{1F4DD} ${options.title}</span>
2759
+ <span>${icon} ${options.title}</span>
2141
2760
  <span class="bt-mode-badge ${badgeClass}">${options.modeBadge}</span>
2142
2761
  </div>
2143
2762
  <button class="bt-close-btn" id="btn-close" title="Close (Esc)">\u2715</button>
@@ -2146,14 +2765,19 @@ var NoteInspector = class {
2146
2765
  <span>${icon}</span>
2147
2766
  <span class="bt-pill-content">${options.pillText}</span>
2148
2767
  </div>
2149
- <textarea class="bt-textarea" placeholder="Describe the layout issue, styling bug, or note for AI agent..." autofocus></textarea>
2768
+ <textarea class="bt-textarea" placeholder="Describe the layout issue, user action, or note for AI agent..." autofocus></textarea>
2150
2769
  <div class="bt-modal-footer">
2151
2770
  <div class="bt-kbd-hint">
2152
2771
  <kbd>${isMac ? "\u2318" : "Ctrl"}+Enter</kbd> save \xB7 <kbd>Esc</kbd> cancel
2153
2772
  </div>
2154
2773
  <div class="bt-modal-actions">
2155
2774
  <button class="bt-btn bt-btn-cancel" id="btn-cancel">Cancel</button>
2156
- <button class="bt-btn bt-btn-save" id="btn-save">Save Note</button>
2775
+ <button class="bt-btn bt-btn-next-step" id="btn-next-step" title="Save this step and immediately select the next element">
2776
+ <span>\u27A1\uFE0F</span> ${this.activeScenario ? "Save & Next Step" : "Save as Step 1 (Flow)"}
2777
+ </button>
2778
+ ${this.activeScenario ? `<button class="bt-btn bt-btn-finish-flow" id="btn-finish-flow" title="Save final step and complete scenario">
2779
+ <span>\u2713</span> Save & Finish Flow
2780
+ </button>` : `<button class="bt-btn bt-btn-save" id="btn-save">Save Note</button>`}
2157
2781
  </div>
2158
2782
  </div>
2159
2783
  </div>
@@ -2162,6 +2786,8 @@ var NoteInspector = class {
2162
2786
  const btnClose = backdrop.querySelector("#btn-close");
2163
2787
  const btnCancel = backdrop.querySelector("#btn-cancel");
2164
2788
  const btnSave = backdrop.querySelector("#btn-save");
2789
+ const btnNextStep = backdrop.querySelector("#btn-next-step");
2790
+ const btnFinishFlow = backdrop.querySelector("#btn-finish-flow");
2165
2791
  const closeModal = () => {
2166
2792
  if (this.modalOverlay) {
2167
2793
  root.removeChild(this.modalOverlay);
@@ -2177,22 +2803,29 @@ var NoteInspector = class {
2177
2803
  closeModal();
2178
2804
  }
2179
2805
  };
2180
- const submitNote = async () => {
2806
+ const handleAction = async (action) => {
2181
2807
  const message = textarea.value.trim();
2182
2808
  if (!message) return;
2183
- btnSave.textContent = "Saving...";
2184
- btnSave.disabled = true;
2809
+ btnNextStep.disabled = true;
2810
+ if (btnSave) btnSave.disabled = true;
2811
+ if (btnFinishFlow) btnFinishFlow.disabled = true;
2185
2812
  try {
2186
- await options.onSave(message);
2813
+ await options.onSave(message, action);
2187
2814
  } finally {
2188
2815
  closeModal();
2189
2816
  }
2190
2817
  };
2191
- btnSave.onclick = submitNote;
2818
+ if (btnSave) {
2819
+ btnSave.onclick = () => handleAction("save");
2820
+ }
2821
+ btnNextStep.onclick = () => handleAction("next_step");
2822
+ if (btnFinishFlow) {
2823
+ btnFinishFlow.onclick = () => handleAction("finish_flow");
2824
+ }
2192
2825
  textarea.onkeydown = (e) => {
2193
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey || !e.shiftKey)) {
2826
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2194
2827
  e.preventDefault();
2195
- submitNote();
2828
+ handleAction(this.activeScenario ? "next_step" : "save");
2196
2829
  }
2197
2830
  if (e.key === "Escape") {
2198
2831
  closeModal();
@@ -2201,7 +2834,7 @@ var NoteInspector = class {
2201
2834
  root.appendChild(backdrop);
2202
2835
  setTimeout(() => textarea?.focus(), 50);
2203
2836
  }
2204
- async saveVisualNote(targetEl, message, noteType = "element") {
2837
+ async saveVisualNote(targetEl, message, noteType = "element", scenario) {
2205
2838
  const rect = targetEl.getBoundingClientRect();
2206
2839
  const selector = noteType === "page" ? "body" : getSemanticSelector(targetEl);
2207
2840
  let screenshotDataUrl;
@@ -2228,33 +2861,38 @@ var NoteInspector = class {
2228
2861
  visible: rect.width > 0 && rect.height > 0,
2229
2862
  confidence: selector.startsWith("[data-test") ? "high" : selector.startsWith("#") ? "medium" : "low"
2230
2863
  };
2864
+ const route = typeof window !== "undefined" ? window.location.pathname + window.location.search : "/";
2865
+ const url = typeof window !== "undefined" ? window.location.href : "http://localhost/";
2866
+ const viewport = typeof window !== "undefined" ? { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio || 1 } : { width: 1280, height: 800, devicePixelRatio: 1 };
2867
+ const scroll = typeof window !== "undefined" ? { scrollX: window.scrollX, scrollY: window.scrollY } : { scrollX: 0, scrollY: 0 };
2231
2868
  const notePayload = {
2232
2869
  type: "create_note",
2233
2870
  sessionId: this.transport.getSessionId() || "",
2234
2871
  noteType,
2235
2872
  message,
2236
- route: window.location.pathname + window.location.search,
2237
- url: window.location.href,
2238
- viewport: {
2239
- width: window.innerWidth,
2240
- height: window.innerHeight,
2241
- devicePixelRatio: window.devicePixelRatio || 1
2242
- },
2243
- scroll: {
2244
- scrollX: window.scrollX,
2245
- scrollY: window.scrollY
2246
- },
2873
+ route,
2874
+ url,
2875
+ viewport,
2876
+ scroll,
2247
2877
  target,
2248
2878
  elementContext,
2249
2879
  screenshot: screenshotDataUrl,
2880
+ scenarioId: scenario?.scenarioId,
2881
+ stepNumber: scenario?.stepNumber,
2882
+ scenarioTitle: scenario?.scenarioTitle,
2250
2883
  timestamp: Date.now()
2251
2884
  };
2252
2885
  this.transport.send(notePayload);
2253
2886
  if (this.options.onNoteCreated) {
2254
2887
  this.options.onNoteCreated(notePayload);
2255
2888
  }
2889
+ if (scenario?.scenarioId) {
2890
+ this.showToast(`Step ${scenario.stepNumber || 1} recorded`, "\u{1F3AC}");
2891
+ } else {
2892
+ this.showToast("Visual note saved", "\u2728");
2893
+ }
2256
2894
  }
2257
- async saveRegionVisualNote(region, message) {
2895
+ async saveRegionVisualNote(region, message, scenario) {
2258
2896
  let screenshotDataUrl;
2259
2897
  try {
2260
2898
  const snap = await this.screenshotDriver.captureElement(document.body || document.documentElement);
@@ -2263,123 +2901,115 @@ var NoteInspector = class {
2263
2901
  }
2264
2902
  } catch {
2265
2903
  }
2904
+ const route = typeof window !== "undefined" ? window.location.pathname + window.location.search : "/";
2905
+ const url = typeof window !== "undefined" ? window.location.href : "http://localhost/";
2906
+ const viewport = typeof window !== "undefined" ? { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio || 1 } : { width: 1280, height: 800, devicePixelRatio: 1 };
2907
+ const scroll = typeof window !== "undefined" ? { scrollX: window.scrollX, scrollY: window.scrollY } : { scrollX: 0, scrollY: 0 };
2266
2908
  const notePayload = {
2267
2909
  type: "create_note",
2268
2910
  sessionId: this.transport.getSessionId() || "",
2269
2911
  noteType: "region",
2270
2912
  message,
2271
- route: window.location.pathname + window.location.search,
2272
- url: window.location.href,
2273
- viewport: {
2274
- width: window.innerWidth,
2275
- height: window.innerHeight,
2276
- devicePixelRatio: window.devicePixelRatio || 1
2277
- },
2278
- scroll: {
2279
- scrollX: window.scrollX,
2280
- scrollY: window.scrollY
2281
- },
2913
+ route,
2914
+ url,
2915
+ viewport,
2916
+ scroll,
2282
2917
  region,
2283
2918
  screenshot: screenshotDataUrl,
2919
+ scenarioId: scenario?.scenarioId,
2920
+ stepNumber: scenario?.stepNumber,
2921
+ scenarioTitle: scenario?.scenarioTitle,
2284
2922
  timestamp: Date.now()
2285
2923
  };
2286
2924
  this.transport.send(notePayload);
2287
2925
  if (this.options.onNoteCreated) {
2288
2926
  this.options.onNoteCreated(notePayload);
2289
2927
  }
2290
- }
2291
- async cropDataUrl(dataUrl, region) {
2292
- return new Promise((resolve) => {
2293
- const img = new Image();
2294
- img.onload = () => {
2295
- try {
2296
- const canvas = document.createElement("canvas");
2297
- canvas.width = region.width;
2298
- canvas.height = region.height;
2299
- const ctx = canvas.getContext("2d");
2300
- if (!ctx) {
2301
- resolve(dataUrl);
2302
- return;
2303
- }
2304
- const dpr = window.devicePixelRatio || 1;
2305
- ctx.drawImage(
2306
- img,
2307
- region.x * dpr,
2308
- region.y * dpr,
2309
- region.width * dpr,
2310
- region.height * dpr,
2311
- 0,
2312
- 0,
2313
- region.width,
2314
- region.height
2315
- );
2316
- resolve(canvas.toDataURL("image/webp", 0.9));
2317
- } catch {
2318
- resolve(dataUrl);
2319
- }
2320
- };
2321
- img.onerror = () => resolve(dataUrl);
2322
- img.src = dataUrl;
2323
- });
2928
+ if (scenario?.scenarioId) {
2929
+ this.showToast(`Step ${scenario.stepNumber || 1} region recorded`, "\u{1F3AC}");
2930
+ } else {
2931
+ this.showToast("Region note saved", "\u{1F4D0}");
2932
+ }
2324
2933
  }
2325
2934
  extractElementContext(el) {
2326
2935
  const selector = getSemanticSelector(el);
2327
- const tag = el.tagName.toLowerCase();
2328
2936
  const attributes = {};
2329
2937
  for (let i = 0; i < el.attributes.length; i++) {
2330
2938
  const attr = el.attributes[i];
2331
- if (attr.name === "value" && el.type === "password") {
2939
+ if (this.options.maskSelectors?.some((mask) => {
2940
+ try {
2941
+ return el.matches(mask);
2942
+ } catch {
2943
+ return false;
2944
+ }
2945
+ }) && (attr.name === "value" || attr.name === "data-secret")) {
2332
2946
  attributes[attr.name] = "[REDACTED]";
2333
2947
  } else {
2334
2948
  attributes[attr.name] = attr.value;
2335
2949
  }
2336
2950
  }
2337
- let outerHTML = "";
2338
- try {
2339
- const clone = el.cloneNode(true);
2340
- for (const passInput of Array.from(clone.querySelectorAll('input[type="password"]'))) {
2341
- passInput.setAttribute("value", "[REDACTED]");
2342
- }
2343
- outerHTML = truncate(clone.outerHTML, 10240);
2344
- } catch {
2345
- outerHTML = truncate(el.outerHTML, 10240);
2951
+ let outerHTML = el.outerHTML;
2952
+ if (outerHTML && outerHTML.length > 1e3) {
2953
+ outerHTML = truncate(outerHTML, 1e3);
2346
2954
  }
2347
- let parent;
2348
- if (el.parentElement && el.parentElement !== document.body) {
2349
- parent = {
2350
- selector: getSemanticSelector(el.parentElement),
2351
- tag: el.parentElement.tagName.toLowerCase()
2352
- };
2955
+ let innerText = el.innerText || el.textContent || "";
2956
+ if (innerText && innerText.length > 200) {
2957
+ innerText = truncate(innerText, 200);
2353
2958
  }
2959
+ const componentSource = resolveComponentSource(el);
2354
2960
  return {
2355
2961
  selector,
2356
- tag,
2962
+ tag: el.tagName.toLowerCase(),
2357
2963
  attributes,
2358
2964
  outerHTML,
2359
- innerText: truncate(el.textContent?.trim(), 200),
2360
- parent
2965
+ innerText,
2966
+ componentSource,
2967
+ parent: el.parentElement ? {
2968
+ selector: getSemanticSelector(el.parentElement),
2969
+ tag: el.parentElement.tagName.toLowerCase()
2970
+ } : void 0
2361
2971
  };
2362
2972
  }
2973
+ async cropDataUrl(dataUrl, region) {
2974
+ return new Promise((resolve) => {
2975
+ const img = new Image();
2976
+ img.onload = () => {
2977
+ const canvas = document.createElement("canvas");
2978
+ canvas.width = region.width;
2979
+ canvas.height = region.height;
2980
+ const ctx = canvas.getContext("2d");
2981
+ if (!ctx) {
2982
+ resolve(dataUrl);
2983
+ return;
2984
+ }
2985
+ const dpr = window.devicePixelRatio || 1;
2986
+ ctx.drawImage(
2987
+ img,
2988
+ region.x * dpr,
2989
+ region.y * dpr,
2990
+ region.width * dpr,
2991
+ region.height * dpr,
2992
+ 0,
2993
+ 0,
2994
+ region.width,
2995
+ region.height
2996
+ );
2997
+ resolve(canvas.toDataURL("image/png"));
2998
+ };
2999
+ img.onerror = () => resolve(dataUrl);
3000
+ img.src = dataUrl;
3001
+ });
3002
+ }
2363
3003
  destroy() {
2364
3004
  for (const cleanup of this.cleanups) {
2365
- try {
2366
- cleanup();
2367
- } catch {
2368
- }
3005
+ cleanup();
2369
3006
  }
2370
3007
  this.cleanups = [];
2371
- if (this.container && this.container.parentElement) {
2372
- this.container.parentElement.removeChild(this.container);
2373
- }
2374
- this.container = null;
2375
- this.shadowRoot = null;
2376
- this.toolbarElement = null;
2377
- this.regionOverlay = null;
2378
- this.regionBox = null;
2379
- this.regionBanner = null;
2380
- this.markersContainer = null;
2381
- this.modalOverlay = null;
2382
- this.cardOverlay = null;
3008
+ if (this.container && this.container.parentNode) {
3009
+ this.container.parentNode.removeChild(this.container);
3010
+ this.container = null;
3011
+ this.shadowRoot = null;
3012
+ }
2383
3013
  }
2384
3014
  };
2385
3015
 
@@ -2416,7 +3046,11 @@ var BrowserTrackClient = class {
2416
3046
  if (this.options.notes.enabled) {
2417
3047
  this.inspector = new NoteInspector(this.transport, this.screenshotDriver, {
2418
3048
  shortcut: this.options.notes.shortcut,
2419
- maskSelectors: this.options.notes.maskSelectors
3049
+ maskSelectors: this.options.notes.maskSelectors,
3050
+ showToolbar: this.options.notes.showToolbar,
3051
+ showBadges: this.options.notes.showBadges,
3052
+ hidden: this.options.hidden,
3053
+ hideQueryParam: this.options.hideQueryParam
2420
3054
  });
2421
3055
  }
2422
3056
  }
@@ -2482,6 +3116,14 @@ var BrowserTrackClient = class {
2482
3116
  this.inspector.setMode("element");
2483
3117
  }
2484
3118
  }
3119
+ isUIVisible() {
3120
+ return this.inspector ? this.inspector.isVisible() : false;
3121
+ }
3122
+ setUIVisible(visible) {
3123
+ if (this.inspector) {
3124
+ this.inspector.setVisible(visible);
3125
+ }
3126
+ }
2485
3127
  setInspectMode(mode) {
2486
3128
  if (this.inspector) {
2487
3129
  this.inspector.setMode(mode);
@@ -2622,8 +3264,10 @@ if (typeof window !== "undefined") {
2622
3264
  const autoInit = currentScript?.getAttribute("data-auto-init") !== "false";
2623
3265
  const daemonUrl = currentScript?.getAttribute("data-daemon-url") || void 0;
2624
3266
  const projectId = currentScript?.getAttribute("data-project-id") || void 0;
3267
+ const hidden = currentScript?.getAttribute("data-hidden") === "true";
3268
+ const hideQueryParam = currentScript?.getAttribute("data-hide-query-param") || void 0;
2625
3269
  if (autoInit) {
2626
- init({ daemonUrl, projectId });
3270
+ init({ daemonUrl, projectId, hidden, hideQueryParam });
2627
3271
  }
2628
3272
  } catch {
2629
3273
  }
@@ -2631,11 +3275,13 @@ if (typeof window !== "undefined") {
2631
3275
 
2632
3276
  export {
2633
3277
  BreadcrumbBuffer,
3278
+ resolveComponentSource,
2634
3279
  DEFAULT_OPTIONS,
3280
+ shouldHideUIFromUrl,
2635
3281
  BrowserScriptScreenshotDriver,
2636
3282
  NoteInspector,
2637
3283
  BrowserTrackClient,
2638
3284
  init,
2639
3285
  getClient
2640
3286
  };
2641
- //# sourceMappingURL=chunk-SKCMT2DE.js.map
3287
+ //# sourceMappingURL=chunk-INXDWPJW.js.map