uidex 0.6.0 → 0.7.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.
Files changed (39) hide show
  1. package/README.md +3 -3
  2. package/dist/cli/cli.cjs +1510 -1244
  3. package/dist/cli/cli.cjs.map +1 -1
  4. package/dist/cloud/index.cjs +385 -175
  5. package/dist/cloud/index.cjs.map +1 -1
  6. package/dist/cloud/index.d.cts +192 -4
  7. package/dist/cloud/index.d.ts +192 -4
  8. package/dist/cloud/index.js +377 -177
  9. package/dist/cloud/index.js.map +1 -1
  10. package/dist/headless/index.cjs +82 -255
  11. package/dist/headless/index.cjs.map +1 -1
  12. package/dist/headless/index.d.cts +5 -11
  13. package/dist/headless/index.d.ts +5 -11
  14. package/dist/headless/index.js +82 -257
  15. package/dist/headless/index.js.map +1 -1
  16. package/dist/index.cjs +721 -1053
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +149 -160
  19. package/dist/index.d.ts +149 -160
  20. package/dist/index.js +741 -1068
  21. package/dist/index.js.map +1 -1
  22. package/dist/react/index.cjs +729 -1000
  23. package/dist/react/index.cjs.map +1 -1
  24. package/dist/react/index.d.cts +99 -86
  25. package/dist/react/index.d.ts +99 -86
  26. package/dist/react/index.js +745 -1015
  27. package/dist/react/index.js.map +1 -1
  28. package/dist/scan/index.cjs +1518 -1237
  29. package/dist/scan/index.cjs.map +1 -1
  30. package/dist/scan/index.d.cts +209 -12
  31. package/dist/scan/index.d.ts +209 -12
  32. package/dist/scan/index.js +1515 -1236
  33. package/dist/scan/index.js.map +1 -1
  34. package/package.json +22 -21
  35. package/templates/claude/SKILL.md +71 -0
  36. package/templates/claude/references/audit.md +43 -0
  37. package/templates/claude/{rules.md → references/conventions.md} +25 -28
  38. package/templates/claude/audit.md +0 -43
  39. /package/templates/claude/{api.md → references/api.md} +0 -0
package/dist/index.cjs CHANGED
@@ -32,7 +32,6 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  ENTITY_KINDS: () => ENTITY_KINDS,
34
34
  KIND_STYLE: () => KIND_STYLE,
35
- SURFACE_CONTAINER_CLASS: () => SURFACE_CONTAINER_CLASS,
36
35
  SURFACE_HOST_CLASS: () => SURFACE_HOST_CLASS,
37
36
  SURFACE_IGNORE_SELECTOR: () => SURFACE_IGNORE_SELECTOR,
38
37
  UnknownEntityKindError: () => UnknownEntityKindError,
