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
|
@@ -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:
|
|
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:
|
|
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:
|
|
1705
|
+
padding: 7px 14px;
|
|
1600
1706
|
border-radius: 7px;
|
|
1601
|
-
font-size:
|
|
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:
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
1995
|
+
pageNotes.forEach((pNote) => {
|
|
1799
1996
|
const pill = document.createElement("div");
|
|
1800
1997
|
pill.className = "bt-page-note-pill";
|
|
1801
|
-
|
|
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>${
|
|
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
|
-
<
|
|
1855
|
-
<
|
|
1856
|
-
|
|
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.
|
|
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
|
-
}
|
|
2052
|
-
|
|
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.
|
|
2065
|
-
|
|
2066
|
-
this.
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2134
|
-
const
|
|
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
|
|
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,
|
|
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-
|
|
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
|
|
2479
|
+
const handleAction = async (action) => {
|
|
2181
2480
|
const message = textarea.value.trim();
|
|
2182
2481
|
if (!message) return;
|
|
2183
|
-
|
|
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
|
|
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
|
|
2499
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
2194
2500
|
e.preventDefault();
|
|
2195
|
-
|
|
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
|
|
2237
|
-
url
|
|
2238
|
-
viewport
|
|
2239
|
-
|
|
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
|
|
2272
|
-
url
|
|
2273
|
-
viewport
|
|
2274
|
-
|
|
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
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
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 (
|
|
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
|
-
|
|
2339
|
-
|
|
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
|
|
2348
|
-
if (
|
|
2349
|
-
|
|
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
|
|
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
|
-
|
|
2366
|
-
cleanup();
|
|
2367
|
-
} catch {
|
|
2368
|
-
}
|
|
2676
|
+
cleanup();
|
|
2369
2677
|
}
|
|
2370
2678
|
this.cleanups = [];
|
|
2371
|
-
if (this.container && this.container.
|
|
2372
|
-
this.container.
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
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-
|
|
2942
|
+
//# sourceMappingURL=chunk-WB7ZKWK7.js.map
|