skopix 2.0.64 → 2.0.66

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.
@@ -1843,6 +1843,19 @@ export async function dashboardCommand(options) {
1843
1843
  }
1844
1844
 
1845
1845
  // ─── STEP LIBRARY ──────────────────────────────────────────────────
1846
+ if (pathname.match(/^\/api\/step-library\/[^/]+\/usages$/) && method === 'GET') {
1847
+ const id = decodeURIComponent(pathname.split('/')[3]);
1848
+ const library = await listLibrarySteps(suitesDir);
1849
+ const step = library.find(s => s.id === id);
1850
+ if (!step) { sendJSON(res, 404, { error: 'Not found' }); return; }
1851
+ const sel = (step.stableSelector || step.selector || '').toLowerCase();
1852
+ const allTests = await listAllTests(suitesDir);
1853
+ const usages = allTests
1854
+ .filter(t => t.steps && t.steps.some(s => (s.stableSelector||s.selector||'').toLowerCase() === sel))
1855
+ .map(t => ({ id: t.id, name: t.name, scope: t.scope }));
1856
+ sendJSON(res, 200, usages);
1857
+ return;
1858
+ }
1846
1859
  if (pathname === '/api/step-library' && method === 'GET') {
1847
1860
  sendJSON(res, 200, await listLibrarySteps(suitesDir));
1848
1861
  return;
@@ -3998,6 +4011,17 @@ async function startStepTester(testerId, url, selector, mode, steps) {
3998
4011
  // Push page content left to avoid covering elements
3999
4012
  document.body.style.marginRight = '320px';
4000
4013
  document.body.style.boxSizing = 'border-box';
4014
+ // Also fix any fixed/sticky headers
4015
+ const fixedEls = Array.from(document.querySelectorAll('*')).filter(el => {
4016
+ const s = window.getComputedStyle(el);
4017
+ return (s.position === 'fixed' || s.position === 'sticky') && el.id !== '__skopix_preview';
4018
+ });
4019
+ fixedEls.forEach(el => {
4020
+ const s = window.getComputedStyle(el);
4021
+ const right = parseInt(s.right) || 0;
4022
+ el.style.right = (right + 320) + 'px';
4023
+ el.setAttribute('data-skopix-fixed', '1');
4024
+ });
4001
4025
  const tb = document.createElement('div');
4002
4026
  tb.id = '__skopix_preview';
4003
4027
  tb.style.cssText = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skopix",
3
- "version": "2.0.64",
3
+ "version": "2.0.66",
4
4
  "description": "Browser-based QA tool — record tests by using your app, replay them deterministically, generate Playwright code automatically",
5
5
  "main": "cli/index.js",
6
6
  "bin": {
@@ -1825,6 +1825,15 @@ body.viewer-mode .saved-test-row { cursor: default !important; }
1825
1825
  </div>
1826
1826
  </div>
1827
1827
 
1828
+ <!-- USAGES POPOVER -->
1829
+ <div id="lib-usages-popover" style="display:none;position:fixed;z-index:9999;background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:0;min-width:220px;max-width:320px;box-shadow:0 8px 32px rgba(0,0,0,0.4)">
1830
+ <div style="padding:10px 14px;border-bottom:1px solid var(--border);font-family:var(--mono);font-size:10px;color:var(--muted);letter-spacing:0.1em;display:flex;justify-content:space-between;align-items:center">
1831
+ <span>USED IN TESTS</span>
1832
+ <span style="cursor:pointer;color:var(--muted)" onclick="closeUsagesPopover()">✕</span>
1833
+ </div>
1834
+ <div id="lib-usages-list" style="max-height:240px;overflow-y:auto;padding:6px 0"></div>
1835
+ </div>
1836
+
1828
1837
  <div class="view" id="view-suites">
1829
1838
  <div class="topbar">
1830
1839
  <div>
@@ -5787,7 +5796,11 @@ function renderLibrarySteps(steps) {
5787
5796
  ${s.defaultAction||'—'}
5788
5797
  </span>
5789
5798
  </td>
5790
- <td style="padding:12px 16px;font-family:var(--mono);font-size:12px;color:var(--muted)">${s.usageCount||0}</td>
5799
+ <td style="padding:12px 16px;font-family:var(--mono);font-size:12px">
5800
+ ${(s.usageCount||0) > 0
5801
+ ? `<span class="lib-uses-btn" data-id="${escapeAttr(s.id)}" style="color:#22d3ee;cursor:pointer;text-decoration:underline;text-underline-offset:2px">${s.usageCount||0}</span>`
5802
+ : `<span style="color:var(--muted2)">0</span>`}
5803
+ </td>
5791
5804
  <td style="padding:12px 16px;text-align:right">
5792
5805
  <button class="btn btn-ghost lib-test-btn" style="padding:4px 10px;font-size:11px;margin-right:4px;color:#f59e0b;border-color:rgba(245,158,11,0.3)" data-id="${escapeAttr(s.id)}" data-selector="${escapeAttr(sel)}">Test</button>
5793
5806
  <button class="btn btn-ghost lib-edit-btn" style="padding:4px 10px;font-size:11px;margin-right:4px" data-id="${escapeAttr(s.id)}">Edit</button>
@@ -5907,7 +5920,51 @@ function populateLibraryTagFilter() {
5907
5920
  sel.onchange = prev;
5908
5921
  }
5909
5922
 
5910
- // ── STEP TESTER ──────────────────────────────────────────────────────────────
5923
+ // ── USAGES POPOVER ───────────────────────────────────────────────────────────
5924
+ async function showUsagesPopover(id, anchorEl) {
5925
+ const popover = document.getElementById('lib-usages-popover');
5926
+ const list = document.getElementById('lib-usages-list');
5927
+ list.innerHTML = '<div style="padding:12px 14px;font-family:var(--mono);font-size:11px;color:var(--muted)">Loading...</div>';
5928
+ popover.style.display = 'block';
5929
+
5930
+ // Position near the clicked element
5931
+ const rect = anchorEl.getBoundingClientRect();
5932
+ const top = rect.bottom + 8;
5933
+ const left = Math.min(rect.left, window.innerWidth - 340);
5934
+ popover.style.top = top + 'px';
5935
+ popover.style.left = left + 'px';
5936
+
5937
+ try {
5938
+ const res = await fetch(API_BASE + '/api/step-library/' + encodeURIComponent(id) + '/usages');
5939
+ const usages = await res.json();
5940
+ if (!usages.length) {
5941
+ list.innerHTML = '<div style="padding:12px 14px;font-family:var(--mono);font-size:11px;color:var(--muted)">No tests found using this selector</div>';
5942
+ return;
5943
+ }
5944
+ list.innerHTML = usages.map(u => `
5945
+ <div style="padding:8px 14px;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--border);cursor:pointer"
5946
+ onmouseover="this.style.background='var(--surface2)'" onmouseout="this.style.background=''"
5947
+ onclick="closeUsagesPopover();openTestEditorById('${escapeAttr(u.scope)}','${escapeAttr(u.id)}')">
5948
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="flex-shrink:0;color:var(--muted)"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
5949
+ <span style="font-size:12px;color:var(--text)">${escapeHtml(u.name)}</span>
5950
+ <span style="font-family:var(--mono);font-size:10px;color:var(--muted);margin-left:auto">${escapeHtml(u.scope)}</span>
5951
+ </div>`).join('');
5952
+ } catch {
5953
+ list.innerHTML = '<div style="padding:12px 14px;color:var(--red);font-size:12px">Failed to load</div>';
5954
+ }
5955
+ }
5956
+
5957
+ function closeUsagesPopover() {
5958
+ document.getElementById('lib-usages-popover').style.display = 'none';
5959
+ }
5960
+
5961
+ // Close popover when clicking outside
5962
+ document.addEventListener('click', (e) => {
5963
+ const popover = document.getElementById('lib-usages-popover');
5964
+ if (popover && !popover.contains(e.target) && !e.target.closest('.lib-uses-btn')) {
5965
+ closeUsagesPopover();
5966
+ }
5967
+ });
5911
5968
  let stepTesterState = { testerId: null };
5912
5969
 
5913
5970
  function openLibraryStepTester(id, selector) {
@@ -5969,9 +6026,11 @@ document.addEventListener('click', (e) => {
5969
6026
  const testBtn = e.target.closest('.lib-test-btn');
5970
6027
  const editBtn = e.target.closest('.lib-edit-btn');
5971
6028
  const deleteBtn = e.target.closest('.lib-delete-btn');
6029
+ const usesBtn = e.target.closest('.lib-uses-btn');
5972
6030
  if (testBtn) { openLibraryStepTester(testBtn.dataset.id, testBtn.dataset.selector); }
5973
6031
  if (editBtn) { editLibraryStep(editBtn.dataset.id); }
5974
6032
  if (deleteBtn) { deleteLibraryStepUI(deleteBtn.dataset.id, deleteBtn.dataset.name); }
6033
+ if (usesBtn) { showUsagesPopover(usesBtn.dataset.id, usesBtn); }
5975
6034
  });
5976
6035
 
5977
6036
  function openAddLibraryStep() {