browsertrack 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/dist/{chunk-UP5JKCFY.js → chunk-464D4U2U.js} +3 -2
- package/dist/chunk-464D4U2U.js.map +1 -0
- package/dist/{chunk-WB7ZKWK7.js → chunk-INXDWPJW.js} +354 -9
- package/dist/chunk-INXDWPJW.js.map +1 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.cjs +357 -10
- package/dist/client/index.d.ts +36 -4
- package/dist/client/index.js +7 -3
- package/dist/client.iife.js +42 -19
- package/dist/{notes-BMnonq46.d.ts → commands-fjuqKzkm.d.ts} +125 -114
- package/dist/core/index.d.ts +2 -2
- package/dist/daemon/index.d.ts +3 -3
- package/dist/{engine-B43IohQY.d.ts → engine-CmchnMDq.d.ts} +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/mcp/index.d.ts +4 -4
- package/dist/mcp/index.js +1 -1
- package/dist/{projects-D5J-egVN.d.ts → projects-DB7S312i.d.ts} +1 -1
- package/dist/{server-BztYp1Zc.d.ts → server-DiVmTrIR.d.ts} +1 -1
- package/docs/component-resolver.md +108 -0
- package/docs/index.md +1 -0
- package/docs/mcp-reference.md +2 -2
- package/docs/visual-notes.md +36 -0
- package/package.json +1 -1
- package/packages/client/src/client.ts +18 -1
- package/packages/client/src/config.ts +84 -1
- package/packages/client/src/index.ts +4 -1
- package/packages/client/src/interceptors/interaction.ts +3 -0
- package/packages/client/src/notes/inspector.ts +107 -5
- package/packages/client/src/source/resolver.ts +272 -0
- package/packages/core/src/types/events.ts +3 -0
- package/packages/core/src/types/notes.ts +11 -0
- package/packages/mcp/src/handlers.ts +1 -0
- package/test/client/component-resolver.test.ts +141 -0
- package/test/client/interceptors.test.ts +56 -0
- package/dist/chunk-UP5JKCFY.js.map +0 -1
- package/dist/chunk-WB7ZKWK7.js.map +0 -1
package/dist/client/index.cjs
CHANGED
|
@@ -28,7 +28,9 @@ __export(src_exports, {
|
|
|
28
28
|
DEFAULT_OPTIONS: () => DEFAULT_OPTIONS,
|
|
29
29
|
NoteInspector: () => NoteInspector,
|
|
30
30
|
getClient: () => getClient,
|
|
31
|
-
init: () => init
|
|
31
|
+
init: () => init,
|
|
32
|
+
resolveComponentSource: () => resolveComponentSource,
|
|
33
|
+
shouldHideUIFromUrl: () => shouldHideUIFromUrl
|
|
32
34
|
});
|
|
33
35
|
module.exports = __toCommonJS(src_exports);
|
|
34
36
|
|
|
@@ -167,12 +169,220 @@ function truncate(str, maxLength = 200) {
|
|
|
167
169
|
return str.slice(0, maxLength) + "...";
|
|
168
170
|
}
|
|
169
171
|
|
|
172
|
+
// packages/client/src/source/resolver.ts
|
|
173
|
+
function resolveComponentSource(el) {
|
|
174
|
+
if (!el || typeof el !== "object") return void 0;
|
|
175
|
+
const reactInfo = resolveReactComponent(el);
|
|
176
|
+
if (reactInfo) return reactInfo;
|
|
177
|
+
const vueInfo = resolveVueComponent(el);
|
|
178
|
+
if (vueInfo) return vueInfo;
|
|
179
|
+
const svelteInfo = resolveSvelteComponent(el);
|
|
180
|
+
if (svelteInfo) return svelteInfo;
|
|
181
|
+
if (el.tagName && el.tagName.includes("-")) {
|
|
182
|
+
return {
|
|
183
|
+
framework: "web-component",
|
|
184
|
+
componentName: el.tagName.toLowerCase(),
|
|
185
|
+
hierarchy: [el.tagName.toLowerCase()]
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const attrInfo = resolveDataAttributeComponent(el);
|
|
189
|
+
if (attrInfo) return attrInfo;
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
function resolveReactComponent(el) {
|
|
193
|
+
try {
|
|
194
|
+
const fiberKey = Object.keys(el).find(
|
|
195
|
+
(key) => key.startsWith("__reactFiber$") || key.startsWith("__reactInternalInstance$")
|
|
196
|
+
);
|
|
197
|
+
if (!fiberKey) return void 0;
|
|
198
|
+
const hostFiber = el[fiberKey];
|
|
199
|
+
if (!hostFiber) return void 0;
|
|
200
|
+
let sourceFile;
|
|
201
|
+
let sourceLine;
|
|
202
|
+
let sourceColumn;
|
|
203
|
+
let componentName;
|
|
204
|
+
const hierarchy = [];
|
|
205
|
+
let props;
|
|
206
|
+
if (hostFiber._debugSource) {
|
|
207
|
+
sourceFile = hostFiber._debugSource.fileName;
|
|
208
|
+
sourceLine = hostFiber._debugSource.lineNumber;
|
|
209
|
+
sourceColumn = hostFiber._debugSource.columnNumber;
|
|
210
|
+
}
|
|
211
|
+
if (hostFiber._debugOwner) {
|
|
212
|
+
const ownerType = hostFiber._debugOwner.type;
|
|
213
|
+
componentName = getReactComponentName(ownerType);
|
|
214
|
+
if (!sourceFile && hostFiber._debugOwner._debugSource) {
|
|
215
|
+
sourceFile = hostFiber._debugOwner._debugSource.fileName;
|
|
216
|
+
sourceLine = hostFiber._debugOwner._debugSource.lineNumber;
|
|
217
|
+
sourceColumn = hostFiber._debugOwner._debugSource.columnNumber;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
let curr = hostFiber;
|
|
221
|
+
while (curr) {
|
|
222
|
+
const type = curr.type;
|
|
223
|
+
const name = getReactComponentName(type);
|
|
224
|
+
if (name && !name.startsWith("html:") && name !== "Fragment") {
|
|
225
|
+
if (!componentName) {
|
|
226
|
+
componentName = name;
|
|
227
|
+
}
|
|
228
|
+
if (!hierarchy.includes(name)) {
|
|
229
|
+
hierarchy.unshift(name);
|
|
230
|
+
}
|
|
231
|
+
if (!sourceFile && curr._debugSource) {
|
|
232
|
+
sourceFile = curr._debugSource.fileName;
|
|
233
|
+
sourceLine = curr._debugSource.lineNumber;
|
|
234
|
+
sourceColumn = curr._debugSource.columnNumber;
|
|
235
|
+
}
|
|
236
|
+
if (!props && curr.memoizedProps && typeof curr.memoizedProps === "object") {
|
|
237
|
+
props = sanitizeProps(curr.memoizedProps);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
curr = curr.return;
|
|
241
|
+
}
|
|
242
|
+
if (!componentName && !sourceFile) {
|
|
243
|
+
return void 0;
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
framework: "react",
|
|
247
|
+
componentName,
|
|
248
|
+
sourceFile: normalizeFilePath(sourceFile),
|
|
249
|
+
sourceLine,
|
|
250
|
+
sourceColumn,
|
|
251
|
+
hierarchy: hierarchy.length > 0 ? hierarchy : componentName ? [componentName] : void 0,
|
|
252
|
+
props
|
|
253
|
+
};
|
|
254
|
+
} catch {
|
|
255
|
+
return void 0;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function getReactComponentName(type) {
|
|
259
|
+
if (!type) return void 0;
|
|
260
|
+
if (typeof type === "string") return void 0;
|
|
261
|
+
if (type.displayName) return type.displayName;
|
|
262
|
+
if (type.name) return type.name;
|
|
263
|
+
if (type.render?.displayName) return type.render.displayName;
|
|
264
|
+
if (type.render?.name) return type.render.name;
|
|
265
|
+
return void 0;
|
|
266
|
+
}
|
|
267
|
+
function resolveVueComponent(el) {
|
|
268
|
+
try {
|
|
269
|
+
const vueParent = el.__vueParentComponent || el.__vnode?.ctx;
|
|
270
|
+
if (vueParent) {
|
|
271
|
+
const type = vueParent.type || {};
|
|
272
|
+
const componentName = type.name || type.__name || type.displayName || "AnonymousComponent";
|
|
273
|
+
const sourceFile = type.__file;
|
|
274
|
+
const hierarchy = [];
|
|
275
|
+
let curr = vueParent;
|
|
276
|
+
while (curr) {
|
|
277
|
+
const cType = curr.type || {};
|
|
278
|
+
const cName = cType.name || cType.__name || cType.displayName;
|
|
279
|
+
if (cName && !hierarchy.includes(cName)) {
|
|
280
|
+
hierarchy.unshift(cName);
|
|
281
|
+
}
|
|
282
|
+
curr = curr.parent;
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
framework: "vue",
|
|
286
|
+
componentName,
|
|
287
|
+
sourceFile: normalizeFilePath(sourceFile),
|
|
288
|
+
hierarchy: hierarchy.length > 0 ? hierarchy : [componentName],
|
|
289
|
+
props: vueParent.props ? sanitizeProps(vueParent.props) : void 0
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const vue2Instance = el.__vue__;
|
|
293
|
+
if (vue2Instance) {
|
|
294
|
+
const options = vue2Instance.$options || {};
|
|
295
|
+
const componentName = options.name || options._componentTag || "VueComponent";
|
|
296
|
+
const sourceFile = options.__file;
|
|
297
|
+
return {
|
|
298
|
+
framework: "vue",
|
|
299
|
+
componentName,
|
|
300
|
+
sourceFile: normalizeFilePath(sourceFile),
|
|
301
|
+
hierarchy: [componentName],
|
|
302
|
+
props: vue2Instance.$props ? sanitizeProps(vue2Instance.$props) : void 0
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return void 0;
|
|
306
|
+
} catch {
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function resolveSvelteComponent(el) {
|
|
311
|
+
try {
|
|
312
|
+
let curr = el;
|
|
313
|
+
while (curr) {
|
|
314
|
+
const meta = curr.__svelte_meta;
|
|
315
|
+
if (meta && meta.loc) {
|
|
316
|
+
return {
|
|
317
|
+
framework: "svelte",
|
|
318
|
+
sourceFile: normalizeFilePath(meta.loc.file),
|
|
319
|
+
sourceLine: meta.loc.line,
|
|
320
|
+
sourceColumn: meta.loc.column,
|
|
321
|
+
componentName: meta.loc.file ? getBaseNameWithoutExt(meta.loc.file) : void 0,
|
|
322
|
+
hierarchy: meta.loc.file ? [getBaseNameWithoutExt(meta.loc.file)] : void 0
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
curr = curr.parentElement;
|
|
326
|
+
}
|
|
327
|
+
return void 0;
|
|
328
|
+
} catch {
|
|
329
|
+
return void 0;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function resolveDataAttributeComponent(el) {
|
|
333
|
+
try {
|
|
334
|
+
const compEl = el.closest("[data-component], [data-component-name], [data-source-file]");
|
|
335
|
+
if (!compEl) return void 0;
|
|
336
|
+
const componentName = compEl.getAttribute("data-component") || compEl.getAttribute("data-component-name") || void 0;
|
|
337
|
+
const sourceFile = compEl.getAttribute("data-source-file") || void 0;
|
|
338
|
+
const lineAttr = compEl.getAttribute("data-source-line");
|
|
339
|
+
const sourceLine = lineAttr ? parseInt(lineAttr, 10) : void 0;
|
|
340
|
+
if (!componentName && !sourceFile) return void 0;
|
|
341
|
+
return {
|
|
342
|
+
framework: "vanilla",
|
|
343
|
+
componentName,
|
|
344
|
+
sourceFile: normalizeFilePath(sourceFile),
|
|
345
|
+
sourceLine: isNaN(sourceLine) ? void 0 : sourceLine,
|
|
346
|
+
hierarchy: componentName ? [componentName] : void 0
|
|
347
|
+
};
|
|
348
|
+
} catch {
|
|
349
|
+
return void 0;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
function normalizeFilePath(filePath) {
|
|
353
|
+
if (!filePath) return void 0;
|
|
354
|
+
const cleaned = filePath.split("?")[0];
|
|
355
|
+
return cleaned;
|
|
356
|
+
}
|
|
357
|
+
function getBaseNameWithoutExt(filePath) {
|
|
358
|
+
const parts = filePath.split("/");
|
|
359
|
+
const last = parts[parts.length - 1] || filePath;
|
|
360
|
+
return last.split(".")[0] || last;
|
|
361
|
+
}
|
|
362
|
+
function sanitizeProps(props) {
|
|
363
|
+
const result = {};
|
|
364
|
+
for (const [key, value] of Object.entries(props)) {
|
|
365
|
+
if (key.startsWith("__") || typeof value === "function") continue;
|
|
366
|
+
if (value === null || value === void 0) {
|
|
367
|
+
result[key] = value;
|
|
368
|
+
} else if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
369
|
+
result[key] = value;
|
|
370
|
+
} else if (Array.isArray(value)) {
|
|
371
|
+
result[key] = `Array(${value.length})`;
|
|
372
|
+
} else if (typeof value === "object") {
|
|
373
|
+
result[key] = "[Object]";
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return result;
|
|
377
|
+
}
|
|
378
|
+
|
|
170
379
|
// packages/client/src/interceptors/interaction.ts
|
|
171
380
|
function extractElementSummary(el) {
|
|
172
381
|
const selector = getSemanticSelector(el);
|
|
173
382
|
const tag = el.tagName.toLowerCase();
|
|
174
383
|
const id = el.id || void 0;
|
|
175
384
|
const classes = Array.from(el.classList || []);
|
|
385
|
+
const componentSource = resolveComponentSource(el);
|
|
176
386
|
let boundingRect = void 0;
|
|
177
387
|
let visible = true;
|
|
178
388
|
try {
|
|
@@ -204,7 +414,8 @@ function extractElementSummary(el) {
|
|
|
204
414
|
boundingRect,
|
|
205
415
|
visible,
|
|
206
416
|
innerText,
|
|
207
|
-
outerHTML
|
|
417
|
+
outerHTML,
|
|
418
|
+
componentSource
|
|
208
419
|
};
|
|
209
420
|
}
|
|
210
421
|
function setupInteractionInterceptors(onInteraction) {
|
|
@@ -522,10 +733,62 @@ var DEFAULT_OPTIONS = {
|
|
|
522
733
|
enabled: true,
|
|
523
734
|
shortcut: "Alt+Click",
|
|
524
735
|
showBadges: true,
|
|
736
|
+
showToolbar: true,
|
|
525
737
|
maskSelectors: ['input[type="password"]', "[data-sensitive]"]
|
|
526
738
|
},
|
|
739
|
+
hidden: false,
|
|
740
|
+
hideQueryParam: void 0,
|
|
527
741
|
debug: false
|
|
528
742
|
};
|
|
743
|
+
var HIDE_VALUES = /* @__PURE__ */ new Set(["0", "false", "hidden", "hide", "off", "none", "disabled", "ui_off", "silent"]);
|
|
744
|
+
function shouldHideUIFromUrl(customParam, searchString) {
|
|
745
|
+
let search = searchString;
|
|
746
|
+
if (search === void 0) {
|
|
747
|
+
if (typeof window === "undefined" || !window.location) return false;
|
|
748
|
+
search = window.location.search;
|
|
749
|
+
}
|
|
750
|
+
if (!search) return false;
|
|
751
|
+
try {
|
|
752
|
+
const params = new URLSearchParams(search);
|
|
753
|
+
if (customParam) {
|
|
754
|
+
const customKeys = Array.isArray(customParam) ? customParam : [customParam];
|
|
755
|
+
for (const key of customKeys) {
|
|
756
|
+
if (params.has(key)) {
|
|
757
|
+
const val = (params.get(key) || "").toLowerCase().trim();
|
|
758
|
+
if (val === "" || val === "1" || val === "true" || HIDE_VALUES.has(val)) {
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
for (const flag of ["no_bt", "no_browsertrack", "hide_bt", "hide_browsertrack"]) {
|
|
765
|
+
if (params.has(flag)) {
|
|
766
|
+
const val = (params.get(flag) || "").toLowerCase().trim();
|
|
767
|
+
if (val === "" || val === "1" || val === "true" || val === "yes") {
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
if (params.has("bt")) {
|
|
773
|
+
const val = (params.get("bt") || "").toLowerCase().trim();
|
|
774
|
+
if (HIDE_VALUES.has(val)) return true;
|
|
775
|
+
}
|
|
776
|
+
if (params.has("browsertrack")) {
|
|
777
|
+
const val = (params.get("browsertrack") || "").toLowerCase().trim();
|
|
778
|
+
if (HIDE_VALUES.has(val)) return true;
|
|
779
|
+
}
|
|
780
|
+
if (params.has("bt_ui")) {
|
|
781
|
+
const val = (params.get("bt_ui") || "").toLowerCase().trim();
|
|
782
|
+
if (HIDE_VALUES.has(val) || val === "0" || val === "false") return true;
|
|
783
|
+
}
|
|
784
|
+
if (params.has("bt_hide")) {
|
|
785
|
+
const val = (params.get("bt_hide") || "").toLowerCase().trim();
|
|
786
|
+
if (val === "" || val === "1" || val === "true") return true;
|
|
787
|
+
}
|
|
788
|
+
} catch {
|
|
789
|
+
}
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
529
792
|
|
|
530
793
|
// packages/client/src/interceptors/console.ts
|
|
531
794
|
function setupConsoleInterceptors(onConsole) {
|
|
@@ -1195,13 +1458,18 @@ var NoteInspector = class {
|
|
|
1195
1458
|
__publicField(this, "dragStartY", 0);
|
|
1196
1459
|
__publicField(this, "savedNotes", []);
|
|
1197
1460
|
__publicField(this, "showMarkers", true);
|
|
1461
|
+
__publicField(this, "isHidden", false);
|
|
1198
1462
|
__publicField(this, "cleanups", []);
|
|
1199
1463
|
this.transport = transport;
|
|
1200
1464
|
this.screenshotDriver = screenshotDriver;
|
|
1465
|
+
const hiddenByQuery = shouldHideUIFromUrl(options.hideQueryParam);
|
|
1466
|
+
this.isHidden = options.hidden === true || hiddenByQuery;
|
|
1467
|
+
this.showMarkers = options.showBadges !== false && !this.isHidden;
|
|
1201
1468
|
this.options = {
|
|
1202
1469
|
shortcut: "Alt+Click",
|
|
1203
1470
|
maskSelectors: ['input[type="password"]', "[data-sensitive]"],
|
|
1204
1471
|
showToolbar: true,
|
|
1472
|
+
showBadges: true,
|
|
1205
1473
|
...options
|
|
1206
1474
|
};
|
|
1207
1475
|
}
|
|
@@ -1279,12 +1547,48 @@ var NoteInspector = class {
|
|
|
1279
1547
|
this.hideHighlight();
|
|
1280
1548
|
}
|
|
1281
1549
|
}
|
|
1550
|
+
isVisible() {
|
|
1551
|
+
return !this.isHidden;
|
|
1552
|
+
}
|
|
1553
|
+
setVisible(visible) {
|
|
1554
|
+
this.isHidden = !visible;
|
|
1555
|
+
this.showMarkers = this.options.showBadges !== false && !this.isHidden;
|
|
1556
|
+
if (this.container) {
|
|
1557
|
+
this.container.style.display = this.isHidden ? "none" : "block";
|
|
1558
|
+
}
|
|
1559
|
+
if (this.toolbarElement) {
|
|
1560
|
+
this.toolbarElement.style.display = this.isHidden ? "none" : "flex";
|
|
1561
|
+
} else if (!this.isHidden && this.options.showToolbar !== false) {
|
|
1562
|
+
this.createToolbar();
|
|
1563
|
+
}
|
|
1564
|
+
if (this.isHidden) {
|
|
1565
|
+
this.hideHighlight();
|
|
1566
|
+
this.hideRegionOverlay();
|
|
1567
|
+
if (this.modalOverlay && this.shadowRoot) {
|
|
1568
|
+
this.shadowRoot.removeChild(this.modalOverlay);
|
|
1569
|
+
this.modalOverlay = null;
|
|
1570
|
+
}
|
|
1571
|
+
if (this.cardOverlay && this.shadowRoot) {
|
|
1572
|
+
this.shadowRoot.removeChild(this.cardOverlay);
|
|
1573
|
+
this.cardOverlay = null;
|
|
1574
|
+
}
|
|
1575
|
+
if (this.markersContainer) {
|
|
1576
|
+
this.markersContainer.innerHTML = "";
|
|
1577
|
+
}
|
|
1578
|
+
} else {
|
|
1579
|
+
this.renderMarkers();
|
|
1580
|
+
this.updateToolbarCount();
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1282
1583
|
ensureContainer() {
|
|
1283
1584
|
if (typeof document === "undefined") return null;
|
|
1284
1585
|
if (!this.container) {
|
|
1285
1586
|
this.container = document.createElement("div");
|
|
1286
1587
|
this.container.id = "browsertrack-inspector-host";
|
|
1287
1588
|
this.container.style.cssText = "all: initial; position: fixed; top: 0; left: 0; width: 0; height: 0; z-index: 2147483647; pointer-events: none;";
|
|
1589
|
+
if (this.isHidden) {
|
|
1590
|
+
this.container.style.display = "none";
|
|
1591
|
+
}
|
|
1288
1592
|
this.shadowRoot = this.container.attachShadow({ mode: "open" });
|
|
1289
1593
|
const style = document.createElement("style");
|
|
1290
1594
|
style.textContent = `
|
|
@@ -1727,6 +2031,22 @@ var NoteInspector = class {
|
|
|
1727
2031
|
color: #cbd5e1;
|
|
1728
2032
|
}
|
|
1729
2033
|
|
|
2034
|
+
.bt-component-pill {
|
|
2035
|
+
display: flex;
|
|
2036
|
+
align-items: center;
|
|
2037
|
+
gap: 6px;
|
|
2038
|
+
background: rgba(15, 23, 42, 0.95);
|
|
2039
|
+
border: 1px solid rgba(99, 102, 241, 0.35);
|
|
2040
|
+
border-radius: 6px;
|
|
2041
|
+
padding: 6px 10px;
|
|
2042
|
+
font-size: 11.5px;
|
|
2043
|
+
color: #c7d2fe;
|
|
2044
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
2045
|
+
overflow: hidden;
|
|
2046
|
+
text-overflow: ellipsis;
|
|
2047
|
+
white-space: nowrap;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
1730
2050
|
.bt-scenario-stepper {
|
|
1731
2051
|
display: flex;
|
|
1732
2052
|
align-items: center;
|
|
@@ -1966,7 +2286,7 @@ var NoteInspector = class {
|
|
|
1966
2286
|
this.toastContainer = document.createElement("div");
|
|
1967
2287
|
this.toastContainer.className = "bt-toast-container";
|
|
1968
2288
|
this.shadowRoot.appendChild(this.toastContainer);
|
|
1969
|
-
if (this.options.showToolbar) {
|
|
2289
|
+
if (this.options.showToolbar && !this.isHidden) {
|
|
1970
2290
|
this.createToolbar();
|
|
1971
2291
|
}
|
|
1972
2292
|
}
|
|
@@ -2067,7 +2387,7 @@ var NoteInspector = class {
|
|
|
2067
2387
|
const root = this.ensureContainer();
|
|
2068
2388
|
if (!root || !this.markersContainer) return;
|
|
2069
2389
|
this.markersContainer.innerHTML = "";
|
|
2070
|
-
if (!this.showMarkers) return;
|
|
2390
|
+
if (!this.showMarkers || this.isHidden) return;
|
|
2071
2391
|
const currentPath = window.location.pathname;
|
|
2072
2392
|
const activeNotes = this.savedNotes.filter(
|
|
2073
2393
|
(n) => n.status === "OPEN" && (n.route === currentPath || !n.route || n.route === "/" || window.location.href.includes(n.route))
|
|
@@ -2198,6 +2518,13 @@ var NoteInspector = class {
|
|
|
2198
2518
|
|
|
2199
2519
|
${scenarioStepsHtml}
|
|
2200
2520
|
|
|
2521
|
+
${note.elementContext?.componentSource?.componentName ? `<div class="bt-component-pill" title="${note.elementContext.componentSource.sourceFile ? note.elementContext.componentSource.sourceFile + (note.elementContext.componentSource.sourceLine ? ":" + note.elementContext.componentSource.sourceLine : "") : note.elementContext.componentSource.componentName}">
|
|
2522
|
+
<span>\u{1F9EC}</span>
|
|
2523
|
+
<span style="font-weight: 600; color: #a5b4fc;"><${note.elementContext.componentSource.componentName}></span>
|
|
2524
|
+
${note.elementContext.componentSource.sourceFile ? `<span style="color: #64748b; font-size: 11px; margin-left: 4px;">${note.elementContext.componentSource.sourceFile}${note.elementContext.componentSource.sourceLine ? ":" + note.elementContext.componentSource.sourceLine : ""}</span>` : ""}
|
|
2525
|
+
${note.elementContext.componentSource.framework ? `<span class="bt-mode-badge" style="background: rgba(99, 102, 241, 0.15); color: #818cf8; margin-left: auto; font-size: 10px;">${note.elementContext.componentSource.framework.toUpperCase()}</span>` : ""}
|
|
2526
|
+
</div>` : ""}
|
|
2527
|
+
|
|
2201
2528
|
<div class="bt-target-pill" title="${contextText}">
|
|
2202
2529
|
<span>\u{1F3F7}\uFE0F</span>
|
|
2203
2530
|
<span class="bt-pill-content">${contextText}</span>
|
|
@@ -2289,7 +2616,7 @@ var NoteInspector = class {
|
|
|
2289
2616
|
}
|
|
2290
2617
|
setupListeners() {
|
|
2291
2618
|
const onMouseMove = (e) => {
|
|
2292
|
-
if (this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
|
|
2619
|
+
if (this.isHidden || this.modalOverlay || this.cardOverlay || this.isDraggingRegion || this.activeMode === "region") return;
|
|
2293
2620
|
const path = e.composedPath ? e.composedPath() : [];
|
|
2294
2621
|
if (this.container && path.includes(this.container)) {
|
|
2295
2622
|
this.hideHighlight();
|
|
@@ -2308,7 +2635,7 @@ var NoteInspector = class {
|
|
|
2308
2635
|
}
|
|
2309
2636
|
};
|
|
2310
2637
|
const onClick = (e) => {
|
|
2311
|
-
if (this.modalOverlay || this.cardOverlay) return;
|
|
2638
|
+
if (this.isHidden || this.modalOverlay || this.cardOverlay) return;
|
|
2312
2639
|
const path = e.composedPath ? e.composedPath() : [];
|
|
2313
2640
|
if (this.container && path.includes(this.container)) {
|
|
2314
2641
|
return;
|
|
@@ -2496,10 +2823,12 @@ var NoteInspector = class {
|
|
|
2496
2823
|
if (noteType === "element") {
|
|
2497
2824
|
this.updateHighlight(targetEl);
|
|
2498
2825
|
}
|
|
2826
|
+
const comp = noteType === "element" ? resolveComponentSource(targetEl) : void 0;
|
|
2827
|
+
const compPrefix = comp?.componentName ? `\u{1F9EC} <${comp.componentName}>${comp.sourceFile ? " (" + comp.sourceFile + (comp.sourceLine ? ":" + comp.sourceLine : "") + ")" : ""} \xB7 ` : "";
|
|
2499
2828
|
this.renderNoteModal({
|
|
2500
2829
|
title: this.activeScenario ? `Step ${this.activeScenario.stepNumber}: ${this.activeScenario.title}` : "Add Visual Note",
|
|
2501
2830
|
modeBadge: this.activeScenario ? `STEP ${this.activeScenario.stepNumber}` : noteType.toUpperCase(),
|
|
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}`,
|
|
2831
|
+
pillText: noteType === "page" ? `Page Viewport: ${window.innerWidth} \xD7 ${window.innerHeight} px` : `${compPrefix}${getSemanticSelector(targetEl)} (${Math.round(targetEl.getBoundingClientRect().width)}\xD7${Math.round(targetEl.getBoundingClientRect().height)}) \xB7 Viewport: ${window.innerWidth}\xD7${window.innerHeight}`,
|
|
2503
2832
|
onSave: async (message, action) => {
|
|
2504
2833
|
let scenarioParam;
|
|
2505
2834
|
if (action === "next_step" && !this.activeScenario) {
|
|
@@ -2769,12 +3098,14 @@ var NoteInspector = class {
|
|
|
2769
3098
|
if (innerText && innerText.length > 200) {
|
|
2770
3099
|
innerText = truncate(innerText, 200);
|
|
2771
3100
|
}
|
|
3101
|
+
const componentSource = resolveComponentSource(el);
|
|
2772
3102
|
return {
|
|
2773
3103
|
selector,
|
|
2774
3104
|
tag: el.tagName.toLowerCase(),
|
|
2775
3105
|
attributes,
|
|
2776
3106
|
outerHTML,
|
|
2777
3107
|
innerText,
|
|
3108
|
+
componentSource,
|
|
2778
3109
|
parent: el.parentElement ? {
|
|
2779
3110
|
selector: getSemanticSelector(el.parentElement),
|
|
2780
3111
|
tag: el.parentElement.tagName.toLowerCase()
|
|
@@ -2857,7 +3188,11 @@ var BrowserTrackClient = class {
|
|
|
2857
3188
|
if (this.options.notes.enabled) {
|
|
2858
3189
|
this.inspector = new NoteInspector(this.transport, this.screenshotDriver, {
|
|
2859
3190
|
shortcut: this.options.notes.shortcut,
|
|
2860
|
-
maskSelectors: this.options.notes.maskSelectors
|
|
3191
|
+
maskSelectors: this.options.notes.maskSelectors,
|
|
3192
|
+
showToolbar: this.options.notes.showToolbar,
|
|
3193
|
+
showBadges: this.options.notes.showBadges,
|
|
3194
|
+
hidden: this.options.hidden,
|
|
3195
|
+
hideQueryParam: this.options.hideQueryParam
|
|
2861
3196
|
});
|
|
2862
3197
|
}
|
|
2863
3198
|
}
|
|
@@ -2923,6 +3258,14 @@ var BrowserTrackClient = class {
|
|
|
2923
3258
|
this.inspector.setMode("element");
|
|
2924
3259
|
}
|
|
2925
3260
|
}
|
|
3261
|
+
isUIVisible() {
|
|
3262
|
+
return this.inspector ? this.inspector.isVisible() : false;
|
|
3263
|
+
}
|
|
3264
|
+
setUIVisible(visible) {
|
|
3265
|
+
if (this.inspector) {
|
|
3266
|
+
this.inspector.setVisible(visible);
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
2926
3269
|
setInspectMode(mode) {
|
|
2927
3270
|
if (this.inspector) {
|
|
2928
3271
|
this.inspector.setMode(mode);
|
|
@@ -3063,8 +3406,10 @@ if (typeof window !== "undefined") {
|
|
|
3063
3406
|
const autoInit = currentScript?.getAttribute("data-auto-init") !== "false";
|
|
3064
3407
|
const daemonUrl = currentScript?.getAttribute("data-daemon-url") || void 0;
|
|
3065
3408
|
const projectId = currentScript?.getAttribute("data-project-id") || void 0;
|
|
3409
|
+
const hidden = currentScript?.getAttribute("data-hidden") === "true";
|
|
3410
|
+
const hideQueryParam = currentScript?.getAttribute("data-hide-query-param") || void 0;
|
|
3066
3411
|
if (autoInit) {
|
|
3067
|
-
init({ daemonUrl, projectId });
|
|
3412
|
+
init({ daemonUrl, projectId, hidden, hideQueryParam });
|
|
3068
3413
|
}
|
|
3069
3414
|
} catch {
|
|
3070
3415
|
}
|
|
@@ -3077,5 +3422,7 @@ if (typeof window !== "undefined") {
|
|
|
3077
3422
|
DEFAULT_OPTIONS,
|
|
3078
3423
|
NoteInspector,
|
|
3079
3424
|
getClient,
|
|
3080
|
-
init
|
|
3425
|
+
init,
|
|
3426
|
+
resolveComponentSource,
|
|
3427
|
+
shouldHideUIFromUrl
|
|
3081
3428
|
});
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as Breadcrumb,
|
|
1
|
+
import { B as Breadcrumb, j as ElementSummary, b as ClientCommand, d as CommandResponse, w as VisualNote, R as RegionContext, f as ComponentSourceInfo } from '../commands-fjuqKzkm.js';
|
|
2
2
|
|
|
3
3
|
interface BrowserDiagClientOptions {
|
|
4
4
|
daemonUrl?: string;
|
|
@@ -14,16 +14,32 @@ interface BrowserDiagClientOptions {
|
|
|
14
14
|
enabled?: boolean;
|
|
15
15
|
shortcut?: string;
|
|
16
16
|
showBadges?: boolean;
|
|
17
|
+
showToolbar?: boolean;
|
|
17
18
|
maskSelectors?: string[];
|
|
18
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Hide visible BrowserTrack UI (toolbar dock, pins, hover overlay) by default.
|
|
22
|
+
*/
|
|
23
|
+
hidden?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Custom query parameter(s) used to hide the UI (e.g. 'e2e', 'no_ui', 'clean').
|
|
26
|
+
* Built-in query params like ?bt=0, ?bt=false, ?bt=hidden, ?browsertrack=false, ?no_bt are supported automatically.
|
|
27
|
+
*/
|
|
28
|
+
hideQueryParam?: string | string[];
|
|
19
29
|
debug?: boolean;
|
|
20
30
|
}
|
|
21
|
-
declare const DEFAULT_OPTIONS: Required<Omit<BrowserDiagClientOptions, 'notes'>> & {
|
|
31
|
+
declare const DEFAULT_OPTIONS: Required<Omit<BrowserDiagClientOptions, 'notes' | 'hideQueryParam'>> & {
|
|
32
|
+
hideQueryParam?: string | string[];
|
|
22
33
|
notes: Required<NonNullable<BrowserDiagClientOptions['notes']>>;
|
|
23
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Checks if the tool's visible UI should be hidden based on URL search query parameters.
|
|
37
|
+
*/
|
|
38
|
+
declare function shouldHideUIFromUrl(customParam?: string | string[], searchString?: string): boolean;
|
|
24
39
|
|
|
25
40
|
declare class BrowserTrackClient {
|
|
26
|
-
options: Required<Omit<BrowserDiagClientOptions, 'notes'>> & {
|
|
41
|
+
options: Required<Omit<BrowserDiagClientOptions, 'notes' | 'hideQueryParam'>> & {
|
|
42
|
+
hideQueryParam?: string | string[];
|
|
27
43
|
notes: Required<NonNullable<BrowserDiagClientOptions['notes']>>;
|
|
28
44
|
};
|
|
29
45
|
private breadcrumbs;
|
|
@@ -40,6 +56,8 @@ declare class BrowserTrackClient {
|
|
|
40
56
|
openNoteEditor(el?: HTMLElement, noteType?: 'element' | 'page'): void;
|
|
41
57
|
startRegionSelection(): void;
|
|
42
58
|
startElementSelection(): void;
|
|
59
|
+
isUIVisible(): boolean;
|
|
60
|
+
setUIVisible(visible: boolean): void;
|
|
43
61
|
setInspectMode(mode: 'element' | 'region' | 'page' | 'idle'): void;
|
|
44
62
|
createNote(targetEl: HTMLElement, message: string, noteType?: 'element' | 'page'): Promise<void>;
|
|
45
63
|
private handleRuntimeError;
|
|
@@ -140,6 +158,9 @@ interface InspectorOptions {
|
|
|
140
158
|
shortcut?: string;
|
|
141
159
|
maskSelectors?: string[];
|
|
142
160
|
showToolbar?: boolean;
|
|
161
|
+
showBadges?: boolean;
|
|
162
|
+
hidden?: boolean;
|
|
163
|
+
hideQueryParam?: string | string[];
|
|
143
164
|
onNoteCreated?: (note: Partial<VisualNote>) => void;
|
|
144
165
|
}
|
|
145
166
|
interface ActiveScenarioState {
|
|
@@ -157,6 +178,7 @@ interface ActiveScenarioState {
|
|
|
157
178
|
* 5. Real-time Saved Note & Step Markers (pins with step sequencing numbers)
|
|
158
179
|
* 6. Interactive Note & Step Detail Card (with previous/next step walk-through navigation)
|
|
159
180
|
* 7. Floating quick dock / toolbar in bottom-right corner with Notes count and Flow controls
|
|
181
|
+
* 8. Automatic invisibility via query parameter (?bt=0, ?bt=false, ?bt=hidden, ?no_bt, custom query params)
|
|
160
182
|
*/
|
|
161
183
|
declare class NoteInspector {
|
|
162
184
|
private transport;
|
|
@@ -183,6 +205,7 @@ declare class NoteInspector {
|
|
|
183
205
|
private dragStartY;
|
|
184
206
|
private savedNotes;
|
|
185
207
|
private showMarkers;
|
|
208
|
+
private isHidden;
|
|
186
209
|
private cleanups;
|
|
187
210
|
constructor(transport: WebSocketTransport, screenshotDriver: ScreenshotDriver, options?: InspectorOptions);
|
|
188
211
|
init(): void;
|
|
@@ -191,6 +214,8 @@ declare class NoteInspector {
|
|
|
191
214
|
startScenario(title?: string): void;
|
|
192
215
|
finishScenario(): void;
|
|
193
216
|
setMode(mode: NoteInspectMode): void;
|
|
217
|
+
isVisible(): boolean;
|
|
218
|
+
setVisible(visible: boolean): void;
|
|
194
219
|
private ensureContainer;
|
|
195
220
|
private createToolbar;
|
|
196
221
|
private updateToolbarState;
|
|
@@ -221,10 +246,17 @@ declare class NoteInspector {
|
|
|
221
246
|
destroy(): void;
|
|
222
247
|
}
|
|
223
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Resolves framework component metadata, source file paths, line numbers,
|
|
251
|
+
* and component hierarchy directly from a live DOM element.
|
|
252
|
+
* Supports React (Fiber), Vue 2 & 3, Svelte, Custom Elements, and standard data attributes.
|
|
253
|
+
*/
|
|
254
|
+
declare function resolveComponentSource(el: HTMLElement): ComponentSourceInfo | undefined;
|
|
255
|
+
|
|
224
256
|
/**
|
|
225
257
|
* Initializes the BrowserTrack client.
|
|
226
258
|
*/
|
|
227
259
|
declare function init(options?: BrowserDiagClientOptions): BrowserTrackClient;
|
|
228
260
|
declare function getClient(): BrowserTrackClient | null;
|
|
229
261
|
|
|
230
|
-
export { type ActiveScenarioState, BreadcrumbBuffer, type BrowserDiagClientOptions, BrowserScriptScreenshotDriver, BrowserTrackClient, DEFAULT_OPTIONS, type InspectorOptions, type NoteInspectMode, NoteInspector, type ScreenshotDriver, type ScreenshotResult, getClient, init };
|
|
262
|
+
export { type ActiveScenarioState, BreadcrumbBuffer, type BrowserDiagClientOptions, BrowserScriptScreenshotDriver, BrowserTrackClient, DEFAULT_OPTIONS, type InspectorOptions, type NoteInspectMode, NoteInspector, type ScreenshotDriver, type ScreenshotResult, getClient, init, resolveComponentSource, shouldHideUIFromUrl };
|
package/dist/client/index.js
CHANGED
|
@@ -5,8 +5,10 @@ import {
|
|
|
5
5
|
DEFAULT_OPTIONS,
|
|
6
6
|
NoteInspector,
|
|
7
7
|
getClient,
|
|
8
|
-
init
|
|
9
|
-
|
|
8
|
+
init,
|
|
9
|
+
resolveComponentSource,
|
|
10
|
+
shouldHideUIFromUrl
|
|
11
|
+
} from "../chunk-INXDWPJW.js";
|
|
10
12
|
import "../chunk-6VA7GBAO.js";
|
|
11
13
|
export {
|
|
12
14
|
BreadcrumbBuffer,
|
|
@@ -15,6 +17,8 @@ export {
|
|
|
15
17
|
DEFAULT_OPTIONS,
|
|
16
18
|
NoteInspector,
|
|
17
19
|
getClient,
|
|
18
|
-
init
|
|
20
|
+
init,
|
|
21
|
+
resolveComponentSource,
|
|
22
|
+
shouldHideUIFromUrl
|
|
19
23
|
};
|
|
20
24
|
//# sourceMappingURL=index.js.map
|