@@ -47,7 +46,6 @@ __export(src_exports, {
47
46
  createCommandPaletteView: () => createCommandPaletteView,
48
47
  createConsoleCapture: () => createConsoleCapture,
49
48
  createCursorTooltip: () => createCursorTooltip,
50
- createHighlightController: () => createHighlightController,
51
49
  createIngest: () => createIngest,
52
50
  createInspector: () => createInspector,
53
51
  createMenuBar: () => createMenuBar,
@@ -64,13 +62,11 @@ __export(src_exports, {
64
62
  createThemeDetector: () => createThemeDetector,
65
63
  createUidex: () => createUidex,
66
64
  createViewStack: () => createViewStack,
67
- defaultResolveMatch: () => defaultResolveMatch,
68
65
  displayName: () => displayName,
69
66
  entityKey: () => entityKey,
70
67
  featureDetailView: () => featureDetailView,
71
68
  flowDetailView: () => flowDetailView,
72
69
  formatShortcutLabel: () => formatShortcutLabel,
73
- getNativeFetch: () => getNativeFetch,
74
70
  isMetaKind: () => isMetaKind,
75
71
  nativeFetch: () => nativeFetch,
76
72
  pageDetailView: () => pageDetailView,
@@ -175,13 +171,14 @@ function createRegistry() {
175
171
  };
176
172
  const getPatternsForKind = (kind) => {
177
173
  const cached = patternCache.get(kind);
178
- if (cached !== void 0)
179
- return cached;
174
+ if (cached !== void 0) return cached;
180
175
  const patterns = [];
181
176
  for (const [key, entity] of store[kind]) {
182
- if (key.endsWith("*")) {
177
+ if (key.includes("*")) {
178
+ const segments = key.split("*");
183
179
  patterns.push({
184
- prefix: key.slice(0, -1),
180
+ segments,
181
+ staticLength: segments.reduce((n, s) => n + s.length, 0),
185
182
  entity
186
183
  });
187
184
  }
@@ -192,13 +189,25 @@ function createRegistry() {
192
189
  );
193
190
  return patterns;
194
191
  };
192
+ const matchesSegments = (segments, id) => {
193
+ const first = segments[0];
194
+ const last = segments[segments.length - 1];
195
+ if (!id.startsWith(first)) return false;
196
+ let pos = first.length;
197
+ for (let i = 1; i < segments.length - 1; i++) {
198
+ const idx = id.indexOf(segments[i], pos);
199
+ if (idx === -1) return false;
200
+ pos = idx + segments[i].length;
201
+ }
202
+ return id.endsWith(last) && id.length - last.length >= pos;
203
+ };
195
204
  const matchPattern = (kind, id) => {
196
205
  assertEntityKind(kind);
197
206
  const patterns = getPatternsForKind(kind);
198
207
  if (patterns.length === 0) return void 0;
199
208
  let best;
200
209
  for (const entry of patterns) {
201
- if (id.startsWith(entry.prefix) && (best === void 0 || entry.prefix.length > best.prefix.length)) {
210
+ if (matchesSegments(entry.segments, id) && (best === void 0 || entry.staticLength > best.staticLength)) {
202
211
  best = entry;
203
212
  }
204
213
  }
@@ -418,9 +427,6 @@ function createConsoleCapture(options = {}) {
418
427
 
419
428
  // src/browser/ingest/native-fetch.ts
420
429
  var nativeFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
421
- function getNativeFetch() {
422
- return nativeFetch;
423
- }
424
430
 
425
431
  // src/browser/ingest/network.ts
426
432
  var DEFAULT_LIMIT2 = 20;
@@ -503,7 +509,7 @@ function createNetworkCapture(options = {}) {
503
509
 
504
510
  // src/browser/ingest/index.ts
505
511
  function createIngest(options = {}) {
506
- const { session, ...opts } = options;
512
+ const opts = options;
507
513
  const wantConsole = opts.captureConsole !== false;
508
514
  const wantNetwork = opts.captureNetwork !== false;
509
515
  const consoleCapture = wantConsole ? createConsoleCapture({
@@ -522,14 +528,12 @@ function createIngest(options = {}) {
522
528
  consoleCapture?.start();
523
529
  networkCapture?.start();
524
530
  active = Boolean(consoleCapture?.isActive || networkCapture?.isActive);
525
- if (active) session?.setIngest(true);
526
531
  }
527
532
  function stop() {
528
533
  if (!active) return;
529
534
  consoleCapture?.stop();
530
535
  networkCapture?.stop();
531
536
  active = false;
532
- session?.setIngest(false);
533
537
  }
534
538
  return {
535
539
  start,
@@ -651,8 +655,7 @@ var COMMAND_PALETTE_ENTRY = {
651
655
  function createModeStore(options) {
652
656
  const { nav, bindings } = options;
653
657
  const store = (0, import_vanilla.createStore)(() => ({
654
- mode: "idle",
655
- inspectorActive: false
658
+ mode: "idle"
656
659
  }));
657
660
  const transition = {
658
661
  openPalette() {
@@ -661,17 +664,17 @@ function createModeStore(options) {
661
664
  bindings?.destroyInspector?.();
662
665
  }
663
666
  nav.nav.reset([COMMAND_PALETTE_ENTRY]);
664
- store.setState({ mode: "palette", inspectorActive: false });
667
+ store.setState({ mode: "palette" });
665
668
  },
666
669
  openInspector() {
667
670
  bindings?.mountInspector?.();
668
671
  nav.nav.clear();
669
- store.setState({ mode: "inspecting", inspectorActive: true });
672
+ store.setState({ mode: "inspecting" });
670
673
  },
671
674
  closeInspector() {
672
675
  bindings?.destroyInspector?.();
673
676
  nav.nav.clear();
674
- store.setState({ mode: "idle", inspectorActive: false });
677
+ store.setState({ mode: "idle" });
675
678
  },
676
679
  toggleInspector() {
677
680
  if (store.getState().mode === "inspecting") {
@@ -686,7 +689,7 @@ function createModeStore(options) {
686
689
  bindings?.destroyInspector?.();
687
690
  }
688
691
  nav.nav.reset(initialStack);
689
- store.setState({ mode: "viewing", inspectorActive: false });
692
+ store.setState({ mode: "viewing" });
690
693
  },
691
694
  dismiss() {
692
695
  const prev = store.getState();
@@ -694,7 +697,7 @@ function createModeStore(options) {
694
697
  bindings?.destroyInspector?.();
695
698
  }
696
699
  nav.nav.clear();
697
- store.setState({ mode: "idle", inspectorActive: false });
700
+ store.setState({ mode: "idle" });
698
701
  },
699
702
  popOrTransition() {
700
703
  const { stack } = nav.getState();
@@ -702,12 +705,12 @@ function createModeStore(options) {
702
705
  nav.nav.pop();
703
706
  } else if (stack.length === 2 && stack[0]?.id === "command-palette") {
704
707
  nav.nav.reset([COMMAND_PALETTE_ENTRY]);
705
- store.setState({ mode: "palette", inspectorActive: false });
708
+ store.setState({ mode: "palette" });
706
709
  } else if (stack.length === 2) {
707
710
  nav.nav.pop();
708
711
  } else {
709
712
  nav.nav.clear();
710
- store.setState({ mode: "idle", inspectorActive: false });
713
+ store.setState({ mode: "idle" });
711
714
  }
712
715
  },
713
716
  pushView(entry) {
@@ -724,7 +727,7 @@ function createModeStore(options) {
724
727
  case "inspecting":
725
728
  bindings?.destroyInspector?.();
726
729
  nav.nav.reset([entry]);
727
- store.setState({ mode: "viewing", inspectorActive: false });
730
+ store.setState({ mode: "viewing" });
728
731
  break;
729
732
  case "palette":
730
733
  case "viewing":
@@ -759,14 +762,6 @@ function createNavigationStore() {
759
762
  store.setState({ stack: s.slice(0, -1) });
760
763
  }
761
764
  },
762
- replace(entry) {
763
- const s = store.getState().stack;
764
- if (s.length === 0) {
765
- store.setState({ stack: [entry] });
766
- } else {
767
- store.setState({ stack: [...s.slice(0, -1), entry] });
768
- }
769
- },
770
765
  clear() {
771
766
  store.setState({ stack: [] });
772
767
  },
@@ -781,14 +776,11 @@ function createNavigationStore() {
781
776
 
782
777
  // src/browser/session/store.ts
783
778
  var defaultSnapshot = {
784
- hover: null,
785
- selection: null,
786
779
  stack: [],
787
780
  pinnedHighlight: null,
788
- inspectorActive: false,
781
+ mode: "idle",
789
782
  theme: "auto",
790
783
  resolvedTheme: "light",
791
- ingestActive: false,
792
784
  user: null
793
785
  };
794
786
  function resolveTheme(preference, detect) {
@@ -839,7 +831,6 @@ function createSession(options = {}) {
839
831
  } else if (highlightMode === "transient") {
840
832
  onUpdateOverlay?.(hlCtx);
841
833
  }
842
- store.setState({ hover: ref2 });
843
834
  },
844
835
  unhover() {
845
836
  if (highlightMode === "transient") {
@@ -849,7 +840,6 @@ function createSession(options = {}) {
849
840
  hlCtx.color = null;
850
841
  onHideOverlay?.();
851
842
  }
852
- store.setState({ hover: null });
853
843
  },
854
844
  pin(ref2) {
855
845
  const pinRef = ref2 ?? hlCtx.ref;
@@ -877,14 +867,11 @@ function createSession(options = {}) {
877
867
  };
878
868
  const store = (0, import_vanilla3.createStore)(() => ({
879
869
  ...defaultSnapshot,
880
- hover: overrides.hover ?? null,
881
- selection: overrides.selection ?? null,
882
870
  stack: [],
883
871
  pinnedHighlight: null,
884
- inspectorActive: false,
872
+ mode: "idle",
885
873
  theme: initialPref,
886
874
  resolvedTheme: initialResolved,
887
- ingestActive: overrides.ingestActive ?? false,
888
875
  user: overrides.user ?? null
889
876
  }));
890
877
  nav.subscribe(() => {
@@ -894,29 +881,21 @@ function createSession(options = {}) {
894
881
  }
895
882
  });
896
883
  modeStore.subscribe(() => {
897
- const { inspectorActive } = modeStore.getState();
898
- if (store.getState().inspectorActive !== inspectorActive) {
899
- store.setState({ inspectorActive });
884
+ const { mode } = modeStore.getState();
885
+ if (store.getState().mode !== mode) {
886
+ store.setState({ mode });
900
887
  }
901
888
  });
902
889
  const session = store;
903
890
  session.nav = nav;
904
891
  session.mode = modeStore;
905
892
  session.highlight = highlightActions;
906
- session.select = (ref2) => {
907
- if (sameRef(store.getState().selection, ref2)) return;
908
- store.setState({ selection: ref2 });
909
- };
910
893
  session.setTheme = (theme, resolved) => {
911
894
  const state = store.getState();
912
895
  const nextResolved = resolved ?? resolveTheme(theme, detectTheme);
913
896
  if (state.theme === theme && state.resolvedTheme === nextResolved) return;
914
897
  store.setState({ theme, resolvedTheme: nextResolved });
915
898
  };
916
- session.setIngest = (active) => {
917
- if (store.getState().ingestActive === active) return;
918
- store.setState({ ingestActive: active });
919
- };
920
899
  if (initialStack.length > 0) {
921
900
  modeStore.transition.openPalette();
922
901
  for (let i = 1; i < initialStack.length; i++) {
@@ -1229,9 +1208,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1229
1208
  .right-0 {
1230
1209
  right: calc(var(--spacing) * 0);
1231
1210
  }
1232
- .right-2 {
1233
- right: calc(var(--spacing) * 2);
1234
- }
1235
1211
  .bottom-full {
1236
1212
  bottom: 100%;
1237
1213
  }
@@ -1274,9 +1250,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1274
1250
  .mx-2 {
1275
1251
  margin-inline: calc(var(--spacing) * 2);
1276
1252
  }
1277
- .my-1 {
1278
- margin-block: calc(var(--spacing) * 1);
1279
- }
1280
1253
  .ms-auto {
1281
1254
  margin-inline-start: auto;
1282
1255
  }
@@ -1313,9 +1286,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1313
1286
  .inline-flex {
1314
1287
  display: inline-flex;
1315
1288
  }
1316
- .list-item {
1317
- display: list-item;
1318
- }
1319
1289
  .table {
1320
1290
  display: table;
1321
1291
  }
@@ -1323,10 +1293,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1323
1293
  width: calc(var(--spacing) * 2);
1324
1294
  height: calc(var(--spacing) * 2);
1325
1295
  }
1326
- .size-3 {
1327
- width: calc(var(--spacing) * 3);
1328
- height: calc(var(--spacing) * 3);
1329
- }
1330
1296
  .size-3\\.5 {
1331
1297
  width: calc(var(--spacing) * 3.5);
1332
1298
  height: calc(var(--spacing) * 3.5);
@@ -1384,15 +1350,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1384
1350
  .h-\\[26rem\\] {
1385
1351
  height: 26rem;
1386
1352
  }
1387
- .h-auto {
1388
- height: auto;
1389
- }
1390
1353
  .h-full {
1391
1354
  height: 100%;
1392
1355
  }
1393
- .h-px {
1394
- height: 1px;
1395
- }
1396
1356
  .max-h-32 {
1397
1357
  max-height: calc(var(--spacing) * 32);
1398
1358
  }
@@ -1402,9 +1362,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1402
1362
  .min-h-0 {
1403
1363
  min-height: calc(var(--spacing) * 0);
1404
1364
  }
1405
- .min-h-7 {
1406
- min-height: calc(var(--spacing) * 7);
1407
- }
1408
1365
  .min-h-8 {
1409
1366
  min-height: calc(var(--spacing) * 8);
1410
1367
  }
@@ -1429,9 +1386,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1429
1386
  .w-56 {
1430
1387
  width: calc(var(--spacing) * 56);
1431
1388
  }
1432
- .w-auto {
1433
- width: auto;
1434
- }
1435
1389
  .w-full {
1436
1390
  width: 100%;
1437
1391
  }
@@ -1506,9 +1460,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1506
1460
  .animate-spin {
1507
1461
  animation: var(--animate-spin);
1508
1462
  }
1509
- .cursor-default {
1510
- cursor: default;
1511
- }
1512
1463
  .cursor-pointer {
1513
1464
  cursor: pointer;
1514
1465
  }
@@ -1518,9 +1469,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1518
1469
  .scroll-py-2 {
1519
1470
  scroll-padding-block: calc(var(--spacing) * 2);
1520
1471
  }
1521
- .list-none {
1522
- list-style-type: none;
1523
- }
1524
1472
  .appearance-none {
1525
1473
  appearance: none;
1526
1474
  }
@@ -1542,9 +1490,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1542
1490
  .justify-center {
1543
1491
  justify-content: center;
1544
1492
  }
1545
- .justify-start {
1546
- justify-content: flex-start;
1547
- }
1548
1493
  .gap-0 {
1549
1494
  gap: calc(var(--spacing) * 0);
1550
1495
  }
@@ -1569,9 +1514,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1569
1514
  .gap-6 {
1570
1515
  gap: calc(var(--spacing) * 6);
1571
1516
  }
1572
- .self-start {
1573
- align-self: flex-start;
1574
- }
1575
1517
  .truncate {
1576
1518
  overflow: hidden;
1577
1519
  text-overflow: ellipsis;
@@ -1786,9 +1728,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1786
1728
  .p-6 {
1787
1729
  padding: calc(var(--spacing) * 6);
1788
1730
  }
1789
- .px-0 {
1790
- padding-inline: calc(var(--spacing) * 0);
1791
- }
1792
1731
  .px-1 {
1793
1732
  padding-inline: calc(var(--spacing) * 1);
1794
1733
  }
@@ -1813,9 +1752,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1813
1752
  .px-6 {
1814
1753
  padding-inline: calc(var(--spacing) * 6);
1815
1754
  }
1816
- .py-0 {
1817
- padding-block: calc(var(--spacing) * 0);
1818
- }
1819
1755
  .py-1 {
1820
1756
  padding-block: calc(var(--spacing) * 1);
1821
1757
  }
@@ -1888,9 +1824,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1888
1824
  .text-\\[9px\\] {
1889
1825
  font-size: 9px;
1890
1826
  }
1891
- .text-\\[10px\\] {
1892
- font-size: 10px;
1893
- }
1894
1827
  .text-\\[11px\\] {
1895
1828
  font-size: 11px;
1896
1829
  }
@@ -2130,10 +2063,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2130
2063
  outline-style: var(--tw-outline-style);
2131
2064
  outline-width: 1px;
2132
2065
  }
2133
- .blur {
2134
- --tw-blur: blur(8px);
2135
- filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
2136
- }
2137
2066
  .filter {
2138
2067
  filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
2139
2068
  }
@@ -2299,25 +2228,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2299
2228
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2300
2229
  }
2301
2230
  }
2302
- .focus-within\\:border-ring {
2303
- &:focus-within {
2304
- border-color: var(--ring);
2305
- }
2306
- }
2307
- .focus-within\\:ring-\\[3px\\] {
2308
- &:focus-within {
2309
- --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
2310
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2311
- }
2312
- }
2313
- .focus-within\\:ring-ring\\/30 {
2314
- &:focus-within {
2315
- --tw-ring-color: var(--ring);
2316
- @supports (color: color-mix(in lab, red, red)) {
2317
- --tw-ring-color: color-mix(in oklab, var(--ring) 30%, transparent);
2318
- }
2319
- }
2320
- }
2321
2231
  .hover\\:border-destructive\\/30 {
2322
2232
  &:hover {
2323
2233
  @media (hover: hover) {
@@ -2461,11 +2371,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2461
2371
  pointer-events: none;
2462
2372
  }
2463
2373
  }
2464
- .disabled\\:cursor-not-allowed {
2465
- &:disabled {
2466
- cursor: not-allowed;
2467
- }
2468
- }
2469
2374
  .disabled\\:opacity-50 {
2470
2375
  &:disabled {
2471
2376
  opacity: 50%;
@@ -2476,11 +2381,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2476
2381
  opacity: 60%;
2477
2382
  }
2478
2383
  }
2479
- .has-disabled\\:opacity-60 {
2480
- &:has(*:disabled) {
2481
- opacity: 60%;
2482
- }
2483
- }
2484
2384
  .aria-invalid\\:border-destructive\\/40 {
2485
2385
  &[aria-invalid="true"] {
2486
2386
  border-color: var(--destructive);
@@ -2887,32 +2787,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2887
2787
  height: calc(var(--spacing) * 4.5);
2888
2788
  }
2889
2789
  }
2890
- .\\[\\&\\>svg\\]\\:pointer-events-none {
2891
- &>svg {
2892
- pointer-events: none;
2893
- }
2894
- }
2895
- .\\[\\&\\>svg\\]\\:-mx-0\\.5 {
2896
- &>svg {
2897
- margin-inline: calc(var(--spacing) * -0.5);
2898
- }
2899
- }
2900
- .\\[\\&\\>svg\\]\\:shrink-0 {
2901
- &>svg {
2902
- flex-shrink: 0;
2903
- }
2904
- }
2905
- .\\[\\&\\>svg\\:not\\(\\[class\\*\\=\\'opacity-\\'\\]\\)\\]\\:opacity-80 {
2906
- &>svg:not([class*='opacity-']) {
2907
- opacity: 80%;
2908
- }
2909
- }
2910
- .\\[\\&\\>svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-4 {
2911
- &>svg:not([class*='size-']) {
2912
- width: calc(var(--spacing) * 4);
2913
- height: calc(var(--spacing) * 4);
2914
- }
2915
- }
2916
2790
  .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:bg-accent {
2917
2791
  [data-kbd-nav] & {
2918
2792
  &:focus-within {
@@ -3474,18 +3348,23 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3474
3348
 
3475
3349
  // src/browser/surface/constants.ts
3476
3350
  var SURFACE_HOST_CLASS = "uidex-surface-host";
3477
- var SURFACE_CONTAINER_CLASS = "uidex-container";
3478
3351
  var Z_BASE = 2147483630;
3479
3352
  var Z_OVERLAY = 2147483635;
3480
3353
  var Z_PIN_LAYER = 2147483640;
3481
3354
  var Z_CHROME = 2147483645;
3482
- var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS},.${SURFACE_CONTAINER_CLASS}`;
3355
+ var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS}`;
3483
3356
  var UIDEX_ATTR_TO_KIND = [
3484
3357
  ["data-uidex", "element"],
3485
3358
  ["data-uidex-region", "region"],
3486
3359
  ["data-uidex-widget", "widget"],
3487
3360
  ["data-uidex-primitive", "primitive"]
3488
3361
  ];
3362
+ var DOM_BACKED_KINDS = /* @__PURE__ */ new Set([
3363
+ "element",
3364
+ "region",
3365
+ "widget",
3366
+ "primitive"
3367
+ ]);
3489
3368
 
3490
3369
  // src/browser/surface/host.ts
3491
3370
  function createSurfaceHost(options) {
@@ -3678,29 +3557,6 @@ function entityForRef(ref2, registry) {
3678
3557
  }
3679
3558
  return { kind: ref2.kind, id: ref2.id };
3680
3559
  }
3681
- function defaultResolveMatch(target, registry) {
3682
- if (target.closest(SURFACE_IGNORE_SELECTOR)) return null;
3683
- let node = target;
3684
- while (node) {
3685
- if (node instanceof HTMLElement) {
3686
- for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
3687
- const id = node.getAttribute(attr);
3688
- if (id) {
3689
- const ref2 = { kind, id };
3690
- const entity = entityForRef(ref2, registry);
3691
- return {
3692
- element: node,
3693
- ref: ref2,
3694
- entity,
3695
- label: displayName(entity, node)
3696
- };
3697
- }
3698
- }
3699
- }
3700
- node = node.parentElement;
3701
- }
3702
- return null;
3703
- }
3704
3560
  function defaultResolveAllMatches(target, registry) {
3705
3561
  if (target.closest(SURFACE_IGNORE_SELECTOR)) return [];
3706
3562
  const semantic = [];
@@ -3745,27 +3601,6 @@ function resolveEntityElement(ref2) {
3745
3601
  if (el2 instanceof HTMLElement && el2.isConnected) return el2;
3746
3602
  return null;
3747
3603
  }
3748
- function createHighlightController(overlay) {
3749
- let lastKey = null;
3750
- return {
3751
- show(ref2, opts) {
3752
- const el2 = resolveEntityElement(ref2);
3753
- if (!el2) {
3754
- lastKey = null;
3755
- overlay.hide();
3756
- return;
3757
- }
3758
- const key = `${ref2.kind}:${ref2.id}`;
3759
- if (key === lastKey && overlay.isVisible) return;
3760
- lastKey = key;
3761
- overlay.show(el2, opts);
3762
- },
3763
- hide() {
3764
- lastKey = null;
3765
- overlay.hide();
3766
- }
3767
- };
3768
- }
3769
3604
  function createInspector(options) {
3770
3605
  const {
3771
3606
  session,
@@ -3819,7 +3654,6 @@ function createInspector(options) {
3819
3654
  e.preventDefault();
3820
3655
  e.stopPropagation();
3821
3656
  const match = stack[layerIndex];
3822
- session.select(match.ref);
3823
3657
  onSelect?.(match, { x: e.clientX, y: e.clientY });
3824
3658
  };
3825
3659
  const onContextMenu = (e) => {
@@ -4138,49 +3972,12 @@ function createMenuBar(options) {
4138
3972
  },
4139
3973
  pinIcon
4140
3974
  );
4141
- const commitCycler = el("div", {
4142
- class: "relative z-1 inline-flex items-center gap-0.5",
4143
- attrs: { "data-uidex-menubar-commit-cycler": "" }
4144
- });
4145
- commitCycler.hidden = true;
4146
- const prevIcon = (0, import_lucide3.createElement)(import_lucide3.ChevronLeft);
4147
- prevIcon.setAttribute("class", "size-3");
4148
- prevIcon.setAttribute("aria-hidden", "true");
4149
- const prevBtn = el(
4150
- "button",
4151
- {
4152
- class: BUTTON_CLASS,
4153
- attrs: { type: "button", "aria-label": "Previous commit" },
4154
- style: { width: "18px", height: "18px" }
4155
- },
4156
- prevIcon
4157
- );
4158
- const commitLabel = el("span", {
4159
- class: "relative z-1 whitespace-nowrap px-1 text-[10px] font-mono text-muted-foreground",
4160
- attrs: { "data-uidex-menubar-commit-label": "" }
4161
- });
4162
- const nextIcon = (0, import_lucide3.createElement)(import_lucide3.ChevronRight);
4163
- nextIcon.setAttribute("class", "size-3");
4164
- nextIcon.setAttribute("aria-hidden", "true");
4165
- const nextBtn = el(
4166
- "button",
4167
- {
4168
- class: BUTTON_CLASS,
4169
- attrs: { type: "button", "aria-label": "Next commit" },
4170
- style: { width: "18px", height: "18px" }
4171
- },
4172
- nextIcon
4173
- );
4174
- commitCycler.appendChild(prevBtn);
4175
- commitCycler.appendChild(commitLabel);
4176
- commitCycler.appendChild(nextBtn);
4177
3975
  const pinWrapper = el("div", {
4178
3976
  class: "relative z-1 inline-flex items-center gap-0.5",
4179
3977
  attrs: { "data-uidex-menubar-pin-wrapper": "" }
4180
3978
  });
4181
3979
  pinWrapper.hidden = true;
4182
3980
  pinWrapper.appendChild(pinBtn);
4183
- pinWrapper.appendChild(commitCycler);
4184
3981
  root.appendChild(pinWrapper);
4185
3982
  const updatePinUI = () => {
4186
3983
  if (!activePinLayer) {
@@ -4188,16 +3985,7 @@ function createMenuBar(options) {
4188
3985
  return;
4189
3986
  }
4190
3987
  const pinsVisible = activePinLayer.visible;
4191
- const state = activePinLayer.filterState;
4192
- const hasCommits = state.commits.length > 0;
4193
3988
  pinWrapper.hidden = false;
4194
- commitCycler.hidden = !pinsVisible || !hasCommits;
4195
- if (state.commitIndex === -1 || !state.commits[state.commitIndex]) {
4196
- commitLabel.textContent = `all (${state.commits.length})`;
4197
- } else {
4198
- const sha = state.commits[state.commitIndex] ?? "";
4199
- commitLabel.textContent = sha.slice(0, 7);
4200
- }
4201
3989
  pinBtn.className = cn(BUTTON_CLASS, pinsVisible && BUTTON_ACTIVE_CLASS);
4202
3990
  };
4203
3991
  pinBtn.addEventListener("click", (e) => {
@@ -4206,14 +3994,6 @@ function createMenuBar(options) {
4206
3994
  activePinLayer.setVisible(!activePinLayer.visible);
4207
3995
  }
4208
3996
  });
4209
- prevBtn.addEventListener("click", (e) => {
4210
- e.stopPropagation();
4211
- activePinLayer?.prevCommit();
4212
- });
4213
- nextBtn.addEventListener("click", (e) => {
4214
- e.stopPropagation();
4215
- activePinLayer?.nextCommit();
4216
- });
4217
3997
  let unsubscribePinFilter = activePinLayer?.onFilterChange(() => updatePinUI());
4218
3998
  const presenceIcon = (0, import_lucide3.createElement)(import_lucide3.Users);
4219
3999
  presenceIcon.setAttribute("class", "size-3.5");
@@ -4307,7 +4087,7 @@ function createMenuBar(options) {
4307
4087
  container.appendChild(root);
4308
4088
  const syncButtonStates = () => {
4309
4089
  const state = session.getState();
4310
- const inspectActive = state.inspectorActive;
4090
+ const inspectActive = state.mode === "inspecting";
4311
4091
  inspectBtn.setAttribute(
4312
4092
  "data-uidex-menubar-inspect-active",
4313
4093
  inspectActive ? "true" : "false"
@@ -4426,6 +4206,49 @@ function createMenuBar(options) {
4426
4206
  };
4427
4207
  }
4428
4208
 
4209
+ // src/browser/internal/repositioner.ts
4210
+ function createRepositioner(onReflow) {
4211
+ let rafId = null;
4212
+ let attached = false;
4213
+ const schedule = () => {
4214
+ if (rafId !== null) return;
4215
+ rafId = typeof requestAnimationFrame === "function" ? requestAnimationFrame(() => {
4216
+ rafId = null;
4217
+ onReflow();
4218
+ }) : setTimeout(() => {
4219
+ rafId = null;
4220
+ onReflow();
4221
+ }, 0);
4222
+ };
4223
+ const cancel = () => {
4224
+ if (rafId === null) return;
4225
+ if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(rafId);
4226
+ else clearTimeout(rafId);
4227
+ rafId = null;
4228
+ };
4229
+ const onScroll = () => schedule();
4230
+ const onResize = () => schedule();
4231
+ const attach = () => {
4232
+ if (attached) return;
4233
+ attached = true;
4234
+ window.addEventListener("resize", onResize);
4235
+ window.addEventListener("scroll", onScroll, {
4236
+ capture: true,
4237
+ passive: true
4238
+ });
4239
+ };
4240
+ const detach = () => {
4241
+ if (!attached) return;
4242
+ attached = false;
4243
+ window.removeEventListener("resize", onResize);
4244
+ window.removeEventListener("scroll", onScroll, {
4245
+ capture: true
4246
+ });
4247
+ cancel();
4248
+ };
4249
+ return { schedule, cancel, attach, detach };
4250
+ }
4251
+
4429
4252
  // src/browser/surface/overlay.ts
4430
4253
  var DEFAULT_COLOR = "#34d399";
4431
4254
  var DEFAULT_BORDER_WIDTH = 2;
@@ -4493,44 +4316,7 @@ function createOverlay(deps) {
4493
4316
  fillOpacity: DEFAULT_FILL_OPACITY,
4494
4317
  backdrop: false
4495
4318
  };
4496
- let rafId = null;
4497
- let attached = false;
4498
- const schedule = () => {
4499
- if (rafId !== null) return;
4500
- rafId = typeof requestAnimationFrame === "function" ? requestAnimationFrame(() => {
4501
- rafId = null;
4502
- updatePosition();
4503
- }) : setTimeout(() => {
4504
- rafId = null;
4505
- updatePosition();
4506
- }, 0);
4507
- };
4508
- const cancelSchedule = () => {
4509
- if (rafId === null) return;
4510
- if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(rafId);
4511
- else clearTimeout(rafId);
4512
- rafId = null;
4513
- };
4514
- const onScroll = () => schedule();
4515
- const onResize = () => schedule();
4516
- const attach = () => {
4517
- if (attached) return;
4518
- attached = true;
4519
- window.addEventListener("resize", onResize);
4520
- window.addEventListener("scroll", onScroll, {
4521
- capture: true,
4522
- passive: true
4523
- });
4524
- };
4525
- const detach = () => {
4526
- if (!attached) return;
4527
- attached = false;
4528
- window.removeEventListener("resize", onResize);
4529
- window.removeEventListener("scroll", onScroll, {
4530
- capture: true
4531
- });
4532
- cancelSchedule();
4533
- };
4319
+ const repositioner = createRepositioner(() => updatePosition());
4534
4320
  function updatePosition() {
4535
4321
  if (!target) return;
4536
4322
  const rect = target.getBoundingClientRect();
@@ -4594,16 +4380,16 @@ function createOverlay(deps) {
4594
4380
  box.offsetHeight;
4595
4381
  }
4596
4382
  box.style.opacity = "1";
4597
- attach();
4383
+ repositioner.attach();
4598
4384
  },
4599
4385
  hide() {
4600
4386
  target = null;
4601
4387
  box.style.opacity = "0";
4602
4388
  backdrop.style.opacity = "0";
4603
- detach();
4389
+ repositioner.detach();
4604
4390
  },
4605
4391
  destroy() {
4606
- detach();
4392
+ repositioner.detach();
4607
4393
  box.remove();
4608
4394
  backdrop.remove();
4609
4395
  target = null;
@@ -4720,8 +4506,7 @@ function createSurfaceShell(options) {
4720
4506
  const overlay = createOverlay({ container: host.shadowRoot });
4721
4507
  cleanup.add(overlay);
4722
4508
  const tooltip = createCursorTooltip({
4723
- container: host.chromeEl,
4724
- session: options.session
4509
+ container: host.chromeEl
4725
4510
  });
4726
4511
  cleanup.add(tooltip);
4727
4512
  const afterHover = options.inspector?.onAfterHover;
@@ -4905,9 +4690,6 @@ function createPinLayer(options) {
4905
4690
  const seenIds = /* @__PURE__ */ new Set();
4906
4691
  const byComp = /* @__PURE__ */ new Map();
4907
4692
  const indicators = /* @__PURE__ */ new Map();
4908
- let filter = { branch: null, commit: null };
4909
- let commits = [];
4910
- let commitIndex = -1;
4911
4693
  const filterCbs = /* @__PURE__ */ new Set();
4912
4694
  const notifyFilter = () => {
4913
4695
  for (const cb of filterCbs) cb();
@@ -4922,8 +4704,6 @@ function createPinLayer(options) {
4922
4704
  }
4923
4705
  };
4924
4706
  const rerender = () => {
4925
- commits = [];
4926
- commitIndex = -1;
4927
4707
  rebuildFiltered();
4928
4708
  for (const id of Array.from(indicators.keys())) {
4929
4709
  if (!byComp.has(id)) removeIndicator(id);
@@ -4932,45 +4712,8 @@ function createPinLayer(options) {
4932
4712
  notifyFilter();
4933
4713
  onPinsChanged?.();
4934
4714
  };
4935
- let rafId = null;
4936
- let winAttached = false;
4937
4715
  let obs = null;
4938
- const schedulePos = () => {
4939
- if (rafId !== null) return;
4940
- rafId = typeof requestAnimationFrame === "function" ? requestAnimationFrame(() => {
4941
- rafId = null;
4942
- posAll();
4943
- }) : setTimeout(() => {
4944
- rafId = null;
4945
- posAll();
4946
- }, 0);
4947
- };
4948
- const cancelPos = () => {
4949
- if (rafId === null) return;
4950
- if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(rafId);
4951
- else clearTimeout(rafId);
4952
- rafId = null;
4953
- };
4954
- const onScroll = () => schedulePos();
4955
- const onResize = () => schedulePos();
4956
- const attachWin = () => {
4957
- if (winAttached) return;
4958
- winAttached = true;
4959
- window.addEventListener("scroll", onScroll, {
4960
- capture: true,
4961
- passive: true
4962
- });
4963
- window.addEventListener("resize", onResize);
4964
- };
4965
- const detachWin = () => {
4966
- if (!winAttached) return;
4967
- winAttached = false;
4968
- window.removeEventListener("scroll", onScroll, {
4969
- capture: true
4970
- });
4971
- window.removeEventListener("resize", onResize);
4972
- cancelPos();
4973
- };
4716
+ const repositioner = createRepositioner(() => posAll());
4974
4717
  const attachObs = () => {
4975
4718
  if (obs || typeof MutationObserver === "undefined") return;
4976
4719
  obs = new MutationObserver((recs) => {
@@ -4994,7 +4737,7 @@ function createPinLayer(options) {
4994
4737
  s.anchor = resolveEntityElement(parseComponentRef(s.componentId));
4995
4738
  changed = true;
4996
4739
  }
4997
- if (changed) schedulePos();
4740
+ if (changed) repositioner.schedule();
4998
4741
  };
4999
4742
  const expand = (st) => {
5000
4743
  if (st.expanded) return;
@@ -5236,7 +4979,7 @@ function createPinLayer(options) {
5236
4979
  if (!st) {
5237
4980
  st = buildIndicator(componentId);
5238
4981
  indicators.set(componentId, st);
5239
- attachWin();
4982
+ repositioner.attach();
5240
4983
  attachObs();
5241
4984
  }
5242
4985
  if (st.pinIndex >= pins.length) st.pinIndex = 0;
@@ -5250,7 +4993,7 @@ function createPinLayer(options) {
5250
4993
  st.wrap.remove();
5251
4994
  indicators.delete(componentId);
5252
4995
  if (indicators.size === 0) {
5253
- detachWin();
4996
+ repositioner.detach();
5254
4997
  detachObs();
5255
4998
  }
5256
4999
  };
@@ -5285,17 +5028,22 @@ function createPinLayer(options) {
5285
5028
  seenIds.clear();
5286
5029
  byComp.clear();
5287
5030
  for (const id of Array.from(indicators.keys())) removeIndicator(id);
5288
- commits = [];
5289
- commitIndex = -1;
5290
5031
  notifyFilter();
5291
5032
  },
5292
5033
  getPinsForElement: (id) => byComp.get(id) ?? [],
5293
- getAllPinsForElement: (id) => allPins.filter((p) => (p.entity ?? "") === id),
5294
5034
  getAllPins: () => allPins.slice(),
5295
5035
  attachChannel(channel) {
5296
- return channel.onPin((pin) => {
5036
+ const offPin = channel.onPin((pin) => {
5297
5037
  layer.addPin(pin);
5298
5038
  });
5039
+ const offArchived = channel.onPinArchived?.((reportId) => {
5040
+ layer.removePin(reportId);
5041
+ }) ?? (() => {
5042
+ });
5043
+ return () => {
5044
+ offPin();
5045
+ offArchived();
5046
+ };
5299
5047
  },
5300
5048
  attachCloud(opts) {
5301
5049
  const offCh = opts.channel ? layer.attachChannel(opts.channel) : () => {
@@ -5326,39 +5074,6 @@ function createPinLayer(options) {
5326
5074
  async refresh() {
5327
5075
  if (activeRefresh) await activeRefresh();
5328
5076
  },
5329
- get filterState() {
5330
- return {
5331
- branch: filter.branch,
5332
- commit: filter.commit,
5333
- commits,
5334
- commitIndex
5335
- };
5336
- },
5337
- setFilter(next) {
5338
- filter = {
5339
- branch: next.branch !== void 0 ? next.branch : filter.branch,
5340
- commit: next.commit !== void 0 ? next.commit : filter.commit
5341
- };
5342
- rerender();
5343
- },
5344
- nextCommit() {
5345
- if (!commits.length) return;
5346
- commitIndex = commitIndex >= commits.length - 1 ? -1 : commitIndex + 1;
5347
- filter = {
5348
- ...filter,
5349
- commit: commitIndex === -1 ? null : commits[commitIndex]
5350
- };
5351
- rerender();
5352
- },
5353
- prevCommit() {
5354
- if (!commits.length) return;
5355
- commitIndex = commitIndex <= -1 ? commits.length - 1 : commitIndex - 1;
5356
- filter = {
5357
- ...filter,
5358
- commit: commitIndex === -1 ? null : commits[commitIndex]
5359
- };
5360
- rerender();
5361
- },
5362
5077
  onFilterChange(cb) {
5363
5078
  filterCbs.add(cb);
5364
5079
  return () => {
@@ -5377,7 +5092,7 @@ function createPinLayer(options) {
5377
5092
  },
5378
5093
  destroy() {
5379
5094
  layer.clear();
5380
- detachWin();
5095
+ repositioner.detach();
5381
5096
  detachObs();
5382
5097
  layerEl.remove();
5383
5098
  activeRefresh = null;
@@ -5420,9 +5135,11 @@ function createRouter(options) {
5420
5135
  if (view === null || typeof view !== "object" || typeof view.id !== "string" || view.id.length === 0) {
5421
5136
  throw new ViewValidationError("View must have a non-empty string id");
5422
5137
  }
5423
- if (typeof view.surface !== "function") {
5138
+ const hasSurface = typeof view.surface === "function";
5139
+ const hasRender = typeof view.render === "function";
5140
+ if (!hasSurface && !hasRender) {
5424
5141
  throw new ViewValidationError(
5425
- `View ${view.id}: 'surface' must be a function`
5142
+ `View ${view.id}: a 'surface' function (or a 'render' override) is required`
5426
5143
  );
5427
5144
  }
5428
5145
  if (!view.matches && !view.palette) {
@@ -5480,7 +5197,6 @@ function createRouter(options) {
5480
5197
  if (idx >= 0) recentRefs.splice(idx, 1);
5481
5198
  recentRefs.unshift(ref2);
5482
5199
  if (recentRefs.length > MAX_RECENTS) recentRefs.length = MAX_RECENTS;
5483
- options.session.select(ref2);
5484
5200
  const entry = { id: match.view.id, ref: ref2 };
5485
5201
  const { mode } = options.session.mode.getState();
5486
5202
  if (mode === "idle" || mode === "inspecting") {
@@ -5535,70 +5251,20 @@ function detectDev() {
5535
5251
 
5536
5252
  // src/browser/internal/lit.ts
5537
5253
  var import_lit_html2 = require("lit-html");
5538
- var import_repeat = require("lit-html/directives/repeat.js");
5539
5254
  var import_ref = require("lit-html/directives/ref.js");
5540
- var import_class_map = require("lit-html/directives/class-map.js");
5541
5255
 
5542
- // src/browser/internal/apply-props.ts
5543
- function applyProps(node, props) {
5544
- const removers = [];
5545
- for (const [key, rawValue] of Object.entries(props)) {
5546
- if (key === "children" || key === "dangerouslySetInnerHTML") continue;
5547
- if (key.startsWith("on") && typeof rawValue === "function") {
5548
- const eventName = key.slice(2).toLowerCase();
5549
- const isTextControl = node instanceof HTMLInputElement && node.type !== "checkbox" && node.type !== "radio" || node instanceof HTMLTextAreaElement;
5550
- const effectiveEvent = eventName === "change" && isTextControl ? "input" : eventName;
5551
- const listener = rawValue;
5552
- node.addEventListener(effectiveEvent, listener);
5553
- removers.push(() => node.removeEventListener(effectiveEvent, listener));
5554
- continue;
5555
- }
5556
- if (rawValue === void 0 || rawValue === null) {
5557
- node.removeAttribute(key);
5558
- continue;
5559
- }
5560
- if (typeof rawValue === "boolean") {
5561
- if (rawValue) node.setAttribute(key, "");
5562
- else node.removeAttribute(key);
5563
- continue;
5564
- }
5565
- if (key === "style" && typeof rawValue === "object") {
5566
- Object.assign(
5567
- node.style,
5568
- rawValue
5569
- );
5570
- continue;
5571
- }
5572
- if (key === "className" || key === "class") {
5573
- node.setAttribute("class", String(rawValue));
5574
- continue;
5575
- }
5576
- if (key === "htmlFor") {
5577
- node.setAttribute("for", String(rawValue));
5578
- continue;
5579
- }
5580
- if (key === "tabIndex") {
5581
- ;
5582
- node.tabIndex = Number(rawValue);
5583
- continue;
5584
- }
5585
- node.setAttribute(key, String(rawValue));
5586
- }
5587
- return () => removers.forEach((fn) => fn());
5588
- }
5589
-
5590
- // src/browser/internal/lit-icon.ts
5591
- var import_lit_html = require("lit-html");
5592
- var import_directive = require("lit-html/directive.js");
5593
- var import_lucide5 = require("lucide");
5594
- var LucideIconDirective = class extends import_directive.Directive {
5595
- _icon;
5596
- _class;
5597
- _el;
5598
- constructor(partInfo) {
5599
- super(partInfo);
5600
- if (partInfo.type !== import_directive.PartType.CHILD) {
5601
- throw new Error("icon() can only be used in a child position");
5256
+ // src/browser/internal/lit-icon.ts
5257
+ var import_lit_html = require("lit-html");
5258
+ var import_directive = require("lit-html/directive.js");
5259
+ var import_lucide5 = require("lucide");
5260
+ var LucideIconDirective = class extends import_directive.Directive {
5261
+ _icon;
5262
+ _class;
5263
+ _el;
5264
+ constructor(partInfo) {
5265
+ super(partInfo);
5266
+ if (partInfo.type !== import_directive.PartType.CHILD) {
5267
+ throw new Error("icon() can only be used in a child position");
5602
5268
  }
5603
5269
  }
5604
5270
  update(_part, [iconNode, className]) {
@@ -5619,6 +5285,16 @@ var LucideIconDirective = class extends import_directive.Directive {
5619
5285
  };
5620
5286
  var icon = (0, import_directive.directive)(LucideIconDirective);
5621
5287
 
5288
+ // src/browser/views/primitives/chip.ts
5289
+ var CHIP_CLASS = "inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground";
5290
+ function createChip(options = {}, children = []) {
5291
+ const { class: extra, ...rest } = options;
5292
+ return el("span", { ...rest, class: cn(CHIP_CLASS, extra) }, children);
5293
+ }
5294
+
5295
+ // src/browser/views/primitives/kind-icon.ts
5296
+ var import_lucide6 = require("lucide");
5297
+
5622
5298
  // src/browser/ui/cva.ts
5623
5299
  function cva(base, config = {}) {
5624
5300
  return (props = {}) => {
@@ -5666,16 +5342,6 @@ function badgeTpl(content, options = {}) {
5666
5342
  `;
5667
5343
  }
5668
5344
 
5669
- // src/browser/views/primitives/chip.ts
5670
- var CHIP_CLASS = "inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground";
5671
- function createChip(options = {}, children = []) {
5672
- const { class: extra, ...rest } = options;
5673
- return el("span", { ...rest, class: cn(CHIP_CLASS, extra) }, children);
5674
- }
5675
-
5676
- // src/browser/views/primitives/kind-icon.ts
5677
- var import_lucide6 = require("lucide");
5678
-
5679
5345
  // src/browser/views/primitives/icon-tile.ts
5680
5346
  var TILE_CLASS = "inline-flex size-6 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground";
5681
5347
  var ICON_SIZE_CLASS_RE = /\b(h-\d+|w-\d+|size-\d+)\b/g;
@@ -6167,6 +5833,54 @@ function createScrollArea(props = {}) {
6167
5833
  );
6168
5834
  }
6169
5835
 
5836
+ // src/browser/internal/apply-props.ts
5837
+ function applyProps(node, props) {
5838
+ const removers = [];
5839
+ for (const [key, rawValue] of Object.entries(props)) {
5840
+ if (key === "children" || key === "dangerouslySetInnerHTML") continue;
5841
+ if (key.startsWith("on") && typeof rawValue === "function") {
5842
+ const eventName = key.slice(2).toLowerCase();
5843
+ const isTextControl = node instanceof HTMLInputElement && node.type !== "checkbox" && node.type !== "radio" || node instanceof HTMLTextAreaElement;
5844
+ const effectiveEvent = eventName === "change" && isTextControl ? "input" : eventName;
5845
+ const listener = rawValue;
5846
+ node.addEventListener(effectiveEvent, listener);
5847
+ removers.push(() => node.removeEventListener(effectiveEvent, listener));
5848
+ continue;
5849
+ }
5850
+ if (rawValue === void 0 || rawValue === null) {
5851
+ node.removeAttribute(key);
5852
+ continue;
5853
+ }
5854
+ if (typeof rawValue === "boolean") {
5855
+ if (rawValue) node.setAttribute(key, "");
5856
+ else node.removeAttribute(key);
5857
+ continue;
5858
+ }
5859
+ if (key === "style" && typeof rawValue === "object") {
5860
+ Object.assign(
5861
+ node.style,
5862
+ rawValue
5863
+ );
5864
+ continue;
5865
+ }
5866
+ if (key === "className" || key === "class") {
5867
+ node.setAttribute("class", String(rawValue));
5868
+ continue;
5869
+ }
5870
+ if (key === "htmlFor") {
5871
+ node.setAttribute("for", String(rawValue));
5872
+ continue;
5873
+ }
5874
+ if (key === "tabIndex") {
5875
+ ;
5876
+ node.tabIndex = Number(rawValue);
5877
+ continue;
5878
+ }
5879
+ node.setAttribute(key, String(rawValue));
5880
+ }
5881
+ return () => removers.forEach((fn) => fn());
5882
+ }
5883
+
6170
5884
  // src/browser/views/builder/spread-props.ts
6171
5885
  function spreadProps(node, props) {
6172
5886
  return applyProps(node, props);
@@ -6186,9 +5900,6 @@ function createPersistentSpreads() {
6186
5900
  };
6187
5901
  }
6188
5902
 
6189
- // src/browser/views/render/detail.ts
6190
- var import_lucide7 = require("lucide");
6191
-
6192
5903
  // src/browser/internal/arrow-nav.ts
6193
5904
  var NAV_KEYS = /* @__PURE__ */ new Set(["ArrowDown", "ArrowUp", "Home", "End"]);
6194
5905
  function bindArrowNav(options) {
@@ -6260,30 +5971,6 @@ function focusItem(items, idx) {
6260
5971
  items[idx].focus();
6261
5972
  }
6262
5973
 
6263
- // src/browser/views/builder/filter.ts
6264
- function normalizeQuery(query) {
6265
- return query.trim().toLowerCase();
6266
- }
6267
- function matchesQuery(haystack, query) {
6268
- const q = normalizeQuery(query);
6269
- if (!q) return true;
6270
- return haystack.toLowerCase().includes(q);
6271
- }
6272
- function filterEntities(entities, query) {
6273
- const q = normalizeQuery(query);
6274
- if (!q) return entities;
6275
- return entities.filter((e) => {
6276
- const name = displayName(e).toLowerCase();
6277
- const id = entityKey(e).toLowerCase();
6278
- return name.includes(q) || id.includes(q) || e.kind.toLowerCase().includes(q);
6279
- });
6280
- }
6281
- function filterIds(ids, query) {
6282
- const q = normalizeQuery(query);
6283
- if (!q) return ids;
6284
- return ids.filter((id) => id.toLowerCase().includes(q));
6285
- }
6286
-
6287
5974
  // src/browser/views/labels.ts
6288
5975
  var SECTION_LABELS = {
6289
5976
  acceptance: "Acceptance criteria",
@@ -6308,171 +5995,6 @@ var LIST_ITEM_STATE_CLASS = "data-[disabled]:pointer-events-none data-[disabled]
6308
5995
  var LIST_ITEM_INTERACTIVE_CLASS = "uidex-item-interactive [[data-kbd-nav]_&]:focus:bg-accent [[data-kbd-nav]_&]:focus:text-accent-foreground [[data-kbd-nav]_&]:focus-within:bg-accent [[data-kbd-nav]_&]:focus-within:text-accent-foreground";
6309
5996
  var LIST_GROUP_LABEL_CLASS = "text-muted-foreground px-2 py-1.5 text-xs font-medium";
6310
5997
 
6311
- // src/browser/ui/button.ts
6312
- var buttonBase = "focus-visible:ring-ring focus-visible:ring-offset-background disabled:opacity-60 relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg border text-sm font-medium outline-none transition-shadow focus-visible:ring-2 focus-visible:ring-offset-1 disabled:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg]:pointer-events-none [&_svg]:shrink-0";
6313
- var buttonVariants = cva(buttonBase, {
6314
- defaultVariants: { size: "default", variant: "default" },
6315
- variants: {
6316
- size: {
6317
- default: "h-8 px-3",
6318
- sm: "h-7 gap-1.5 px-2.5",
6319
- xs: "h-6 gap-1 rounded-md px-2 text-xs",
6320
- lg: "h-9 px-3.5",
6321
- xl: "h-10 px-4 text-base",
6322
- icon: "size-8",
6323
- "icon-sm": "size-7",
6324
- "icon-lg": "size-9",
6325
- "icon-xs": "size-6 rounded-md"
6326
- },
6327
- variant: {
6328
- default: "border-primary bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
6329
- destructive: "border-destructive bg-destructive shadow-xs hover:bg-destructive/90 text-white",
6330
- "destructive-outline": "border-input bg-popover text-destructive-foreground shadow-xs/5 hover:border-destructive/30 hover:bg-destructive/5 dark:bg-input/30",
6331
- ghost: "text-foreground hover:bg-accent hover:text-accent-foreground border-transparent",
6332
- link: "text-foreground border-transparent underline-offset-4 hover:underline",
6333
- outline: "border-input bg-popover text-foreground shadow-xs/5 hover:bg-accent hover:text-accent-foreground dark:bg-input/30",
6334
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90 border-transparent"
6335
- }
6336
- }
6337
- });
6338
-
6339
- // src/browser/views/primitives/entity-presence.ts
6340
- var DOM_BACKED_KINDS = /* @__PURE__ */ new Set([
6341
- "element",
6342
- "region",
6343
- "widget",
6344
- "primitive"
6345
- ]);
6346
- var TOUCH_RESOLVE_KINDS = [
6347
- "element",
6348
- "widget",
6349
- "region",
6350
- "primitive"
6351
- ];
6352
- function isAbsentFromPage(ref2, registry) {
6353
- if (DOM_BACKED_KINDS.has(ref2.kind)) {
6354
- return !resolveEntityElement(ref2);
6355
- }
6356
- if (ref2.kind === "flow" && registry) {
6357
- const flow = registry.get("flow", ref2.id);
6358
- if (!flow) return true;
6359
- for (const touchId of flow.touches) {
6360
- for (const kind of TOUCH_RESOLVE_KINDS) {
6361
- const entity = registry.get(kind, touchId);
6362
- if (entity && resolveEntityElement({ kind, id: touchId })) return false;
6363
- }
6364
- }
6365
- return true;
6366
- }
6367
- return false;
6368
- }
6369
-
6370
- // src/browser/ui/kbd.ts
6371
- var KBD_CLASS = "bg-muted text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded px-1 font-sans text-xs font-medium [&_svg:not([class*='size-'])]:size-3";
6372
- var COMMAND_SHORTCUT_CLASS = "text-muted-foreground/70 ms-auto font-sans text-xs font-medium tracking-widest";
6373
- function createCommandShortcut(options = {}, children = []) {
6374
- const { class: extra, attrs, ...rest } = options;
6375
- return el(
6376
- "kbd",
6377
- {
6378
- ...rest,
6379
- class: cn(COMMAND_SHORTCUT_CLASS, extra),
6380
- attrs: { "data-slot": "command-shortcut", ...attrs }
6381
- },
6382
- children
6383
- );
6384
- }
6385
- function kbdTpl(text, className) {
6386
- return import_lit_html2.html`<kbd class=${cn(KBD_CLASS, className)} data-slot="kbd"
6387
- >${text}</kbd
6388
- >`;
6389
- }
6390
- function commandShortcutTpl(text, className) {
6391
- return import_lit_html2.html`<kbd
6392
- class=${cn(COMMAND_SHORTCUT_CLASS, className)}
6393
- data-slot="command-shortcut"
6394
- >${text}</kbd
6395
- >`;
6396
- }
6397
-
6398
- // src/browser/views/primitives/row.ts
6399
- var LABEL_CLASS = "min-w-0 flex-1 truncate";
6400
- var SUBTITLE_CLASS = "truncate text-xs text-muted-foreground";
6401
- function fillRowWithHandle(host, content) {
6402
- if (content.leading) host.append(content.leading);
6403
- const label = el("span", { class: LABEL_CLASS, text: content.label });
6404
- host.append(label);
6405
- if (content.subtitle) {
6406
- host.append(
6407
- el("span", {
6408
- class: SUBTITLE_CLASS,
6409
- attrs: { "data-uidex-row-subtitle": "" },
6410
- text: content.subtitle
6411
- })
6412
- );
6413
- }
6414
- if (content.trailing != null) {
6415
- host.append(
6416
- typeof content.trailing === "string" ? createCommandShortcut({ text: content.trailing }) : content.trailing
6417
- );
6418
- }
6419
- return { label };
6420
- }
6421
- function rowTpl(content) {
6422
- return import_lit_html2.html`
6423
- ${content.leading ?? import_lit_html2.nothing}
6424
- <span class=${LABEL_CLASS}>${content.label}</span>
6425
- ${content.subtitle ? import_lit_html2.html`<span class=${SUBTITLE_CLASS} data-uidex-row-subtitle
6426
- >${content.subtitle}</span
6427
- >` : import_lit_html2.nothing}
6428
- ${content.trailing != null ? typeof content.trailing === "string" ? commandShortcutTpl(content.trailing) : content.trailing : import_lit_html2.nothing}
6429
- `;
6430
- }
6431
-
6432
- // src/browser/views/primitives/entity-link.ts
6433
- var ACTION_CLASS = cn(
6434
- LIST_ITEM_CLASS,
6435
- LIST_ITEM_INTERACTIVE_CLASS,
6436
- "w-full cursor-pointer text-left font-normal"
6437
- );
6438
- function entityLinkTpl(options) {
6439
- const { ctx, target, label, leading, class: extraClass } = options;
6440
- const absent = isAbsentFromPage(target);
6441
- const resolvedLabel = label ?? `${target.kind}: ${target.id}`;
6442
- const onClick = () => ctx.views.navigate(target);
6443
- const preview = () => {
6444
- ctx.highlight.show(target, { color: KIND_STYLE[target.kind].color });
6445
- };
6446
- const restoreParent = () => {
6447
- if (ctx.ref) {
6448
- ctx.highlight.show(ctx.ref, { color: KIND_STYLE[ctx.ref.kind].color });
6449
- } else {
6450
- ctx.highlight.hide();
6451
- }
6452
- };
6453
- return import_lit_html2.html`
6454
- <button
6455
- type="button"
6456
- class=${cn(ACTION_CLASS, "w-full", absent && "opacity-50", extraClass)}
6457
- data-slot="list-item-button"
6458
- data-uidex-entity-link
6459
- data-uidex-ref-kind=${target.kind}
6460
- data-uidex-ref-id=${target.id}
6461
- title=${absent ? "Not on this page" : ""}
6462
- @click=${onClick}
6463
- @mouseenter=${preview}
6464
- @mouseleave=${restoreParent}
6465
- @focus=${preview}
6466
- @blur=${restoreParent}
6467
- >
6468
- ${rowTpl({
6469
- leading,
6470
- label: resolvedLabel
6471
- })}
6472
- </button>
6473
- `;
6474
- }
6475
-
6476
5998
  // src/browser/views/primitives/text.ts
6477
5999
  var MUTED_CLASS = "text-sm text-muted-foreground";
6478
6000
  var HEADING_CLASS = LIST_GROUP_LABEL_CLASS;
@@ -6484,7 +6006,8 @@ function headingTpl(text, className) {
6484
6006
  return import_lit_html2.html`<h3 class=${cn(HEADING_CLASS, className)}>${text}</h3>`;
6485
6007
  }
6486
6008
 
6487
- // src/browser/views/render/detail.ts
6009
+ // src/browser/views/render/detail-actions.ts
6010
+ var import_lucide7 = require("lucide");
6488
6011
  var ICON_MAP = {
6489
6012
  "archive-x": import_lucide7.ArchiveX,
6490
6013
  copy: import_lucide7.Copy,
@@ -6500,23 +6023,6 @@ var ICON_MAP = {
6500
6023
  function iconFor(icon2) {
6501
6024
  return icon2 ? ICON_MAP[icon2] : null;
6502
6025
  }
6503
- function subtitleTpl(subtitle) {
6504
- const text = subtitle.extra ? `${subtitle.rawId} \xB7 ${subtitle.extra}` : subtitle.rawId;
6505
- return import_lit_html2.html`<p
6506
- class=${cn("text-muted-foreground font-mono text-xs", "px-2")}
6507
- data-uidex-detail-subtitle
6508
- >
6509
- ${text}
6510
- </p>`;
6511
- }
6512
- function notFoundTpl(ref2) {
6513
- return import_lit_html2.html`<p
6514
- class=${cn("text-muted-foreground text-sm", "p-4")}
6515
- data-uidex-detail-missing
6516
- >
6517
- ${ref2.kind}: ${ref2.id} not found in registry
6518
- </p>`;
6519
- }
6520
6026
  function renderActions(actions, ctx, root, heading) {
6521
6027
  const cleanups = [];
6522
6028
  const buttons = [];
@@ -6627,6 +6133,166 @@ function renderActions(actions, ctx, root, heading) {
6627
6133
  }
6628
6134
  return { node: section, buttons, cleanup: composeCleanups(cleanups) };
6629
6135
  }
6136
+
6137
+ // src/browser/views/render/detail-sections.ts
6138
+ var import_static = require("lit-html/static.js");
6139
+
6140
+ // src/browser/views/builder/filter.ts
6141
+ function normalizeQuery(query) {
6142
+ return query.trim().toLowerCase();
6143
+ }
6144
+ function matchesQuery(haystack, query) {
6145
+ const q = normalizeQuery(query);
6146
+ if (!q) return true;
6147
+ return haystack.toLowerCase().includes(q);
6148
+ }
6149
+ function filterEntities(entities, query) {
6150
+ const q = normalizeQuery(query);
6151
+ if (!q) return entities;
6152
+ return entities.filter((e) => {
6153
+ const name = displayName(e).toLowerCase();
6154
+ const id = entityKey(e).toLowerCase();
6155
+ return name.includes(q) || id.includes(q) || e.kind.toLowerCase().includes(q);
6156
+ });
6157
+ }
6158
+ function filterIds(ids, query) {
6159
+ const q = normalizeQuery(query);
6160
+ if (!q) return ids;
6161
+ return ids.filter((id) => id.toLowerCase().includes(q));
6162
+ }
6163
+
6164
+ // src/browser/views/primitives/entity-presence.ts
6165
+ var TOUCH_RESOLVE_KINDS = [
6166
+ "element",
6167
+ "widget",
6168
+ "region",
6169
+ "primitive"
6170
+ ];
6171
+ function isAbsentFromPage(ref2, registry) {
6172
+ if (DOM_BACKED_KINDS.has(ref2.kind)) {
6173
+ return !resolveEntityElement(ref2);
6174
+ }
6175
+ if (ref2.kind === "flow" && registry) {
6176
+ const flow = registry.get("flow", ref2.id);
6177
+ if (!flow) return true;
6178
+ for (const touchId of flow.touches) {
6179
+ for (const kind of TOUCH_RESOLVE_KINDS) {
6180
+ const entity = registry.get(kind, touchId);
6181
+ if (entity && resolveEntityElement({ kind, id: touchId })) return false;
6182
+ }
6183
+ }
6184
+ return true;
6185
+ }
6186
+ return false;
6187
+ }
6188
+
6189
+ // src/browser/ui/kbd.ts
6190
+ var KBD_CLASS = "bg-muted text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 select-none items-center justify-center gap-1 rounded px-1 font-sans text-xs font-medium [&_svg:not([class*='size-'])]:size-3";
6191
+ var COMMAND_SHORTCUT_CLASS = "text-muted-foreground/70 ms-auto font-sans text-xs font-medium tracking-widest";
6192
+ function createCommandShortcut(options = {}, children = []) {
6193
+ const { class: extra, attrs, ...rest } = options;
6194
+ return el(
6195
+ "kbd",
6196
+ {
6197
+ ...rest,
6198
+ class: cn(COMMAND_SHORTCUT_CLASS, extra),
6199
+ attrs: { "data-slot": "command-shortcut", ...attrs }
6200
+ },
6201
+ children
6202
+ );
6203
+ }
6204
+ function kbdTpl(text, className) {
6205
+ return import_lit_html2.html`<kbd class=${cn(KBD_CLASS, className)} data-slot="kbd"
6206
+ >${text}</kbd
6207
+ >`;
6208
+ }
6209
+ function commandShortcutTpl(text, className) {
6210
+ return import_lit_html2.html`<kbd
6211
+ class=${cn(COMMAND_SHORTCUT_CLASS, className)}
6212
+ data-slot="command-shortcut"
6213
+ >${text}</kbd
6214
+ >`;
6215
+ }
6216
+
6217
+ // src/browser/views/primitives/row.ts
6218
+ var LABEL_CLASS = "min-w-0 flex-1 truncate";
6219
+ var SUBTITLE_CLASS = "truncate text-xs text-muted-foreground";
6220
+ function fillRowWithHandle(host, content) {
6221
+ if (content.leading) host.append(content.leading);
6222
+ const label = el("span", { class: LABEL_CLASS, text: content.label });
6223
+ host.append(label);
6224
+ if (content.subtitle) {
6225
+ host.append(
6226
+ el("span", {
6227
+ class: SUBTITLE_CLASS,
6228
+ attrs: { "data-uidex-row-subtitle": "" },
6229
+ text: content.subtitle
6230
+ })
6231
+ );
6232
+ }
6233
+ if (content.trailing != null) {
6234
+ host.append(
6235
+ typeof content.trailing === "string" ? createCommandShortcut({ text: content.trailing }) : content.trailing
6236
+ );
6237
+ }
6238
+ return { label };
6239
+ }
6240
+ function rowTpl(content) {
6241
+ return import_lit_html2.html`
6242
+ ${content.leading ?? import_lit_html2.nothing}
6243
+ <span class=${LABEL_CLASS}>${content.label}</span>
6244
+ ${content.subtitle ? import_lit_html2.html`<span class=${SUBTITLE_CLASS} data-uidex-row-subtitle
6245
+ >${content.subtitle}</span
6246
+ >` : import_lit_html2.nothing}
6247
+ ${content.trailing != null ? typeof content.trailing === "string" ? commandShortcutTpl(content.trailing) : content.trailing : import_lit_html2.nothing}
6248
+ `;
6249
+ }
6250
+
6251
+ // src/browser/views/primitives/entity-link.ts
6252
+ var ACTION_CLASS = cn(
6253
+ LIST_ITEM_CLASS,
6254
+ LIST_ITEM_INTERACTIVE_CLASS,
6255
+ "w-full cursor-pointer text-left font-normal"
6256
+ );
6257
+ function entityLinkTpl(options) {
6258
+ const { ctx, target, label, leading, class: extraClass } = options;
6259
+ const absent = isAbsentFromPage(target);
6260
+ const resolvedLabel = label ?? `${target.kind}: ${target.id}`;
6261
+ const onClick = () => ctx.views.navigate(target);
6262
+ const preview = () => {
6263
+ ctx.highlight.show(target, { color: KIND_STYLE[target.kind].color });
6264
+ };
6265
+ const restoreParent = () => {
6266
+ if (ctx.ref) {
6267
+ ctx.highlight.show(ctx.ref, { color: KIND_STYLE[ctx.ref.kind].color });
6268
+ } else {
6269
+ ctx.highlight.hide();
6270
+ }
6271
+ };
6272
+ return import_lit_html2.html`
6273
+ <button
6274
+ type="button"
6275
+ class=${cn(ACTION_CLASS, "w-full", absent && "opacity-50", extraClass)}
6276
+ data-slot="list-item-button"
6277
+ data-uidex-entity-link
6278
+ data-uidex-ref-kind=${target.kind}
6279
+ data-uidex-ref-id=${target.id}
6280
+ title=${absent ? "Not on this page" : ""}
6281
+ @click=${onClick}
6282
+ @mouseenter=${preview}
6283
+ @mouseleave=${restoreParent}
6284
+ @focus=${preview}
6285
+ @blur=${restoreParent}
6286
+ >
6287
+ ${rowTpl({
6288
+ leading,
6289
+ label: resolvedLabel
6290
+ })}
6291
+ </button>
6292
+ `;
6293
+ }
6294
+
6295
+ // src/browser/views/render/detail-sections.ts
6630
6296
  function entityListItems(ctx, entities) {
6631
6297
  return import_lit_html2.html`${entities.map((entity) => {
6632
6298
  const eRef = { kind: entity.kind, id: entityKey(entity) };
@@ -6640,71 +6306,29 @@ function entityListItems(ctx, entities) {
6640
6306
  </li>`;
6641
6307
  })}`;
6642
6308
  }
6643
- function composesListTpl(ctx, label, entities) {
6644
- if (entities.length === 0) return null;
6645
- return import_lit_html2.html`
6646
- <section class="flex flex-col" data-uidex-detail-composes>
6647
- ${headingTpl(label)}
6648
- <ul class="flex flex-col">
6649
- ${entityListItems(ctx, entities)}
6650
- </ul>
6651
- </section>
6652
- `;
6653
- }
6654
- function usedByListTpl(ctx, label, entities) {
6655
- if (entities.length === 0) return null;
6656
- return import_lit_html2.html`
6657
- <section class="flex flex-col" data-uidex-detail-used-by>
6658
- ${headingTpl(label)}
6659
- <ul class="flex flex-col">
6660
- ${entityListItems(ctx, entities)}
6661
- </ul>
6662
- </section>
6663
- `;
6664
- }
6665
- function flowListTpl(ctx, flows) {
6666
- if (flows.length === 0) return null;
6667
- return import_lit_html2.html`
6668
- <section class="flex flex-col" data-uidex-detail-flows>
6669
- ${headingTpl(SECTION_LABELS.flows)}
6670
- <ul class="flex flex-col">
6671
- ${flows.map(
6672
- (flow) => import_lit_html2.html`<li>
6673
- ${entityLinkTpl({
6674
- ctx,
6675
- target: { kind: "flow", id: flow.id },
6676
- label: displayName(flow),
6677
- leading: kindIconTileTpl("flow")
6678
- })}
6679
- </li>`
6680
- )}
6681
- </ul>
6682
- </section>
6683
- `;
6684
- }
6685
- function touchesTpl(ctx, entities, query) {
6309
+ var DETAIL_SECTION_ATTRS = {
6310
+ composes: import_static.literal`data-uidex-detail-composes`,
6311
+ "used-by": import_static.literal`data-uidex-detail-used-by`,
6312
+ flows: import_static.literal`data-uidex-detail-flows`,
6313
+ touches: import_static.literal`data-uidex-detail-touches`
6314
+ };
6315
+ function entitySectionTpl(ctx, opts) {
6316
+ const { label, entities, dataAttr, emptyText } = opts;
6317
+ const attr = DETAIL_SECTION_ATTRS[dataAttr];
6686
6318
  if (entities.length === 0) {
6687
- return import_lit_html2.html`
6688
- <section class="flex flex-col gap-2" data-uidex-detail-touches>
6689
- ${headingTpl(SECTION_LABELS.touches)}
6690
- ${mutedTextTpl(query ? "No matches" : "No entities touched")}
6319
+ if (emptyText === void 0) return null;
6320
+ return import_static.html`
6321
+ <section class="flex flex-col gap-2" ${attr}>
6322
+ ${headingTpl(label)}
6323
+ ${mutedTextTpl(emptyText)}
6691
6324
  </section>
6692
6325
  `;
6693
6326
  }
6694
- return import_lit_html2.html`
6695
- <section class="flex flex-col" data-uidex-detail-touches>
6696
- ${headingTpl(SECTION_LABELS.touches)}
6327
+ return import_static.html`
6328
+ <section class="flex flex-col" ${attr}>
6329
+ ${headingTpl(label)}
6697
6330
  <ul class="flex flex-col">
6698
- ${entities.map(
6699
- (entity) => import_lit_html2.html`<li>
6700
- ${entityLinkTpl({
6701
- ctx,
6702
- target: { kind: entity.kind, id: entityKey(entity) },
6703
- label: displayName(entity),
6704
- leading: kindIconTileTpl(entity.kind)
6705
- })}
6706
- </li>`
6707
- )}
6331
+ ${entityListItems(ctx, entities)}
6708
6332
  </ul>
6709
6333
  </section>
6710
6334
  `;
@@ -6861,6 +6485,20 @@ function screenshotTpl(url) {
6861
6485
  </section>
6862
6486
  `;
6863
6487
  }
6488
+ function lazyScreenshotEl(load) {
6489
+ const holder = document.createElement("div");
6490
+ holder.hidden = true;
6491
+ void load().then(
6492
+ (url) => {
6493
+ if (!url) return;
6494
+ (0, import_lit_html2.render)(screenshotTpl(url), holder);
6495
+ holder.hidden = false;
6496
+ },
6497
+ () => {
6498
+ }
6499
+ );
6500
+ return holder;
6501
+ }
6864
6502
  function metadataListTpl(entries) {
6865
6503
  return import_lit_html2.html`
6866
6504
  <div
@@ -6891,28 +6529,30 @@ function sectionTpl(section, ctx, query) {
6891
6529
  return acceptanceChecklistTpl(section.items);
6892
6530
  }
6893
6531
  case "composes":
6894
- return composesListTpl(
6895
- ctx,
6896
- section.label,
6897
- section.filterable ? filterEntities(section.entities, query) : section.entities
6898
- );
6532
+ return entitySectionTpl(ctx, {
6533
+ label: section.label,
6534
+ entities: section.filterable ? filterEntities(section.entities, query) : section.entities,
6535
+ dataAttr: "composes"
6536
+ });
6899
6537
  case "used-by":
6900
- return usedByListTpl(
6901
- ctx,
6902
- section.label,
6903
- section.filterable ? filterEntities(section.entities, query) : section.entities
6904
- );
6538
+ return entitySectionTpl(ctx, {
6539
+ label: section.label,
6540
+ entities: section.filterable ? filterEntities(section.entities, query) : section.entities,
6541
+ dataAttr: "used-by"
6542
+ });
6905
6543
  case "flows":
6906
- return flowListTpl(
6907
- ctx,
6908
- section.filterable ? filterEntities(section.flows, query) : section.flows
6909
- );
6544
+ return entitySectionTpl(ctx, {
6545
+ label: SECTION_LABELS.flows,
6546
+ entities: section.filterable ? filterEntities(section.flows, query) : section.flows,
6547
+ dataAttr: "flows"
6548
+ });
6910
6549
  case "touches":
6911
- return touchesTpl(
6912
- ctx,
6913
- section.filterable ? filterEntities(section.entities, query) : section.entities,
6914
- query
6915
- );
6550
+ return entitySectionTpl(ctx, {
6551
+ label: SECTION_LABELS.touches,
6552
+ entities: section.filterable ? filterEntities(section.entities, query) : section.entities,
6553
+ dataAttr: "touches",
6554
+ emptyText: query ? "No matches" : "No entities touched"
6555
+ });
6916
6556
  case "steps":
6917
6557
  return stepsTpl(
6918
6558
  ctx,
@@ -6924,11 +6564,32 @@ function sectionTpl(section, ctx, query) {
6924
6564
  section.filterable ? filterIds(section.paths, query) : section.paths
6925
6565
  );
6926
6566
  case "screenshot":
6927
- return screenshotTpl(section.url);
6567
+ if (section.url) return screenshotTpl(section.url);
6568
+ if (section.load) return import_lit_html2.html`${lazyScreenshotEl(section.load)}`;
6569
+ return null;
6928
6570
  case "metadata":
6929
6571
  return section.entries.length > 0 ? metadataListTpl(section.entries) : null;
6930
6572
  }
6931
6573
  }
6574
+
6575
+ // src/browser/views/render/detail.ts
6576
+ function subtitleTpl(subtitle) {
6577
+ const text = subtitle.extra ? `${subtitle.rawId} \xB7 ${subtitle.extra}` : subtitle.rawId;
6578
+ return import_lit_html2.html`<p
6579
+ class=${cn("text-muted-foreground font-mono text-xs", "px-2")}
6580
+ data-uidex-detail-subtitle
6581
+ >
6582
+ ${text}
6583
+ </p>`;
6584
+ }
6585
+ function notFoundTpl(ref2) {
6586
+ return import_lit_html2.html`<p
6587
+ class=${cn("text-muted-foreground text-sm", "p-4")}
6588
+ data-uidex-detail-missing
6589
+ >
6590
+ ${ref2.kind}: ${ref2.id} not found in registry
6591
+ </p>`;
6592
+ }
6932
6593
  function hasFilterableSections(sections) {
6933
6594
  return sections.some((s) => "filterable" in s && s.filterable === true);
6934
6595
  }
@@ -7289,6 +6950,34 @@ function createScreenshotLightbox(trigger, dataUrl, options) {
7289
6950
  };
7290
6951
  }
7291
6952
 
6953
+ // src/browser/ui/button.ts
6954
+ var buttonBase = "focus-visible:ring-ring focus-visible:ring-offset-background disabled:opacity-60 relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg border text-sm font-medium outline-none transition-shadow focus-visible:ring-2 focus-visible:ring-offset-1 disabled:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg]:pointer-events-none [&_svg]:shrink-0";
6955
+ var buttonVariants = cva(buttonBase, {
6956
+ defaultVariants: { size: "default", variant: "default" },
6957
+ variants: {
6958
+ size: {
6959
+ default: "h-8 px-3",
6960
+ sm: "h-7 gap-1.5 px-2.5",
6961
+ xs: "h-6 gap-1 rounded-md px-2 text-xs",
6962
+ lg: "h-9 px-3.5",
6963
+ xl: "h-10 px-4 text-base",
6964
+ icon: "size-8",
6965
+ "icon-sm": "size-7",
6966
+ "icon-lg": "size-9",
6967
+ "icon-xs": "size-6 rounded-md"
6968
+ },
6969
+ variant: {
6970
+ default: "border-primary bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
6971
+ destructive: "border-destructive bg-destructive shadow-xs hover:bg-destructive/90 text-white",
6972
+ "destructive-outline": "border-input bg-popover text-destructive-foreground shadow-xs/5 hover:border-destructive/30 hover:bg-destructive/5 dark:bg-input/30",
6973
+ ghost: "text-foreground hover:bg-accent hover:text-accent-foreground border-transparent",
6974
+ link: "text-foreground border-transparent underline-offset-4 hover:underline",
6975
+ outline: "border-input bg-popover text-foreground shadow-xs/5 hover:bg-accent hover:text-accent-foreground dark:bg-input/30",
6976
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90 border-transparent"
6977
+ }
6978
+ }
6979
+ });
6980
+
7292
6981
  // src/browser/views/render/form.ts
7293
6982
  var fieldSeq = 0;
7294
6983
  var nextFieldId = () => `uidex-field-${++fieldSeq}`;
@@ -7485,9 +7174,9 @@ function iconMediaTpl(iconTpl) {
7485
7174
  </div>
7486
7175
  `;
7487
7176
  }
7488
- function loadingViewTpl() {
7177
+ function loadingViewTpl(rootRef) {
7489
7178
  return import_lit_html2.html`
7490
- <div class=${EMPTY_ROOT} aria-live="polite" hidden>
7179
+ <div class=${EMPTY_ROOT} aria-live="polite" hidden ${(0, import_ref.ref)(rootRef)}>
7491
7180
  ${iconMediaTpl(icon(import_lucide8.Loader2, "animate-spin"))}
7492
7181
  <div class="flex max-w-sm flex-col items-center text-center">
7493
7182
  <div class=${SKELETON + " h-6 w-36 rounded-md"}></div>
@@ -7500,42 +7189,47 @@ function loadingViewTpl() {
7500
7189
  </div>
7501
7190
  `;
7502
7191
  }
7503
- function successViewTpl() {
7192
+ function successViewTpl(refs) {
7504
7193
  return import_lit_html2.html`
7505
- <div class=${EMPTY_ROOT} hidden data-uidex-success-view>
7194
+ <div class=${EMPTY_ROOT} hidden data-uidex-success-view ${(0, import_ref.ref)(refs.root)}>
7506
7195
  ${iconMediaTpl(icon(import_lucide8.CircleCheck))}
7507
7196
  <div class="flex max-w-sm flex-col items-center text-center">
7508
7197
  <div
7509
7198
  class="font-heading text-xl font-semibold"
7510
7199
  data-uidex-success-title
7200
+ ${(0, import_ref.ref)(refs.title)}
7511
7201
  ></div>
7512
7202
  </div>
7513
7203
  <div
7514
7204
  class="flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm"
7515
7205
  data-uidex-success-actions
7206
+ ${(0, import_ref.ref)(refs.actions)}
7516
7207
  >
7517
7208
  <a
7518
7209
  class=${buttonVariants({ variant: "outline", size: "sm" })}
7519
7210
  target="_blank"
7520
7211
  rel="noreferrer"
7521
7212
  data-uidex-success-link
7213
+ ${(0, import_ref.ref)(refs.link)}
7522
7214
  ></a>
7523
7215
  </div>
7524
7216
  </div>
7525
7217
  `;
7526
7218
  }
7527
- function errorViewTpl() {
7219
+ function errorViewTpl(refs) {
7528
7220
  return import_lit_html2.html`
7529
- <div class=${EMPTY_ROOT} hidden data-uidex-error-view>
7221
+ <div class=${EMPTY_ROOT} hidden data-uidex-error-view ${(0, import_ref.ref)(refs.root)}>
7530
7222
  ${iconMediaTpl(icon(import_lucide8.CircleX))}
7531
7223
  <div class="flex max-w-sm flex-col items-center gap-1 text-center">
7532
7224
  <div
7533
7225
  class="font-heading text-xl font-semibold"
7534
7226
  data-uidex-error-title
7227
+ ${(0, import_ref.ref)(refs.title)}
7535
7228
  ></div>
7536
7229
  <p
7537
7230
  class="text-muted-foreground text-sm"
7538
7231
  data-uidex-error-description
7232
+ ${(0, import_ref.ref)(refs.description)}
7539
7233
  ></p>
7540
7234
  </div>
7541
7235
  <div
@@ -7546,6 +7240,7 @@ function errorViewTpl() {
7546
7240
  class=${buttonVariants({ variant: "outline", size: "sm" })}
7547
7241
  data-slot="button"
7548
7242
  data-uidex-retry-button
7243
+ ${(0, import_ref.ref)(refs.retry)}
7549
7244
  >
7550
7245
  Try Again
7551
7246
  </button>
@@ -7587,8 +7282,15 @@ function renderFormSurface(surface, ctx, root) {
7587
7282
  });
7588
7283
  }
7589
7284
  const formRef = (0, import_ref.createRef)();
7590
- const statusRef = (0, import_ref.createRef)();
7591
- const submitRef = (0, import_ref.createRef)();
7285
+ const loadingRef = (0, import_ref.createRef)();
7286
+ const successRef = (0, import_ref.createRef)();
7287
+ const successTitleRef = (0, import_ref.createRef)();
7288
+ const successLinkRef = (0, import_ref.createRef)();
7289
+ const successActionsRef = (0, import_ref.createRef)();
7290
+ const errorRef = (0, import_ref.createRef)();
7291
+ const errorTitleRef = (0, import_ref.createRef)();
7292
+ const errorDescriptionRef = (0, import_ref.createRef)();
7293
+ const retryRef = (0, import_ref.createRef)();
7592
7294
  (0, import_lit_html2.render)(
7593
7295
  import_lit_html2.html`
7594
7296
  <section
@@ -7603,7 +7305,19 @@ function renderFormSurface(surface, ctx, root) {
7603
7305
  novalidate
7604
7306
  data-uidex-form=${surface.id}
7605
7307
  ></form>
7606
- ${loadingViewTpl()} ${successViewTpl()} ${errorViewTpl()}
7308
+ ${loadingViewTpl(loadingRef)}
7309
+ ${successViewTpl({
7310
+ root: successRef,
7311
+ title: successTitleRef,
7312
+ link: successLinkRef,
7313
+ actions: successActionsRef
7314
+ })}
7315
+ ${errorViewTpl({
7316
+ root: errorRef,
7317
+ title: errorTitleRef,
7318
+ description: errorDescriptionRef,
7319
+ retry: retryRef
7320
+ })}
7607
7321
  </section>
7608
7322
  `,
7609
7323
  root
@@ -7633,32 +7347,15 @@ function renderFormSurface(surface, ctx, root) {
7633
7347
  }
7634
7348
  });
7635
7349
  form.append(status);
7636
- const sectionEl = root.querySelector(
7637
- "[data-uidex-form-surface]"
7638
- );
7639
- const loadingView = sectionEl.querySelector("[aria-live='polite'][hidden]") ?? sectionEl.children[1];
7640
- const successView = sectionEl.querySelector(
7641
- "[data-uidex-success-view]"
7642
- );
7643
- const successTitle = sectionEl.querySelector(
7644
- "[data-uidex-success-title]"
7645
- );
7646
- const successLink = sectionEl.querySelector(
7647
- "[data-uidex-success-link]"
7648
- );
7649
- const successActions = successLink.parentElement;
7650
- const errorView = sectionEl.querySelector(
7651
- "[data-uidex-error-view]"
7652
- );
7653
- const errorTitle = sectionEl.querySelector(
7654
- "[data-uidex-error-title]"
7655
- );
7656
- const errorDescription = sectionEl.querySelector(
7657
- "[data-uidex-error-description]"
7658
- );
7659
- const retryButton = sectionEl.querySelector(
7660
- "[data-uidex-retry-button]"
7661
- );
7350
+ const loadingView = loadingRef.value;
7351
+ const successView = successRef.value;
7352
+ const successTitle = successTitleRef.value;
7353
+ const successLink = successLinkRef.value;
7354
+ const successActions = successActionsRef.value;
7355
+ const errorView = errorRef.value;
7356
+ const errorTitle = errorTitleRef.value;
7357
+ const errorDescription = errorDescriptionRef.value;
7358
+ const retryButton = retryRef.value;
7662
7359
  function clearAllFieldErrors() {
7663
7360
  for (const f of fields) f.setError(null);
7664
7361
  }
@@ -8059,7 +7756,7 @@ function defaultFilter(item, query) {
8059
7756
  function rowTag(item) {
8060
7757
  return item.tag ?? item.value;
8061
7758
  }
8062
- function resolveLeadingTpl(item, ctx) {
7759
+ function resolveLeadingTpl(item) {
8063
7760
  if (item.entityChip) return kindIconTileTpl(item.entityChip.entity.kind);
8064
7761
  if (item.leading) {
8065
7762
  const node = item.leading();
@@ -8164,7 +7861,7 @@ function buildItemsDom(surface, ctx, filteredItems, allByValue, content) {
8164
7861
  data-uidex-item-value=${item.value}
8165
7862
  >
8166
7863
  ${rowTpl({
8167
- leading: resolveLeadingTpl(item, ctx),
7864
+ leading: resolveLeadingTpl(item),
8168
7865
  label: item.label,
8169
7866
  subtitle: item.subtitle,
8170
7867
  trailing: item.trailing ?? item.shortcut
@@ -8209,7 +7906,7 @@ function renderListSurface(surface, ctx, root) {
8209
7906
  return root.ownerDocument ?? document;
8210
7907
  };
8211
7908
  const hasDefault = surface.defaultHighlight !== void 0 && allByValue.has(surface.defaultHighlight);
8212
- const { section, scrollRoot, viewport, content } = renderShell(surface, root);
7909
+ const { scrollRoot, viewport, content } = renderShell(surface, root);
8213
7910
  let maps = buildItemsDom(surface, ctx, filteredItems, allByValue, content);
8214
7911
  const controller = createListController({
8215
7912
  surfaceId: surface.id,
@@ -8905,52 +8602,16 @@ function sameRef2(a, b) {
8905
8602
  if (a === null || b === null) return false;
8906
8603
  return a.kind === b.kind && a.id === b.id;
8907
8604
  }
8908
- function resolveHints(view, ctx) {
8909
- const h = view.hints;
8910
- if (!h) return [];
8911
- if (typeof h === "function") {
8912
- try {
8913
- return h(ctx) ?? [];
8914
- } catch (err) {
8915
- console.error(`[uidex] view "${view.id}" hints() threw`, err);
8916
- return [];
8917
- }
8918
- }
8919
- return h;
8920
- }
8921
- function resolveTitle(view, ctx) {
8922
- const t = view.title;
8923
- if (!t) return "";
8924
- if (typeof t === "function") {
8925
- try {
8926
- return t(ctx) ?? "";
8927
- } catch (err) {
8928
- console.error(`[uidex] view "${view.id}" title() threw`, err);
8929
- return "";
8930
- }
8931
- }
8932
- return t;
8933
- }
8934
- function resolveActions(view, ctx, globalActions) {
8935
- const result = [];
8936
- const fn = view.actions;
8937
- if (fn) {
8938
- try {
8939
- const viewActions2 = fn(ctx) ?? [];
8940
- result.push(...viewActions2);
8941
- } catch (err) {
8942
- console.error(`[uidex] view "${view.id}" actions() threw`, err);
8943
- }
8944
- }
8945
- if (globalActions) {
8605
+ function resolveProp(view, ctx, value, propName, fallback) {
8606
+ if (typeof value === "function") {
8946
8607
  try {
8947
- const globals = globalActions(ctx) ?? [];
8948
- result.push(...globals);
8608
+ return value(ctx) ?? fallback;
8949
8609
  } catch (err) {
8950
- console.error(`[uidex] globalActions() threw`, err);
8610
+ console.error(`[uidex] view "${view.id}" ${propName}() threw`, err);
8611
+ return fallback;
8951
8612
  }
8952
8613
  }
8953
- return result;
8614
+ return value ?? fallback;
8954
8615
  }
8955
8616
  function createViewStack(options) {
8956
8617
  const { container, views, session, registry, highlight } = options;
@@ -9018,39 +8679,40 @@ function createViewStack(options) {
9018
8679
  color: KIND_STYLE[top.ctx.ref.kind].color
9019
8680
  });
9020
8681
  }
9021
- function updateChrome() {
9022
- if (!shell) return;
9023
- const top = mounted[mounted.length - 1];
9024
- if (!top) return;
8682
+ function updateNavButtons(shell2, top) {
9025
8683
  const atRoot = mounted.length <= 1 && top.view.id === "command-palette";
9026
- shell.backBtn.hidden = atRoot;
9027
- shell.searchIcon.hidden = !atRoot;
8684
+ shell2.backBtn.hidden = atRoot;
8685
+ shell2.searchIcon.hidden = !atRoot;
8686
+ }
8687
+ function updateTitle(shell2, top) {
9028
8688
  const searchable = top.view.searchable !== false;
9029
- shell.searchInput.hidden = !searchable;
9030
- const titleText = searchable ? "" : resolveTitle(top.view, top.ctx);
9031
- shell.headerTitle.textContent = titleText;
9032
- shell.headerTitle.hidden = searchable || !titleText;
9033
- shell.footerLeft.replaceChildren();
8689
+ shell2.searchInput.hidden = !searchable;
8690
+ const titleText = searchable ? "" : resolveProp(top.view, top.ctx, top.view.title, "title", "");
8691
+ shell2.headerTitle.textContent = titleText;
8692
+ shell2.headerTitle.hidden = searchable || !titleText;
8693
+ }
8694
+ function updateFooterChip(shell2, top) {
8695
+ shell2.footerLeft.replaceChildren();
9034
8696
  const refEntity = top.ctx.ref ? top.ctx.registry.get(top.ctx.ref.kind, top.ctx.ref.id) : null;
9035
8697
  if (refEntity) {
9036
- shell.footerLeft.append(
8698
+ shell2.footerLeft.append(
9037
8699
  renderKindChip({
9038
8700
  entity: refEntity,
9039
8701
  withKindName: true
9040
8702
  })
9041
8703
  );
9042
8704
  } else {
9043
- shell.footerLeft.append(shell.logo);
8705
+ shell2.footerLeft.append(shell2.logo);
9044
8706
  }
9045
- hintChangeSub?.();
9046
- hintChangeSub = null;
9047
- highlightActionsSub?.();
9048
- highlightActionsSub = null;
9049
- shell.footerRight.replaceChildren();
9050
- const footerItems = [];
9051
- const enterHint = resolveHints(top.view, top.ctx).find(
9052
- (h) => h.key.includes("\u21B5")
9053
- );
8707
+ }
8708
+ function buildEnterHint(top, footerItems) {
8709
+ const enterHint = resolveProp(
8710
+ top.view,
8711
+ top.ctx,
8712
+ top.view.hints,
8713
+ "hints",
8714
+ []
8715
+ ).find((h) => h.key.includes("\u21B5"));
9054
8716
  const src = top.mounted.submitIntent;
9055
8717
  if (src) {
9056
8718
  const key = enterHint?.key ?? "\u21B5";
@@ -9071,28 +8733,52 @@ function createViewStack(options) {
9071
8733
  } else if (enterHint) {
9072
8734
  footerItems.push(createHint(enterHint.key, enterHint.label));
9073
8735
  }
8736
+ }
8737
+ function buildActions(shell2, top, footerItems) {
9074
8738
  const actionsDivider = createFooterDivider();
9075
- const viewActions2 = resolveActions(top.view, top.ctx);
8739
+ const viewActions2 = resolveProp(
8740
+ top.view,
8741
+ top.ctx,
8742
+ top.view.actions,
8743
+ "actions",
8744
+ []
8745
+ );
9076
8746
  const hlSrc = top.mounted.highlightActions;
9077
8747
  const syncActions = () => {
9078
8748
  const hlActions = hlSrc?.get() ?? [];
9079
8749
  const combined = [...hlActions, ...viewActions2];
9080
- shell.actionsPopup.setActions(combined);
8750
+ shell2.actionsPopup.setActions(combined);
9081
8751
  const visible = combined.length > 0;
9082
- shell.actionsPopup.trigger.hidden = !visible;
8752
+ shell2.actionsPopup.trigger.hidden = !visible;
9083
8753
  actionsDivider.hidden = !visible || footerItems.length === 0;
9084
8754
  };
9085
8755
  if (hlSrc) {
9086
8756
  highlightActionsSub = hlSrc.subscribe(syncActions);
9087
8757
  }
9088
8758
  for (let i = 0; i < footerItems.length; i++) {
9089
- if (i > 0) shell.footerRight.append(createFooterDivider());
9090
- shell.footerRight.append(footerItems[i]);
8759
+ if (i > 0) shell2.footerRight.append(createFooterDivider());
8760
+ shell2.footerRight.append(footerItems[i]);
9091
8761
  }
9092
- shell.footerRight.append(actionsDivider);
9093
- shell.footerRight.append(shell.actionsPopup.trigger);
8762
+ shell2.footerRight.append(actionsDivider);
8763
+ shell2.footerRight.append(shell2.actionsPopup.trigger);
9094
8764
  syncActions();
9095
8765
  }
8766
+ function updateChrome() {
8767
+ if (!shell) return;
8768
+ const top = mounted[mounted.length - 1];
8769
+ if (!top) return;
8770
+ updateNavButtons(shell, top);
8771
+ updateTitle(shell, top);
8772
+ updateFooterChip(shell, top);
8773
+ hintChangeSub?.();
8774
+ hintChangeSub = null;
8775
+ highlightActionsSub?.();
8776
+ highlightActionsSub = null;
8777
+ shell.footerRight.replaceChildren();
8778
+ const footerItems = [];
8779
+ buildEnterHint(top, footerItems);
8780
+ buildActions(shell, top, footerItems);
8781
+ }
9096
8782
  function render2() {
9097
8783
  if (!container.isConnected) return;
9098
8784
  const stack = session.getState().stack;
@@ -9189,7 +8875,7 @@ function createViewStack(options) {
9189
8875
 
9190
8876
  // src/browser/views/built-in/ids.ts
9191
8877
  var BUILT_IN_VIEW_IDS = {
9192
- archiveReason: "archive-reason",
8878
+ closeReason: "close-reason",
9193
8879
  commandPalette: "command-palette",
9194
8880
  elements: "elements",
9195
8881
  entityReports: "entity-reports",
@@ -9240,13 +8926,13 @@ function parentDetail(ref2) {
9240
8926
  return { id: DETAIL_VIEW_FOR_KIND[ref2.kind], ref: ref2 };
9241
8927
  }
9242
8928
 
9243
- // src/browser/views/built-in/archive-reason.ts
8929
+ // src/browser/views/built-in/close-reason.ts
9244
8930
  var import_lucide11 = require("lucide");
9245
8931
  var pendingReportId = null;
9246
- var afterArchive = null;
9247
- function setArchiveTarget(reportId, onDone) {
8932
+ var afterClose = null;
8933
+ function setCloseTarget(reportId, onDone) {
9248
8934
  pendingReportId = reportId;
9249
- afterArchive = onDone;
8935
+ afterClose = onDone;
9250
8936
  }
9251
8937
  function leadingIcon(iconNode) {
9252
8938
  return () => {
@@ -9261,16 +8947,16 @@ function spinnerTile() {
9261
8947
  return createIconTile(spinner);
9262
8948
  }
9263
8949
  var REASONS = [
9264
- { value: "fixed", label: "Fixed", icon: import_lucide11.CircleCheck },
8950
+ { value: "fixed", label: "Resolved", icon: import_lucide11.CircleCheck },
9265
8951
  { value: "not_a_bug", label: "Not a bug", icon: import_lucide11.BugOff },
9266
8952
  { value: "duplicate", label: "Duplicate", icon: import_lucide11.Copy },
9267
8953
  { value: "wont_fix", label: "Won't fix", icon: import_lucide11.Ban }
9268
8954
  ];
9269
- var archiveReasonView = {
9270
- id: BUILT_IN_VIEW_IDS.archiveReason,
8955
+ var closeReasonView = {
8956
+ id: BUILT_IN_VIEW_IDS.closeReason,
9271
8957
  matches: () => false,
9272
8958
  parent: (ref2) => ref2 ? { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 } : null,
9273
- title: "Archive reason",
8959
+ title: "Close reason",
9274
8960
  searchable: false,
9275
8961
  focusTarget: (host) => host.querySelector("[data-uidex-list-content]"),
9276
8962
  surface: () => ({ kind: "list", id: "unused", items: [] }),
@@ -9283,12 +8969,12 @@ var archiveReasonView = {
9283
8969
  }));
9284
8970
  const surface = {
9285
8971
  kind: "list",
9286
- id: "uidex-archive-reason",
8972
+ id: "uidex-close-reason",
9287
8973
  searchable: false,
9288
8974
  items,
9289
8975
  emptyLabel: "No reasons available",
9290
8976
  onSelect: async (item) => {
9291
- if (busy || !pendingReportId || !ctx.registry.archiveReport) return;
8977
+ if (busy || !pendingReportId || !ctx.registry.closeReport) return;
9292
8978
  busy = true;
9293
8979
  const row = root.querySelector(
9294
8980
  `[data-uidex-item-value="${item.value}"]`
@@ -9300,13 +8986,10 @@ var archiveReasonView = {
9300
8986
  row.style.pointerEvents = "none";
9301
8987
  }
9302
8988
  try {
9303
- await ctx.registry.archiveReport(
9304
- pendingReportId,
9305
- item.value
9306
- );
9307
- const done = afterArchive;
8989
+ await ctx.registry.closeReport(pendingReportId, item.value);
8990
+ const done = afterClose;
9308
8991
  pendingReportId = null;
9309
- afterArchive = null;
8992
+ afterClose = null;
9310
8993
  done?.();
9311
8994
  } catch {
9312
8995
  busy = false;
@@ -9324,7 +9007,7 @@ var archiveReasonView = {
9324
9007
  }
9325
9008
  const error = document.createElement("p");
9326
9009
  error.className = "text-destructive px-4 py-2 text-xs";
9327
- error.textContent = "Archive failed \u2014 try again";
9010
+ error.textContent = "Close failed \u2014 try again";
9328
9011
  root.querySelector("[data-uidex-list-content]")?.prepend(error);
9329
9012
  setTimeout(() => error.remove(), 3e3);
9330
9013
  }
@@ -9643,6 +9326,7 @@ function createCommandPaletteView(shortcut) {
9643
9326
 
9644
9327
  // src/browser/internal/screenshot.ts
9645
9328
  var import_modern_screenshot = require("modern-screenshot");
9329
+ var WEBP_QUALITY = 0.85;
9646
9330
  function resolveBackgroundColor(el2) {
9647
9331
  let node = el2;
9648
9332
  while (node) {
@@ -9656,8 +9340,7 @@ function isUidexChrome(node) {
9656
9340
  if (node.nodeType !== Node.ELEMENT_NODE) return false;
9657
9341
  const el2 = node;
9658
9342
  if (el2.shadowRoot !== null) return true;
9659
- const cls = el2.classList;
9660
- return cls.contains(SURFACE_HOST_CLASS) || cls.contains(SURFACE_CONTAINER_CLASS);
9343
+ return el2.classList.contains(SURFACE_HOST_CLASS);
9661
9344
  }
9662
9345
  async function captureScreenshot(options = {}) {
9663
9346
  if (typeof document === "undefined") {
@@ -9669,8 +9352,10 @@ async function captureScreenshot(options = {}) {
9669
9352
  const scale = options.maxWidth && width > options.maxWidth ? options.maxWidth / width : 1;
9670
9353
  const padding = 16;
9671
9354
  const height = target.scrollHeight || target.clientHeight;
9672
- return (0, import_modern_screenshot.domToPng)(target, {
9355
+ const encode = options.format === "png" ? import_modern_screenshot.domToPng : import_modern_screenshot.domToWebp;
9356
+ return encode(target, {
9673
9357
  scale,
9358
+ quality: WEBP_QUALITY,
9674
9359
  width: width + padding * 2,
9675
9360
  height: height + padding * 2,
9676
9361
  backgroundColor: resolveBackgroundColor(target),
@@ -9726,12 +9411,6 @@ function collectFlowsTouching(ctx, targetId) {
9726
9411
  }
9727
9412
 
9728
9413
  // src/browser/views/builder/detail-builder.ts
9729
- var DOM_BACKED_KINDS2 = /* @__PURE__ */ new Set([
9730
- "element",
9731
- "region",
9732
- "widget",
9733
- "primitive"
9734
- ]);
9735
9414
  var DETAIL_HINTS = [{ key: "\u21B5", label: "Open" }];
9736
9415
  function copyPathAction(ref2, loc) {
9737
9416
  const identifier = `${ref2.kind}:${ref2.id}`;
@@ -9833,7 +9512,10 @@ function copyScreenshotAction(ref2) {
9833
9512
  const target = resolveEntityElement(ref2) ?? void 0;
9834
9513
  const dataUrl = await captureScreenshot({
9835
9514
  target,
9836
- maxWidth: 1280
9515
+ maxWidth: 1280,
9516
+ // Clipboard write requires PNG — the async Clipboard API rejects
9517
+ // image/webp (`ClipboardItem` throws on Chrome/Safari).
9518
+ format: "png"
9837
9519
  });
9838
9520
  const res = await fetch(dataUrl);
9839
9521
  const blob = await res.blob();
@@ -9884,7 +9566,7 @@ function createEntityDetailView(config) {
9884
9566
  if (cloud?.integrations.getCachedConfig()?.hasJira) {
9885
9567
  actions.push({ ...jiraAction(ctx.ref), group: "Report" });
9886
9568
  }
9887
- if (DOM_BACKED_KINDS2.has(kind) && resolveEntityElement(ctx.ref)) {
9569
+ if (DOM_BACKED_KINDS.has(kind) && resolveEntityElement(ctx.ref)) {
9888
9570
  actions.push({ ...highlightElementAction(ctx.ref), group: "Inspect" });
9889
9571
  actions.push({ ...copyScreenshotAction(ctx.ref), group: "Inspect" });
9890
9572
  }
@@ -9910,7 +9592,7 @@ function createEntityDetailView(config) {
9910
9592
  sections.push(s);
9911
9593
  }
9912
9594
  }
9913
- if (!DOM_BACKED_KINDS2.has(kind)) {
9595
+ if (!DOM_BACKED_KINDS.has(kind)) {
9914
9596
  sections.push({
9915
9597
  id: "composes",
9916
9598
  label: SECTION_LABELS.contains,
@@ -9918,7 +9600,7 @@ function createEntityDetailView(config) {
9918
9600
  filterable: true
9919
9601
  });
9920
9602
  }
9921
- if (DOM_BACKED_KINDS2.has(kind) && meta?.features?.length) {
9603
+ if (DOM_BACKED_KINDS.has(kind) && meta?.features?.length) {
9922
9604
  const featureEntities = meta.features.map((fId) => ctx.registry.get("feature", fId)).filter((e) => !!e);
9923
9605
  if (featureEntities.length > 0) {
9924
9606
  sections.push({
@@ -9948,35 +9630,14 @@ function createEntityDetailView(config) {
9948
9630
  }
9949
9631
 
9950
9632
  // src/browser/views/built-in/entity-detail.ts
9951
- function collectDomParents(ctx, ref2) {
9952
- const el2 = resolveEntityElement(ref2);
9953
- if (!el2) return [];
9954
- const parents = [];
9955
- const seen = /* @__PURE__ */ new Set();
9956
- let node = el2.parentElement;
9957
- while (node) {
9958
- if (node instanceof HTMLElement) {
9959
- for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
9960
- const id = node.getAttribute(attr);
9961
- if (id) {
9962
- const key = `${kind}:${id}`;
9963
- if (!seen.has(key)) {
9964
- seen.add(key);
9965
- const entity = ctx.registry.get(kind, id);
9966
- if (entity) parents.push(entity);
9967
- }
9968
- }
9969
- }
9970
- }
9971
- node = node.parentElement;
9972
- }
9973
- return parents;
9633
+ function collectFeatureConsumers(ctx, featureId) {
9634
+ return ctx.registry.list("page").filter((page) => page.meta?.features?.includes(featureId));
9974
9635
  }
9975
- function usedBySection(ctx, ref2) {
9636
+ function usedBySection(ctx, featureId) {
9976
9637
  return {
9977
9638
  id: "used-by",
9978
9639
  label: SECTION_LABELS.usedBy,
9979
- entities: collectDomParents(ctx, ref2),
9640
+ entities: collectFeatureConsumers(ctx, featureId),
9980
9641
  filterable: true
9981
9642
  };
9982
9643
  }
@@ -9997,9 +9658,7 @@ var featureDetailView = createEntityDetailView({
9997
9658
  id: "feature-detail",
9998
9659
  kind: "feature",
9999
9660
  fallbackTitle: "Feature",
10000
- extraSections: (ctx, entity) => [
10001
- usedBySection(ctx, { kind: "feature", id: entity.id })
10002
- ]
9661
+ extraSections: (ctx, entity) => [usedBySection(ctx, entity.id)]
10003
9662
  });
10004
9663
  var regionDetailView = createEntityDetailView({
10005
9664
  id: "region-detail",
@@ -10127,6 +9786,15 @@ var reportDetailView = {
10127
9786
  }
10128
9787
  if (report.screenshot) {
10129
9788
  sections.push({ id: "screenshot", url: report.screenshot });
9789
+ } else {
9790
+ const pins = ctx.cloud?.pins;
9791
+ const fetchScreenshot = pins?.screenshot;
9792
+ if (pins && fetchScreenshot) {
9793
+ sections.push({
9794
+ id: "screenshot",
9795
+ load: () => fetchScreenshot.call(pins, report.id)
9796
+ });
9797
+ }
10130
9798
  }
10131
9799
  const metaEntries = [];
10132
9800
  if (report.url) metaEntries.push({ label: "URL", value: report.url });
@@ -10137,14 +9805,14 @@ var reportDetailView = {
10137
9805
  sections.push({ id: "metadata", entries: metaEntries });
10138
9806
  }
10139
9807
  const actions = [];
10140
- if (ctx.registry.archiveReport) {
9808
+ if (ctx.registry.closeReport) {
10141
9809
  actions.push({
10142
- id: "archive",
10143
- label: "Archive",
9810
+ id: "close",
9811
+ label: "Close",
10144
9812
  icon: "archive-x",
10145
9813
  run: () => {
10146
- setArchiveTarget(report.id, () => ctx.pop());
10147
- ctx.push({ id: BUILT_IN_VIEW_IDS.archiveReason });
9814
+ setCloseTarget(report.id, () => ctx.pop());
9815
+ ctx.push({ id: BUILT_IN_VIEW_IDS.closeReason });
10148
9816
  }
10149
9817
  });
10150
9818
  }
@@ -10182,13 +9850,13 @@ function reportToItem(report, ctx) {
10182
9850
  const label = raw.length > 80 ? raw.slice(0, 80) + "\u2026" : raw;
10183
9851
  const kind = ctx.ref?.kind ?? "element";
10184
9852
  const actions = [];
10185
- if (ctx.registry.archiveReport) {
9853
+ if (ctx.registry.closeReport) {
10186
9854
  actions.push({
10187
- id: `archive-${report.id}`,
10188
- label: "Archive",
9855
+ id: `close-${report.id}`,
9856
+ label: "Close",
10189
9857
  perform: () => {
10190
- setArchiveTarget(report.id, () => ctx.pop());
10191
- ctx.push({ id: BUILT_IN_VIEW_IDS.archiveReason });
9858
+ setCloseTarget(report.id, () => ctx.pop());
9859
+ ctx.push({ id: BUILT_IN_VIEW_IDS.closeReason });
10192
9860
  }
10193
9861
  });
10194
9862
  }
@@ -10341,7 +10009,7 @@ function capitalize2(s) {
10341
10009
  return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
10342
10010
  }
10343
10011
  function renderPayloadMarkdown(payload) {
10344
- const heading = payload.title ?? `${capitalize2(payload.type)} Report`;
10012
+ const heading = payload.title ?? `${capitalize2(payload.type ?? "")} Report`;
10345
10013
  const ctx = payload.context;
10346
10014
  const lines = [
10347
10015
  `# ${heading}`,
@@ -10410,12 +10078,6 @@ var reportFields = [
10410
10078
  ];
10411
10079
 
10412
10080
  // src/browser/views/built-in/report/view-builder.ts
10413
- var DOM_BACKED_KINDS3 = /* @__PURE__ */ new Set([
10414
- "element",
10415
- "region",
10416
- "widget",
10417
- "primitive"
10418
- ]);
10419
10081
  var KIND_TO_ATTR = new Map(
10420
10082
  UIDEX_ATTR_TO_KIND.map(([attr, kind]) => [kind, attr])
10421
10083
  );
@@ -10486,7 +10148,7 @@ function createReportView(config) {
10486
10148
  const cloud = resolveCloud(ctx);
10487
10149
  const extra = extraFields ? extraFields(ctx) : [];
10488
10150
  const fields = [...extra, ...baseFields ?? reportFields];
10489
- const isDomBacked = ctx.ref != null && DOM_BACKED_KINDS3.has(ctx.ref.kind);
10151
+ const isDomBacked = ctx.ref != null && DOM_BACKED_KINDS.has(ctx.ref.kind);
10490
10152
  const targetEl = ctx.ref && isDomBacked ? resolveElement(ctx.ref) : null;
10491
10153
  const screenshotPromise = isDomBacked ? captureScreenshot({
10492
10154
  target: targetEl ?? void 0,
@@ -11063,7 +10725,7 @@ var pinSettingsView = {
11063
10725
  // src/browser/views/built-in/index.ts
11064
10726
  function buildDefaultViews(shortcut) {
11065
10727
  return [
11066
- archiveReasonView,
10728
+ closeReasonView,
11067
10729
  createCommandPaletteView(shortcut),
11068
10730
  explorePageView,
11069
10731
  componentDetailView,
@@ -11091,6 +10753,27 @@ function buildDefaultViews(shortcut) {
11091
10753
  }
11092
10754
 
11093
10755
  // src/browser/create-uidex.ts
10756
+ function getVisibleEntities() {
10757
+ if (typeof document === "undefined") return [];
10758
+ const selector = UIDEX_ATTR_TO_KIND.map(([a]) => `[${a}]`).join(",");
10759
+ const nodes = document.querySelectorAll(selector);
10760
+ const ids = [];
10761
+ const seen = /* @__PURE__ */ new Set();
10762
+ for (const node of nodes) {
10763
+ if (node.closest(SURFACE_IGNORE_SELECTOR)) continue;
10764
+ for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
10765
+ const id = node.getAttribute(attr);
10766
+ if (!id) continue;
10767
+ const key = `${kind}:${id}`;
10768
+ if (!seen.has(key)) {
10769
+ seen.add(key);
10770
+ ids.push(key);
10771
+ }
10772
+ break;
10773
+ }
10774
+ }
10775
+ return ids;
10776
+ }
11094
10777
  function createUidex(options = {}) {
11095
10778
  const registry = createRegistry();
11096
10779
  const inspectorRef = { current: null };
@@ -11125,7 +10808,7 @@ function createUidex(options = {}) {
11125
10808
  const views = createRouter({ session });
11126
10809
  const cloud = options.cloud ?? null;
11127
10810
  const ingestOpts = resolveIngestOptions(options.ingest, cloud !== null);
11128
- const ingest = ingestOpts ? createIngest({ session, ...ingestOpts }) : null;
10811
+ const ingest = ingestOpts ? createIngest(ingestOpts) : null;
11129
10812
  if (options.defaultViews !== false) {
11130
10813
  for (const view of buildDefaultViews(options.shortcut)) views.add(view);
11131
10814
  }
@@ -11135,6 +10818,94 @@ function createUidex(options = {}) {
11135
10818
  let shadowRoot = null;
11136
10819
  const mountCleanup = createCleanupStack();
11137
10820
  let mounted = false;
10821
+ function syncReportsToRegistry() {
10822
+ const layer = pinLayerRef.current;
10823
+ if (!layer) return;
10824
+ const byEntity = /* @__PURE__ */ new Map();
10825
+ for (const pin of layer.getAllPins()) {
10826
+ const cid = pin.entity ?? "";
10827
+ if (!cid) continue;
10828
+ const list = byEntity.get(cid);
10829
+ if (list) list.push(pin);
10830
+ else byEntity.set(cid, [pin]);
10831
+ }
10832
+ for (const [cid, pins] of byEntity) {
10833
+ const ref2 = parseComponentRef(cid);
10834
+ registry.setReports(ref2.kind, ref2.id, pins);
10835
+ }
10836
+ }
10837
+ function setupKeyBindings(root, viewStack) {
10838
+ const bindings = bindShadowKeys({
10839
+ shadowRoot: root,
10840
+ session,
10841
+ getActionsPopup: () => viewStack.getActionsPopup(),
10842
+ getPinLayer: () => pinLayerRef.current,
10843
+ shortcut: options.shortcut
10844
+ });
10845
+ mountCleanup.add(bindings);
10846
+ return bindings;
10847
+ }
10848
+ function setupPinLayer(root, shell, channel, getCurrentRoute, getMatchMode, getPathname) {
10849
+ if (cloud) {
10850
+ registry.closeReport = async (reportId, status) => {
10851
+ pinLayerRef.current?.removePin(reportId);
10852
+ await cloud.pins.close(
10853
+ reportId,
10854
+ status
10855
+ );
10856
+ syncReportsToRegistry();
10857
+ };
10858
+ }
10859
+ const pinLayer = createPinLayer({
10860
+ container: root,
10861
+ onOpenPinDetail: (componentId, reportId) => {
10862
+ const ref2 = parseComponentRef(componentId);
10863
+ setSelectedReportId(reportId);
10864
+ const entry = { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 };
10865
+ session.mode.transition.enterViewing(views.buildStack(entry));
10866
+ },
10867
+ onHoverPin: (anchor, componentId) => {
10868
+ const overlay = overlayRef.current;
10869
+ if (!overlay) return;
10870
+ if (!anchor || !componentId) {
10871
+ overlay.hide();
10872
+ return;
10873
+ }
10874
+ overlay.show(anchor, {
10875
+ color: isDarkMode() ? "#e4e4e7" : "#27272a"
10876
+ });
10877
+ },
10878
+ onPinsChanged: syncReportsToRegistry
10879
+ });
10880
+ shell.menuBar.setPinLayer(pinLayer);
10881
+ pinLayerRef.current = pinLayer;
10882
+ mountCleanup.add(() => {
10883
+ pinLayer.destroy();
10884
+ pinLayerRef.current = null;
10885
+ registry.closeReport = void 0;
10886
+ });
10887
+ if (cloud) {
10888
+ const detach = pinLayer.attachCloud({
10889
+ cloud,
10890
+ channel,
10891
+ getRoute: getCurrentRoute,
10892
+ getPathname,
10893
+ getVisibleEntities,
10894
+ getMatchMode,
10895
+ onError: (err) => console.warn("[uidex] pin fetch failed", err)
10896
+ });
10897
+ mountCleanup.add(detach);
10898
+ if (channel && typeof window !== "undefined") {
10899
+ const onRouteChange = () => {
10900
+ const route = getCurrentRoute();
10901
+ channel?.joinRoute(route);
10902
+ void pinLayer.refresh();
10903
+ };
10904
+ const detachRoute = bindRouteChange(onRouteChange);
10905
+ mountCleanup.add(detachRoute);
10906
+ }
10907
+ }
10908
+ }
11138
10909
  function mount(target) {
11139
10910
  if (mounted) return;
11140
10911
  const mountTarget = target ?? (typeof document !== "undefined" ? document.body : null);
@@ -11143,28 +10914,7 @@ function createUidex(options = {}) {
11143
10914
  }
11144
10915
  const getPathname = () => typeof location !== "undefined" ? location.pathname : "/";
11145
10916
  const getCurrentRoute = () => options.getRoute?.() ?? findCurrentRoutePath(registry) ?? getPathname();
11146
- const getVisibleEntities = () => {
11147
- if (typeof document === "undefined") return [];
11148
- const selector = UIDEX_ATTR_TO_KIND.map(([a]) => `[${a}]`).join(",");
11149
- const nodes = document.querySelectorAll(selector);
11150
- const ids = [];
11151
- const seen = /* @__PURE__ */ new Set();
11152
- for (const node of nodes) {
11153
- if (node.closest(SURFACE_IGNORE_SELECTOR)) continue;
11154
- for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
11155
- const id = node.getAttribute(attr);
11156
- if (!id) continue;
11157
- const key = `${kind}:${id}`;
11158
- if (!seen.has(key)) {
11159
- seen.add(key);
11160
- ids.push(key);
11161
- }
11162
- break;
11163
- }
11164
- }
11165
- return ids;
11166
- };
11167
- const getMatchMode = () => (typeof localStorage !== "undefined" ? localStorage.getItem("uidex:pin-match-mode") : null) ?? "route";
10917
+ const getMatchMode = () => readMode();
11168
10918
  let channel = null;
11169
10919
  if (cloud && options.user) {
11170
10920
  channel = cloud.realtime.connect({
@@ -11185,7 +10935,6 @@ function createUidex(options = {}) {
11185
10935
  onSelect: (match) => {
11186
10936
  const route = views.resolve(match.ref);
11187
10937
  if (!route) return;
11188
- session.select(match.ref);
11189
10938
  const entry = { id: route.view.id, ref: match.ref };
11190
10939
  session.mode.transition.enterViewing(views.buildStack(entry));
11191
10940
  }
@@ -11236,92 +10985,15 @@ function createUidex(options = {}) {
11236
10985
  });
11237
10986
  mountCleanup.add(viewStack);
11238
10987
  if (shadowRoot) {
11239
- keyBindings = bindShadowKeys({
10988
+ keyBindings = setupKeyBindings(shadowRoot, viewStack);
10989
+ setupPinLayer(
11240
10990
  shadowRoot,
11241
- session,
11242
- getActionsPopup: () => viewStack.getActionsPopup(),
11243
- getPinLayer: () => pinLayerRef.current,
11244
- shortcut: options.shortcut
11245
- });
11246
- mountCleanup.add(keyBindings);
11247
- }
11248
- if (shadowRoot) {
11249
- const syncReportsToRegistry = () => {
11250
- const layer = pinLayerRef.current;
11251
- if (!layer) return;
11252
- const byEntity = /* @__PURE__ */ new Map();
11253
- for (const pin of layer.getAllPins()) {
11254
- const cid = pin.entity ?? "";
11255
- if (!cid) continue;
11256
- const list = byEntity.get(cid);
11257
- if (list) list.push(pin);
11258
- else byEntity.set(cid, [pin]);
11259
- }
11260
- for (const [cid, pins] of byEntity) {
11261
- const ref2 = parseComponentRef(cid);
11262
- registry.setReports(ref2.kind, ref2.id, pins);
11263
- }
11264
- };
11265
- if (cloud) {
11266
- registry.archiveReport = async (reportId, reason) => {
11267
- pinLayerRef.current?.removePin(reportId);
11268
- await cloud.pins.archive(
11269
- reportId,
11270
- reason
11271
- );
11272
- syncReportsToRegistry();
11273
- };
11274
- }
11275
- const pinLayer = createPinLayer({
11276
- container: shadowRoot,
11277
- currentBranch: options.git?.branch ?? null,
11278
- onOpenPinDetail: (componentId, reportId) => {
11279
- const ref2 = parseComponentRef(componentId);
11280
- setSelectedReportId(reportId);
11281
- const entry = { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 };
11282
- session.mode.transition.enterViewing(views.buildStack(entry));
11283
- },
11284
- onHoverPin: (anchor, componentId) => {
11285
- const overlay = overlayRef.current;
11286
- if (!overlay) return;
11287
- if (!anchor || !componentId) {
11288
- overlay.hide();
11289
- return;
11290
- }
11291
- overlay.show(anchor, {
11292
- color: isDarkMode() ? "#e4e4e7" : "#27272a"
11293
- });
11294
- },
11295
- onPinsChanged: syncReportsToRegistry
11296
- });
11297
- shell.menuBar.setPinLayer(pinLayer);
11298
- pinLayerRef.current = pinLayer;
11299
- mountCleanup.add(() => {
11300
- pinLayer.destroy();
11301
- pinLayerRef.current = null;
11302
- registry.archiveReport = void 0;
11303
- });
11304
- if (cloud) {
11305
- const detach = pinLayer.attachCloud({
11306
- cloud,
11307
- channel,
11308
- getRoute: getCurrentRoute,
11309
- getPathname,
11310
- getVisibleEntities,
11311
- getMatchMode,
11312
- onError: (err) => console.warn("[uidex] pin fetch failed", err)
11313
- });
11314
- mountCleanup.add(detach);
11315
- if (channel && typeof window !== "undefined") {
11316
- const onRouteChange = () => {
11317
- const route = getCurrentRoute();
11318
- channel?.joinRoute(route);
11319
- void pinLayer.refresh();
11320
- };
11321
- const detachRoute = bindRouteChange(onRouteChange);
11322
- mountCleanup.add(detachRoute);
11323
- }
11324
- }
10991
+ shell,
10992
+ channel,
10993
+ getCurrentRoute,
10994
+ getMatchMode,
10995
+ getPathname
10996
+ );
11325
10997
  }
11326
10998
  if (ingest) {
11327
10999
  ingest.start();
@@ -11352,7 +11024,6 @@ function createUidex(options = {}) {
11352
11024
  0 && (module.exports = {
11353
11025
  ENTITY_KINDS,
11354
11026
  KIND_STYLE,
11355
- SURFACE_CONTAINER_CLASS,
11356
11027
  SURFACE_HOST_CLASS,
11357
11028
  SURFACE_IGNORE_SELECTOR,
11358
11029
  UnknownEntityKindError,
@@ -11367,7 +11038,6 @@ function createUidex(options = {}) {
11367
11038
  createCommandPaletteView,
11368
11039
  createConsoleCapture,
11369
11040
  createCursorTooltip,
11370
- createHighlightController,
11371
11041
  createIngest,
11372
11042
  createInspector,
11373
11043
  createMenuBar,
@@ -11384,13 +11054,11 @@ function createUidex(options = {}) {
11384
11054
  createThemeDetector,
11385
11055
  createUidex,
11386
11056
  createViewStack,
11387
- defaultResolveMatch,
11388
11057
  displayName,
11389
11058
  entityKey,
11390
11059
  featureDetailView,
11391
11060
  flowDetailView,
11392
11061
  formatShortcutLabel,
11393
- getNativeFetch,
11394
11062
  isMetaKind,
11395
11063
  nativeFetch,
11396
11064
  pageDetailView,