browsertrack 0.2.1 → 0.2.3
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 +9 -6
- package/README.md +2 -0
- package/dist/{chunk-464D4U2U.js → chunk-5NR5K3ER.js} +299 -44
- package/dist/chunk-5NR5K3ER.js.map +1 -0
- package/dist/{chunk-3HOXPTM2.js → chunk-G5CIZSQM.js} +808 -53
- package/dist/chunk-G5CIZSQM.js.map +1 -0
- package/dist/{chunk-6VA7GBAO.js → chunk-ONPW7AYL.js} +144 -12
- package/dist/chunk-ONPW7AYL.js.map +1 -0
- package/dist/{chunk-INXDWPJW.js → chunk-PG4JJDCV.js} +353 -119
- package/dist/chunk-PG4JJDCV.js.map +1 -0
- package/dist/cli/index.js +1058 -546
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.cjs +449 -128
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +2 -2
- package/dist/client.iife.js +121 -15
- package/dist/core/index.d.ts +32 -2
- package/dist/core/index.js +11 -1
- package/dist/daemon/index.d.ts +2 -2
- package/dist/daemon/index.js +6 -8
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16 -7
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +7 -4
- package/dist/{server-DiVmTrIR.d.ts → server-BRG-RQQP.d.ts} +10 -1
- package/docs/cli.md +4 -1
- package/docs/getting-started.md +60 -6
- package/docs/mcp-reference.md +42 -0
- package/package.json +1 -1
- package/packages/cli/src/index.ts +247 -151
- package/packages/client/src/interceptors/navigation.ts +38 -26
- package/packages/client/src/interceptors/network.ts +22 -17
- package/packages/client/src/notes/inspector.ts +289 -65
- package/packages/client/src/source/resolver.ts +9 -3
- package/packages/client/src/transport/websocket.ts +23 -18
- package/packages/core/src/index.ts +1 -0
- package/packages/core/src/safety.ts +86 -0
- package/packages/core/src/selector.ts +110 -15
- package/packages/daemon/src/server/daemon.ts +7 -1
- package/packages/daemon/src/server/http.ts +125 -5
- package/packages/daemon/src/server/ws.ts +33 -29
- package/packages/daemon/src/storage/db.ts +57 -35
- package/packages/mcp/src/handlers.ts +121 -45
- package/packages/mcp/src/server.ts +221 -3
- package/test/core/safety.test.ts +106 -0
- package/test/core/selector.test.ts +133 -0
- package/test/daemon/storage.test.ts +36 -0
- package/test/e2e/daemon-mcp-e2e.test.ts +10 -0
- package/test/mcp/auto-start.test.ts +87 -0
- package/dist/chunk-3HOXPTM2.js.map +0 -1
- package/dist/chunk-464D4U2U.js.map +0 -1
- package/dist/chunk-6VA7GBAO.js.map +0 -1
- package/dist/chunk-7OCOQGDN.js +0 -635
- package/dist/chunk-7OCOQGDN.js.map +0 -1
- package/dist/chunk-INXDWPJW.js.map +0 -1
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSemanticSelector,
|
|
3
3
|
redactUrl,
|
|
4
|
+
resolveMeaningfulTarget,
|
|
5
|
+
safeJsonStringify,
|
|
4
6
|
truncate
|
|
5
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ONPW7AYL.js";
|
|
6
8
|
|
|
7
9
|
// packages/client/src/breadcrumbs.ts
|
|
8
10
|
var BreadcrumbBuffer = class {
|
|
@@ -78,7 +80,9 @@ function resolveReactComponent(el) {
|
|
|
78
80
|
}
|
|
79
81
|
}
|
|
80
82
|
let curr = hostFiber;
|
|
81
|
-
|
|
83
|
+
let depth = 0;
|
|
84
|
+
while (curr && depth < 50) {
|
|
85
|
+
depth++;
|
|
82
86
|
const type = curr.type;
|
|
83
87
|
const name = getReactComponentName(type);
|
|
84
88
|
if (name && !name.startsWith("html:") && name !== "Fragment") {
|
|
@@ -133,7 +137,9 @@ function resolveVueComponent(el) {
|
|
|
133
137
|
const sourceFile = type.__file;
|
|
134
138
|
const hierarchy = [];
|
|
135
139
|
let curr = vueParent;
|
|
136
|
-
|
|
140
|
+
let depth = 0;
|
|
141
|
+
while (curr && depth < 50) {
|
|
142
|
+
depth++;
|
|
137
143
|
const cType = curr.type || {};
|
|
138
144
|
const cName = cType.name || cType.__name || cType.displayName;
|
|
139
145
|
if (cName && !hierarchy.includes(cName)) {
|
|
@@ -170,7 +176,9 @@ function resolveVueComponent(el) {
|
|
|
170
176
|
function resolveSvelteComponent(el) {
|
|
171
177
|
try {
|
|
172
178
|
let curr = el;
|
|
173
|
-
|
|
179
|
+
let depth = 0;
|
|
180
|
+
while (curr && depth < 50) {
|
|
181
|
+
depth++;
|
|
174
182
|
const meta = curr.__svelte_meta;
|
|
175
183
|
if (meta && meta.loc) {
|
|
176
184
|
return {
|
|
@@ -709,12 +717,16 @@ function setupConsoleInterceptors(onConsole) {
|
|
|
709
717
|
function setupNavigationInterceptors(onNavigation) {
|
|
710
718
|
if (typeof window === "undefined" || typeof history === "undefined") return () => {
|
|
711
719
|
};
|
|
712
|
-
let currentUrl =
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
720
|
+
let currentUrl = "";
|
|
721
|
+
try {
|
|
722
|
+
currentUrl = redactUrl(window.location.href);
|
|
723
|
+
onNavigation({
|
|
724
|
+
to: currentUrl,
|
|
725
|
+
type: "initial",
|
|
726
|
+
timestamp: Date.now()
|
|
727
|
+
});
|
|
728
|
+
} catch {
|
|
729
|
+
}
|
|
718
730
|
const originalPushState = history.pushState;
|
|
719
731
|
const originalReplaceState = history.replaceState;
|
|
720
732
|
history.pushState = function(data, unused, url) {
|
|
@@ -750,26 +762,32 @@ function setupNavigationInterceptors(onNavigation) {
|
|
|
750
762
|
return result;
|
|
751
763
|
};
|
|
752
764
|
const onPopState = () => {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
765
|
+
try {
|
|
766
|
+
const from = currentUrl;
|
|
767
|
+
const to = redactUrl(window.location.href);
|
|
768
|
+
currentUrl = to;
|
|
769
|
+
onNavigation({
|
|
770
|
+
from,
|
|
771
|
+
to,
|
|
772
|
+
type: "popstate",
|
|
773
|
+
timestamp: Date.now()
|
|
774
|
+
});
|
|
775
|
+
} catch {
|
|
776
|
+
}
|
|
762
777
|
};
|
|
763
778
|
const onHashChange = () => {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
779
|
+
try {
|
|
780
|
+
const from = currentUrl;
|
|
781
|
+
const to = redactUrl(window.location.href);
|
|
782
|
+
currentUrl = to;
|
|
783
|
+
onNavigation({
|
|
784
|
+
from,
|
|
785
|
+
to,
|
|
786
|
+
type: "hashchange",
|
|
787
|
+
timestamp: Date.now()
|
|
788
|
+
});
|
|
789
|
+
} catch {
|
|
790
|
+
}
|
|
773
791
|
};
|
|
774
792
|
window.addEventListener("popstate", onPopState);
|
|
775
793
|
window.addEventListener("hashchange", onHashChange);
|
|
@@ -806,31 +824,38 @@ function setupNetworkInterceptors(onNetwork) {
|
|
|
806
824
|
urlStr = "unknown_url";
|
|
807
825
|
}
|
|
808
826
|
const safeUrl = redactUrl(urlStr);
|
|
827
|
+
let response;
|
|
809
828
|
try {
|
|
810
|
-
|
|
811
|
-
const durationMs = Date.now() - startTime;
|
|
812
|
-
onNetwork({
|
|
813
|
-
url: safeUrl,
|
|
814
|
-
method,
|
|
815
|
-
status: response.status,
|
|
816
|
-
statusText: response.statusText,
|
|
817
|
-
durationMs,
|
|
818
|
-
timestamp: startTime
|
|
819
|
-
});
|
|
820
|
-
return response;
|
|
829
|
+
response = await originalFetch.apply(window, [input, init2]);
|
|
821
830
|
} catch (err) {
|
|
822
|
-
const
|
|
831
|
+
const durationMs2 = Date.now() - startTime;
|
|
823
832
|
const isAbort = err?.name === "AbortError";
|
|
833
|
+
try {
|
|
834
|
+
onNetwork({
|
|
835
|
+
url: safeUrl,
|
|
836
|
+
method,
|
|
837
|
+
durationMs: durationMs2,
|
|
838
|
+
error: err?.message || "Network request failed",
|
|
839
|
+
aborted: isAbort,
|
|
840
|
+
timestamp: startTime
|
|
841
|
+
});
|
|
842
|
+
} catch {
|
|
843
|
+
}
|
|
844
|
+
throw err;
|
|
845
|
+
}
|
|
846
|
+
const durationMs = Date.now() - startTime;
|
|
847
|
+
try {
|
|
824
848
|
onNetwork({
|
|
825
849
|
url: safeUrl,
|
|
826
850
|
method,
|
|
851
|
+
status: response.status,
|
|
852
|
+
statusText: response.statusText,
|
|
827
853
|
durationMs,
|
|
828
|
-
error: err?.message || "Network request failed",
|
|
829
|
-
aborted: isAbort,
|
|
830
854
|
timestamp: startTime
|
|
831
855
|
});
|
|
832
|
-
|
|
856
|
+
} catch {
|
|
833
857
|
}
|
|
858
|
+
return response;
|
|
834
859
|
};
|
|
835
860
|
cleanups.push(() => {
|
|
836
861
|
window.fetch = originalFetch;
|
|
@@ -1216,15 +1241,18 @@ var WebSocketTransport = class {
|
|
|
1216
1241
|
}
|
|
1217
1242
|
}
|
|
1218
1243
|
send(payload) {
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1244
|
+
try {
|
|
1245
|
+
const raw = safeJsonStringify(payload);
|
|
1246
|
+
if (this.isConnected()) {
|
|
1247
|
+
try {
|
|
1248
|
+
this.ws.send(raw);
|
|
1249
|
+
} catch {
|
|
1250
|
+
this.enqueue(raw);
|
|
1251
|
+
}
|
|
1252
|
+
} else {
|
|
1224
1253
|
this.enqueue(raw);
|
|
1225
1254
|
}
|
|
1226
|
-
}
|
|
1227
|
-
this.enqueue(raw);
|
|
1255
|
+
} catch {
|
|
1228
1256
|
}
|
|
1229
1257
|
}
|
|
1230
1258
|
enqueue(raw) {
|
|
@@ -1249,17 +1277,17 @@ var WebSocketTransport = class {
|
|
|
1249
1277
|
}
|
|
1250
1278
|
sendHello() {
|
|
1251
1279
|
if (typeof window === "undefined") return;
|
|
1252
|
-
const hello = {
|
|
1253
|
-
type: "hello",
|
|
1254
|
-
origin: window.location.origin,
|
|
1255
|
-
url: window.location.href,
|
|
1256
|
-
title: document.title,
|
|
1257
|
-
userAgent: navigator.userAgent,
|
|
1258
|
-
timestamp: Date.now(),
|
|
1259
|
-
projectId: this.projectId
|
|
1260
|
-
};
|
|
1261
1280
|
try {
|
|
1262
|
-
|
|
1281
|
+
const hello = {
|
|
1282
|
+
type: "hello",
|
|
1283
|
+
origin: window.location.origin,
|
|
1284
|
+
url: window.location.href,
|
|
1285
|
+
title: document.title,
|
|
1286
|
+
userAgent: navigator.userAgent,
|
|
1287
|
+
timestamp: Date.now(),
|
|
1288
|
+
projectId: this.projectId
|
|
1289
|
+
};
|
|
1290
|
+
this.ws.send(safeJsonStringify(hello));
|
|
1263
1291
|
} catch {
|
|
1264
1292
|
}
|
|
1265
1293
|
}
|
|
@@ -1316,6 +1344,7 @@ var NoteInspector = class {
|
|
|
1316
1344
|
savedNotes = [];
|
|
1317
1345
|
showMarkers = true;
|
|
1318
1346
|
isHidden = false;
|
|
1347
|
+
isToolbarCollapsed = false;
|
|
1319
1348
|
cleanups = [];
|
|
1320
1349
|
constructor(transport, screenshotDriver, options = {}) {
|
|
1321
1350
|
this.transport = transport;
|
|
@@ -1323,6 +1352,12 @@ var NoteInspector = class {
|
|
|
1323
1352
|
const hiddenByQuery = shouldHideUIFromUrl(options.hideQueryParam);
|
|
1324
1353
|
this.isHidden = options.hidden === true || hiddenByQuery;
|
|
1325
1354
|
this.showMarkers = options.showBadges !== false && !this.isHidden;
|
|
1355
|
+
try {
|
|
1356
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
1357
|
+
this.isToolbarCollapsed = window.sessionStorage.getItem("bt_toolbar_collapsed") === "true";
|
|
1358
|
+
}
|
|
1359
|
+
} catch {
|
|
1360
|
+
}
|
|
1326
1361
|
this.options = {
|
|
1327
1362
|
shortcut: "Alt+Click",
|
|
1328
1363
|
maskSelectors: ['input[type="password"]', "[data-sensitive]"],
|
|
@@ -1450,11 +1485,28 @@ var NoteInspector = class {
|
|
|
1450
1485
|
this.shadowRoot = this.container.attachShadow({ mode: "open" });
|
|
1451
1486
|
const style = document.createElement("style");
|
|
1452
1487
|
style.textContent = `
|
|
1453
|
-
:
|
|
1454
|
-
|
|
1455
|
-
|
|
1488
|
+
@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
|
|
1489
|
+
|
|
1490
|
+
:host, :host *, :host *::before, :host *::after {
|
|
1491
|
+
box-sizing: border-box;
|
|
1456
1492
|
-webkit-font-smoothing: antialiased;
|
|
1457
1493
|
-moz-osx-font-smoothing: grayscale;
|
|
1494
|
+
text-rendering: optimizeLegibility;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
:host {
|
|
1498
|
+
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
1499
|
+
color-scheme: dark;
|
|
1500
|
+
letter-spacing: -0.012em;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
button, input, textarea, select {
|
|
1504
|
+
font-family: inherit;
|
|
1505
|
+
letter-spacing: inherit;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
code, pre, kbd, .bt-mono, .bt-badge, .bt-region-badge, .bt-target-pill, .bt-component-pill {
|
|
1509
|
+
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
1458
1510
|
}
|
|
1459
1511
|
|
|
1460
1512
|
/* 1. Element Highlight Overlay */
|
|
@@ -1688,7 +1740,7 @@ var NoteInspector = class {
|
|
|
1688
1740
|
pointer-events: auto;
|
|
1689
1741
|
z-index: 2147483646;
|
|
1690
1742
|
user-select: none;
|
|
1691
|
-
transition: all 0.
|
|
1743
|
+
transition: all 0.22s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1692
1744
|
}
|
|
1693
1745
|
|
|
1694
1746
|
.bt-toolbar-btn {
|
|
@@ -1749,6 +1801,86 @@ var NoteInspector = class {
|
|
|
1749
1801
|
margin: 0 2px;
|
|
1750
1802
|
}
|
|
1751
1803
|
|
|
1804
|
+
.bt-toolbar-collapse-btn {
|
|
1805
|
+
background: transparent;
|
|
1806
|
+
border: none;
|
|
1807
|
+
color: #64748b;
|
|
1808
|
+
font-size: 11px;
|
|
1809
|
+
font-weight: 700;
|
|
1810
|
+
padding: 6px 8px;
|
|
1811
|
+
border-radius: 20px;
|
|
1812
|
+
cursor: pointer;
|
|
1813
|
+
display: flex;
|
|
1814
|
+
align-items: center;
|
|
1815
|
+
justify-content: center;
|
|
1816
|
+
transition: all 0.15s ease;
|
|
1817
|
+
font-family: inherit;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
.bt-toolbar-collapse-btn:hover {
|
|
1821
|
+
background: #1e293b;
|
|
1822
|
+
color: #f8fafc;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
/* Collapsed Toolbar State */
|
|
1826
|
+
.bt-toolbar.bt-toolbar-collapsed {
|
|
1827
|
+
padding: 0;
|
|
1828
|
+
background: transparent;
|
|
1829
|
+
border: none;
|
|
1830
|
+
box-shadow: none;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
.bt-toolbar.bt-toolbar-collapsed .bt-toolbar-btn,
|
|
1834
|
+
.bt-toolbar.bt-toolbar-collapsed .bt-toolbar-divider,
|
|
1835
|
+
.bt-toolbar.bt-toolbar-collapsed .bt-toolbar-collapse-btn {
|
|
1836
|
+
display: none !important;
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
.bt-toolbar-trigger {
|
|
1840
|
+
display: none;
|
|
1841
|
+
background: #0f172a;
|
|
1842
|
+
border: 1px solid #334155;
|
|
1843
|
+
border-radius: 30px;
|
|
1844
|
+
padding: 6px 14px;
|
|
1845
|
+
color: #38bdf8;
|
|
1846
|
+
font-size: 12px;
|
|
1847
|
+
font-weight: 600;
|
|
1848
|
+
cursor: pointer;
|
|
1849
|
+
align-items: center;
|
|
1850
|
+
gap: 8px;
|
|
1851
|
+
box-shadow: 0 10px 25px -3px rgba(0,0,0,0.6), 0 4px 6px -4px rgba(0,0,0,0.4);
|
|
1852
|
+
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
|
|
1853
|
+
font-family: inherit;
|
|
1854
|
+
user-select: none;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
.bt-toolbar.bt-toolbar-collapsed .bt-toolbar-trigger {
|
|
1858
|
+
display: inline-flex;
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
.bt-toolbar-trigger:hover {
|
|
1862
|
+
background: #1e293b;
|
|
1863
|
+
color: #ffffff;
|
|
1864
|
+
transform: translateY(-2px);
|
|
1865
|
+
box-shadow: 0 12px 28px -3px rgba(0,0,0,0.7);
|
|
1866
|
+
border-color: #38bdf8;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
.bt-pulse-dot {
|
|
1870
|
+
width: 7px;
|
|
1871
|
+
height: 7px;
|
|
1872
|
+
border-radius: 50%;
|
|
1873
|
+
background: #38bdf8;
|
|
1874
|
+
box-shadow: 0 0 8px #38bdf8;
|
|
1875
|
+
display: inline-block;
|
|
1876
|
+
animation: bt-dot-pulse 2s infinite;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
@keyframes bt-dot-pulse {
|
|
1880
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
1881
|
+
50% { opacity: 0.4; transform: scale(0.85); }
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1752
1884
|
/* 5. Modals & Popover Card */
|
|
1753
1885
|
.bt-modal-backdrop {
|
|
1754
1886
|
position: fixed;
|
|
@@ -2159,8 +2291,13 @@ var NoteInspector = class {
|
|
|
2159
2291
|
createToolbar() {
|
|
2160
2292
|
if (!this.shadowRoot || this.toolbarElement) return;
|
|
2161
2293
|
this.toolbarElement = document.createElement("div");
|
|
2162
|
-
this.toolbarElement.className = "bt-toolbar"
|
|
2294
|
+
this.toolbarElement.className = `bt-toolbar ${this.isToolbarCollapsed ? "bt-toolbar-collapsed" : ""}`;
|
|
2163
2295
|
this.toolbarElement.innerHTML = `
|
|
2296
|
+
<button class="bt-toolbar-trigger" id="bt-toolbar-expand" title="Expand BrowserTrack toolbar (Alt+T)">
|
|
2297
|
+
<span class="bt-pulse-dot"></span>
|
|
2298
|
+
<span>\u26A1 BrowserTrack</span>
|
|
2299
|
+
<span class="bt-count-pill" id="bt-notes-count-collapsed">0</span>
|
|
2300
|
+
</button>
|
|
2164
2301
|
<button class="bt-toolbar-btn" id="bt-mode-element" title="Inspect element (or hold Alt+Click)">
|
|
2165
2302
|
<span>\u{1F3AF}</span> Element
|
|
2166
2303
|
</button>
|
|
@@ -2178,12 +2315,26 @@ var NoteInspector = class {
|
|
|
2178
2315
|
<button class="bt-toolbar-btn active" id="bt-toggle-notes" title="Toggle visible note markers on screen">
|
|
2179
2316
|
<span>\u{1F4CC}</span> Notes <span class="bt-count-pill" id="bt-notes-count">0</span>
|
|
2180
2317
|
</button>
|
|
2318
|
+
<div class="bt-toolbar-divider"></div>
|
|
2319
|
+
<button class="bt-toolbar-collapse-btn" id="bt-toolbar-collapse" title="Collapse toolbar (Alt+T)">
|
|
2320
|
+
<span>\u276F</span>
|
|
2321
|
+
</button>
|
|
2181
2322
|
`;
|
|
2323
|
+
const btnExpand = this.toolbarElement.querySelector("#bt-toolbar-expand");
|
|
2324
|
+
const btnCollapse = this.toolbarElement.querySelector("#bt-toolbar-collapse");
|
|
2182
2325
|
const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
|
|
2183
2326
|
const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
|
|
2184
2327
|
const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
|
|
2185
2328
|
const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
|
|
2186
2329
|
const btnToggleNotes = this.toolbarElement.querySelector("#bt-toggle-notes");
|
|
2330
|
+
btnExpand.onclick = (e) => {
|
|
2331
|
+
e.stopPropagation();
|
|
2332
|
+
this.toggleToolbarCollapse(false);
|
|
2333
|
+
};
|
|
2334
|
+
btnCollapse.onclick = (e) => {
|
|
2335
|
+
e.stopPropagation();
|
|
2336
|
+
this.toggleToolbarCollapse(true);
|
|
2337
|
+
};
|
|
2187
2338
|
btnElement.onclick = (e) => {
|
|
2188
2339
|
e.stopPropagation();
|
|
2189
2340
|
this.setMode(this.activeMode === "element" ? "idle" : "element");
|
|
@@ -2212,6 +2363,17 @@ var NoteInspector = class {
|
|
|
2212
2363
|
};
|
|
2213
2364
|
this.shadowRoot.appendChild(this.toolbarElement);
|
|
2214
2365
|
}
|
|
2366
|
+
toggleToolbarCollapse(force) {
|
|
2367
|
+
if (!this.toolbarElement) return;
|
|
2368
|
+
this.isToolbarCollapsed = typeof force === "boolean" ? force : !this.isToolbarCollapsed;
|
|
2369
|
+
this.toolbarElement.classList.toggle("bt-toolbar-collapsed", this.isToolbarCollapsed);
|
|
2370
|
+
try {
|
|
2371
|
+
if (typeof window !== "undefined" && window.sessionStorage) {
|
|
2372
|
+
window.sessionStorage.setItem("bt_toolbar_collapsed", String(this.isToolbarCollapsed));
|
|
2373
|
+
}
|
|
2374
|
+
} catch {
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2215
2377
|
updateToolbarState() {
|
|
2216
2378
|
if (!this.toolbarElement) return;
|
|
2217
2379
|
const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
|
|
@@ -2235,11 +2397,15 @@ var NoteInspector = class {
|
|
|
2235
2397
|
}
|
|
2236
2398
|
updateToolbarCount() {
|
|
2237
2399
|
if (!this.toolbarElement) return;
|
|
2400
|
+
const openCount = this.savedNotes.filter((n) => n.status === "OPEN").length;
|
|
2238
2401
|
const countEl = this.toolbarElement.querySelector("#bt-notes-count");
|
|
2239
2402
|
if (countEl) {
|
|
2240
|
-
const openCount = this.savedNotes.filter((n) => n.status === "OPEN").length;
|
|
2241
2403
|
countEl.textContent = String(openCount);
|
|
2242
2404
|
}
|
|
2405
|
+
const countCollapsedEl = this.toolbarElement.querySelector("#bt-notes-count-collapsed");
|
|
2406
|
+
if (countCollapsedEl) {
|
|
2407
|
+
countCollapsedEl.textContent = String(openCount);
|
|
2408
|
+
}
|
|
2243
2409
|
}
|
|
2244
2410
|
renderMarkers() {
|
|
2245
2411
|
const root = this.ensureContainer();
|
|
@@ -2247,9 +2413,15 @@ var NoteInspector = class {
|
|
|
2247
2413
|
this.markersContainer.innerHTML = "";
|
|
2248
2414
|
if (!this.showMarkers || this.isHidden) return;
|
|
2249
2415
|
const currentPath = window.location.pathname;
|
|
2250
|
-
const activeNotes = this.savedNotes.filter(
|
|
2251
|
-
(n
|
|
2252
|
-
|
|
2416
|
+
const activeNotes = this.savedNotes.filter((n) => {
|
|
2417
|
+
if (n.status !== "OPEN") return false;
|
|
2418
|
+
if (!n.route) return true;
|
|
2419
|
+
if (n.route === currentPath) return true;
|
|
2420
|
+
if (n.route.includes("#") || n.route.includes("?")) {
|
|
2421
|
+
return window.location.href.includes(n.route);
|
|
2422
|
+
}
|
|
2423
|
+
return false;
|
|
2424
|
+
});
|
|
2253
2425
|
const pageNotes = [];
|
|
2254
2426
|
activeNotes.forEach((note, index) => {
|
|
2255
2427
|
if (note.type === "page") {
|
|
@@ -2289,6 +2461,10 @@ var NoteInspector = class {
|
|
|
2289
2461
|
}
|
|
2290
2462
|
const rect = targetEl ? targetEl.getBoundingClientRect() : note.target?.boundingRect;
|
|
2291
2463
|
if (rect) {
|
|
2464
|
+
const isOffscreen = targetEl && (rect.bottom < 0 || rect.top > window.innerHeight || rect.right < 0 || rect.left > window.innerWidth);
|
|
2465
|
+
if (isOffscreen) {
|
|
2466
|
+
return;
|
|
2467
|
+
}
|
|
2292
2468
|
const pin = document.createElement("div");
|
|
2293
2469
|
pin.className = markerClass;
|
|
2294
2470
|
pin.setAttribute("data-note-id", note.id);
|
|
@@ -2474,65 +2650,90 @@ var NoteInspector = class {
|
|
|
2474
2650
|
}
|
|
2475
2651
|
setupListeners() {
|
|
2476
2652
|
const onMouseMove = (e) => {
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
this.
|
|
2481
|
-
|
|
2482
|
-
}
|
|
2483
|
-
if (e.altKey || this.activeMode === "element") {
|
|
2484
|
-
const target = document.elementFromPoint(e.clientX, e.clientY);
|
|
2485
|
-
if (target && target !== this.container && !this.container?.contains(target)) {
|
|
2486
|
-
this.hoveredElement = target;
|
|
2487
|
-
this.updateHighlight(target);
|
|
2653
|
+
try {
|
|
2654
|
+
if (this.isHidden || this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
|
|
2655
|
+
const path = e.composedPath ? e.composedPath() : [];
|
|
2656
|
+
if (this.container && path.includes(this.container)) {
|
|
2657
|
+
this.hideHighlight();
|
|
2488
2658
|
return;
|
|
2489
2659
|
}
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2660
|
+
if (e.altKey || this.activeMode === "element") {
|
|
2661
|
+
const rawTarget = document.elementFromPoint(e.clientX, e.clientY);
|
|
2662
|
+
const target = rawTarget ? resolveMeaningfulTarget(rawTarget) : null;
|
|
2663
|
+
if (target && target !== this.container && !this.container?.contains(target)) {
|
|
2664
|
+
this.hoveredElement = target;
|
|
2665
|
+
this.updateHighlight(target);
|
|
2666
|
+
return;
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
if (!e.altKey && this.activeMode !== "element") {
|
|
2670
|
+
this.hideHighlight();
|
|
2671
|
+
}
|
|
2672
|
+
} catch {
|
|
2493
2673
|
}
|
|
2494
2674
|
};
|
|
2495
2675
|
const onClick = (e) => {
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
e.
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
this.
|
|
2509
|
-
|
|
2510
|
-
this.
|
|
2676
|
+
try {
|
|
2677
|
+
if (this.isHidden || this.modalOverlay || this.cardOverlay) return;
|
|
2678
|
+
const path = e.composedPath ? e.composedPath() : [];
|
|
2679
|
+
if (this.container && path.includes(this.container)) {
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
if (this.activeMode === "region") return;
|
|
2683
|
+
if (e.altKey || this.activeMode === "element") {
|
|
2684
|
+
e.preventDefault();
|
|
2685
|
+
e.stopPropagation();
|
|
2686
|
+
const rawTarget = this.hoveredElement || document.elementFromPoint(e.clientX, e.clientY);
|
|
2687
|
+
const target = rawTarget ? resolveMeaningfulTarget(rawTarget) : null;
|
|
2688
|
+
if (target && target !== this.container && !this.container?.contains(target)) {
|
|
2689
|
+
this.selectedElement = target;
|
|
2690
|
+
this.openNoteEditor(target, "element");
|
|
2691
|
+
if (this.activeMode === "element" && !this.activeScenario) {
|
|
2692
|
+
this.setMode("idle");
|
|
2693
|
+
}
|
|
2511
2694
|
}
|
|
2512
2695
|
}
|
|
2696
|
+
} catch {
|
|
2513
2697
|
}
|
|
2514
2698
|
};
|
|
2515
2699
|
const onKeyDown = (e) => {
|
|
2516
|
-
|
|
2517
|
-
if (
|
|
2518
|
-
|
|
2519
|
-
this.
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2700
|
+
try {
|
|
2701
|
+
if (e.altKey && (e.key === "t" || e.key === "T")) {
|
|
2702
|
+
e.preventDefault();
|
|
2703
|
+
this.toggleToolbarCollapse();
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2706
|
+
if (e.key === "Escape") {
|
|
2707
|
+
if (this.cardOverlay && this.cardOverlay.parentElement && this.shadowRoot) {
|
|
2708
|
+
this.shadowRoot.removeChild(this.cardOverlay);
|
|
2709
|
+
this.cardOverlay = null;
|
|
2710
|
+
} else if (this.activeMode === "region" || this.activeMode === "element") {
|
|
2711
|
+
if (!this.activeScenario) {
|
|
2712
|
+
this.setMode("idle");
|
|
2713
|
+
}
|
|
2523
2714
|
}
|
|
2524
2715
|
}
|
|
2716
|
+
} catch {
|
|
2525
2717
|
}
|
|
2526
2718
|
};
|
|
2527
2719
|
const onKeyUp = (e) => {
|
|
2528
|
-
|
|
2529
|
-
this.
|
|
2720
|
+
try {
|
|
2721
|
+
if (e.key === "Alt" && !this.modalOverlay && !this.cardOverlay && this.activeMode !== "element") {
|
|
2722
|
+
this.hideHighlight();
|
|
2723
|
+
}
|
|
2724
|
+
} catch {
|
|
2530
2725
|
}
|
|
2531
2726
|
};
|
|
2532
2727
|
const onScrollOrResize = () => {
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2728
|
+
try {
|
|
2729
|
+
requestAnimationFrame(() => {
|
|
2730
|
+
try {
|
|
2731
|
+
this.updateMarkerPositions();
|
|
2732
|
+
} catch {
|
|
2733
|
+
}
|
|
2734
|
+
});
|
|
2735
|
+
} catch {
|
|
2736
|
+
}
|
|
2536
2737
|
};
|
|
2537
2738
|
window.addEventListener("mousemove", onMouseMove, { capture: true, passive: true });
|
|
2538
2739
|
window.addEventListener("click", onClick, { capture: true });
|
|
@@ -2609,6 +2810,11 @@ var NoteInspector = class {
|
|
|
2609
2810
|
this.regionBox.appendChild(badge);
|
|
2610
2811
|
}
|
|
2611
2812
|
badge.textContent = `Region: ${Math.round(width)} \xD7 ${Math.round(height)} px`;
|
|
2813
|
+
if (top < 32) {
|
|
2814
|
+
badge.style.top = "4px";
|
|
2815
|
+
} else {
|
|
2816
|
+
badge.style.top = "-26px";
|
|
2817
|
+
}
|
|
2612
2818
|
};
|
|
2613
2819
|
this.regionOverlay.onmouseup = (e) => {
|
|
2614
2820
|
if (!this.isDraggingRegion) return;
|
|
@@ -2667,6 +2873,18 @@ var NoteInspector = class {
|
|
|
2667
2873
|
const selector = getSemanticSelector(el);
|
|
2668
2874
|
const label = this.activeScenario ? `\u{1F3AC} Step ${this.activeScenario.stepNumber} \xB7 ${selector}` : selector;
|
|
2669
2875
|
badge.textContent = `${label} (${Math.round(rect.width)} \xD7 ${Math.round(rect.height)} px)`;
|
|
2876
|
+
if (rect.top < 32) {
|
|
2877
|
+
badge.style.top = `${rect.height + 4}px`;
|
|
2878
|
+
} else {
|
|
2879
|
+
badge.style.top = "-26px";
|
|
2880
|
+
}
|
|
2881
|
+
if (rect.left + 260 > window.innerWidth) {
|
|
2882
|
+
badge.style.left = "auto";
|
|
2883
|
+
badge.style.right = "0";
|
|
2884
|
+
} else {
|
|
2885
|
+
badge.style.left = "0";
|
|
2886
|
+
badge.style.right = "auto";
|
|
2887
|
+
}
|
|
2670
2888
|
}
|
|
2671
2889
|
hideHighlight() {
|
|
2672
2890
|
if (this.highlightOverlay) {
|
|
@@ -2809,10 +3027,11 @@ var NoteInspector = class {
|
|
|
2809
3027
|
btnNextStep.disabled = true;
|
|
2810
3028
|
if (btnSave) btnSave.disabled = true;
|
|
2811
3029
|
if (btnFinishFlow) btnFinishFlow.disabled = true;
|
|
3030
|
+
closeModal();
|
|
2812
3031
|
try {
|
|
2813
3032
|
await options.onSave(message, action);
|
|
2814
|
-
}
|
|
2815
|
-
|
|
3033
|
+
} catch {
|
|
3034
|
+
this.showToast("Failed to save note", "\u26A0\uFE0F");
|
|
2816
3035
|
}
|
|
2817
3036
|
};
|
|
2818
3037
|
if (btnSave) {
|
|
@@ -2839,8 +3058,12 @@ var NoteInspector = class {
|
|
|
2839
3058
|
const selector = noteType === "page" ? "body" : getSemanticSelector(targetEl);
|
|
2840
3059
|
let screenshotDataUrl;
|
|
2841
3060
|
try {
|
|
2842
|
-
const
|
|
2843
|
-
|
|
3061
|
+
const snapPromise = this.screenshotDriver.captureElement(targetEl);
|
|
3062
|
+
const timeoutPromise = new Promise(
|
|
3063
|
+
(resolve) => setTimeout(() => resolve({ ok: false }), 800)
|
|
3064
|
+
);
|
|
3065
|
+
const snap = await Promise.race([snapPromise, timeoutPromise]);
|
|
3066
|
+
if (snap && snap.ok && snap.dataUrl) {
|
|
2844
3067
|
screenshotDataUrl = snap.dataUrl;
|
|
2845
3068
|
}
|
|
2846
3069
|
} catch {
|
|
@@ -2895,9 +3118,16 @@ var NoteInspector = class {
|
|
|
2895
3118
|
async saveRegionVisualNote(region, message, scenario) {
|
|
2896
3119
|
let screenshotDataUrl;
|
|
2897
3120
|
try {
|
|
2898
|
-
const
|
|
2899
|
-
|
|
2900
|
-
|
|
3121
|
+
const centerX = region.x + region.width / 2;
|
|
3122
|
+
const centerY = region.y + region.height / 2;
|
|
3123
|
+
const targetElement = document.elementFromPoint(centerX, centerY) || document.body;
|
|
3124
|
+
const snapPromise = this.screenshotDriver.captureElement(targetElement);
|
|
3125
|
+
const timeoutPromise = new Promise(
|
|
3126
|
+
(resolve) => setTimeout(() => resolve({ ok: false }), 800)
|
|
3127
|
+
);
|
|
3128
|
+
const snap = await Promise.race([snapPromise, timeoutPromise]);
|
|
3129
|
+
if (snap && snap.ok && snap.dataUrl) {
|
|
3130
|
+
screenshotDataUrl = snap.dataUrl;
|
|
2901
3131
|
}
|
|
2902
3132
|
} catch {
|
|
2903
3133
|
}
|
|
@@ -2994,7 +3224,11 @@ var NoteInspector = class {
|
|
|
2994
3224
|
region.width,
|
|
2995
3225
|
region.height
|
|
2996
3226
|
);
|
|
2997
|
-
|
|
3227
|
+
try {
|
|
3228
|
+
resolve(canvas.toDataURL("image/png"));
|
|
3229
|
+
} catch {
|
|
3230
|
+
resolve(dataUrl);
|
|
3231
|
+
}
|
|
2998
3232
|
};
|
|
2999
3233
|
img.onerror = () => resolve(dataUrl);
|
|
3000
3234
|
img.src = dataUrl;
|
|
@@ -3284,4 +3518,4 @@ export {
|
|
|
3284
3518
|
init,
|
|
3285
3519
|
getClient
|
|
3286
3520
|
};
|
|
3287
|
-
//# sourceMappingURL=chunk-
|
|
3521
|
+
//# sourceMappingURL=chunk-PG4JJDCV.js.map
|