browsertrack 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/AGENTS.md +4 -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-SPCIROIU.js → chunk-7OCOQGDN.js} +24 -5
  5. package/dist/chunk-7OCOQGDN.js.map +1 -0
  6. package/dist/{chunk-G2Y3CXCY.js → chunk-UP5JKCFY.js} +84 -2
  7. package/dist/chunk-UP5JKCFY.js.map +1 -0
  8. package/dist/{chunk-SKCMT2DE.js → chunk-WB7ZKWK7.js} +453 -152
  9. package/dist/chunk-WB7ZKWK7.js.map +1 -0
  10. package/dist/cli/index.js +200 -5
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/client/index.cjs +452 -151
  13. package/dist/client/index.d.ts +28 -9
  14. package/dist/client/index.js +1 -1
  15. package/dist/client.iife.js +185 -29
  16. package/dist/core/index.d.ts +2 -2
  17. package/dist/daemon/index.d.ts +3 -3
  18. package/dist/daemon/index.js +2 -2
  19. package/dist/{engine-CeT9URuN.d.ts → engine-B43IohQY.d.ts} +13 -2
  20. package/dist/index.d.ts +4 -4
  21. package/dist/index.js +4 -4
  22. package/dist/mcp/index.d.ts +4 -4
  23. package/dist/mcp/index.js +2 -2
  24. package/dist/{notes-CBvN91Wf.d.ts → notes-BMnonq46.d.ts} +24 -1
  25. package/dist/{projects-CY8ungMt.d.ts → projects-D5J-egVN.d.ts} +1 -1
  26. package/dist/{server-Dd8NX2Mk.d.ts → server-BztYp1Zc.d.ts} +1 -1
  27. package/docs/index.md +2 -1
  28. package/docs/mcp-reference.md +14 -1
  29. package/docs/scenarios-flows.md +86 -0
  30. package/package.json +1 -1
  31. package/packages/client/src/notes/inspector.ts +533 -161
  32. package/packages/core/src/types/notes.ts +25 -0
  33. package/packages/daemon/src/notes/engine.ts +6 -0
  34. package/packages/daemon/src/server/ws.ts +23 -1
  35. package/packages/daemon/src/storage/db.ts +106 -3
  36. package/packages/mcp/src/handlers.ts +58 -0
  37. package/packages/mcp/src/tools.ts +28 -0
  38. package/test/client/interceptors.test.ts +64 -0
  39. package/test/daemon/scenario-storage.test.ts +158 -0
  40. package/dist/chunk-G2Y3CXCY.js.map +0 -1
  41. package/dist/chunk-ILRYKMME.js.map +0 -1
  42. package/dist/chunk-SKCMT2DE.js.map +0 -1
  43. package/dist/chunk-SPCIROIU.js.map +0 -1
