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.
- package/AGENTS.md +4 -2
- package/dist/{chunk-ILRYKMME.js → chunk-3HOXPTM2.js} +97 -3
- package/dist/chunk-3HOXPTM2.js.map +1 -0
- package/dist/{chunk-SPCIROIU.js → chunk-7OCOQGDN.js} +24 -5
- package/dist/chunk-7OCOQGDN.js.map +1 -0
- package/dist/{chunk-G2Y3CXCY.js → chunk-UP5JKCFY.js} +84 -2
- package/dist/chunk-UP5JKCFY.js.map +1 -0
- package/dist/{chunk-SKCMT2DE.js → chunk-WB7ZKWK7.js} +453 -152
- package/dist/chunk-WB7ZKWK7.js.map +1 -0
- package/dist/cli/index.js +200 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.cjs +452 -151
- package/dist/client/index.d.ts +28 -9
- package/dist/client/index.js +1 -1
- package/dist/client.iife.js +185 -29
- package/dist/core/index.d.ts +2 -2
- package/dist/daemon/index.d.ts +3 -3
- package/dist/daemon/index.js +2 -2
- package/dist/{engine-CeT9URuN.d.ts → engine-B43IohQY.d.ts} +13 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/mcp/index.d.ts +4 -4
- package/dist/mcp/index.js +2 -2
- package/dist/{notes-CBvN91Wf.d.ts → notes-BMnonq46.d.ts} +24 -1
- package/dist/{projects-CY8ungMt.d.ts → projects-D5J-egVN.d.ts} +1 -1
- package/dist/{server-Dd8NX2Mk.d.ts → server-BztYp1Zc.d.ts} +1 -1
- package/docs/index.md +2 -1
- package/docs/mcp-reference.md +14 -1
- package/docs/scenarios-flows.md +86 -0
- package/package.json +1 -1
- package/packages/client/src/notes/inspector.ts +533 -161
- package/packages/core/src/types/notes.ts +25 -0
- package/packages/daemon/src/notes/engine.ts +6 -0
- package/packages/daemon/src/server/ws.ts +23 -1
- package/packages/daemon/src/storage/db.ts +106 -3
- package/packages/mcp/src/handlers.ts +58 -0
- package/packages/mcp/src/tools.ts +28 -0
- package/test/client/interceptors.test.ts +64 -0
- package/test/daemon/scenario-storage.test.ts +158 -0
- package/dist/chunk-G2Y3CXCY.js.map +0 -1
- package/dist/chunk-ILRYKMME.js.map +0 -1
- package/dist/chunk-SKCMT2DE.js.map +0 -1
- package/dist/chunk-SPCIROIU.js.map +0 -1
package/dist/client/index.cjs
CHANGED
|
@@ -1184,7 +1184,9 @@ var NoteInspector = class {
|
|
|
1184
1184
|
__publicField(this, "toolbarElement", null);
|
|
1185
1185
|
__publicField(this, "modalOverlay", null);
|
|
1186
1186
|
__publicField(this, "cardOverlay", null);
|
|
1187
|
+
__publicField(this, "toastContainer", null);
|
|
1187
1188
|
__publicField(this, "activeMode", "idle");
|
|
1189
|
+
__publicField(this, "activeScenario", null);
|
|
1188
1190
|
__publicField(this, "hoveredElement", null);
|
|
1189
1191
|
__publicField(this, "selectedElement", null);
|
|
1190
1192
|
__publicField(this, "selectedRegion", null);
|
|
@@ -1225,6 +1227,41 @@ var NoteInspector = class {
|
|
|
1225
1227
|
this.updateToolbarCount();
|
|
1226
1228
|
this.renderMarkers();
|
|
1227
1229
|
}
|
|
1230
|
+
showToast(message, icon = "\u2728", durationMs = 2500) {
|
|
1231
|
+
if (typeof document === "undefined") return;
|
|
1232
|
+
const root = this.ensureContainer();
|
|
1233
|
+
if (!root || !this.toastContainer) return;
|
|
1234
|
+
const toast = document.createElement("div");
|
|
1235
|
+
toast.className = "bt-toast";
|
|
1236
|
+
toast.innerHTML = `<span>${icon}</span> <span>${message}</span>`;
|
|
1237
|
+
this.toastContainer.appendChild(toast);
|
|
1238
|
+
setTimeout(() => {
|
|
1239
|
+
toast.classList.add("bt-toast-fadeout");
|
|
1240
|
+
setTimeout(() => {
|
|
1241
|
+
if (toast.parentElement) {
|
|
1242
|
+
toast.parentElement.removeChild(toast);
|
|
1243
|
+
}
|
|
1244
|
+
}, 250);
|
|
1245
|
+
}, durationMs);
|
|
1246
|
+
}
|
|
1247
|
+
startScenario(title) {
|
|
1248
|
+
const defaultTitle = title || `Scenario ${(/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}`;
|
|
1249
|
+
this.activeScenario = {
|
|
1250
|
+
id: `scen_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`,
|
|
1251
|
+
title: defaultTitle,
|
|
1252
|
+
stepNumber: 1
|
|
1253
|
+
};
|
|
1254
|
+
this.updateToolbarState();
|
|
1255
|
+
this.setMode("element");
|
|
1256
|
+
this.showToast(`Started flow: "${defaultTitle}"`, "\u{1F3AC}");
|
|
1257
|
+
}
|
|
1258
|
+
finishScenario() {
|
|
1259
|
+
const title = this.activeScenario?.title;
|
|
1260
|
+
this.activeScenario = null;
|
|
1261
|
+
this.updateToolbarState();
|
|
1262
|
+
this.setMode("idle");
|
|
1263
|
+
this.showToast(title ? `Finished flow: "${title}"` : "Flow recording finished", "\u2713");
|
|
1264
|
+
}
|
|
1228
1265
|
setMode(mode) {
|
|
1229
1266
|
this.activeMode = mode;
|
|
1230
1267
|
this.updateToolbarState();
|
|
@@ -1404,6 +1441,12 @@ var NoteInspector = class {
|
|
|
1404
1441
|
z-index: 2147483641;
|
|
1405
1442
|
}
|
|
1406
1443
|
|
|
1444
|
+
.bt-note-marker-step {
|
|
1445
|
+
background: linear-gradient(135deg, #f59e0b, #d97706);
|
|
1446
|
+
border-color: #fef3c7;
|
|
1447
|
+
box-shadow: 0 4px 14px rgba(245, 158, 11, 0.4), 0 0 0 1px rgba(217, 119, 6, 0.5);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1407
1450
|
@keyframes bt-pop-in {
|
|
1408
1451
|
0% { transform: scale(0.6); opacity: 0; }
|
|
1409
1452
|
100% { transform: scale(1); opacity: 1; }
|
|
@@ -1414,6 +1457,10 @@ var NoteInspector = class {
|
|
|
1414
1457
|
box-shadow: 0 8px 20px rgba(37,99,235,0.6), 0 0 0 2px #60a5fa;
|
|
1415
1458
|
}
|
|
1416
1459
|
|
|
1460
|
+
.bt-note-marker-step:hover {
|
|
1461
|
+
box-shadow: 0 8px 20px rgba(245, 158, 11, 0.7), 0 0 0 2px #fde68a;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1417
1464
|
.bt-marker-resolved {
|
|
1418
1465
|
background: linear-gradient(135deg, #475569, #334155);
|
|
1419
1466
|
border-color: #94a3b8;
|
|
@@ -1510,6 +1557,19 @@ var NoteInspector = class {
|
|
|
1510
1557
|
box-shadow: 0 2px 6px rgba(37, 99, 235, 0.35);
|
|
1511
1558
|
}
|
|
1512
1559
|
|
|
1560
|
+
.bt-toolbar-btn.active-scenario {
|
|
1561
|
+
background: linear-gradient(135deg, #f59e0b, #d97706);
|
|
1562
|
+
color: #ffffff;
|
|
1563
|
+
font-weight: 700;
|
|
1564
|
+
box-shadow: 0 2px 8px rgba(245, 158, 11, 0.4);
|
|
1565
|
+
animation: bt-pulse 2s infinite;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
@keyframes bt-pulse {
|
|
1569
|
+
0%, 100% { opacity: 1; }
|
|
1570
|
+
50% { opacity: 0.85; }
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1513
1573
|
.bt-count-pill {
|
|
1514
1574
|
background: rgba(255, 255, 255, 0.2);
|
|
1515
1575
|
color: #ffffff;
|
|
@@ -1557,7 +1617,7 @@ var NoteInspector = class {
|
|
|
1557
1617
|
border: 1px solid #334155;
|
|
1558
1618
|
border-radius: 14px;
|
|
1559
1619
|
padding: 20px;
|
|
1560
|
-
width:
|
|
1620
|
+
width: 470px;
|
|
1561
1621
|
max-width: 92vw;
|
|
1562
1622
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.8), 0 0 0 1px rgba(255, 255, 255, 0.05);
|
|
1563
1623
|
display: flex;
|
|
@@ -1580,6 +1640,7 @@ var NoteInspector = class {
|
|
|
1580
1640
|
align-items: center;
|
|
1581
1641
|
gap: 8px;
|
|
1582
1642
|
color: #f8fafc;
|
|
1643
|
+
flex-wrap: wrap;
|
|
1583
1644
|
}
|
|
1584
1645
|
|
|
1585
1646
|
.bt-mode-badge {
|
|
@@ -1609,6 +1670,12 @@ var NoteInspector = class {
|
|
|
1609
1670
|
border: 1px solid rgba(168, 85, 247, 0.4);
|
|
1610
1671
|
}
|
|
1611
1672
|
|
|
1673
|
+
.bt-badge-step {
|
|
1674
|
+
background: rgba(245, 158, 11, 0.15);
|
|
1675
|
+
color: #fbbf24;
|
|
1676
|
+
border: 1px solid rgba(245, 158, 11, 0.4);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1612
1679
|
.bt-badge-status-open {
|
|
1613
1680
|
background: rgba(16, 185, 129, 0.15);
|
|
1614
1681
|
color: #34d399;
|
|
@@ -1660,6 +1727,43 @@ var NoteInspector = class {
|
|
|
1660
1727
|
color: #cbd5e1;
|
|
1661
1728
|
}
|
|
1662
1729
|
|
|
1730
|
+
.bt-scenario-stepper {
|
|
1731
|
+
display: flex;
|
|
1732
|
+
align-items: center;
|
|
1733
|
+
justify-content: space-between;
|
|
1734
|
+
background: #090d16;
|
|
1735
|
+
border: 1px solid #1e293b;
|
|
1736
|
+
border-radius: 8px;
|
|
1737
|
+
padding: 6px 10px;
|
|
1738
|
+
font-size: 12px;
|
|
1739
|
+
font-weight: 600;
|
|
1740
|
+
color: #f59e0b;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
.bt-step-nav-btn {
|
|
1744
|
+
background: #1e293b;
|
|
1745
|
+
color: #f8fafc;
|
|
1746
|
+
border: 1px solid #334155;
|
|
1747
|
+
padding: 4px 10px;
|
|
1748
|
+
border-radius: 6px;
|
|
1749
|
+
cursor: pointer;
|
|
1750
|
+
font-size: 11px;
|
|
1751
|
+
font-weight: 600;
|
|
1752
|
+
transition: all 0.12s ease;
|
|
1753
|
+
font-family: inherit;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
.bt-step-nav-btn:hover:not(:disabled) {
|
|
1757
|
+
background: #2563eb;
|
|
1758
|
+
border-color: #3b82f6;
|
|
1759
|
+
color: #ffffff;
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
.bt-step-nav-btn:disabled {
|
|
1763
|
+
opacity: 0.3;
|
|
1764
|
+
cursor: not-allowed;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1663
1767
|
.bt-note-message-box {
|
|
1664
1768
|
background: #090d16;
|
|
1665
1769
|
border: 1px solid #1e293b;
|
|
@@ -1704,8 +1808,9 @@ var NoteInspector = class {
|
|
|
1704
1808
|
display: flex;
|
|
1705
1809
|
justify-content: space-between;
|
|
1706
1810
|
align-items: center;
|
|
1707
|
-
gap:
|
|
1811
|
+
gap: 10px;
|
|
1708
1812
|
margin-top: 2px;
|
|
1813
|
+
flex-wrap: wrap;
|
|
1709
1814
|
}
|
|
1710
1815
|
|
|
1711
1816
|
.bt-kbd-hint {
|
|
@@ -1733,12 +1838,13 @@ var NoteInspector = class {
|
|
|
1733
1838
|
justify-content: flex-end;
|
|
1734
1839
|
align-items: center;
|
|
1735
1840
|
gap: 8px;
|
|
1841
|
+
flex-wrap: wrap;
|
|
1736
1842
|
}
|
|
1737
1843
|
|
|
1738
1844
|
.bt-btn {
|
|
1739
|
-
padding:
|
|
1845
|
+
padding: 7px 14px;
|
|
1740
1846
|
border-radius: 7px;
|
|
1741
|
-
font-size:
|
|
1847
|
+
font-size: 12px;
|
|
1742
1848
|
font-weight: 600;
|
|
1743
1849
|
cursor: pointer;
|
|
1744
1850
|
border: 1px solid transparent;
|
|
@@ -1747,7 +1853,7 @@ var NoteInspector = class {
|
|
|
1747
1853
|
display: inline-flex;
|
|
1748
1854
|
align-items: center;
|
|
1749
1855
|
justify-content: center;
|
|
1750
|
-
gap:
|
|
1856
|
+
gap: 5px;
|
|
1751
1857
|
}
|
|
1752
1858
|
|
|
1753
1859
|
.bt-btn-cancel {
|
|
@@ -1769,6 +1875,25 @@ var NoteInspector = class {
|
|
|
1769
1875
|
box-shadow: 0 4px 10px rgba(37, 99, 235, 0.45);
|
|
1770
1876
|
}
|
|
1771
1877
|
|
|
1878
|
+
.bt-btn-next-step {
|
|
1879
|
+
background: linear-gradient(135deg, #6366f1, #4f46e5);
|
|
1880
|
+
color: #ffffff;
|
|
1881
|
+
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.4);
|
|
1882
|
+
}
|
|
1883
|
+
.bt-btn-next-step:hover {
|
|
1884
|
+
background: linear-gradient(135deg, #4f46e5, #4338ca);
|
|
1885
|
+
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.55);
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
.bt-btn-finish-flow {
|
|
1889
|
+
background: linear-gradient(135deg, #10b981, #059669);
|
|
1890
|
+
color: #ffffff;
|
|
1891
|
+
box-shadow: 0 2px 8px rgba(16, 185, 129, 0.35);
|
|
1892
|
+
}
|
|
1893
|
+
.bt-btn-finish-flow:hover {
|
|
1894
|
+
background: linear-gradient(135deg, #059669, #047857);
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1772
1897
|
.bt-btn-resolve {
|
|
1773
1898
|
background: rgba(16, 185, 129, 0.15);
|
|
1774
1899
|
color: #6ee7b7;
|
|
@@ -1788,6 +1913,47 @@ var NoteInspector = class {
|
|
|
1788
1913
|
background: rgba(239, 68, 68, 0.3);
|
|
1789
1914
|
color: #ffffff;
|
|
1790
1915
|
}
|
|
1916
|
+
|
|
1917
|
+
/* 6. Toast Notifications */
|
|
1918
|
+
.bt-toast-container {
|
|
1919
|
+
position: fixed;
|
|
1920
|
+
bottom: 74px;
|
|
1921
|
+
right: 24px;
|
|
1922
|
+
display: flex;
|
|
1923
|
+
flex-direction: column;
|
|
1924
|
+
gap: 8px;
|
|
1925
|
+
pointer-events: none;
|
|
1926
|
+
z-index: 2147483647;
|
|
1927
|
+
align-items: flex-end;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
.bt-toast {
|
|
1931
|
+
background: #0f172a;
|
|
1932
|
+
color: #f8fafc;
|
|
1933
|
+
border: 1px solid #334155;
|
|
1934
|
+
border-radius: 30px;
|
|
1935
|
+
padding: 8px 16px;
|
|
1936
|
+
font-size: 12.5px;
|
|
1937
|
+
font-weight: 500;
|
|
1938
|
+
display: inline-flex;
|
|
1939
|
+
align-items: center;
|
|
1940
|
+
gap: 8px;
|
|
1941
|
+
box-shadow: 0 10px 25px -3px rgba(0, 0, 0, 0.6), 0 4px 6px -4px rgba(0, 0, 0, 0.4);
|
|
1942
|
+
pointer-events: auto;
|
|
1943
|
+
animation: bt-toast-in 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1944
|
+
transition: opacity 0.25s ease, transform 0.25s ease;
|
|
1945
|
+
user-select: none;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
.bt-toast-fadeout {
|
|
1949
|
+
opacity: 0;
|
|
1950
|
+
transform: translateY(6px);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
@keyframes bt-toast-in {
|
|
1954
|
+
from { opacity: 0; transform: translateY(8px) scale(0.95); }
|
|
1955
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
1956
|
+
}
|
|
1791
1957
|
`;
|
|
1792
1958
|
this.shadowRoot.appendChild(style);
|
|
1793
1959
|
this.highlightOverlay = document.createElement("div");
|
|
@@ -1797,6 +1963,9 @@ var NoteInspector = class {
|
|
|
1797
1963
|
this.markersContainer = document.createElement("div");
|
|
1798
1964
|
this.markersContainer.className = "bt-markers-layer";
|
|
1799
1965
|
this.shadowRoot.appendChild(this.markersContainer);
|
|
1966
|
+
this.toastContainer = document.createElement("div");
|
|
1967
|
+
this.toastContainer.className = "bt-toast-container";
|
|
1968
|
+
this.shadowRoot.appendChild(this.toastContainer);
|
|
1800
1969
|
if (this.options.showToolbar) {
|
|
1801
1970
|
this.createToolbar();
|
|
1802
1971
|
}
|
|
@@ -1824,6 +1993,10 @@ var NoteInspector = class {
|
|
|
1824
1993
|
<span>\u{1F4C4}</span> Page
|
|
1825
1994
|
</button>
|
|
1826
1995
|
<div class="bt-toolbar-divider"></div>
|
|
1996
|
+
<button class="bt-toolbar-btn" id="bt-mode-flow" title="Record multi-step reproduction flow">
|
|
1997
|
+
<span>\u{1F3AC}</span> Flow
|
|
1998
|
+
</button>
|
|
1999
|
+
<div class="bt-toolbar-divider"></div>
|
|
1827
2000
|
<button class="bt-toolbar-btn active" id="bt-toggle-notes" title="Toggle visible note markers on screen">
|
|
1828
2001
|
<span>\u{1F4CC}</span> Notes <span class="bt-count-pill" id="bt-notes-count">0</span>
|
|
1829
2002
|
</button>
|
|
@@ -1831,6 +2004,7 @@ var NoteInspector = class {
|
|
|
1831
2004
|
const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
|
|
1832
2005
|
const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
|
|
1833
2006
|
const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
|
|
2007
|
+
const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
|
|
1834
2008
|
const btnToggleNotes = this.toolbarElement.querySelector("#bt-toggle-notes");
|
|
1835
2009
|
btnElement.onclick = (e) => {
|
|
1836
2010
|
e.stopPropagation();
|
|
@@ -1844,6 +2018,14 @@ var NoteInspector = class {
|
|
|
1844
2018
|
e.stopPropagation();
|
|
1845
2019
|
this.setMode("page");
|
|
1846
2020
|
};
|
|
2021
|
+
btnFlow.onclick = (e) => {
|
|
2022
|
+
e.stopPropagation();
|
|
2023
|
+
if (this.activeScenario) {
|
|
2024
|
+
this.finishScenario();
|
|
2025
|
+
} else {
|
|
2026
|
+
this.startScenario();
|
|
2027
|
+
}
|
|
2028
|
+
};
|
|
1847
2029
|
btnToggleNotes.onclick = (e) => {
|
|
1848
2030
|
e.stopPropagation();
|
|
1849
2031
|
this.showMarkers = !this.showMarkers;
|
|
@@ -1857,9 +2039,21 @@ var NoteInspector = class {
|
|
|
1857
2039
|
const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
|
|
1858
2040
|
const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
|
|
1859
2041
|
const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
|
|
2042
|
+
const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
|
|
1860
2043
|
btnElement?.classList.toggle("active", this.activeMode === "element");
|
|
1861
2044
|
btnRegion?.classList.toggle("active", this.activeMode === "region");
|
|
1862
2045
|
btnPage?.classList.toggle("active", this.activeMode === "page");
|
|
2046
|
+
if (btnFlow) {
|
|
2047
|
+
if (this.activeScenario) {
|
|
2048
|
+
btnFlow.className = "bt-toolbar-btn active-scenario";
|
|
2049
|
+
btnFlow.innerHTML = `<span>\u{1F3AC}</span> Step ${this.activeScenario.stepNumber} (Finish)`;
|
|
2050
|
+
btnFlow.setAttribute("title", `Click to finish recording "${this.activeScenario.title}"`);
|
|
2051
|
+
} else {
|
|
2052
|
+
btnFlow.className = "bt-toolbar-btn";
|
|
2053
|
+
btnFlow.innerHTML = `<span>\u{1F3AC}</span> Flow`;
|
|
2054
|
+
btnFlow.setAttribute("title", "Record multi-step reproduction flow");
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
1863
2057
|
}
|
|
1864
2058
|
updateToolbarCount() {
|
|
1865
2059
|
if (!this.toolbarElement) return;
|
|
@@ -1884,6 +2078,9 @@ var NoteInspector = class {
|
|
|
1884
2078
|
pageNotes.push(note);
|
|
1885
2079
|
return;
|
|
1886
2080
|
}
|
|
2081
|
+
const isStep = !!note.scenarioId && note.stepNumber != null;
|
|
2082
|
+
const stepLabel = isStep ? `\u{1F3AC} Step ${note.stepNumber}` : `#${index + 1}`;
|
|
2083
|
+
const markerClass = `bt-note-marker ${isStep ? "bt-note-marker-step" : ""}`;
|
|
1887
2084
|
if (note.type === "region" && note.region) {
|
|
1888
2085
|
const regBox = document.createElement("div");
|
|
1889
2086
|
regBox.className = "bt-region-marker-box";
|
|
@@ -1893,11 +2090,11 @@ var NoteInspector = class {
|
|
|
1893
2090
|
regBox.style.height = `${note.region.height}px`;
|
|
1894
2091
|
this.markersContainer.appendChild(regBox);
|
|
1895
2092
|
const pin = document.createElement("div");
|
|
1896
|
-
pin.className =
|
|
2093
|
+
pin.className = markerClass;
|
|
1897
2094
|
pin.style.left = `${Math.max(4, note.region.x - 12)}px`;
|
|
1898
2095
|
pin.style.top = `${Math.max(4, note.region.y - 12)}px`;
|
|
1899
|
-
pin.title = note.message;
|
|
1900
|
-
pin.innerHTML = `<span
|
|
2096
|
+
pin.title = isStep ? `[${note.scenarioTitle || "Scenario"}] Step ${note.stepNumber}: ${note.message}` : note.message;
|
|
2097
|
+
pin.innerHTML = `<span>${isStep ? "\u{1F3AC}" : "\u{1F4D0}"}</span> <span>${stepLabel}</span>`;
|
|
1901
2098
|
pin.onclick = (e) => {
|
|
1902
2099
|
e.stopPropagation();
|
|
1903
2100
|
this.openNoteCard(note);
|
|
@@ -1915,12 +2112,12 @@ var NoteInspector = class {
|
|
|
1915
2112
|
const rect = targetEl ? targetEl.getBoundingClientRect() : note.target?.boundingRect;
|
|
1916
2113
|
if (rect) {
|
|
1917
2114
|
const pin = document.createElement("div");
|
|
1918
|
-
pin.className =
|
|
2115
|
+
pin.className = markerClass;
|
|
1919
2116
|
pin.setAttribute("data-note-id", note.id);
|
|
1920
|
-
pin.title = note.message;
|
|
2117
|
+
pin.title = isStep ? `[${note.scenarioTitle || "Scenario"}] Step ${note.stepNumber}: ${note.message}` : note.message;
|
|
1921
2118
|
pin.style.left = `${Math.max(4, rect.left - 10)}px`;
|
|
1922
2119
|
pin.style.top = `${Math.max(4, rect.top - 12)}px`;
|
|
1923
|
-
pin.innerHTML = `<span
|
|
2120
|
+
pin.innerHTML = `<span>${isStep ? "\u{1F3AC}" : "\u{1F4DD}"}</span> <span>${stepLabel}</span>`;
|
|
1924
2121
|
pin.onclick = (e) => {
|
|
1925
2122
|
e.stopPropagation();
|
|
1926
2123
|
this.openNoteCard(note);
|
|
@@ -1935,10 +2132,11 @@ var NoteInspector = class {
|
|
|
1935
2132
|
if (pageNotes.length > 0) {
|
|
1936
2133
|
const pageDock = document.createElement("div");
|
|
1937
2134
|
pageDock.className = "bt-page-notes-dock";
|
|
1938
|
-
pageNotes.forEach((pNote
|
|
2135
|
+
pageNotes.forEach((pNote) => {
|
|
1939
2136
|
const pill = document.createElement("div");
|
|
1940
2137
|
pill.className = "bt-page-note-pill";
|
|
1941
|
-
|
|
2138
|
+
const label = pNote.scenarioId && pNote.stepNumber ? `Step ${pNote.stepNumber}: ` : "";
|
|
2139
|
+
pill.innerHTML = `<span>\u{1F4C4}</span> <span>${label}${truncate(pNote.message, 25)}</span>`;
|
|
1942
2140
|
pill.onclick = (e) => {
|
|
1943
2141
|
e.stopPropagation();
|
|
1944
2142
|
this.openNoteCard(pNote);
|
|
@@ -1967,17 +2165,39 @@ var NoteInspector = class {
|
|
|
1967
2165
|
const icon = note.type === "region" ? "\u{1F4D0}" : note.type === "page" ? "\u{1F4C4}" : "\u{1F3AF}";
|
|
1968
2166
|
const contextText = note.type === "region" && note.region ? `Region: ${note.region.width} \xD7 ${note.region.height} px \xB7 Route: ${note.route}` : note.type === "page" ? `Page Note \xB7 Route: ${note.route}` : `${note.target?.selector || "Element"} (${note.target?.boundingRect?.width || 0}\xD7${note.target?.boundingRect?.height || 0}px) \xB7 ${note.route}`;
|
|
1969
2167
|
const formattedDate = new Date(note.createdAt).toLocaleString();
|
|
2168
|
+
let scenarioStepsHtml = "";
|
|
2169
|
+
let scenarioSteps = [];
|
|
2170
|
+
if (note.scenarioId) {
|
|
2171
|
+
scenarioSteps = this.savedNotes.filter((n) => n.scenarioId === note.scenarioId).sort((a, b) => (a.stepNumber || 0) - (b.stepNumber || 0));
|
|
2172
|
+
const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
|
|
2173
|
+
const prevStep = currentIndex > 0 ? scenarioSteps[currentIndex - 1] : null;
|
|
2174
|
+
const nextStep = currentIndex >= 0 && currentIndex < scenarioSteps.length - 1 ? scenarioSteps[currentIndex + 1] : null;
|
|
2175
|
+
scenarioStepsHtml = `
|
|
2176
|
+
<div class="bt-scenario-stepper">
|
|
2177
|
+
<button class="bt-step-nav-btn" id="btn-prev-step" ${!prevStep ? "disabled" : ""}>
|
|
2178
|
+
\u25C0 Step ${prevStep ? prevStep.stepNumber : ""}
|
|
2179
|
+
</button>
|
|
2180
|
+
<span>\u{1F3AC} Step ${note.stepNumber || 1} of ${scenarioSteps.length}</span>
|
|
2181
|
+
<button class="bt-step-nav-btn" id="btn-next-step-card" ${!nextStep ? "disabled" : ""}>
|
|
2182
|
+
Step ${nextStep ? nextStep.stepNumber : ""} \u25B6
|
|
2183
|
+
</button>
|
|
2184
|
+
</div>
|
|
2185
|
+
`;
|
|
2186
|
+
}
|
|
1970
2187
|
backdrop.innerHTML = `
|
|
1971
2188
|
<div class="bt-modal">
|
|
1972
2189
|
<div class="bt-modal-header">
|
|
1973
2190
|
<div class="bt-modal-title">
|
|
1974
|
-
<span>${
|
|
2191
|
+
<span>${note.scenarioId ? "\u{1F3AC} " + (note.scenarioTitle || "Scenario Flow") : icon + " Visual Note Details"}</span>
|
|
1975
2192
|
<span class="bt-mode-badge ${badgeClass}">${note.type.toUpperCase()}</span>
|
|
2193
|
+
${note.scenarioId && note.stepNumber ? `<span class="bt-mode-badge bt-badge-step">STEP ${note.stepNumber}</span>` : ""}
|
|
1976
2194
|
<span class="bt-mode-badge ${statusClass}">${note.status}</span>
|
|
1977
2195
|
</div>
|
|
1978
2196
|
<button class="bt-close-btn" id="btn-card-close" title="Close (Esc)">\u2715</button>
|
|
1979
2197
|
</div>
|
|
1980
2198
|
|
|
2199
|
+
${scenarioStepsHtml}
|
|
2200
|
+
|
|
1981
2201
|
<div class="bt-target-pill" title="${contextText}">
|
|
1982
2202
|
<span>\u{1F3F7}\uFE0F</span>
|
|
1983
2203
|
<span class="bt-pill-content">${contextText}</span>
|
|
@@ -1991,9 +2211,14 @@ var NoteInspector = class {
|
|
|
1991
2211
|
</div>
|
|
1992
2212
|
|
|
1993
2213
|
<div class="bt-modal-footer">
|
|
1994
|
-
<
|
|
1995
|
-
<
|
|
1996
|
-
|
|
2214
|
+
<div style="display: flex; gap: 6px;">
|
|
2215
|
+
<button class="bt-btn bt-btn-delete" id="btn-card-delete" title="Delete this note">
|
|
2216
|
+
<span>\u{1F5D1}\uFE0F</span> Delete
|
|
2217
|
+
</button>
|
|
2218
|
+
${note.scenarioId ? `<button class="bt-btn bt-btn-delete" id="btn-card-delete-flow" title="Delete entire scenario flow">
|
|
2219
|
+
<span>\u{1F5D1}\uFE0F</span> Delete Flow
|
|
2220
|
+
</button>` : ""}
|
|
2221
|
+
</div>
|
|
1997
2222
|
<div class="bt-modal-actions">
|
|
1998
2223
|
<button class="bt-btn bt-btn-cancel" id="btn-card-dismiss">Close</button>
|
|
1999
2224
|
<button class="bt-btn ${note.status === "OPEN" ? "bt-btn-resolve" : "bt-btn-save"}" id="btn-card-resolve">
|
|
@@ -2007,6 +2232,9 @@ var NoteInspector = class {
|
|
|
2007
2232
|
const btnDismiss = backdrop.querySelector("#btn-card-dismiss");
|
|
2008
2233
|
const btnResolve = backdrop.querySelector("#btn-card-resolve");
|
|
2009
2234
|
const btnDelete = backdrop.querySelector("#btn-card-delete");
|
|
2235
|
+
const btnDeleteFlow = backdrop.querySelector("#btn-card-delete-flow");
|
|
2236
|
+
const btnPrevStep = backdrop.querySelector("#btn-prev-step");
|
|
2237
|
+
const btnNextStepCard = backdrop.querySelector("#btn-next-step-card");
|
|
2010
2238
|
const closeCard = () => {
|
|
2011
2239
|
if (this.cardOverlay) {
|
|
2012
2240
|
root.removeChild(this.cardOverlay);
|
|
@@ -2019,18 +2247,44 @@ var NoteInspector = class {
|
|
|
2019
2247
|
backdrop.onclick = (e) => {
|
|
2020
2248
|
if (e.target === backdrop) closeCard();
|
|
2021
2249
|
};
|
|
2250
|
+
if (btnPrevStep && scenarioSteps.length > 0) {
|
|
2251
|
+
const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
|
|
2252
|
+
if (currentIndex > 0) {
|
|
2253
|
+
btnPrevStep.onclick = () => {
|
|
2254
|
+
this.openNoteCard(scenarioSteps[currentIndex - 1]);
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
if (btnNextStepCard && scenarioSteps.length > 0) {
|
|
2259
|
+
const currentIndex = scenarioSteps.findIndex((s) => s.id === note.id);
|
|
2260
|
+
if (currentIndex >= 0 && currentIndex < scenarioSteps.length - 1) {
|
|
2261
|
+
btnNextStepCard.onclick = () => {
|
|
2262
|
+
this.openNoteCard(scenarioSteps[currentIndex + 1]);
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2022
2266
|
btnResolve.onclick = () => {
|
|
2023
2267
|
if (note.status === "OPEN") {
|
|
2024
2268
|
this.transport.send({ type: "resolve_note", noteId: note.id });
|
|
2269
|
+
this.showToast("Note marked as resolved", "\u2705");
|
|
2025
2270
|
} else {
|
|
2026
2271
|
this.transport.send({ type: "reopen_note", noteId: note.id });
|
|
2272
|
+
this.showToast("Note reopened", "\u21BA");
|
|
2027
2273
|
}
|
|
2028
2274
|
closeCard();
|
|
2029
2275
|
};
|
|
2030
2276
|
btnDelete.onclick = () => {
|
|
2031
2277
|
this.transport.send({ type: "delete_note", noteId: note.id });
|
|
2278
|
+
this.showToast("Note deleted", "\u{1F5D1}\uFE0F");
|
|
2032
2279
|
closeCard();
|
|
2033
2280
|
};
|
|
2281
|
+
if (btnDeleteFlow && note.scenarioId) {
|
|
2282
|
+
btnDeleteFlow.onclick = () => {
|
|
2283
|
+
this.transport.send({ type: "delete_scenario", scenarioId: note.scenarioId });
|
|
2284
|
+
this.showToast("Scenario flow deleted", "\u{1F5D1}\uFE0F");
|
|
2285
|
+
closeCard();
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
2034
2288
|
root.appendChild(backdrop);
|
|
2035
2289
|
}
|
|
2036
2290
|
setupListeners() {
|
|
@@ -2067,7 +2321,7 @@ var NoteInspector = class {
|
|
|
2067
2321
|
if (target && target !== this.container && !this.container?.contains(target)) {
|
|
2068
2322
|
this.selectedElement = target;
|
|
2069
2323
|
this.openNoteEditor(target, "element");
|
|
2070
|
-
if (this.activeMode === "element") {
|
|
2324
|
+
if (this.activeMode === "element" && !this.activeScenario) {
|
|
2071
2325
|
this.setMode("idle");
|
|
2072
2326
|
}
|
|
2073
2327
|
}
|
|
@@ -2079,7 +2333,9 @@ var NoteInspector = class {
|
|
|
2079
2333
|
this.shadowRoot.removeChild(this.cardOverlay);
|
|
2080
2334
|
this.cardOverlay = null;
|
|
2081
2335
|
} else if (this.activeMode === "region" || this.activeMode === "element") {
|
|
2082
|
-
this.
|
|
2336
|
+
if (!this.activeScenario) {
|
|
2337
|
+
this.setMode("idle");
|
|
2338
|
+
}
|
|
2083
2339
|
}
|
|
2084
2340
|
}
|
|
2085
2341
|
};
|
|
@@ -2185,28 +2441,27 @@ var NoteInspector = class {
|
|
|
2185
2441
|
width: Math.round(width),
|
|
2186
2442
|
height: Math.round(height)
|
|
2187
2443
|
};
|
|
2188
|
-
this.hideRegionOverlay();
|
|
2189
|
-
this.setMode("idle");
|
|
2190
2444
|
this.openRegionNoteEditor(this.selectedRegion);
|
|
2191
|
-
}
|
|
2192
|
-
|
|
2445
|
+
}
|
|
2446
|
+
if (this.regionBox) {
|
|
2447
|
+
this.regionBox.style.display = "none";
|
|
2448
|
+
}
|
|
2449
|
+
if (!this.activeScenario) {
|
|
2193
2450
|
this.setMode("idle");
|
|
2194
2451
|
}
|
|
2195
2452
|
};
|
|
2196
|
-
}
|
|
2197
|
-
if (this.toolbarElement && this.toolbarElement.parentNode === root) {
|
|
2198
|
-
root.insertBefore(this.regionOverlay, this.toolbarElement);
|
|
2199
|
-
} else {
|
|
2200
2453
|
root.appendChild(this.regionOverlay);
|
|
2454
|
+
} else {
|
|
2455
|
+
this.regionOverlay.style.display = "block";
|
|
2201
2456
|
}
|
|
2202
2457
|
}
|
|
2203
2458
|
hideRegionOverlay() {
|
|
2204
|
-
this.
|
|
2205
|
-
|
|
2206
|
-
this.
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2459
|
+
if (this.regionOverlay) {
|
|
2460
|
+
this.regionOverlay.style.display = "none";
|
|
2461
|
+
this.isDraggingRegion = false;
|
|
2462
|
+
if (this.regionBox) {
|
|
2463
|
+
this.regionBox.style.display = "none";
|
|
2464
|
+
}
|
|
2210
2465
|
}
|
|
2211
2466
|
}
|
|
2212
2467
|
updateHighlight(el) {
|
|
@@ -2214,19 +2469,19 @@ var NoteInspector = class {
|
|
|
2214
2469
|
if (!root || !this.highlightOverlay) return;
|
|
2215
2470
|
const rect = el.getBoundingClientRect();
|
|
2216
2471
|
this.highlightOverlay.style.display = "block";
|
|
2217
|
-
this.highlightOverlay.style.top = `${rect.top}px`;
|
|
2218
2472
|
this.highlightOverlay.style.left = `${rect.left}px`;
|
|
2473
|
+
this.highlightOverlay.style.top = `${rect.top}px`;
|
|
2219
2474
|
this.highlightOverlay.style.width = `${rect.width}px`;
|
|
2220
2475
|
this.highlightOverlay.style.height = `${rect.height}px`;
|
|
2221
|
-
const selector = getSemanticSelector(el);
|
|
2222
|
-
const badgeText = `${selector} \xB7 ${Math.round(rect.width)}\xD7${Math.round(rect.height)}`;
|
|
2223
2476
|
let badge = this.highlightOverlay.querySelector(".bt-badge");
|
|
2224
2477
|
if (!badge) {
|
|
2225
2478
|
badge = document.createElement("div");
|
|
2226
2479
|
badge.className = "bt-badge";
|
|
2227
2480
|
this.highlightOverlay.appendChild(badge);
|
|
2228
2481
|
}
|
|
2229
|
-
|
|
2482
|
+
const selector = getSemanticSelector(el);
|
|
2483
|
+
const label = this.activeScenario ? `\u{1F3AC} Step ${this.activeScenario.stepNumber} \xB7 ${selector}` : selector;
|
|
2484
|
+
badge.textContent = `${label} (${Math.round(rect.width)} \xD7 ${Math.round(rect.height)} px)`;
|
|
2230
2485
|
}
|
|
2231
2486
|
hideHighlight() {
|
|
2232
2487
|
if (this.highlightOverlay) {
|
|
@@ -2242,21 +2497,57 @@ var NoteInspector = class {
|
|
|
2242
2497
|
this.updateHighlight(targetEl);
|
|
2243
2498
|
}
|
|
2244
2499
|
this.renderNoteModal({
|
|
2245
|
-
title: "Add Visual Note",
|
|
2246
|
-
modeBadge: noteType.toUpperCase(),
|
|
2500
|
+
title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Visual Note",
|
|
2501
|
+
modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : noteType.toUpperCase(),
|
|
2247
2502
|
pillText: noteType === "page" ? `Page Viewport: ${window.innerWidth} \xD7 ${window.innerHeight} px` : `${getSemanticSelector(targetEl)} (${Math.round(targetEl.getBoundingClientRect().width)}\xD7${Math.round(targetEl.getBoundingClientRect().height)}) \xB7 Viewport: ${window.innerWidth}\xD7${window.innerHeight}`,
|
|
2248
|
-
onSave: async (message) => {
|
|
2249
|
-
|
|
2503
|
+
onSave: async (message, action) => {
|
|
2504
|
+
let scenarioParam;
|
|
2505
|
+
if (action === "next_step" && !this.activeScenario) {
|
|
2506
|
+
this.startScenario();
|
|
2507
|
+
}
|
|
2508
|
+
if (this.activeScenario) {
|
|
2509
|
+
scenarioParam = {
|
|
2510
|
+
scenarioId: this.activeScenario.id,
|
|
2511
|
+
stepNumber: this.activeScenario.stepNumber,
|
|
2512
|
+
scenarioTitle: this.activeScenario.title
|
|
2513
|
+
};
|
|
2514
|
+
}
|
|
2515
|
+
await this.saveVisualNote(targetEl, message, noteType, scenarioParam);
|
|
2516
|
+
if (action === "next_step" && this.activeScenario) {
|
|
2517
|
+
this.activeScenario.stepNumber++;
|
|
2518
|
+
this.updateToolbarState();
|
|
2519
|
+
this.setMode("element");
|
|
2520
|
+
} else if (action === "finish_flow" && this.activeScenario) {
|
|
2521
|
+
this.finishScenario();
|
|
2522
|
+
}
|
|
2250
2523
|
}
|
|
2251
2524
|
});
|
|
2252
2525
|
}
|
|
2253
2526
|
openRegionNoteEditor(region) {
|
|
2254
2527
|
this.renderNoteModal({
|
|
2255
|
-
title: "Add Region Note",
|
|
2256
|
-
modeBadge: "REGION",
|
|
2528
|
+
title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Region Note",
|
|
2529
|
+
modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : "REGION",
|
|
2257
2530
|
pillText: `Selected Area: x:${region.x}, y:${region.y} (${region.width} \xD7 ${region.height} px)`,
|
|
2258
|
-
onSave: async (message) => {
|
|
2259
|
-
|
|
2531
|
+
onSave: async (message, action) => {
|
|
2532
|
+
let scenarioParam;
|
|
2533
|
+
if (action === "next_step" && !this.activeScenario) {
|
|
2534
|
+
this.startScenario();
|
|
2535
|
+
}
|
|
2536
|
+
if (this.activeScenario) {
|
|
2537
|
+
scenarioParam = {
|
|
2538
|
+
scenarioId: this.activeScenario.id,
|
|
2539
|
+
stepNumber: this.activeScenario.stepNumber,
|
|
2540
|
+
scenarioTitle: this.activeScenario.title
|
|
2541
|
+
};
|
|
2542
|
+
}
|
|
2543
|
+
await this.saveRegionVisualNote(region, message, scenarioParam);
|
|
2544
|
+
if (action === "next_step" && this.activeScenario) {
|
|
2545
|
+
this.activeScenario.stepNumber++;
|
|
2546
|
+
this.updateToolbarState();
|
|
2547
|
+
this.setMode("element");
|
|
2548
|
+
} else if (action === "finish_flow" && this.activeScenario) {
|
|
2549
|
+
this.finishScenario();
|
|
2550
|
+
}
|
|
2260
2551
|
}
|
|
2261
2552
|
});
|
|
2262
2553
|
}
|
|
@@ -2270,14 +2561,15 @@ var NoteInspector = class {
|
|
|
2270
2561
|
const backdrop = document.createElement("div");
|
|
2271
2562
|
backdrop.className = "bt-modal-backdrop";
|
|
2272
2563
|
this.modalOverlay = backdrop;
|
|
2273
|
-
const
|
|
2274
|
-
const
|
|
2564
|
+
const isStep = options.modeBadge.startsWith("STEP");
|
|
2565
|
+
const badgeClass = isStep ? "bt-badge-step" : `bt-badge-${options.modeBadge.toLowerCase()}`;
|
|
2566
|
+
const icon = isStep ? "\u{1F3AC}" : options.modeBadge === "REGION" ? "\u{1F4D0}" : options.modeBadge === "PAGE" ? "\u{1F4C4}" : "\u{1F3AF}";
|
|
2275
2567
|
const isMac = typeof navigator !== "undefined" && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
|
2276
2568
|
backdrop.innerHTML = `
|
|
2277
2569
|
<div class="bt-modal">
|
|
2278
2570
|
<div class="bt-modal-header">
|
|
2279
2571
|
<div class="bt-modal-title">
|
|
2280
|
-
<span
|
|
2572
|
+
<span>${icon} ${options.title}</span>
|
|
2281
2573
|
<span class="bt-mode-badge ${badgeClass}">${options.modeBadge}</span>
|
|
2282
2574
|
</div>
|
|
2283
2575
|
<button class="bt-close-btn" id="btn-close" title="Close (Esc)">\u2715</button>
|
|
@@ -2286,14 +2578,19 @@ var NoteInspector = class {
|
|
|
2286
2578
|
<span>${icon}</span>
|
|
2287
2579
|
<span class="bt-pill-content">${options.pillText}</span>
|
|
2288
2580
|
</div>
|
|
2289
|
-
<textarea class="bt-textarea" placeholder="Describe the layout issue,
|
|
2581
|
+
<textarea class="bt-textarea" placeholder="Describe the layout issue, user action, or note for AI agent..." autofocus></textarea>
|
|
2290
2582
|
<div class="bt-modal-footer">
|
|
2291
2583
|
<div class="bt-kbd-hint">
|
|
2292
2584
|
<kbd>${isMac ? "\u2318" : "Ctrl"}+Enter</kbd> save \xB7 <kbd>Esc</kbd> cancel
|
|
2293
2585
|
</div>
|
|
2294
2586
|
<div class="bt-modal-actions">
|
|
2295
2587
|
<button class="bt-btn bt-btn-cancel" id="btn-cancel">Cancel</button>
|
|
2296
|
-
<button class="bt-btn bt-btn-
|
|
2588
|
+
<button class="bt-btn bt-btn-next-step" id="btn-next-step" title="Save this step and immediately select the next element">
|
|
2589
|
+
<span>\u27A1\uFE0F</span> ${this.activeScenario ? "Save & Next Step" : "Save as Step 1 (Flow)"}
|
|
2590
|
+
</button>
|
|
2591
|
+
${this.activeScenario ? `<button class="bt-btn bt-btn-finish-flow" id="btn-finish-flow" title="Save final step and complete scenario">
|
|
2592
|
+
<span>\u2713</span> Save & Finish Flow
|
|
2593
|
+
</button>` : `<button class="bt-btn bt-btn-save" id="btn-save">Save Note</button>`}
|
|
2297
2594
|
</div>
|
|
2298
2595
|
</div>
|
|
2299
2596
|
</div>
|
|
@@ -2302,6 +2599,8 @@ var NoteInspector = class {
|
|
|
2302
2599
|
const btnClose = backdrop.querySelector("#btn-close");
|
|
2303
2600
|
const btnCancel = backdrop.querySelector("#btn-cancel");
|
|
2304
2601
|
const btnSave = backdrop.querySelector("#btn-save");
|
|
2602
|
+
const btnNextStep = backdrop.querySelector("#btn-next-step");
|
|
2603
|
+
const btnFinishFlow = backdrop.querySelector("#btn-finish-flow");
|
|
2305
2604
|
const closeModal = () => {
|
|
2306
2605
|
if (this.modalOverlay) {
|
|
2307
2606
|
root.removeChild(this.modalOverlay);
|
|
@@ -2317,22 +2616,29 @@ var NoteInspector = class {
|
|
|
2317
2616
|
closeModal();
|
|
2318
2617
|
}
|
|
2319
2618
|
};
|
|
2320
|
-
const
|
|
2619
|
+
const handleAction = async (action) => {
|
|
2321
2620
|
const message = textarea.value.trim();
|
|
2322
2621
|
if (!message) return;
|
|
2323
|
-
|
|
2324
|
-
btnSave.disabled = true;
|
|
2622
|
+
btnNextStep.disabled = true;
|
|
2623
|
+
if (btnSave) btnSave.disabled = true;
|
|
2624
|
+
if (btnFinishFlow) btnFinishFlow.disabled = true;
|
|
2325
2625
|
try {
|
|
2326
|
-
await options.onSave(message);
|
|
2626
|
+
await options.onSave(message, action);
|
|
2327
2627
|
} finally {
|
|
2328
2628
|
closeModal();
|
|
2329
2629
|
}
|
|
2330
2630
|
};
|
|
2331
|
-
btnSave
|
|
2631
|
+
if (btnSave) {
|
|
2632
|
+
btnSave.onclick = () => handleAction("save");
|
|
2633
|
+
}
|
|
2634
|
+
btnNextStep.onclick = () => handleAction("next_step");
|
|
2635
|
+
if (btnFinishFlow) {
|
|
2636
|
+
btnFinishFlow.onclick = () => handleAction("finish_flow");
|
|
2637
|
+
}
|
|
2332
2638
|
textarea.onkeydown = (e) => {
|
|
2333
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey
|
|
2639
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
2334
2640
|
e.preventDefault();
|
|
2335
|
-
|
|
2641
|
+
handleAction(this.activeScenario ? "next_step" : "save");
|
|
2336
2642
|
}
|
|
2337
2643
|
if (e.key === "Escape") {
|
|
2338
2644
|
closeModal();
|
|
@@ -2341,7 +2647,7 @@ var NoteInspector = class {
|
|
|
2341
2647
|
root.appendChild(backdrop);
|
|
2342
2648
|
setTimeout(() => textarea?.focus(), 50);
|
|
2343
2649
|
}
|
|
2344
|
-
async saveVisualNote(targetEl, message, noteType = "element") {
|
|
2650
|
+
async saveVisualNote(targetEl, message, noteType = "element", scenario) {
|
|
2345
2651
|
const rect = targetEl.getBoundingClientRect();
|
|
2346
2652
|
const selector = noteType === "page" ? "body" : getSemanticSelector(targetEl);
|
|
2347
2653
|
let screenshotDataUrl;
|
|
@@ -2368,33 +2674,38 @@ var NoteInspector = class {
|
|
|
2368
2674
|
visible: rect.width > 0 && rect.height > 0,
|
|
2369
2675
|
confidence: selector.startsWith("[data-test") ? "high" : selector.startsWith("#") ? "medium" : "low"
|
|
2370
2676
|
};
|
|
2677
|
+
const route = typeof window !== "undefined" ? window.location.pathname + window.location.search : "/";
|
|
2678
|
+
const url = typeof window !== "undefined" ? window.location.href : "http://localhost/";
|
|
2679
|
+
const viewport = typeof window !== "undefined" ? { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio || 1 } : { width: 1280, height: 800, devicePixelRatio: 1 };
|
|
2680
|
+
const scroll = typeof window !== "undefined" ? { scrollX: window.scrollX, scrollY: window.scrollY } : { scrollX: 0, scrollY: 0 };
|
|
2371
2681
|
const notePayload = {
|
|
2372
2682
|
type: "create_note",
|
|
2373
2683
|
sessionId: this.transport.getSessionId() || "",
|
|
2374
2684
|
noteType,
|
|
2375
2685
|
message,
|
|
2376
|
-
route
|
|
2377
|
-
url
|
|
2378
|
-
viewport
|
|
2379
|
-
|
|
2380
|
-
height: window.innerHeight,
|
|
2381
|
-
devicePixelRatio: window.devicePixelRatio || 1
|
|
2382
|
-
},
|
|
2383
|
-
scroll: {
|
|
2384
|
-
scrollX: window.scrollX,
|
|
2385
|
-
scrollY: window.scrollY
|
|
2386
|
-
},
|
|
2686
|
+
route,
|
|
2687
|
+
url,
|
|
2688
|
+
viewport,
|
|
2689
|
+
scroll,
|
|
2387
2690
|
target,
|
|
2388
2691
|
elementContext,
|
|
2389
2692
|
screenshot: screenshotDataUrl,
|
|
2693
|
+
scenarioId: scenario?.scenarioId,
|
|
2694
|
+
stepNumber: scenario?.stepNumber,
|
|
2695
|
+
scenarioTitle: scenario?.scenarioTitle,
|
|
2390
2696
|
timestamp: Date.now()
|
|
2391
2697
|
};
|
|
2392
2698
|
this.transport.send(notePayload);
|
|
2393
2699
|
if (this.options.onNoteCreated) {
|
|
2394
2700
|
this.options.onNoteCreated(notePayload);
|
|
2395
2701
|
}
|
|
2702
|
+
if (scenario?.scenarioId) {
|
|
2703
|
+
this.showToast(`Step ${scenario.stepNumber || 1} recorded`, "\u{1F3AC}");
|
|
2704
|
+
} else {
|
|
2705
|
+
this.showToast("Visual note saved", "\u2728");
|
|
2706
|
+
}
|
|
2396
2707
|
}
|
|
2397
|
-
async saveRegionVisualNote(region, message) {
|
|
2708
|
+
async saveRegionVisualNote(region, message, scenario) {
|
|
2398
2709
|
let screenshotDataUrl;
|
|
2399
2710
|
try {
|
|
2400
2711
|
const snap = await this.screenshotDriver.captureElement(document.body || document.documentElement);
|
|
@@ -2403,123 +2714,113 @@ var NoteInspector = class {
|
|
|
2403
2714
|
}
|
|
2404
2715
|
} catch {
|
|
2405
2716
|
}
|
|
2717
|
+
const route = typeof window !== "undefined" ? window.location.pathname + window.location.search : "/";
|
|
2718
|
+
const url = typeof window !== "undefined" ? window.location.href : "http://localhost/";
|
|
2719
|
+
const viewport = typeof window !== "undefined" ? { width: window.innerWidth, height: window.innerHeight, devicePixelRatio: window.devicePixelRatio || 1 } : { width: 1280, height: 800, devicePixelRatio: 1 };
|
|
2720
|
+
const scroll = typeof window !== "undefined" ? { scrollX: window.scrollX, scrollY: window.scrollY } : { scrollX: 0, scrollY: 0 };
|
|
2406
2721
|
const notePayload = {
|
|
2407
2722
|
type: "create_note",
|
|
2408
2723
|
sessionId: this.transport.getSessionId() || "",
|
|
2409
2724
|
noteType: "region",
|
|
2410
2725
|
message,
|
|
2411
|
-
route
|
|
2412
|
-
url
|
|
2413
|
-
viewport
|
|
2414
|
-
|
|
2415
|
-
height: window.innerHeight,
|
|
2416
|
-
devicePixelRatio: window.devicePixelRatio || 1
|
|
2417
|
-
},
|
|
2418
|
-
scroll: {
|
|
2419
|
-
scrollX: window.scrollX,
|
|
2420
|
-
scrollY: window.scrollY
|
|
2421
|
-
},
|
|
2726
|
+
route,
|
|
2727
|
+
url,
|
|
2728
|
+
viewport,
|
|
2729
|
+
scroll,
|
|
2422
2730
|
region,
|
|
2423
2731
|
screenshot: screenshotDataUrl,
|
|
2732
|
+
scenarioId: scenario?.scenarioId,
|
|
2733
|
+
stepNumber: scenario?.stepNumber,
|
|
2734
|
+
scenarioTitle: scenario?.scenarioTitle,
|
|
2424
2735
|
timestamp: Date.now()
|
|
2425
2736
|
};
|
|
2426
2737
|
this.transport.send(notePayload);
|
|
2427
2738
|
if (this.options.onNoteCreated) {
|
|
2428
2739
|
this.options.onNoteCreated(notePayload);
|
|
2429
2740
|
}
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
try {
|
|
2436
|
-
const canvas = document.createElement("canvas");
|
|
2437
|
-
canvas.width = region.width;
|
|
2438
|
-
canvas.height = region.height;
|
|
2439
|
-
const ctx = canvas.getContext("2d");
|
|
2440
|
-
if (!ctx) {
|
|
2441
|
-
resolve(dataUrl);
|
|
2442
|
-
return;
|
|
2443
|
-
}
|
|
2444
|
-
const dpr = window.devicePixelRatio || 1;
|
|
2445
|
-
ctx.drawImage(
|
|
2446
|
-
img,
|
|
2447
|
-
region.x * dpr,
|
|
2448
|
-
region.y * dpr,
|
|
2449
|
-
region.width * dpr,
|
|
2450
|
-
region.height * dpr,
|
|
2451
|
-
0,
|
|
2452
|
-
0,
|
|
2453
|
-
region.width,
|
|
2454
|
-
region.height
|
|
2455
|
-
);
|
|
2456
|
-
resolve(canvas.toDataURL("image/webp", 0.9));
|
|
2457
|
-
} catch {
|
|
2458
|
-
resolve(dataUrl);
|
|
2459
|
-
}
|
|
2460
|
-
};
|
|
2461
|
-
img.onerror = () => resolve(dataUrl);
|
|
2462
|
-
img.src = dataUrl;
|
|
2463
|
-
});
|
|
2741
|
+
if (scenario?.scenarioId) {
|
|
2742
|
+
this.showToast(`Step ${scenario.stepNumber || 1} region recorded`, "\u{1F3AC}");
|
|
2743
|
+
} else {
|
|
2744
|
+
this.showToast("Region note saved", "\u{1F4D0}");
|
|
2745
|
+
}
|
|
2464
2746
|
}
|
|
2465
2747
|
extractElementContext(el) {
|
|
2466
2748
|
const selector = getSemanticSelector(el);
|
|
2467
|
-
const tag = el.tagName.toLowerCase();
|
|
2468
2749
|
const attributes = {};
|
|
2469
2750
|
for (let i = 0; i < el.attributes.length; i++) {
|
|
2470
2751
|
const attr = el.attributes[i];
|
|
2471
|
-
if (
|
|
2752
|
+
if (this.options.maskSelectors?.some((mask) => {
|
|
2753
|
+
try {
|
|
2754
|
+
return el.matches(mask);
|
|
2755
|
+
} catch {
|
|
2756
|
+
return false;
|
|
2757
|
+
}
|
|
2758
|
+
}) && (attr.name === "value" || attr.name === "data-secret")) {
|
|
2472
2759
|
attributes[attr.name] = "[REDACTED]";
|
|
2473
2760
|
} else {
|
|
2474
2761
|
attributes[attr.name] = attr.value;
|
|
2475
2762
|
}
|
|
2476
2763
|
}
|
|
2477
|
-
let outerHTML =
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
for (const passInput of Array.from(clone.querySelectorAll('input[type="password"]'))) {
|
|
2481
|
-
passInput.setAttribute("value", "[REDACTED]");
|
|
2482
|
-
}
|
|
2483
|
-
outerHTML = truncate(clone.outerHTML, 10240);
|
|
2484
|
-
} catch {
|
|
2485
|
-
outerHTML = truncate(el.outerHTML, 10240);
|
|
2764
|
+
let outerHTML = el.outerHTML;
|
|
2765
|
+
if (outerHTML && outerHTML.length > 1e3) {
|
|
2766
|
+
outerHTML = truncate(outerHTML, 1e3);
|
|
2486
2767
|
}
|
|
2487
|
-
let
|
|
2488
|
-
if (
|
|
2489
|
-
|
|
2490
|
-
selector: getSemanticSelector(el.parentElement),
|
|
2491
|
-
tag: el.parentElement.tagName.toLowerCase()
|
|
2492
|
-
};
|
|
2768
|
+
let innerText = el.innerText || el.textContent || "";
|
|
2769
|
+
if (innerText && innerText.length > 200) {
|
|
2770
|
+
innerText = truncate(innerText, 200);
|
|
2493
2771
|
}
|
|
2494
2772
|
return {
|
|
2495
2773
|
selector,
|
|
2496
|
-
tag,
|
|
2774
|
+
tag: el.tagName.toLowerCase(),
|
|
2497
2775
|
attributes,
|
|
2498
2776
|
outerHTML,
|
|
2499
|
-
innerText
|
|
2500
|
-
parent
|
|
2777
|
+
innerText,
|
|
2778
|
+
parent: el.parentElement ? {
|
|
2779
|
+
selector: getSemanticSelector(el.parentElement),
|
|
2780
|
+
tag: el.parentElement.tagName.toLowerCase()
|
|
2781
|
+
} : void 0
|
|
2501
2782
|
};
|
|
2502
2783
|
}
|
|
2784
|
+
async cropDataUrl(dataUrl, region) {
|
|
2785
|
+
return new Promise((resolve) => {
|
|
2786
|
+
const img = new Image();
|
|
2787
|
+
img.onload = () => {
|
|
2788
|
+
const canvas = document.createElement("canvas");
|
|
2789
|
+
canvas.width = region.width;
|
|
2790
|
+
canvas.height = region.height;
|
|
2791
|
+
const ctx = canvas.getContext("2d");
|
|
2792
|
+
if (!ctx) {
|
|
2793
|
+
resolve(dataUrl);
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
const dpr = window.devicePixelRatio || 1;
|
|
2797
|
+
ctx.drawImage(
|
|
2798
|
+
img,
|
|
2799
|
+
region.x * dpr,
|
|
2800
|
+
region.y * dpr,
|
|
2801
|
+
region.width * dpr,
|
|
2802
|
+
region.height * dpr,
|
|
2803
|
+
0,
|
|
2804
|
+
0,
|
|
2805
|
+
region.width,
|
|
2806
|
+
region.height
|
|
2807
|
+
);
|
|
2808
|
+
resolve(canvas.toDataURL("image/png"));
|
|
2809
|
+
};
|
|
2810
|
+
img.onerror = () => resolve(dataUrl);
|
|
2811
|
+
img.src = dataUrl;
|
|
2812
|
+
});
|
|
2813
|
+
}
|
|
2503
2814
|
destroy() {
|
|
2504
2815
|
for (const cleanup of this.cleanups) {
|
|
2505
|
-
|
|
2506
|
-
cleanup();
|
|
2507
|
-
} catch {
|
|
2508
|
-
}
|
|
2816
|
+
cleanup();
|
|
2509
2817
|
}
|
|
2510
2818
|
this.cleanups = [];
|
|
2511
|
-
if (this.container && this.container.
|
|
2512
|
-
this.container.
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
this.toolbarElement = null;
|
|
2517
|
-
this.regionOverlay = null;
|
|
2518
|
-
this.regionBox = null;
|
|
2519
|
-
this.regionBanner = null;
|
|
2520
|
-
this.markersContainer = null;
|
|
2521
|
-
this.modalOverlay = null;
|
|
2522
|
-
this.cardOverlay = null;
|
|
2819
|
+
if (this.container && this.container.parentNode) {
|
|
2820
|
+
this.container.parentNode.removeChild(this.container);
|
|
2821
|
+
this.container = null;
|
|
2822
|
+
this.shadowRoot = null;
|
|
2823
|
+
}
|
|
2523
2824
|
}
|
|
2524
2825
|
};
|
|
2525
2826
|
|