browsertrack 0.2.2 → 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.
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  getSemanticSelector,
3
3
  redactUrl,
4
+ resolveMeaningfulTarget,
4
5
  safeJsonStringify,
5
6
  truncate
6
- } from "./chunk-QRZ57ME3.js";
7
+ } from "./chunk-ONPW7AYL.js";
7
8
 
8
9
  // packages/client/src/breadcrumbs.ts
9
10
  var BreadcrumbBuffer = class {
@@ -1343,6 +1344,7 @@ var NoteInspector = class {
1343
1344
  savedNotes = [];
1344
1345
  showMarkers = true;
1345
1346
  isHidden = false;
1347
+ isToolbarCollapsed = false;
1346
1348
  cleanups = [];
1347
1349
  constructor(transport, screenshotDriver, options = {}) {
1348
1350
  this.transport = transport;
@@ -1350,6 +1352,12 @@ var NoteInspector = class {
1350
1352
  const hiddenByQuery = shouldHideUIFromUrl(options.hideQueryParam);
1351
1353
  this.isHidden = options.hidden === true || hiddenByQuery;
1352
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
+ }
1353
1361
  this.options = {
1354
1362
  shortcut: "Alt+Click",
1355
1363
  maskSelectors: ['input[type="password"]', "[data-sensitive]"],
@@ -1477,11 +1485,28 @@ var NoteInspector = class {
1477
1485
  this.shadowRoot = this.container.attachShadow({ mode: "open" });
1478
1486
  const style = document.createElement("style");
1479
1487
  style.textContent = `
1480
- :host {
1481
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
1482
- color-scheme: dark;
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;
1483
1492
  -webkit-font-smoothing: antialiased;
1484
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;
1485
1510
  }
1486
1511
 
1487
1512
  /* 1. Element Highlight Overlay */
@@ -1715,7 +1740,7 @@ var NoteInspector = class {
1715
1740
  pointer-events: auto;
1716
1741
  z-index: 2147483646;
1717
1742
  user-select: none;
1718
- transition: all 0.2s ease;
1743
+ transition: all 0.22s cubic-bezier(0.16, 1, 0.3, 1);
1719
1744
  }
1720
1745
 
1721
1746
  .bt-toolbar-btn {
@@ -1776,6 +1801,86 @@ var NoteInspector = class {
1776
1801
  margin: 0 2px;
1777
1802
  }
1778
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
+
1779
1884
  /* 5. Modals & Popover Card */
1780
1885
  .bt-modal-backdrop {
1781
1886
  position: fixed;
@@ -2186,8 +2291,13 @@ var NoteInspector = class {
2186
2291
  createToolbar() {
2187
2292
  if (!this.shadowRoot || this.toolbarElement) return;
2188
2293
  this.toolbarElement = document.createElement("div");
2189
- this.toolbarElement.className = "bt-toolbar";
2294
+ this.toolbarElement.className = `bt-toolbar ${this.isToolbarCollapsed ? "bt-toolbar-collapsed" : ""}`;
2190
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>
2191
2301
  <button class="bt-toolbar-btn" id="bt-mode-element" title="Inspect element (or hold Alt+Click)">
2192
2302
  <span>\u{1F3AF}</span> Element
2193
2303
  </button>
@@ -2205,12 +2315,26 @@ var NoteInspector = class {
2205
2315
  <button class="bt-toolbar-btn active" id="bt-toggle-notes" title="Toggle visible note markers on screen">
2206
2316
  <span>\u{1F4CC}</span> Notes <span class="bt-count-pill" id="bt-notes-count">0</span>
2207
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>
2208
2322
  `;
2323
+ const btnExpand = this.toolbarElement.querySelector("#bt-toolbar-expand");
2324
+ const btnCollapse = this.toolbarElement.querySelector("#bt-toolbar-collapse");
2209
2325
  const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
2210
2326
  const btnRegion = this.toolbarElement.querySelector("#bt-mode-region");
2211
2327
  const btnPage = this.toolbarElement.querySelector("#bt-mode-page");
2212
2328
  const btnFlow = this.toolbarElement.querySelector("#bt-mode-flow");
2213
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
+ };
2214
2338
  btnElement.onclick = (e) => {
2215
2339
  e.stopPropagation();
2216
2340
  this.setMode(this.activeMode === "element" ? "idle" : "element");
@@ -2239,6 +2363,17 @@ var NoteInspector = class {
2239
2363
  };
2240
2364
  this.shadowRoot.appendChild(this.toolbarElement);
2241
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
+ }
2242
2377
  updateToolbarState() {
2243
2378
  if (!this.toolbarElement) return;
2244
2379
  const btnElement = this.toolbarElement.querySelector("#bt-mode-element");
@@ -2262,11 +2397,15 @@ var NoteInspector = class {
2262
2397
  }
2263
2398
  updateToolbarCount() {
2264
2399
  if (!this.toolbarElement) return;
2400
+ const openCount = this.savedNotes.filter((n) => n.status === "OPEN").length;
2265
2401
  const countEl = this.toolbarElement.querySelector("#bt-notes-count");
2266
2402
  if (countEl) {
2267
- const openCount = this.savedNotes.filter((n) => n.status === "OPEN").length;
2268
2403
  countEl.textContent = String(openCount);
2269
2404
  }
2405
+ const countCollapsedEl = this.toolbarElement.querySelector("#bt-notes-count-collapsed");
2406
+ if (countCollapsedEl) {
2407
+ countCollapsedEl.textContent = String(openCount);
2408
+ }
2270
2409
  }
2271
2410
  renderMarkers() {
2272
2411
  const root = this.ensureContainer();
@@ -2322,6 +2461,10 @@ var NoteInspector = class {
2322
2461
  }
2323
2462
  const rect = targetEl ? targetEl.getBoundingClientRect() : note.target?.boundingRect;
2324
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
+ }
2325
2468
  const pin = document.createElement("div");
2326
2469
  pin.className = markerClass;
2327
2470
  pin.setAttribute("data-note-id", note.id);
@@ -2515,7 +2658,8 @@ var NoteInspector = class {
2515
2658
  return;
2516
2659
  }
2517
2660
  if (e.altKey || this.activeMode === "element") {
2518
- const target = document.elementFromPoint(e.clientX, e.clientY);
2661
+ const rawTarget = document.elementFromPoint(e.clientX, e.clientY);
2662
+ const target = rawTarget ? resolveMeaningfulTarget(rawTarget) : null;
2519
2663
  if (target && target !== this.container && !this.container?.contains(target)) {
2520
2664
  this.hoveredElement = target;
2521
2665
  this.updateHighlight(target);
@@ -2539,7 +2683,8 @@ var NoteInspector = class {
2539
2683
  if (e.altKey || this.activeMode === "element") {
2540
2684
  e.preventDefault();
2541
2685
  e.stopPropagation();
2542
- const target = this.hoveredElement || document.elementFromPoint(e.clientX, e.clientY);
2686
+ const rawTarget = this.hoveredElement || document.elementFromPoint(e.clientX, e.clientY);
2687
+ const target = rawTarget ? resolveMeaningfulTarget(rawTarget) : null;
2543
2688
  if (target && target !== this.container && !this.container?.contains(target)) {
2544
2689
  this.selectedElement = target;
2545
2690
  this.openNoteEditor(target, "element");
@@ -2553,6 +2698,11 @@ var NoteInspector = class {
2553
2698
  };
2554
2699
  const onKeyDown = (e) => {
2555
2700
  try {
2701
+ if (e.altKey && (e.key === "t" || e.key === "T")) {
2702
+ e.preventDefault();
2703
+ this.toggleToolbarCollapse();
2704
+ return;
2705
+ }
2556
2706
  if (e.key === "Escape") {
2557
2707
  if (this.cardOverlay && this.cardOverlay.parentElement && this.shadowRoot) {
2558
2708
  this.shadowRoot.removeChild(this.cardOverlay);
@@ -2660,6 +2810,11 @@ var NoteInspector = class {
2660
2810
  this.regionBox.appendChild(badge);
2661
2811
  }
2662
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
+ }
2663
2818
  };
2664
2819
  this.regionOverlay.onmouseup = (e) => {
2665
2820
  if (!this.isDraggingRegion) return;
@@ -2718,6 +2873,18 @@ var NoteInspector = class {
2718
2873
  const selector = getSemanticSelector(el);
2719
2874
  const label = this.activeScenario ? `\u{1F3AC} Step ${this.activeScenario.stepNumber} \xB7 ${selector}` : selector;
2720
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
+ }
2721
2888
  }
2722
2889
  hideHighlight() {
2723
2890
  if (this.highlightOverlay) {
@@ -2860,10 +3027,11 @@ var NoteInspector = class {
2860
3027
  btnNextStep.disabled = true;
2861
3028
  if (btnSave) btnSave.disabled = true;
2862
3029
  if (btnFinishFlow) btnFinishFlow.disabled = true;
3030
+ closeModal();
2863
3031
  try {
2864
3032
  await options.onSave(message, action);
2865
- } finally {
2866
- closeModal();
3033
+ } catch {
3034
+ this.showToast("Failed to save note", "\u26A0\uFE0F");
2867
3035
  }
2868
3036
  };
2869
3037
  if (btnSave) {
@@ -2890,8 +3058,12 @@ var NoteInspector = class {
2890
3058
  const selector = noteType === "page" ? "body" : getSemanticSelector(targetEl);
2891
3059
  let screenshotDataUrl;
2892
3060
  try {
2893
- const snap = await this.screenshotDriver.captureElement(targetEl);
2894
- if (snap.ok) {
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) {
2895
3067
  screenshotDataUrl = snap.dataUrl;
2896
3068
  }
2897
3069
  } catch {
@@ -2946,9 +3118,16 @@ var NoteInspector = class {
2946
3118
  async saveRegionVisualNote(region, message, scenario) {
2947
3119
  let screenshotDataUrl;
2948
3120
  try {
2949
- const snap = await this.screenshotDriver.captureElement(document.body || document.documentElement);
2950
- if (snap.ok && snap.dataUrl) {
2951
- screenshotDataUrl = await this.cropDataUrl(snap.dataUrl, region);
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;
2952
3131
  }
2953
3132
  } catch {
2954
3133
  }
@@ -3339,4 +3518,4 @@ export {
3339
3518
  init,
3340
3519
  getClient
3341
3520
  };
3342
- //# sourceMappingURL=chunk-4HRLW6YF.js.map
3521
+ //# sourceMappingURL=chunk-PG4JJDCV.js.map