@@ -1043,7 +1043,9 @@ var NoteInspector = class {
1043
1043
  toolbarElement = null;
1044
1044
  modalOverlay = null;
1045
1045
  cardOverlay = null;
1046
+ toastContainer = null;
1046
1047
  activeMode = "idle";
1048
+ activeScenario = null;
1047
1049
  hoveredElement = null;
1048
1050
  selectedElement = null;
1049
1051
  selectedRegion = null;
@@ -1085,6 +1087,41 @@ var NoteInspector = class {
1085
1087
  this.updateToolbarCount();
1086
1088
  this.renderMarkers();
1087
1089
  }
1090
+ showToast(message, icon = "\u2728", durationMs = 2500) {
1091
+ if (typeof document === "undefined") return;
1092
+ const root = this.ensureContainer();
1093
+ if (!root || !this.toastContainer) return;
1094
+ const toast = document.createElement("div");
1095
+ toast.className = "bt-toast";
1096
+ toast.innerHTML = `<span>${icon}</span> <span>${message}</span>`;
1097
+ this.toastContainer.appendChild(toast);
1098
+ setTimeout(() => {
1099
+ toast.classList.add("bt-toast-fadeout");
1100
+ setTimeout(() => {
1101
+ if (toast.parentElement) {
1102
+ toast.parentElement.removeChild(toast);
1103
+ }
1104
+ }, 250);
1105
+ }, durationMs);
1106
+ }
1107
+ startScenario(title) {
1108
+ const defaultTitle = title || `Scenario ${(/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
1109
+ this.activeScenario = {
1110
+ id: `scen_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`,
1111
+ title: defaultTitle,
1112
+ stepNumber: 1
1113
+ };
1114
+ this.updateToolbarState();
1115
+ this.setMode("element");
1116
+ this.showToast(`Started flow: "${defaultTitle}"`, "\u{1F3AC}");
1117
+ }
1118
+ finishScenario() {
1119
+ const title = this.activeScenario?.title;
1120
+ this.activeScenario = null;
1121
+ this.updateToolbarState();
1122
+ this.setMode("idle");
1123
+ this.showToast(title ? `Finished flow: "${title}"` : "Flow recording finished", "\u2713");
1124
+ }
1088
1125
  setMode(mode) {
1089
1126
  this.activeMode = mode;
1090
1127
  this.updateToolbarState();
@@ -1264,6 +1301,12 @@ var NoteInspector = class {
1264
1301
  z-index: 2147483641;
1265
1302
  }
1266
1303
 
1304
+ .bt-note-marker-step {
1305
+ background: linear-gradient(135deg, #f59e0b, #d97706);
1306
+ border-color: #fef3c7;
1307
+ box-shadow: 0 4px 14px rgba(245, 158, 11, 0.4), 0 0 0 1px rgba(217, 119, 6, 0.5);
1308
+ }
1309
+
1267
1310
  @keyframes bt-pop-in {
1268
1311
  0% { transform: scale(0.6); opacity: 0; }
1269
1312
  100% { transform: scale(1); opacity: 1; }
@@ -1274,6 +1317,10 @@ var NoteInspector = class {
1274
1317
  box-shadow: 0 8px 20px rgba(37,99,235,0.6), 0 0 0 2px #60a5fa;
1275
1318
  }
1276
1319
 
1320
+ .bt-note-marker-step:hover {
1321
+ box-shadow: 0 8px 20px rgba(245, 158, 11, 0.7), 0 0 0 2px #fde68a;
1322
+ }
1323
+
1277
1324
  .bt-marker-resolved {
1278
1325
  background: linear-gradient(135deg, #475569, #334155);
1279
1326
  border-color: #94a3b8;
@@ -1370,6 +1417,19 @@ var NoteInspector = class {
1370
1417
  box-shadow: 0 2px 6px rgba(37, 99, 235, 0.35);
1371
1418
  }
1372
1419
 
1420
+ .bt-toolbar-btn.active-scenario {
1421
+ background: linear-gradient(135deg, #f59e0b, #d97706);
1422
+ color: #ffffff;
1423
+ font-weight: 700;
1424
+ box-shadow: 0 2px 8px rgba(245, 158, 11, 0.4);
1425
+ animation: bt-pulse 2s infinite;
1426
+ }
1427
+
1428
+ @keyframes bt-pulse {
1429
+ 0%, 100% { opacity: 1; }
1430
+ 50% { opacity: 0.85; }
1431
+ }
1432
+
1373
1433
  .bt-count-pill {
1374
1434
  background: rgba(255, 255, 255, 0.2);
1375
1435
  color: #ffffff;
@@ -1417,7 +1477,7 @@ var NoteInspector = class {
1417
1477
  border: 1px solid #334155;
1418
1478
  border-radius: 14px;
1419
1479
  padding: 20px;
1420
- width: 450px;
1480
+ width: 470px;
1421
1481
  max-width: 92vw;
1422
1482
  box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.05);
1423
1483
  display: flex;
@@ -1440,6 +1500,7 @@ var NoteInspector = class {
1440
1500
  align-items: center;
1441
1501
  gap: 8px;
1442
1502
  color: #f8fafc;
1503
+ flex-wrap: wrap;
1443
1504
  }
1444
1505
 
1445
1506
  .bt-mode-badge {
@@ -1469,6 +1530,12 @@ var NoteInspector = class {
1469
1530
  border: 1px solid rgba(168, 85, 247, 0.4);
1470
1531
  }
1471
1532
 
1533
+ .bt-badge-step {
1534
+ background: rgba(245, 158, 11, 0.15);
1535
+ color: #fbbf24;
1536
+ border: 1px solid rgba(245, 158, 11, 0.4);
1537
+ }
1538
+
1472
1539
  .bt-badge-status-open {
1473
1540
  background: rgba(16, 185, 129, 0.15);
1474
1541
  color: #34d399;
@@ -1520,6 +1587,43 @@ var NoteInspector = class {
1520
1587
  color: #cbd5e1;
1521
1588
  }
1522
1589
 
1590
+ .bt-scenario-stepper {
1591
+ display: flex;
1592
+ align-items: center;
1593
+ justify-content: space-between;
1594
+ background: #090d16;
1595
+ border: 1px solid #1e293b;
1596
+ border-radius: 8px;
1597
+ padding: 6px 10px;
1598
+ font-size: 12px;
1599
+ font-weight: 600;
1600
+ color: #f59e0b;
1601
+ }
1602
+
1603
+ .bt-step-nav-btn {
1604
+ background: #1e293b;
1605
+ color: #f8fafc;
1606
+ border: 1px solid #334155;
1607
+ padding: 4px 10px;
1608
+ border-radius: 6px;
1609
+ cursor: pointer;
1610
+ font-size: 11px;
1611
+ font-weight: 600;
1612
+ transition: all 0.12s ease;
1613
+ font-family: inherit;
1614
+ }
1615
+
1616
+ .bt-step-nav-btn:hover:not(:disabled) {
1617
+ background: #2563eb;
1618
+ border-color: #3b82f6;
1619
+ color: #ffffff;
1620
+ }
1621
+
1622
+ .bt-step-nav-btn:disabled {
1623
+ opacity: 0.3;
1624
+ cursor: not-allowed;
1625
+ }
1626
+
1523
1627
  .bt-note-message-box {
1524
1628
  background: #090d16;
1525
1629
  border: 1px solid #1e293b;
@@ -1564,8 +1668,9 @@ var NoteInspector = class {
1564
1668
  display: flex;
1565
1669
  justify-content: space-between;
1566
1670
  align-items: center;
1567
- gap: 12px;
1671
+ gap: 10px;
1568
1672
  margin-top: 2px;
1673
+ flex-wrap: wrap;
1569
1674
  }
1570
1675
 
1571
1676
  .bt-kbd-hint {
@@ -1593,12 +1698,13 @@ var NoteInspector = class {
1593
1698
  justify-content: flex-end;
1594
1699
  align-items: center;
1595
1700
  gap: 8px;
1701
+ flex-wrap: wrap;
1596
1702
  }
1597
1703
 
1598
1704
  .bt-btn {
1599
- padding: 8px 16px;
1705
+ padding: 7px 14px;
1600
1706
  border-radius: 7px;
1601
- font-size: 12.5px;
1707
+ font-size: 12px;
1602
1708
  font-weight: 600;
1603
1709
  cursor: pointer;
1604
1710
  border: 1px solid transparent;
@@ -1607,7 +1713,7 @@ var NoteInspector = class {
1607
1713
  display: inline-flex;
1608
1714
  align-items: center;
1609
1715
  justify-content: center;
1610
- gap: 6px;
1716
+ gap: 5px;
1611
1717
  }
1612
1718
 
1613
1719
  .bt-btn-cancel {
@@ -1629,6 +1735,25 @@ var NoteInspector = class {
1629
1735
  box-shadow: 0 4px 10px rgba(37, 99, 235, 0.45);
1630
1736
  }
1631
1737
 
1738
+ .bt-btn-next-step {
1739
+ background: linear-gradient(135deg, #6366f1, #4f46e5);
1740
+ color: #ffffff;
1741
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.4);
1742
+ }
1743
+ .bt-btn-next-step:hover {
1744
+ background: linear-gradient(135deg, #4f46e5, #4338ca);
1745
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.55);
1746
+ }
1747
+
1748
+ .bt-btn-finish-flow {
1749
+ background: linear-gradient(135deg, #10b981, #059669);
1750
+ color: #ffffff;
1751
+ box-shadow: 0 2px 8px rgba(16, 185, 129, 0.35);
1752
+ }
1753
+ .bt-btn-finish-flow:hover {
1754
+ background: linear-gradient(135deg, #059669, #047857);
1755
+ }
1756
+
1632
1757
  .bt-btn-resolve {
1633
1758
  background: rgba(16, 185, 129, 0.15);
1634
1759
  color: #6ee7b7;
@@ -1648,6 +1773,47 @@ var NoteInspector = class {
1648
1773
  background: rgba(239, 68, 68, 0.3);
1649
1774
  color: #ffffff;
1650
1775
  }
1776
+
1777
+ /* 6. Toast Notifications */
1778
+ .bt-toast-container {
1779
+ position: fixed;
1780
+ bottom: 74px;
1781
+ right: 24px;
1782
+ display: flex;
1783
+ flex-direction: column;
1784
+ gap: 8px;
1785
+ pointer-events: none;
1786
+ z-index: 2147483647;
1787
+ align-items: flex-end;
1788
+ }
1789
+
1790
+ .bt-toast {
1791
+ background: #0f172a;
1792
+ color: #f8fafc;
1793
+ border: 1px solid #334155;
1794
+ border-radius: 30px;
1795
+ padding: 8px 16px;
1796
+ font-size: 12.5px;
1797
+ font-weight: 500;
1798
+ display: inline-flex;
1799
+ align-items: center;
1800
+ gap: 8px;
1801
+ box-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.6), 0 4px 6px -4px rgba(0, 0, 0, 0.4);
1802
+ pointer-events: auto;
1803
+ animation: bt-toast-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
1804
+ transition: opacity 0.25s ease, transform 0.25s ease;
1805
+ user-select: none;
1806
+ }
1807
+
1808
+ .bt-toast-fadeout {
1809
+ opacity: 0;
1810
+ transform: translateY(6px);
1811
+ }
1812
+
1813
+ @keyframes bt-toast-in {
1814
+ from { opacity: 0; transform: translateY(8px) scale(0.95); }
1815
+ to { opacity: 1; transform: translateY(0) scale(1); }
1816
+ }
1651
1817
  `;
1652
1818
  this.shadowRoot.appendChild(style);
1653
1819
  this.highlightOverlay = document.createElement("div");
@@ -1657,6 +1823,9 @@ var NoteInspector = class {
1657
1823
  this.markersContainer = document.createElement("div");
1658
1824
  this.markersContainer.className = "bt-markers-layer";
1659
1825
  this.shadowRoot.appendChild(this.markersContainer);
1826
+ this.toastContainer = document.createElement("div");
1827
+ this.toastContainer.className = "bt-toast-container";
1828
+ this.shadowRoot.appendChild(this.toastContainer);
1660
1829
  if (this.options.showToolbar) {
1661
1830
  this.createToolbar();
1662
1831
  }
@@ -1684,6 +1853,10 @@ var NoteInspector = class {
1684
1853
  <span>\u{1F4C4}</span> Page
1685
1854
  </button>
1686
1855
  <div class="bt-toolbar-divider"></div>
1856
+ <button class="bt-toolbar-btn" id="bt-mode-flow" title="Record multi-step reproduction flow">
1857
+ <span>\u{1F3AC}</span> Flow
1858
+ </button>
1859
+ <div class="bt-toolbar-divider"></div>
1687
1860
  <button class="bt-toolbar-btn active" id="bt-toggle-notes" title="Toggle visible note markers on screen">
1688
1861
  <span>\u{1F4CC}</span> Notes <span class="bt-count-pill" id="bt-notes-count">0</span>
1689
1862
  </button>
@@ -1691,6 +1864,7 @@ var NoteInspector = class {
1691
1864
  const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
1692
1865
  const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
1693
1866
  const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
1867
+ const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
1694
1868
  const btnToggleNotes = this.toolbarElement.querySelector("#bt-toggle-notes");
1695
1869
  btnElement.onclick = (e) => {
1696
1870
  e.stopPropagation();
@@ -1704,6 +1878,14 @@ var NoteInspector = class {
1704
1878
  e.stopPropagation();
1705
1879
  this.setMode("page");
1706
1880
  };
1881
+ btnFlow.onclick = (e) => {
1882
+ e.stopPropagation();
1883
+ if (this.activeScenario) {
1884
+ this.finishScenario();
1885
+ } else {
1886
+ this.startScenario();
1887
+ }
1888
+ };
1707
1889
  btnToggleNotes.onclick = (e) => {
1708
1890
  e.stopPropagation();
1709
1891
  this.showMarkers = !this.showMarkers;
@@ -1717,9 +1899,21 @@ var NoteInspector = class {
1717
1899
  const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
1718
1900
  const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
1719
1901
  const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
1902
+ const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
1720
1903
  btnElement?.classList.toggle("active", this.activeMode === "element");
1721
1904
  btnRegion?.classList.toggle("active", this.activeMode === "region");
1722
1905
  btnPage?.classList.toggle("active", this.activeMode === "page");
1906
+ if (btnFlow) {
1907
+ if (this.activeScenario) {
1908
+ btnFlow.className = "bt-toolbar-btn active-scenario";
1909
+ btnFlow.innerHTML = `<span>\u{1F3AC}</span> Step ${this.activeScenario.stepNumber} (Finish)`;
1910
+ btnFlow.setAttribute("title", `Click to finish recording "${this.activeScenario.title}"`);
1911
+ } else {
1912
+ btnFlow.className = "bt-toolbar-btn";
1913
+ btnFlow.innerHTML = `<span>\u{1F3AC}</span> Flow`;
1914
+ btnFlow.setAttribute("title", "Record multi-step reproduction flow");
1915
+ }
1916
+ }
1723
1917
  }
1724
1918
  updateToolbarCount() {
1725
1919
  if (!this.toolbarElement) return;
@@ -1744,6 +1938,9 @@ var NoteInspector = class {
1744
1938
  pageNotes.push(note);
1745
1939
  return;
1746
1940
  }
1941
+ const isStep = !!note.scenarioId && note.stepNumber != null;
1942
+ const stepLabel = isStep ? `\u{1F3AC} Step ${note.stepNumber}` : `#${index + 1}`;
1943
+ const markerClass = `bt-note-marker ${isStep ? "bt-note-marker-step" : ""}`;
1747
1944
  if (note.type === "region" && note.region) {
1748
1945
  const regBox = document.createElement("div");
1749
1946
  regBox.className = "bt-region-marker-box";
@@ -1753,11 +1950,11 @@ var NoteInspector = class {
1753
1950
  regBox.style.height = `${note.region.height}px`;
1754
1951
  this.markersContainer.appendChild(regBox);
1755
1952
  const pin = document.createElement("div");
1756
- pin.className = "bt-note-marker";
1953
+ pin.className = markerClass;
1757
1954
  pin.style.left = `${Math.max(4, note.region.x - 12)}px`;
1758
1955
  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>`;
1956
+ pin.title = isStep ? `[${note.scenarioTitle || "Scenario"}] Step ${note.stepNumber}: ${note.message}` : note.message;
1957
+ pin.innerHTML = `<span>${isStep ? "\u{1F3AC}" : "\u{1F4D0}"}</span> <span>${stepLabel}</span>`;
1761
1958
  pin.onclick = (e) => {
1762
1959
  e.stopPropagation();
1763
1960
  this.openNoteCard(note);
@@ -1775,12 +1972,12 @@ var NoteInspector = class {
1775
1972
  const rect = targetEl ? targetEl.getBoundingClientRect() : note.target?.boundingRect;
1776
1973
  if (rect) {
1777
1974
  const pin = document.createElement("div");
1778
- pin.className = "bt-note-marker";
1975
+ pin.className = markerClass;
1779
1976
  pin.setAttribute("data-note-id", note.id);
1780
- pin.title = note.message;
1977
+ pin.title = isStep ? `[${note.scenarioTitle || "Scenario"}] Step ${note.stepNumber}: ${note.message}` : note.message;
1781
1978
  pin.style.left = `${Math.max(4, rect.left - 10)}px`;
1782
1979
  pin.style.top = `${Math.max(4, rect.top - 12)}px`;
1783
- pin.innerHTML = `<span>\u{1F4DD}</span> <span>#${index + 1}</span>`;
1980
+ pin.innerHTML = `<span>${isStep ? "\u{1F3AC}" : "\u{1F4DD}"}</span> <span>${stepLabel}</span>`;
1784
1981
  pin.onclick = (e) => {
1785
1982
  e.stopPropagation();
1786
1983
  this.openNoteCard(note);
@@ -1795,10 +1992,11 @@ var NoteInspector = class {
1795
1992
  if (pageNotes.length > 0) {
1796
1993
  const pageDock = document.createElement("div");
1797
1994
  pageDock.className = "bt-page-notes-dock";
1798
- pageNotes.forEach((pNote, idx) => {
1995
+ pageNotes.forEach((pNote) => {
1799
1996
  const pill = document.createElement("div");
1800
1997
  pill.className = "bt-page-note-pill";
1801
- pill.innerHTML = `<span>\u{1F4C4}</span> <span>Page Note (${truncate(pNote.message, 25)})</span>`;
1998
+ const label = pNote.scenarioId && pNote.stepNumber ? `Step ${pNote.stepNumber}: ` : "";
1999
+ pill.innerHTML = `<span>\u{1F4C4}</span> <span>${label}${truncate(pNote.message, 25)}</span>`;
1802
2000
  pill.onclick = (e) => {
1803
2001
  e.stopPropagation();
1804
2002
  this.openNoteCard(pNote);
@@ -1827,17 +2025,39 @@ var NoteInspector = class {
1827
2025
  const icon = note.type === "region" ? "\u{1F4D0}" : note.type === "page" ? "\u{1F4C4}" : "\u{1F3AF}";
1828
2026
  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
2027
  const formattedDate = new Date(note.createdAt).toLocaleString();
2028
+ let scenarioStepsHtml = "";
2029
+ let scenarioSteps = [];
2030
+ if (note.scenarioId) {
2031
+ scenarioSteps = this.savedNotes.filter((n) => n.scenarioId === note.scenarioId).sort((a, b) => (a.stepNumber || 0) - (b.stepNumber || 0));
2032
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2033
+ const prevStep = currentIndex > 0 ? scenarioSteps[currentIndex - 1] : null;
2034
+ const nextStep = currentIndex >= 0 && currentIndex < scenarioSteps.length - 1 ? scenarioSteps[currentIndex + 1] : null;
2035
+ scenarioStepsHtml = `
2036
+ <div class="bt-scenario-stepper">
2037
+ <button class="bt-step-nav-btn" id="btn-prev-step" ${!prevStep ? "disabled" : ""}>
2038
+ \u25C0 Step ${prevStep ? prevStep.stepNumber : ""}
2039
+ </button>
2040
+ <span>\u{1F3AC} Step ${note.stepNumber || 1} of ${scenarioSteps.length}</span>
2041
+ <button class="bt-step-nav-btn" id="btn-next-step-card" ${!nextStep ? "disabled" : ""}>
2042
+ Step ${nextStep ? nextStep.stepNumber : ""} \u25B6
2043
+ </button>
2044
+ </div>
2045
+ `;
2046
+ }
1830
2047
  backdrop.innerHTML = `
1831
2048
  <div class="bt-modal">
1832
2049
  <div class="bt-modal-header">
1833
2050
  <div class="bt-modal-title">
1834
- <span>${icon} Visual Note Details</span>
2051
+ <span>${note.scenarioId ? "\u{1F3AC} " + (note.scenarioTitle || "Scenario Flow") : icon + " Visual Note Details"}</span>
1835
2052
  <span class="bt-mode-badge ${badgeClass}">${note.type.toUpperCase()}</span>
2053
+ ${note.scenarioId && note.stepNumber ? `<span class="bt-mode-badge bt-badge-step">STEP ${note.stepNumber}</span>` : ""}
1836
2054
  <span class="bt-mode-badge ${statusClass}">${note.status}</span>
1837
2055
  </div>
1838
2056
  <button class="bt-close-btn" id="btn-card-close" title="Close (Esc)">\u2715</button>
1839
2057
  </div>
1840
2058
 
2059
+ ${scenarioStepsHtml}
2060
+
1841
2061
  <div class="bt-target-pill" title="${contextText}">
1842
2062
  <span>\u{1F3F7}\uFE0F</span>
1843
2063
  <span class="bt-pill-content">${contextText}</span>
@@ -1851,9 +2071,14 @@ var NoteInspector = class {
1851
2071
  </div>
1852
2072
 
1853
2073
  <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>
2074
+ <div style="display: flex; gap: 6px;">
2075
+ <button class="bt-btn bt-btn-delete" id="btn-card-delete" title="Delete this note">
2076
+ <span>\u{1F5D1}\uFE0F</span> Delete
2077
+ </button>
2078
+ ${note.scenarioId ? `<button class="bt-btn bt-btn-delete" id="btn-card-delete-flow" title="Delete entire scenario flow">
2079
+ <span>\u{1F5D1}\uFE0F</span> Delete Flow
2080
+ </button>` : ""}
2081
+ </div>
1857
2082
  <div class="bt-modal-actions">
1858
2083
  <button class="bt-btn bt-btn-cancel" id="btn-card-dismiss">Close</button>
1859
2084
  <button class="bt-btn ${note.status === "OPEN" ? "bt-btn-resolve" : "bt-btn-save"}" id="btn-card-resolve">
@@ -1867,6 +2092,9 @@ var NoteInspector = class {
1867
2092
  const btnDismiss = backdrop.querySelector("#btn-card-dismiss");
1868
2093
  const btnResolve = backdrop.querySelector("#btn-card-resolve");
1869
2094
  const btnDelete = backdrop.querySelector("#btn-card-delete");
2095
+ const btnDeleteFlow = backdrop.querySelector("#btn-card-delete-flow");
2096
+ const btnPrevStep = backdrop.querySelector("#btn-prev-step");
2097
+ const btnNextStepCard = backdrop.querySelector("#btn-next-step-card");
1870
2098
  const closeCard = () => {
1871
2099
  if (this.cardOverlay) {
1872
2100
  root.removeChild(this.cardOverlay);
@@ -1879,18 +2107,44 @@ var NoteInspector = class {
1879
2107
  backdrop.onclick = (e) => {
1880
2108
  if (e.target === backdrop) closeCard();
1881
2109
  };
2110
+ if (btnPrevStep && scenarioSteps.length > 0) {
2111
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2112
+ if (currentIndex > 0) {
2113
+ btnPrevStep.onclick = () => {
2114
+ this.openNoteCard(scenarioSteps[currentIndex - 1]);
2115
+ };
2116
+ }
2117
+ }
2118
+ if (btnNextStepCard && scenarioSteps.length > 0) {
2119
+ const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
2120
+ if (currentIndex >= 0 && currentIndex < scenarioSteps.length - 1) {
2121
+ btnNextStepCard.onclick = () => {
2122
+ this.openNoteCard(scenarioSteps[currentIndex + 1]);
2123
+ };
2124
+ }
2125
+ }
1882
2126
  btnResolve.onclick = () => {
1883
2127
  if (note.status === "OPEN") {
1884
2128
  this.transport.send({ type: "resolve_note", noteId: note.id });
2129
+ this.showToast("Note marked as resolved", "\u2705");
1885
2130
  } else {
1886
2131
  this.transport.send({ type: "reopen_note", noteId: note.id });
2132
+ this.showToast("Note reopened", "\u21BA");
1887
2133
  }
1888
2134
  closeCard();
1889
2135
  };
1890
2136
  btnDelete.onclick = () => {
1891
2137
  this.transport.send({ type: "delete_note", noteId: note.id });
2138
+ this.showToast("Note deleted", "\u{1F5D1}\uFE0F");
1892
2139
  closeCard();
1893
2140
  };
2141
+ if (btnDeleteFlow && note.scenarioId) {
2142
+ btnDeleteFlow.onclick = () => {
2143
+ this.transport.send({ type: "delete_scenario", scenarioId: note.scenarioId });
2144
+ this.showToast("Scenario flow deleted", "\u{1F5D1}\uFE0F");
2145
+ closeCard();
2146
+ };
2147
+ }
1894
2148
  root.appendChild(backdrop);
1895
2149
  }
1896
2150
  setupListeners() {
@@ -1927,7 +2181,7 @@ var NoteInspector = class {
1927
2181
  if (target && target !== this.container && !this.container?.contains(target)) {
1928
2182
  this.selectedElement = target;
1929
2183
  this.openNoteEditor(target, "element");
1930
- if (this.activeMode === "element") {
2184
+ if (this.activeMode === "element" && !this.activeScenario) {
1931
2185
  this.setMode("idle");
1932
2186
  }
1933
2187
  }
@@ -1939,7 +2193,9 @@ var NoteInspector = class {
1939
2193
  this.shadowRoot.removeChild(this.cardOverlay);
1940
2194
  this.cardOverlay = null;
1941
2195
  } else if (this.activeMode === "region" || this.activeMode === "element") {
1942
- this.setMode("idle");
2196
+ if (!this.activeScenario) {
2197
+ this.setMode("idle");
2198
+ }
1943
2199
  }
1944
2200
  }
1945
2201
  };
@@ -2045,28 +2301,27 @@ var NoteInspector = class {
2045
2301
  width: Math.round(width),
2046
2302
  height: Math.round(height)
2047
2303
  };
2048
- this.hideRegionOverlay();
2049
- this.setMode("idle");
2050
2304
  this.openRegionNoteEditor(this.selectedRegion);
2051
- } else {
2052
- this.hideRegionOverlay();
2305
+ }
2306
+ if (this.regionBox) {
2307
+ this.regionBox.style.display = "none";
2308
+ }
2309
+ if (!this.activeScenario) {
2053
2310
  this.setMode("idle");
2054
2311
  }
2055
2312
  };
2056
- }
2057
- if (this.toolbarElement && this.toolbarElement.parentNode === root) {
2058
- root.insertBefore(this.regionOverlay, this.toolbarElement);
2059
- } else {
2060
2313
  root.appendChild(this.regionOverlay);
2314
+ } else {
2315
+ this.regionOverlay.style.display = "block";
2061
2316
  }
2062
2317
  }
2063
2318
  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";
2319
+ if (this.regionOverlay) {
2320
+ this.regionOverlay.style.display = "none";
2321
+ this.isDraggingRegion = false;
2322
+ if (this.regionBox) {
2323
+ this.regionBox.style.display = "none";
2324
+ }
2070
2325
  }
2071
2326
  }
2072
2327
  updateHighlight(el) {
@@ -2074,19 +2329,19 @@ var NoteInspector = class {
2074
2329
  if (!root || !this.highlightOverlay) return;
2075
2330
  const rect = el.getBoundingClientRect();
2076
2331
  this.highlightOverlay.style.display = "block";
2077
- this.highlightOverlay.style.top = `${rect.top}px`;
2078
2332
  this.highlightOverlay.style.left = `${rect.left}px`;
2333
+ this.highlightOverlay.style.top = `${rect.top}px`;
2079
2334
  this.highlightOverlay.style.width = `${rect.width}px`;
2080
2335
  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
2336
  let badge = this.highlightOverlay.querySelector(".bt-badge");
2084
2337
  if (!badge) {
2085
2338
  badge = document.createElement("div");
2086
2339
  badge.className = "bt-badge";
2087
2340
  this.highlightOverlay.appendChild(badge);
2088
2341
  }
2089
- badge.textContent = badgeText;
2342
+ const selector = getSemanticSelector(el);
2343
+ const label = this.activeScenario ? `\u{1F3AC} Step ${this.activeScenario.stepNumber} \xB7 ${selector}` : selector;
2344
+ badge.textContent = `${label} (${Math.round(rect.width)} \xD7 ${Math.round(rect.height)} px)`;
2090
2345
  }
2091
2346
  hideHighlight() {
2092
2347
  if (this.highlightOverlay) {
@@ -2102,21 +2357,57 @@ var NoteInspector = class {
2102
2357
  this.updateHighlight(targetEl);
2103
2358
  }
2104
2359
  this.renderNoteModal({
2105
- title: "Add Visual Note",
2106
- modeBadge: noteType.toUpperCase(),
2360
+ title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Visual Note",
2361
+ modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : noteType.toUpperCase(),
2107
2362
  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);
2363
+ onSave: async (message, action) => {
2364
+ let scenarioParam;
2365
+ if (action === "next_step" && !this.activeScenario) {
2366
+ this.startScenario();
2367
+ }
2368
+ if (this.activeScenario) {
2369
+ scenarioParam = {
2370
+ scenarioId: this.activeScenario.id,
2371
+ stepNumber: this.activeScenario.stepNumber,
2372
+ scenarioTitle: this.activeScenario.title
2373
+ };
2374
+ }
2375
+ await this.saveVisualNote(targetEl, message, noteType, scenarioParam);
2376
+ if (action === "next_step" && this.activeScenario) {
2377
+ this.activeScenario.stepNumber++;
2378
+ this.updateToolbarState();
2379
+ this.setMode("element");
2380
+ } else if (action === "finish_flow" && this.activeScenario) {
2381
+ this.finishScenario();
2382
+ }
2110
2383
  }
2111
2384
  });
2112
2385
  }
2113
2386
  openRegionNoteEditor(region) {
2114
2387
  this.renderNoteModal({
2115
- title: "Add Region Note",
2116
- modeBadge: "REGION",
2388
+ title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Region Note",
2389
+ modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : "REGION",
2117
2390
  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);
2391
+ onSave: async (message, action) => {
2392
+ let scenarioParam;
2393
+ if (action === "next_step" && !this.activeScenario) {
2394
+ this.startScenario();
2395
+ }
2396
+ if (this.activeScenario) {
2397
+ scenarioParam = {
2398
+ scenarioId: this.activeScenario.id,
2399
+ stepNumber: this.activeScenario.stepNumber,
2400
+ scenarioTitle: this.activeScenario.title
2401
+ };
2402
+ }
2403
+ await this.saveRegionVisualNote(region, message, scenarioParam);
2404
+ if (action === "next_step" && this.activeScenario) {
2405
+ this.activeScenario.stepNumber++;
2406
+ this.updateToolbarState();
2407
+ this.setMode("element");
2408
+ } else if (action === "finish_flow" && this.activeScenario) {
2409
+ this.finishScenario();
2410
+ }
2120
2411
  }
2121
2412
  });
2122
2413
  }
@@ -2130,14 +2421,15 @@ var NoteInspector = class {
2130
2421
  const backdrop = document.createElement("div");
2131
2422
  backdrop.className = "bt-modal-backdrop";
2132
2423
  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}";
2424
+ const isStep = options.modeBadge.startsWith("STEP");
2425
+ const badgeClass = isStep ? "bt-badge-step" : `bt-badge-${options.modeBadge.toLowerCase()}`;
2426
+ const icon = isStep ? "\u{1F3AC}" : options.modeBadge === "REGION" ? "\u{1F4D0}" : options.modeBadge === "PAGE" ? "\u{1F4C4}" : "\u{1F3AF}";
2135
2427
  const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
2136
2428
  backdrop.innerHTML = `
2137
2429
  <div class="bt-modal">
2138
2430
  <div class="bt-modal-header">
2139
2431
  <div class="bt-modal-title">
2140
- <span>\u{1F4DD} ${options.title}</span>
2432
+ <span>${icon} ${options.title}</span>
2141
2433
  <span class="bt-mode-badge ${badgeClass}">${options.modeBadge}</span>
2142
2434
  </div>
2143
2435
  <button class="bt-close-btn" id="btn-close" title="Close (Esc)">\u2715</button>
@@ -2146,14 +2438,19 @@ var NoteInspector = class {
2146
2438
  <span>${icon}</span>
2147
2439
  <span class="bt-pill-content">${options.pillText}</span>
2148
2440
  </div>
2149
- <textarea class="bt-textarea" placeholder="Describe the layout issue, styling bug, or note for AI agent..." autofocus></textarea>
2441
+ <textarea class="bt-textarea" placeholder="Describe the layout issue, user action, or note for AI agent..." autofocus></textarea>
2150
2442
  <div class="bt-modal-footer">
2151
2443
  <div class="bt-kbd-hint">
2152
2444
  <kbd>${isMac ? "\u2318" : "Ctrl"}+Enter</kbd> save \xB7 <kbd>Esc</kbd> cancel
2153
2445
  </div>
2154
2446
  <div class="bt-modal-actions">
2155
2447
  <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>
2448
+ <button class="bt-btn bt-btn-next-step" id="btn-next-step" title="Save this step and immediately select the next element">
2449
+ <span>\u27A1\uFE0F</span> ${this.activeScenario ? "Save & Next Step" : "Save as Step 1 (Flow)"}
2450
+ </button>
2451
+ ${this.activeScenario ? `<button class="bt-btn bt-btn-finish-flow" id="btn-finish-flow" title="Save final step and complete scenario">
2452
+ <span>\u2713</span> Save & Finish Flow
2453
+ </button>` : `<button class="bt-btn bt-btn-save" id="btn-save">Save Note</button>`}
2157
2454
  </div>
2158
2455
  </div>
2159
2456
  </div>
@@ -2162,6 +2459,8 @@ var NoteInspector = class {
2162
2459
  const btnClose = backdrop.querySelector("#btn-close");
2163
2460
  const btnCancel = backdrop.querySelector("#btn-cancel");
2164
2461
  const btnSave = backdrop.querySelector("#btn-save");
2462
+ const btnNextStep = backdrop.querySelector("#btn-next-step");
2463
+ const btnFinishFlow = backdrop.querySelector("#btn-finish-flow");
2165
2464
  const closeModal = () => {
2166
2465
  if (this.modalOverlay) {
2167
2466
  root.removeChild(this.modalOverlay);
@@ -2177,22 +2476,29 @@ var NoteInspector = class {
2177
2476
  closeModal();
2178
2477
  }
2179
2478
  };
2180
- const submitNote = async () => {
2479
+ const handleAction = async (action) => {
2181
2480
  const message = textarea.value.trim();
2182
2481
  if (!message) return;
2183
- btnSave.textContent = "Saving...";
2184
- btnSave.disabled = true;
2482
+ btnNextStep.disabled = true;
2483
+ if (btnSave) btnSave.disabled = true;
2484
+ if (btnFinishFlow) btnFinishFlow.disabled = true;
2185
2485
  try {
2186
- await options.onSave(message);
2486
+ await options.onSave(message, action);
2187
2487
  } finally {
2188
2488
  closeModal();
2189
2489
  }
2190
2490
  };
2191
- btnSave.onclick = submitNote;
2491
+ if (btnSave) {
2492
+ btnSave.onclick = () => handleAction("save");
2493
+ }
2494
+ btnNextStep.onclick = () => handleAction("next_step");
2495
+ if (btnFinishFlow) {
2496
+ btnFinishFlow.onclick = () => handleAction("finish_flow");
2497
+ }
2192
2498
  textarea.onkeydown = (e) => {
2193
- if (e.key === "Enter" && (e.metaKey || e.ctrlKey || !e.shiftKey)) {
2499
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
2194
2500
  e.preventDefault();
2195
- submitNote();
2501
+ handleAction(this.activeScenario ? "next_step" : "save");
2196
2502
  }
2197
2503
  if (e.key === "Escape") {
2198
2504
  closeModal();
@@ -2201,7 +2507,7 @@ var NoteInspector = class {
2201
2507
  root.appendChild(backdrop);
2202
2508
  setTimeout(() => textarea?.focus(), 50);
2203
2509
  }
2204
- async saveVisualNote(targetEl, message, noteType = "element") {
2510
+ async saveVisualNote(targetEl, message, noteType = "element", scenario) {
2205
2511
  const rect = targetEl.getBoundingClientRect();
2206
2512
  const selector = noteType === "page" ? "body" : getSemanticSelector(targetEl);
2207
2513
  let screenshotDataUrl;
@@ -2228,33 +2534,38 @@ var NoteInspector = class {
2228
2534
  visible: rect.width > 0 && rect.height > 0,
2229
2535
  confidence: selector.startsWith("[data-test") ? "high" : selector.startsWith("#") ? "medium" : "low"
2230
2536
  };
2537
+ const route = typeof window !== "undefined" ? window.location.pathname + window.location.search : "/";
2538
+ const url = typeof window !== "undefined" ? window.location.href : "http://localhost/";
2539
+ const viewport = typeof window !== "undefined" ? { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio || 1 } : { width: 1280, height: 800, devicePixelRatio: 1 };
2540
+ const scroll = typeof window !== "undefined" ? { scrollX: window.scrollX, scrollY: window.scrollY } : { scrollX: 0, scrollY: 0 };
2231
2541
  const notePayload = {
2232
2542
  type: "create_note",
2233
2543
  sessionId: this.transport.getSessionId() || "",
2234
2544
  noteType,
2235
2545
  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
- },
2546
+ route,
2547
+ url,
2548
+ viewport,
2549
+ scroll,
2247
2550
  target,
2248
2551
  elementContext,
2249
2552
  screenshot: screenshotDataUrl,
2553
+ scenarioId: scenario?.scenarioId,
2554
+ stepNumber: scenario?.stepNumber,
2555
+ scenarioTitle: scenario?.scenarioTitle,
2250
2556
  timestamp: Date.now()
2251
2557
  };
2252
2558
  this.transport.send(notePayload);
2253
2559
  if (this.options.onNoteCreated) {
2254
2560
  this.options.onNoteCreated(notePayload);
2255
2561
  }
2562
+ if (scenario?.scenarioId) {
2563
+ this.showToast(`Step ${scenario.stepNumber || 1} recorded`, "\u{1F3AC}");
2564
+ } else {
2565
+ this.showToast("Visual note saved", "\u2728");
2566
+ }
2256
2567
  }
2257
- async saveRegionVisualNote(region, message) {
2568
+ async saveRegionVisualNote(region, message, scenario) {
2258
2569
  let screenshotDataUrl;
2259
2570
  try {
2260
2571
  const snap = await this.screenshotDriver.captureElement(document.body || document.documentElement);
@@ -2263,123 +2574,113 @@ var NoteInspector = class {
2263
2574
  }
2264
2575
  } catch {
2265
2576
  }
2577
+ const route = typeof window !== "undefined" ? window.location.pathname + window.location.search : "/";
2578
+ const url = typeof window !== "undefined" ? window.location.href : "http://localhost/";
2579
+ const viewport = typeof window !== "undefined" ? { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio || 1 } : { width: 1280, height: 800, devicePixelRatio: 1 };
2580
+ const scroll = typeof window !== "undefined" ? { scrollX: window.scrollX, scrollY: window.scrollY } : { scrollX: 0, scrollY: 0 };
2266
2581
  const notePayload = {
2267
2582
  type: "create_note",
2268
2583
  sessionId: this.transport.getSessionId() || "",
2269
2584
  noteType: "region",
2270
2585
  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
- },
2586
+ route,
2587
+ url,
2588
+ viewport,
2589
+ scroll,
2282
2590
  region,
2283
2591
  screenshot: screenshotDataUrl,
2592
+ scenarioId: scenario?.scenarioId,
2593
+ stepNumber: scenario?.stepNumber,
2594
+ scenarioTitle: scenario?.scenarioTitle,
2284
2595
  timestamp: Date.now()
2285
2596
  };
2286
2597
  this.transport.send(notePayload);
2287
2598
  if (this.options.onNoteCreated) {
2288
2599
  this.options.onNoteCreated(notePayload);
2289
2600
  }
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
- });
2601
+ if (scenario?.scenarioId) {
2602
+ this.showToast(`Step ${scenario.stepNumber || 1} region recorded`, "\u{1F3AC}");
2603
+ } else {
2604
+ this.showToast("Region note saved", "\u{1F4D0}");
2605
+ }
2324
2606
  }
2325
2607
  extractElementContext(el) {
2326
2608
  const selector = getSemanticSelector(el);
2327
- const tag = el.tagName.toLowerCase();
2328
2609
  const attributes = {};
2329
2610
  for (let i = 0; i < el.attributes.length; i++) {
2330
2611
  const attr = el.attributes[i];
2331
- if (attr.name === "value" && el.type === "password") {
2612
+ if (this.options.maskSelectors?.some((mask) => {
2613
+ try {
2614
+ return el.matches(mask);
2615
+ } catch {
2616
+ return false;
2617
+ }
2618
+ }) && (attr.name === "value" || attr.name === "data-secret")) {
2332
2619
  attributes[attr.name] = "[REDACTED]";
2333
2620
  } else {
2334
2621
  attributes[attr.name] = attr.value;
2335
2622
  }
2336
2623
  }
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);
2624
+ let outerHTML = el.outerHTML;
2625
+ if (outerHTML && outerHTML.length > 1e3) {
2626
+ outerHTML = truncate(outerHTML, 1e3);
2346
2627
  }
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
- };
2628
+ let innerText = el.innerText || el.textContent || "";
2629
+ if (innerText && innerText.length > 200) {
2630
+ innerText = truncate(innerText, 200);
2353
2631
  }
2354
2632
  return {
2355
2633
  selector,
2356
- tag,
2634
+ tag: el.tagName.toLowerCase(),
2357
2635
  attributes,
2358
2636
  outerHTML,
2359
- innerText: truncate(el.textContent?.trim(), 200),
2360
- parent
2637
+ innerText,
2638
+ parent: el.parentElement ? {
2639
+ selector: getSemanticSelector(el.parentElement),
2640
+ tag: el.parentElement.tagName.toLowerCase()
2641
+ } : void 0
2361
2642
  };
2362
2643
  }
2644
+ async cropDataUrl(dataUrl, region) {
2645
+ return new Promise((resolve) => {
2646
+ const img = new Image();
2647
+ img.onload = () => {
2648
+ const canvas = document.createElement("canvas");
2649
+ canvas.width = region.width;
2650
+ canvas.height = region.height;
2651
+ const ctx = canvas.getContext("2d");
2652
+ if (!ctx) {
2653
+ resolve(dataUrl);
2654
+ return;
2655
+ }
2656
+ const dpr = window.devicePixelRatio || 1;
2657
+ ctx.drawImage(
2658
+ img,
2659
+ region.x * dpr,
2660
+ region.y * dpr,
2661
+ region.width * dpr,
2662
+ region.height * dpr,
2663
+ 0,
2664
+ 0,
2665
+ region.width,
2666
+ region.height
2667
+ );
2668
+ resolve(canvas.toDataURL("image/png"));
2669
+ };
2670
+ img.onerror = () => resolve(dataUrl);
2671
+ img.src = dataUrl;
2672
+ });
2673
+ }
2363
2674
  destroy() {
2364
2675
  for (const cleanup of this.cleanups) {
2365
- try {
2366
- cleanup();
2367
- } catch {
2368
- }
2676
+ cleanup();
2369
2677
  }
2370
2678
  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;
2679
+ if (this.container && this.container.parentNode) {
2680
+ this.container.parentNode.removeChild(this.container);
2681
+ this.container = null;
2682
+ this.shadowRoot = null;
2683
+ }
2383
2684
  }
2384
2685
  };
2385
2686
 
@@ -2638,4 +2939,4 @@ export {
2638
2939
  init,
2639
2940
  getClient
2640
2941
  };
2641
- //# sourceMappingURL=chunk-SKCMT2DE.js.map
2942
+ //# sourceMappingURL=chunk-WB7ZKWK7.js.map