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.js CHANGED
@@ -87,13 +87,14 @@ function createRegistry() {
87
87
  };
88
88
  const getPatternsForKind = (kind) => {
89
89
  const cached = patternCache.get(kind);
90
- if (cached !== void 0)
91
- return cached;
90
+ if (cached !== void 0) return cached;
92
91
  const patterns = [];
93
92
  for (const [key, entity] of store[kind]) {
94
- if (key.endsWith("*")) {
93
+ if (key.includes("*")) {
94
+ const segments = key.split("*");
95
95
  patterns.push({
96
- prefix: key.slice(0, -1),
96
+ segments,
97
+ staticLength: segments.reduce((n, s) => n + s.length, 0),
97
98
  entity
98
99
  });
99
100
  }
@@ -104,13 +105,25 @@ function createRegistry() {
104
105
  );
105
106
  return patterns;
106
107
  };
108
+ const matchesSegments = (segments, id) => {
109
+ const first = segments[0];
110
+ const last = segments[segments.length - 1];
111
+ if (!id.startsWith(first)) return false;
112
+ let pos = first.length;
113
+ for (let i = 1; i < segments.length - 1; i++) {
114
+ const idx = id.indexOf(segments[i], pos);
115
+ if (idx === -1) return false;
116
+ pos = idx + segments[i].length;
117
+ }
118
+ return id.endsWith(last) && id.length - last.length >= pos;
119
+ };
107
120
  const matchPattern = (kind, id) => {
108
121
  assertEntityKind(kind);
109
122
  const patterns = getPatternsForKind(kind);
110
123
  if (patterns.length === 0) return void 0;
111
124
  let best;
112
125
  for (const entry of patterns) {
113
- if (id.startsWith(entry.prefix) && (best === void 0 || entry.prefix.length > best.prefix.length)) {
126
+ if (matchesSegments(entry.segments, id) && (best === void 0 || entry.staticLength > best.staticLength)) {
114
127
  best = entry;
115
128
  }
116
129
  }
@@ -339,9 +352,6 @@ function createConsoleCapture(options = {}) {
339
352
 
340
353
  // src/browser/ingest/native-fetch.ts
341
354
  var nativeFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
342
- function getNativeFetch() {
343
- return nativeFetch;
344
- }
345
355
 
346
356
  // src/browser/ingest/network.ts
347
357
  var DEFAULT_LIMIT2 = 20;
@@ -424,7 +434,7 @@ function createNetworkCapture(options = {}) {
424
434
 
425
435
  // src/browser/ingest/index.ts
426
436
  function createIngest(options = {}) {
427
- const { session, ...opts } = options;
437
+ const opts = options;
428
438
  const wantConsole = opts.captureConsole !== false;
429
439
  const wantNetwork = opts.captureNetwork !== false;
430
440
  const consoleCapture = wantConsole ? createConsoleCapture({
@@ -443,14 +453,12 @@ function createIngest(options = {}) {
443
453
  consoleCapture?.start();
444
454
  networkCapture?.start();
445
455
  active = Boolean(consoleCapture?.isActive || networkCapture?.isActive);
446
- if (active) session?.setIngest(true);
447
456
  }
448
457
  function stop() {
449
458
  if (!active) return;
450
459
  consoleCapture?.stop();
451
460
  networkCapture?.stop();
452
461
  active = false;
453
- session?.setIngest(false);
454
462
  }
455
463
  return {
456
464
  start,
@@ -572,8 +580,7 @@ var COMMAND_PALETTE_ENTRY = {
572
580
  function createModeStore(options) {
573
581
  const { nav, bindings } = options;
574
582
  const store = createStore(() => ({
575
- mode: "idle",
576
- inspectorActive: false
583
+ mode: "idle"
577
584
  }));
578
585
  const transition = {
579
586
  openPalette() {
@@ -582,17 +589,17 @@ function createModeStore(options) {
582
589
  bindings?.destroyInspector?.();
583
590
  }
584
591
  nav.nav.reset([COMMAND_PALETTE_ENTRY]);
585
- store.setState({ mode: "palette", inspectorActive: false });
592
+ store.setState({ mode: "palette" });
586
593
  },
587
594
  openInspector() {
588
595
  bindings?.mountInspector?.();
589
596
  nav.nav.clear();
590
- store.setState({ mode: "inspecting", inspectorActive: true });
597
+ store.setState({ mode: "inspecting" });
591
598
  },
592
599
  closeInspector() {
593
600
  bindings?.destroyInspector?.();
594
601
  nav.nav.clear();
595
- store.setState({ mode: "idle", inspectorActive: false });
602
+ store.setState({ mode: "idle" });
596
603
  },
597
604
  toggleInspector() {
598
605
  if (store.getState().mode === "inspecting") {
@@ -607,7 +614,7 @@ function createModeStore(options) {
607
614
  bindings?.destroyInspector?.();
608
615
  }
609
616
  nav.nav.reset(initialStack);
610
- store.setState({ mode: "viewing", inspectorActive: false });
617
+ store.setState({ mode: "viewing" });
611
618
  },
612
619
  dismiss() {
613
620
  const prev = store.getState();
@@ -615,7 +622,7 @@ function createModeStore(options) {
615
622
  bindings?.destroyInspector?.();
616
623
  }
617
624
  nav.nav.clear();
618
- store.setState({ mode: "idle", inspectorActive: false });
625
+ store.setState({ mode: "idle" });
619
626
  },
620
627
  popOrTransition() {
621
628
  const { stack } = nav.getState();
@@ -623,12 +630,12 @@ function createModeStore(options) {
623
630
  nav.nav.pop();
624
631
  } else if (stack.length === 2 && stack[0]?.id === "command-palette") {
625
632
  nav.nav.reset([COMMAND_PALETTE_ENTRY]);
626
- store.setState({ mode: "palette", inspectorActive: false });
633
+ store.setState({ mode: "palette" });
627
634
  } else if (stack.length === 2) {
628
635
  nav.nav.pop();
629
636
  } else {
630
637
  nav.nav.clear();
631
- store.setState({ mode: "idle", inspectorActive: false });
638
+ store.setState({ mode: "idle" });
632
639
  }
633
640
  },
634
641
  pushView(entry) {
@@ -645,7 +652,7 @@ function createModeStore(options) {
645
652
  case "inspecting":
646
653
  bindings?.destroyInspector?.();
647
654
  nav.nav.reset([entry]);
648
- store.setState({ mode: "viewing", inspectorActive: false });
655
+ store.setState({ mode: "viewing" });
649
656
  break;
650
657
  case "palette":
651
658
  case "viewing":
@@ -680,14 +687,6 @@ function createNavigationStore() {
680
687
  store.setState({ stack: s.slice(0, -1) });
681
688
  }
682
689
  },
683
- replace(entry) {
684
- const s = store.getState().stack;
685
- if (s.length === 0) {
686
- store.setState({ stack: [entry] });
687
- } else {
688
- store.setState({ stack: [...s.slice(0, -1), entry] });
689
- }
690
- },
691
690
  clear() {
692
691
  store.setState({ stack: [] });
693
692
  },
@@ -702,14 +701,11 @@ function createNavigationStore() {
702
701
 
703
702
  // src/browser/session/store.ts
704
703
  var defaultSnapshot = {
705
- hover: null,
706
- selection: null,
707
704
  stack: [],
708
705
  pinnedHighlight: null,
709
- inspectorActive: false,
706
+ mode: "idle",
710
707
  theme: "auto",
711
708
  resolvedTheme: "light",
712
- ingestActive: false,
713
709
  user: null
714
710
  };
715
711
  function resolveTheme(preference, detect) {
@@ -760,7 +756,6 @@ function createSession(options = {}) {
760
756
  } else if (highlightMode === "transient") {
761
757
  onUpdateOverlay?.(hlCtx);
762
758
  }
763
- store.setState({ hover: ref2 });
764
759
  },
765
760
  unhover() {
766
761
  if (highlightMode === "transient") {
@@ -770,7 +765,6 @@ function createSession(options = {}) {
770
765
  hlCtx.color = null;
771
766
  onHideOverlay?.();
772
767
  }
773
- store.setState({ hover: null });
774
768
  },
775
769
  pin(ref2) {
776
770
  const pinRef = ref2 ?? hlCtx.ref;
@@ -798,14 +792,11 @@ function createSession(options = {}) {
798
792
  };
799
793
  const store = createStore3(() => ({
800
794
  ...defaultSnapshot,
801
- hover: overrides.hover ?? null,
802
- selection: overrides.selection ?? null,
803
795
  stack: [],
804
796
  pinnedHighlight: null,
805
- inspectorActive: false,
797
+ mode: "idle",
806
798
  theme: initialPref,
807
799
  resolvedTheme: initialResolved,
808
- ingestActive: overrides.ingestActive ?? false,
809
800
  user: overrides.user ?? null
810
801
  }));
811
802
  nav.subscribe(() => {
@@ -815,29 +806,21 @@ function createSession(options = {}) {
815
806
  }
816
807
  });
817
808
  modeStore.subscribe(() => {
818
- const { inspectorActive } = modeStore.getState();
819
- if (store.getState().inspectorActive !== inspectorActive) {
820
- store.setState({ inspectorActive });
809
+ const { mode } = modeStore.getState();
810
+ if (store.getState().mode !== mode) {
811
+ store.setState({ mode });
821
812
  }
822
813
  });
823
814
  const session = store;
824
815
  session.nav = nav;
825
816
  session.mode = modeStore;
826
817
  session.highlight = highlightActions;
827
- session.select = (ref2) => {
828
- if (sameRef(store.getState().selection, ref2)) return;
829
- store.setState({ selection: ref2 });
830
- };
831
818
  session.setTheme = (theme, resolved) => {
832
819
  const state = store.getState();
833
820
  const nextResolved = resolved ?? resolveTheme(theme, detectTheme);
834
821
  if (state.theme === theme && state.resolvedTheme === nextResolved) return;
835
822
  store.setState({ theme, resolvedTheme: nextResolved });
836
823
  };
837
- session.setIngest = (active) => {
838
- if (store.getState().ingestActive === active) return;
839
- store.setState({ ingestActive: active });
840
- };
841
824
  if (initialStack.length > 0) {
842
825
  modeStore.transition.openPalette();
843
826
  for (let i = 1; i < initialStack.length; i++) {
@@ -1150,9 +1133,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1150
1133
  .right-0 {
1151
1134
  right: calc(var(--spacing) * 0);
1152
1135
  }
1153
- .right-2 {
1154
- right: calc(var(--spacing) * 2);
1155
- }
1156
1136
  .bottom-full {
1157
1137
  bottom: 100%;
1158
1138
  }
@@ -1195,9 +1175,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1195
1175
  .mx-2 {
1196
1176
  margin-inline: calc(var(--spacing) * 2);
1197
1177
  }
1198
- .my-1 {
1199
- margin-block: calc(var(--spacing) * 1);
1200
- }
1201
1178
  .ms-auto {
1202
1179
  margin-inline-start: auto;
1203
1180
  }
@@ -1234,9 +1211,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1234
1211
  .inline-flex {
1235
1212
  display: inline-flex;
1236
1213
  }
1237
- .list-item {
1238
- display: list-item;
1239
- }
1240
1214
  .table {
1241
1215
  display: table;
1242
1216
  }
@@ -1244,10 +1218,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1244
1218
  width: calc(var(--spacing) * 2);
1245
1219
  height: calc(var(--spacing) * 2);
1246
1220
  }
1247
- .size-3 {
1248
- width: calc(var(--spacing) * 3);
1249
- height: calc(var(--spacing) * 3);
1250
- }
1251
1221
  .size-3\\.5 {
1252
1222
  width: calc(var(--spacing) * 3.5);
1253
1223
  height: calc(var(--spacing) * 3.5);
@@ -1305,15 +1275,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1305
1275
  .h-\\[26rem\\] {
1306
1276
  height: 26rem;
1307
1277
  }
1308
- .h-auto {
1309
- height: auto;
1310
- }
1311
1278
  .h-full {
1312
1279
  height: 100%;
1313
1280
  }
1314
- .h-px {
1315
- height: 1px;
1316
- }
1317
1281
  .max-h-32 {
1318
1282
  max-height: calc(var(--spacing) * 32);
1319
1283
  }
@@ -1323,9 +1287,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1323
1287
  .min-h-0 {
1324
1288
  min-height: calc(var(--spacing) * 0);
1325
1289
  }
1326
- .min-h-7 {
1327
- min-height: calc(var(--spacing) * 7);
1328
- }
1329
1290
  .min-h-8 {
1330
1291
  min-height: calc(var(--spacing) * 8);
1331
1292
  }
@@ -1350,9 +1311,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1350
1311
  .w-56 {
1351
1312
  width: calc(var(--spacing) * 56);
1352
1313
  }
1353
- .w-auto {
1354
- width: auto;
1355
- }
1356
1314
  .w-full {
1357
1315
  width: 100%;
1358
1316
  }
@@ -1427,9 +1385,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1427
1385
  .animate-spin {
1428
1386
  animation: var(--animate-spin);
1429
1387
  }
1430
- .cursor-default {
1431
- cursor: default;
1432
- }
1433
1388
  .cursor-pointer {
1434
1389
  cursor: pointer;
1435
1390
  }
@@ -1439,9 +1394,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1439
1394
  .scroll-py-2 {
1440
1395
  scroll-padding-block: calc(var(--spacing) * 2);
1441
1396
  }
1442
- .list-none {
1443
- list-style-type: none;
1444
- }
1445
1397
  .appearance-none {
1446
1398
  appearance: none;
1447
1399
  }
@@ -1463,9 +1415,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1463
1415
  .justify-center {
1464
1416
  justify-content: center;
1465
1417
  }
1466
- .justify-start {
1467
- justify-content: flex-start;
1468
- }
1469
1418
  .gap-0 {
1470
1419
  gap: calc(var(--spacing) * 0);
1471
1420
  }
@@ -1490,9 +1439,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1490
1439
  .gap-6 {
1491
1440
  gap: calc(var(--spacing) * 6);
1492
1441
  }
1493
- .self-start {
1494
- align-self: flex-start;
1495
- }
1496
1442
  .truncate {
1497
1443
  overflow: hidden;
1498
1444
  text-overflow: ellipsis;
@@ -1707,9 +1653,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1707
1653
  .p-6 {
1708
1654
  padding: calc(var(--spacing) * 6);
1709
1655
  }
1710
- .px-0 {
1711
- padding-inline: calc(var(--spacing) * 0);
1712
- }
1713
1656
  .px-1 {
1714
1657
  padding-inline: calc(var(--spacing) * 1);
1715
1658
  }
@@ -1734,9 +1677,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1734
1677
  .px-6 {
1735
1678
  padding-inline: calc(var(--spacing) * 6);
1736
1679
  }
1737
- .py-0 {
1738
- padding-block: calc(var(--spacing) * 0);
1739
- }
1740
1680
  .py-1 {
1741
1681
  padding-block: calc(var(--spacing) * 1);
1742
1682
  }
@@ -1809,9 +1749,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1809
1749
  .text-\\[9px\\] {
1810
1750
  font-size: 9px;
1811
1751
  }
1812
- .text-\\[10px\\] {
1813
- font-size: 10px;
1814
- }
1815
1752
  .text-\\[11px\\] {
1816
1753
  font-size: 11px;
1817
1754
  }
@@ -2051,10 +1988,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2051
1988
  outline-style: var(--tw-outline-style);
2052
1989
  outline-width: 1px;
2053
1990
  }
2054
- .blur {
2055
- --tw-blur: blur(8px);
2056
- 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,);
2057
- }
2058
1991
  .filter {
2059
1992
  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,);
2060
1993
  }
@@ -2220,25 +2153,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2220
2153
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2221
2154
  }
2222
2155
  }
2223
- .focus-within\\:border-ring {
2224
- &:focus-within {
2225
- border-color: var(--ring);
2226
- }
2227
- }
2228
- .focus-within\\:ring-\\[3px\\] {
2229
- &:focus-within {
2230
- --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
2231
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2232
- }
2233
- }
2234
- .focus-within\\:ring-ring\\/30 {
2235
- &:focus-within {
2236
- --tw-ring-color: var(--ring);
2237
- @supports (color: color-mix(in lab, red, red)) {
2238
- --tw-ring-color: color-mix(in oklab, var(--ring) 30%, transparent);
2239
- }
2240
- }
2241
- }
2242
2156
  .hover\\:border-destructive\\/30 {
2243
2157
  &:hover {
2244
2158
  @media (hover: hover) {
@@ -2382,11 +2296,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2382
2296
  pointer-events: none;
2383
2297
  }
2384
2298
  }
2385
- .disabled\\:cursor-not-allowed {
2386
- &:disabled {
2387
- cursor: not-allowed;
2388
- }
2389
- }
2390
2299
  .disabled\\:opacity-50 {
2391
2300
  &:disabled {
2392
2301
  opacity: 50%;
@@ -2397,11 +2306,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2397
2306
  opacity: 60%;
2398
2307
  }
2399
2308
  }
2400
- .has-disabled\\:opacity-60 {
2401
- &:has(*:disabled) {
2402
- opacity: 60%;
2403
- }
2404
- }
2405
2309
  .aria-invalid\\:border-destructive\\/40 {
2406
2310
  &[aria-invalid="true"] {
2407
2311
  border-color: var(--destructive);
@@ -2808,32 +2712,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2808
2712
  height: calc(var(--spacing) * 4.5);
2809
2713
  }
2810
2714
  }
2811
- .\\[\\&\\>svg\\]\\:pointer-events-none {
2812
- &>svg {
2813
- pointer-events: none;
2814
- }
2815
- }
2816
- .\\[\\&\\>svg\\]\\:-mx-0\\.5 {
2817
- &>svg {
2818
- margin-inline: calc(var(--spacing) * -0.5);
2819
- }
2820
- }
2821
- .\\[\\&\\>svg\\]\\:shrink-0 {
2822
- &>svg {
2823
- flex-shrink: 0;
2824
- }
2825
- }
2826
- .\\[\\&\\>svg\\:not\\(\\[class\\*\\=\\'opacity-\\'\\]\\)\\]\\:opacity-80 {
2827
- &>svg:not([class*='opacity-']) {
2828
- opacity: 80%;
2829
- }
2830
- }
2831
- .\\[\\&\\>svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-4 {
2832
- &>svg:not([class*='size-']) {
2833
- width: calc(var(--spacing) * 4);
2834
- height: calc(var(--spacing) * 4);
2835
- }
2836
- }
2837
2715
  .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:bg-accent {
2838
2716
  [data-kbd-nav] & {
2839
2717
  &:focus-within {
@@ -3395,18 +3273,23 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3395
3273
 
3396
3274
  // src/browser/surface/constants.ts
3397
3275
  var SURFACE_HOST_CLASS = "uidex-surface-host";
3398
- var SURFACE_CONTAINER_CLASS = "uidex-container";
3399
3276
  var Z_BASE = 2147483630;
3400
3277
  var Z_OVERLAY = 2147483635;
3401
3278
  var Z_PIN_LAYER = 2147483640;
3402
3279
  var Z_CHROME = 2147483645;
3403
- var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS},.${SURFACE_CONTAINER_CLASS}`;
3280
+ var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS}`;
3404
3281
  var UIDEX_ATTR_TO_KIND = [
3405
3282
  ["data-uidex", "element"],
3406
3283
  ["data-uidex-region", "region"],
3407
3284
  ["data-uidex-widget", "widget"],
3408
3285
  ["data-uidex-primitive", "primitive"]
3409
3286
  ];
3287
+ var DOM_BACKED_KINDS = /* @__PURE__ */ new Set([
3288
+ "element",
3289
+ "region",
3290
+ "widget",
3291
+ "primitive"
3292
+ ]);
3410
3293
 
3411
3294
  // src/browser/surface/host.ts
3412
3295
  function createSurfaceHost(options) {
@@ -3599,29 +3482,6 @@ function entityForRef(ref2, registry) {
3599
3482
  }
3600
3483
  return { kind: ref2.kind, id: ref2.id };
3601
3484
  }
3602
- function defaultResolveMatch(target, registry) {
3603
- if (target.closest(SURFACE_IGNORE_SELECTOR)) return null;
3604
- let node = target;
3605
- while (node) {
3606
- if (node instanceof HTMLElement) {
3607
- for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
3608
- const id = node.getAttribute(attr);
3609
- if (id) {
3610
- const ref2 = { kind, id };
3611
- const entity = entityForRef(ref2, registry);
3612
- return {
3613
- element: node,
3614
- ref: ref2,
3615
- entity,
3616
- label: displayName(entity, node)
3617
- };
3618
- }
3619
- }
3620
- }
3621
- node = node.parentElement;
3622
- }
3623
- return null;
3624
- }
3625
3485
  function defaultResolveAllMatches(target, registry) {
3626
3486
  if (target.closest(SURFACE_IGNORE_SELECTOR)) return [];
3627
3487
  const semantic = [];
@@ -3666,27 +3526,6 @@ function resolveEntityElement(ref2) {
3666
3526
  if (el2 instanceof HTMLElement && el2.isConnected) return el2;
3667
3527
  return null;
3668
3528
  }
3669
- function createHighlightController(overlay) {
3670
- let lastKey = null;
3671
- return {
3672
- show(ref2, opts) {
3673
- const el2 = resolveEntityElement(ref2);
3674
- if (!el2) {
3675
- lastKey = null;
3676
- overlay.hide();
3677
- return;
3678
- }
3679
- const key = `${ref2.kind}:${ref2.id}`;
3680
- if (key === lastKey && overlay.isVisible) return;
3681
- lastKey = key;
3682
- overlay.show(el2, opts);
3683
- },
3684
- hide() {
3685
- lastKey = null;
3686
- overlay.hide();
3687
- }
3688
- };
3689
- }
3690
3529
  function createInspector(options) {
3691
3530
  const {
3692
3531
  session,
@@ -3740,7 +3579,6 @@ function createInspector(options) {
3740
3579
  e.preventDefault();
3741
3580
  e.stopPropagation();
3742
3581
  const match = stack[layerIndex];
3743
- session.select(match.ref);
3744
3582
  onSelect?.(match, { x: e.clientX, y: e.clientY });
3745
3583
  };
3746
3584
  const onContextMenu = (e) => {
@@ -3789,8 +3627,6 @@ function createInspector(options) {
3789
3627
 
3790
3628
  // src/browser/surface/menu-bar.ts
3791
3629
  import {
3792
- ChevronLeft,
3793
- ChevronRight,
3794
3630
  Command,
3795
3631
  Highlighter,
3796
3632
  MapPin,
@@ -4068,49 +3904,12 @@ function createMenuBar(options) {
4068
3904
  },
4069
3905
  pinIcon
4070
3906
  );
4071
- const commitCycler = el("div", {
4072
- class: "relative z-1 inline-flex items-center gap-0.5",
4073
- attrs: { "data-uidex-menubar-commit-cycler": "" }
4074
- });
4075
- commitCycler.hidden = true;
4076
- const prevIcon = createLucideElement2(ChevronLeft);
4077
- prevIcon.setAttribute("class", "size-3");
4078
- prevIcon.setAttribute("aria-hidden", "true");
4079
- const prevBtn = el(
4080
- "button",
4081
- {
4082
- class: BUTTON_CLASS,
4083
- attrs: { type: "button", "aria-label": "Previous commit" },
4084
- style: { width: "18px", height: "18px" }
4085
- },
4086
- prevIcon
4087
- );
4088
- const commitLabel = el("span", {
4089
- class: "relative z-1 whitespace-nowrap px-1 text-[10px] font-mono text-muted-foreground",
4090
- attrs: { "data-uidex-menubar-commit-label": "" }
4091
- });
4092
- const nextIcon = createLucideElement2(ChevronRight);
4093
- nextIcon.setAttribute("class", "size-3");
4094
- nextIcon.setAttribute("aria-hidden", "true");
4095
- const nextBtn = el(
4096
- "button",
4097
- {
4098
- class: BUTTON_CLASS,
4099
- attrs: { type: "button", "aria-label": "Next commit" },
4100
- style: { width: "18px", height: "18px" }
4101
- },
4102
- nextIcon
4103
- );
4104
- commitCycler.appendChild(prevBtn);
4105
- commitCycler.appendChild(commitLabel);
4106
- commitCycler.appendChild(nextBtn);
4107
3907
  const pinWrapper = el("div", {
4108
3908
  class: "relative z-1 inline-flex items-center gap-0.5",
4109
3909
  attrs: { "data-uidex-menubar-pin-wrapper": "" }
4110
3910
  });
4111
3911
  pinWrapper.hidden = true;
4112
3912
  pinWrapper.appendChild(pinBtn);
4113
- pinWrapper.appendChild(commitCycler);
4114
3913
  root.appendChild(pinWrapper);
4115
3914
  const updatePinUI = () => {
4116
3915
  if (!activePinLayer) {
@@ -4118,16 +3917,7 @@ function createMenuBar(options) {
4118
3917
  return;
4119
3918
  }
4120
3919
  const pinsVisible = activePinLayer.visible;
4121
- const state = activePinLayer.filterState;
4122
- const hasCommits = state.commits.length > 0;
4123
3920
  pinWrapper.hidden = false;
4124
- commitCycler.hidden = !pinsVisible || !hasCommits;
4125
- if (state.commitIndex === -1 || !state.commits[state.commitIndex]) {
4126
- commitLabel.textContent = `all (${state.commits.length})`;
4127
- } else {
4128
- const sha = state.commits[state.commitIndex] ?? "";
4129
- commitLabel.textContent = sha.slice(0, 7);
4130
- }
4131
3921
  pinBtn.className = cn(BUTTON_CLASS, pinsVisible && BUTTON_ACTIVE_CLASS);
4132
3922
  };
4133
3923
  pinBtn.addEventListener("click", (e) => {
@@ -4136,14 +3926,6 @@ function createMenuBar(options) {
4136
3926
  activePinLayer.setVisible(!activePinLayer.visible);
4137
3927
  }
4138
3928
  });
4139
- prevBtn.addEventListener("click", (e) => {
4140
- e.stopPropagation();
4141
- activePinLayer?.prevCommit();
4142
- });
4143
- nextBtn.addEventListener("click", (e) => {
4144
- e.stopPropagation();
4145
- activePinLayer?.nextCommit();
4146
- });
4147
3929
  let unsubscribePinFilter = activePinLayer?.onFilterChange(() => updatePinUI());
4148
3930
  const presenceIcon = createLucideElement2(Users);
4149
3931
  presenceIcon.setAttribute("class", "size-3.5");
@@ -4237,7 +4019,7 @@ function createMenuBar(options) {
4237
4019
  container.appendChild(root);
4238
4020
  const syncButtonStates = () => {
4239
4021
  const state = session.getState();
4240
- const inspectActive = state.inspectorActive;
4022
+ const inspectActive = state.mode === "inspecting";
4241
4023
  inspectBtn.setAttribute(
4242
4024
  "data-uidex-menubar-inspect-active",
4243
4025
  inspectActive ? "true" : "false"
@@ -4356,6 +4138,49 @@ function createMenuBar(options) {
4356
4138
  };
4357
4139
  }
4358
4140
 
4141
+ // src/browser/internal/repositioner.ts
4142
+ function createRepositioner(onReflow) {
4143
+ let rafId = null;
4144
+ let attached = false;
4145
+ const schedule = () => {
4146
+ if (rafId !== null) return;
4147
+ rafId = typeof requestAnimationFrame === "function" ? requestAnimationFrame(() => {
4148
+ rafId = null;
4149
+ onReflow();
4150
+ }) : setTimeout(() => {
4151
+ rafId = null;
4152
+ onReflow();
4153
+ }, 0);
4154
+ };
4155
+ const cancel = () => {
4156
+ if (rafId === null) return;
4157
+ if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(rafId);
4158
+ else clearTimeout(rafId);
4159
+ rafId = null;
4160
+ };
4161
+ const onScroll = () => schedule();
4162
+ const onResize = () => schedule();
4163
+ const attach = () => {
4164
+ if (attached) return;
4165
+ attached = true;
4166
+ window.addEventListener("resize", onResize);
4167
+ window.addEventListener("scroll", onScroll, {
4168
+ capture: true,
4169
+ passive: true
4170
+ });
4171
+ };
4172
+ const detach = () => {
4173
+ if (!attached) return;
4174
+ attached = false;
4175
+ window.removeEventListener("resize", onResize);
4176
+ window.removeEventListener("scroll", onScroll, {
4177
+ capture: true
4178
+ });
4179
+ cancel();
4180
+ };
4181
+ return { schedule, cancel, attach, detach };
4182
+ }
4183
+
4359
4184
  // src/browser/surface/overlay.ts
4360
4185
  var DEFAULT_COLOR = "#34d399";
4361
4186
  var DEFAULT_BORDER_WIDTH = 2;
@@ -4423,44 +4248,7 @@ function createOverlay(deps) {
4423
4248
  fillOpacity: DEFAULT_FILL_OPACITY,
4424
4249
  backdrop: false
4425
4250
  };
4426
- let rafId = null;
4427
- let attached = false;
4428
- const schedule = () => {
4429
- if (rafId !== null) return;
4430
- rafId = typeof requestAnimationFrame === "function" ? requestAnimationFrame(() => {
4431
- rafId = null;
4432
- updatePosition();
4433
- }) : setTimeout(() => {
4434
- rafId = null;
4435
- updatePosition();
4436
- }, 0);
4437
- };
4438
- const cancelSchedule = () => {
4439
- if (rafId === null) return;
4440
- if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(rafId);
4441
- else clearTimeout(rafId);
4442
- rafId = null;
4443
- };
4444
- const onScroll = () => schedule();
4445
- const onResize = () => schedule();
4446
- const attach = () => {
4447
- if (attached) return;
4448
- attached = true;
4449
- window.addEventListener("resize", onResize);
4450
- window.addEventListener("scroll", onScroll, {
4451
- capture: true,
4452
- passive: true
4453
- });
4454
- };
4455
- const detach = () => {
4456
- if (!attached) return;
4457
- attached = false;
4458
- window.removeEventListener("resize", onResize);
4459
- window.removeEventListener("scroll", onScroll, {
4460
- capture: true
4461
- });
4462
- cancelSchedule();
4463
- };
4251
+ const repositioner = createRepositioner(() => updatePosition());
4464
4252
  function updatePosition() {
4465
4253
  if (!target) return;
4466
4254
  const rect = target.getBoundingClientRect();
@@ -4524,16 +4312,16 @@ function createOverlay(deps) {
4524
4312
  box.offsetHeight;
4525
4313
  }
4526
4314
  box.style.opacity = "1";
4527
- attach();
4315
+ repositioner.attach();
4528
4316
  },
4529
4317
  hide() {
4530
4318
  target = null;
4531
4319
  box.style.opacity = "0";
4532
4320
  backdrop.style.opacity = "0";
4533
- detach();
4321
+ repositioner.detach();
4534
4322
  },
4535
4323
  destroy() {
4536
- detach();
4324
+ repositioner.detach();
4537
4325
  box.remove();
4538
4326
  backdrop.remove();
4539
4327
  target = null;
@@ -4650,8 +4438,7 @@ function createSurfaceShell(options) {
4650
4438
  const overlay = createOverlay({ container: host.shadowRoot });
4651
4439
  cleanup.add(overlay);
4652
4440
  const tooltip = createCursorTooltip({
4653
- container: host.chromeEl,
4654
- session: options.session
4441
+ container: host.chromeEl
4655
4442
  });
4656
4443
  cleanup.add(tooltip);
4657
4444
  const afterHover = options.inspector?.onAfterHover;
@@ -4843,9 +4630,6 @@ function createPinLayer(options) {
4843
4630
  const seenIds = /* @__PURE__ */ new Set();
4844
4631
  const byComp = /* @__PURE__ */ new Map();
4845
4632
  const indicators = /* @__PURE__ */ new Map();
4846
- let filter = { branch: null, commit: null };
4847
- let commits = [];
4848
- let commitIndex = -1;
4849
4633
  const filterCbs = /* @__PURE__ */ new Set();
4850
4634
  const notifyFilter = () => {
4851
4635
  for (const cb of filterCbs) cb();
@@ -4860,8 +4644,6 @@ function createPinLayer(options) {
4860
4644
  }
4861
4645
  };
4862
4646
  const rerender = () => {
4863
- commits = [];
4864
- commitIndex = -1;
4865
4647
  rebuildFiltered();
4866
4648
  for (const id of Array.from(indicators.keys())) {
4867
4649
  if (!byComp.has(id)) removeIndicator(id);
@@ -4870,45 +4652,8 @@ function createPinLayer(options) {
4870
4652
  notifyFilter();
4871
4653
  onPinsChanged?.();
4872
4654
  };
4873
- let rafId = null;
4874
- let winAttached = false;
4875
4655
  let obs = null;
4876
- const schedulePos = () => {
4877
- if (rafId !== null) return;
4878
- rafId = typeof requestAnimationFrame === "function" ? requestAnimationFrame(() => {
4879
- rafId = null;
4880
- posAll();
4881
- }) : setTimeout(() => {
4882
- rafId = null;
4883
- posAll();
4884
- }, 0);
4885
- };
4886
- const cancelPos = () => {
4887
- if (rafId === null) return;
4888
- if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(rafId);
4889
- else clearTimeout(rafId);
4890
- rafId = null;
4891
- };
4892
- const onScroll = () => schedulePos();
4893
- const onResize = () => schedulePos();
4894
- const attachWin = () => {
4895
- if (winAttached) return;
4896
- winAttached = true;
4897
- window.addEventListener("scroll", onScroll, {
4898
- capture: true,
4899
- passive: true
4900
- });
4901
- window.addEventListener("resize", onResize);
4902
- };
4903
- const detachWin = () => {
4904
- if (!winAttached) return;
4905
- winAttached = false;
4906
- window.removeEventListener("scroll", onScroll, {
4907
- capture: true
4908
- });
4909
- window.removeEventListener("resize", onResize);
4910
- cancelPos();
4911
- };
4656
+ const repositioner = createRepositioner(() => posAll());
4912
4657
  const attachObs = () => {
4913
4658
  if (obs || typeof MutationObserver === "undefined") return;
4914
4659
  obs = new MutationObserver((recs) => {
@@ -4932,7 +4677,7 @@ function createPinLayer(options) {
4932
4677
  s.anchor = resolveEntityElement(parseComponentRef(s.componentId));
4933
4678
  changed = true;
4934
4679
  }
4935
- if (changed) schedulePos();
4680
+ if (changed) repositioner.schedule();
4936
4681
  };
4937
4682
  const expand = (st) => {
4938
4683
  if (st.expanded) return;
@@ -5174,7 +4919,7 @@ function createPinLayer(options) {
5174
4919
  if (!st) {
5175
4920
  st = buildIndicator(componentId);
5176
4921
  indicators.set(componentId, st);
5177
- attachWin();
4922
+ repositioner.attach();
5178
4923
  attachObs();
5179
4924
  }
5180
4925
  if (st.pinIndex >= pins.length) st.pinIndex = 0;
@@ -5188,7 +4933,7 @@ function createPinLayer(options) {
5188
4933
  st.wrap.remove();
5189
4934
  indicators.delete(componentId);
5190
4935
  if (indicators.size === 0) {
5191
- detachWin();
4936
+ repositioner.detach();
5192
4937
  detachObs();
5193
4938
  }
5194
4939
  };
@@ -5223,17 +4968,22 @@ function createPinLayer(options) {
5223
4968
  seenIds.clear();
5224
4969
  byComp.clear();
5225
4970
  for (const id of Array.from(indicators.keys())) removeIndicator(id);
5226
- commits = [];
5227
- commitIndex = -1;
5228
4971
  notifyFilter();
5229
4972
  },
5230
4973
  getPinsForElement: (id) => byComp.get(id) ?? [],
5231
- getAllPinsForElement: (id) => allPins.filter((p) => (p.entity ?? "") === id),
5232
4974
  getAllPins: () => allPins.slice(),
5233
4975
  attachChannel(channel) {
5234
- return channel.onPin((pin) => {
4976
+ const offPin = channel.onPin((pin) => {
5235
4977
  layer.addPin(pin);
5236
4978
  });
4979
+ const offArchived = channel.onPinArchived?.((reportId) => {
4980
+ layer.removePin(reportId);
4981
+ }) ?? (() => {
4982
+ });
4983
+ return () => {
4984
+ offPin();
4985
+ offArchived();
4986
+ };
5237
4987
  },
5238
4988
  attachCloud(opts) {
5239
4989
  const offCh = opts.channel ? layer.attachChannel(opts.channel) : () => {
@@ -5264,39 +5014,6 @@ function createPinLayer(options) {
5264
5014
  async refresh() {
5265
5015
  if (activeRefresh) await activeRefresh();
5266
5016
  },
5267
- get filterState() {
5268
- return {
5269
- branch: filter.branch,
5270
- commit: filter.commit,
5271
- commits,
5272
- commitIndex
5273
- };
5274
- },
5275
- setFilter(next) {
5276
- filter = {
5277
- branch: next.branch !== void 0 ? next.branch : filter.branch,
5278
- commit: next.commit !== void 0 ? next.commit : filter.commit
5279
- };
5280
- rerender();
5281
- },
5282
- nextCommit() {
5283
- if (!commits.length) return;
5284
- commitIndex = commitIndex >= commits.length - 1 ? -1 : commitIndex + 1;
5285
- filter = {
5286
- ...filter,
5287
- commit: commitIndex === -1 ? null : commits[commitIndex]
5288
- };
5289
- rerender();
5290
- },
5291
- prevCommit() {
5292
- if (!commits.length) return;
5293
- commitIndex = commitIndex <= -1 ? commits.length - 1 : commitIndex - 1;
5294
- filter = {
5295
- ...filter,
5296
- commit: commitIndex === -1 ? null : commits[commitIndex]
5297
- };
5298
- rerender();
5299
- },
5300
5017
  onFilterChange(cb) {
5301
5018
  filterCbs.add(cb);
5302
5019
  return () => {
@@ -5315,7 +5032,7 @@ function createPinLayer(options) {
5315
5032
  },
5316
5033
  destroy() {
5317
5034
  layer.clear();
5318
- detachWin();
5035
+ repositioner.detach();
5319
5036
  detachObs();
5320
5037
  layerEl.remove();
5321
5038
  activeRefresh = null;
@@ -5358,9 +5075,11 @@ function createRouter(options) {
5358
5075
  if (view === null || typeof view !== "object" || typeof view.id !== "string" || view.id.length === 0) {
5359
5076
  throw new ViewValidationError("View must have a non-empty string id");
5360
5077
  }
5361
- if (typeof view.surface !== "function") {
5078
+ const hasSurface = typeof view.surface === "function";
5079
+ const hasRender = typeof view.render === "function";
5080
+ if (!hasSurface && !hasRender) {
5362
5081
  throw new ViewValidationError(
5363
- `View ${view.id}: 'surface' must be a function`
5082
+ `View ${view.id}: a 'surface' function (or a 'render' override) is required`
5364
5083
  );
5365
5084
  }
5366
5085
  if (!view.matches && !view.palette) {
@@ -5418,7 +5137,6 @@ function createRouter(options) {
5418
5137
  if (idx >= 0) recentRefs.splice(idx, 1);
5419
5138
  recentRefs.unshift(ref2);
5420
5139
  if (recentRefs.length > MAX_RECENTS) recentRefs.length = MAX_RECENTS;
5421
- options.session.select(ref2);
5422
5140
  const entry = { id: match.view.id, ref: ref2 };
5423
5141
  const { mode } = options.session.mode.getState();
5424
5142
  if (mode === "idle" || mode === "inspecting") {
@@ -5473,74 +5191,24 @@ function detectDev() {
5473
5191
 
5474
5192
  // src/browser/internal/lit.ts
5475
5193
  import { html, svg, render, nothing } from "lit-html";
5476
- import { repeat } from "lit-html/directives/repeat.js";
5477
5194
  import { ref, createRef } from "lit-html/directives/ref.js";
5478
- import { classMap } from "lit-html/directives/class-map.js";
5479
5195
 
5480
- // src/browser/internal/apply-props.ts
5481
- function applyProps(node, props) {
5482
- const removers = [];
5483
- for (const [key, rawValue] of Object.entries(props)) {
5484
- if (key === "children" || key === "dangerouslySetInnerHTML") continue;
5485
- if (key.startsWith("on") && typeof rawValue === "function") {
5486
- const eventName = key.slice(2).toLowerCase();
5487
- const isTextControl = node instanceof HTMLInputElement && node.type !== "checkbox" && node.type !== "radio" || node instanceof HTMLTextAreaElement;
5488
- const effectiveEvent = eventName === "change" && isTextControl ? "input" : eventName;
5489
- const listener = rawValue;
5490
- node.addEventListener(effectiveEvent, listener);
5491
- removers.push(() => node.removeEventListener(effectiveEvent, listener));
5492
- continue;
5493
- }
5494
- if (rawValue === void 0 || rawValue === null) {
5495
- node.removeAttribute(key);
5496
- continue;
5497
- }
5498
- if (typeof rawValue === "boolean") {
5499
- if (rawValue) node.setAttribute(key, "");
5500
- else node.removeAttribute(key);
5501
- continue;
5502
- }
5503
- if (key === "style" && typeof rawValue === "object") {
5504
- Object.assign(
5505
- node.style,
5506
- rawValue
5507
- );
5508
- continue;
5509
- }
5510
- if (key === "className" || key === "class") {
5511
- node.setAttribute("class", String(rawValue));
5512
- continue;
5513
- }
5514
- if (key === "htmlFor") {
5515
- node.setAttribute("for", String(rawValue));
5516
- continue;
5517
- }
5518
- if (key === "tabIndex") {
5519
- ;
5520
- node.tabIndex = Number(rawValue);
5521
- continue;
5522
- }
5523
- node.setAttribute(key, String(rawValue));
5524
- }
5525
- return () => removers.forEach((fn) => fn());
5526
- }
5527
-
5528
- // src/browser/internal/lit-icon.ts
5529
- import { noChange } from "lit-html";
5530
- import {
5531
- Directive,
5532
- directive,
5533
- PartType
5534
- } from "lit-html/directive.js";
5535
- import { createElement } from "lucide";
5536
- var LucideIconDirective = class extends Directive {
5537
- _icon;
5538
- _class;
5539
- _el;
5540
- constructor(partInfo) {
5541
- super(partInfo);
5542
- if (partInfo.type !== PartType.CHILD) {
5543
- throw new Error("icon() can only be used in a child position");
5196
+ // src/browser/internal/lit-icon.ts
5197
+ import { noChange } from "lit-html";
5198
+ import {
5199
+ Directive,
5200
+ directive,
5201
+ PartType
5202
+ } from "lit-html/directive.js";
5203
+ import { createElement } from "lucide";
5204
+ var LucideIconDirective = class extends Directive {
5205
+ _icon;
5206
+ _class;
5207
+ _el;
5208
+ constructor(partInfo) {
5209
+ super(partInfo);
5210
+ if (partInfo.type !== PartType.CHILD) {
5211
+ throw new Error("icon() can only be used in a child position");
5544
5212
  }
5545
5213
  }
5546
5214
  update(_part, [iconNode, className]) {
@@ -5561,6 +5229,16 @@ var LucideIconDirective = class extends Directive {
5561
5229
  };
5562
5230
  var icon = directive(LucideIconDirective);
5563
5231
 
5232
+ // src/browser/views/primitives/chip.ts
5233
+ var CHIP_CLASS = "inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground";
5234
+ function createChip(options = {}, children = []) {
5235
+ const { class: extra, ...rest } = options;
5236
+ return el("span", { ...rest, class: cn(CHIP_CLASS, extra) }, children);
5237
+ }
5238
+
5239
+ // src/browser/views/primitives/kind-icon.ts
5240
+ import { createElement as createLucideElement4 } from "lucide";
5241
+
5564
5242
  // src/browser/ui/cva.ts
5565
5243
  function cva(base, config = {}) {
5566
5244
  return (props = {}) => {
@@ -5608,16 +5286,6 @@ function badgeTpl(content, options = {}) {
5608
5286
  `;
5609
5287
  }
5610
5288
 
5611
- // src/browser/views/primitives/chip.ts
5612
- var CHIP_CLASS = "inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground";
5613
- function createChip(options = {}, children = []) {
5614
- const { class: extra, ...rest } = options;
5615
- return el("span", { ...rest, class: cn(CHIP_CLASS, extra) }, children);
5616
- }
5617
-
5618
- // src/browser/views/primitives/kind-icon.ts
5619
- import { createElement as createLucideElement4 } from "lucide";
5620
-
5621
5289
  // src/browser/views/primitives/icon-tile.ts
5622
5290
  var TILE_CLASS = "inline-flex size-6 shrink-0 items-center justify-center rounded-md bg-muted text-muted-foreground";
5623
5291
  var ICON_SIZE_CLASS_RE = /\b(h-\d+|w-\d+|size-\d+)\b/g;
@@ -6125,6 +5793,54 @@ function createScrollArea(props = {}) {
6125
5793
  );
6126
5794
  }
6127
5795
 
5796
+ // src/browser/internal/apply-props.ts
5797
+ function applyProps(node, props) {
5798
+ const removers = [];
5799
+ for (const [key, rawValue] of Object.entries(props)) {
5800
+ if (key === "children" || key === "dangerouslySetInnerHTML") continue;
5801
+ if (key.startsWith("on") && typeof rawValue === "function") {
5802
+ const eventName = key.slice(2).toLowerCase();
5803
+ const isTextControl = node instanceof HTMLInputElement && node.type !== "checkbox" && node.type !== "radio" || node instanceof HTMLTextAreaElement;
5804
+ const effectiveEvent = eventName === "change" && isTextControl ? "input" : eventName;
5805
+ const listener = rawValue;
5806
+ node.addEventListener(effectiveEvent, listener);
5807
+ removers.push(() => node.removeEventListener(effectiveEvent, listener));
5808
+ continue;
5809
+ }
5810
+ if (rawValue === void 0 || rawValue === null) {
5811
+ node.removeAttribute(key);
5812
+ continue;
5813
+ }
5814
+ if (typeof rawValue === "boolean") {
5815
+ if (rawValue) node.setAttribute(key, "");
5816
+ else node.removeAttribute(key);
5817
+ continue;
5818
+ }
5819
+ if (key === "style" && typeof rawValue === "object") {
5820
+ Object.assign(
5821
+ node.style,
5822
+ rawValue
5823
+ );
5824
+ continue;
5825
+ }
5826
+ if (key === "className" || key === "class") {
5827
+ node.setAttribute("class", String(rawValue));
5828
+ continue;
5829
+ }
5830
+ if (key === "htmlFor") {
5831
+ node.setAttribute("for", String(rawValue));
5832
+ continue;
5833
+ }
5834
+ if (key === "tabIndex") {
5835
+ ;
5836
+ node.tabIndex = Number(rawValue);
5837
+ continue;
5838
+ }
5839
+ node.setAttribute(key, String(rawValue));
5840
+ }
5841
+ return () => removers.forEach((fn) => fn());
5842
+ }
5843
+
6128
5844
  // src/browser/views/builder/spread-props.ts
6129
5845
  function spreadProps(node, props) {
6130
5846
  return applyProps(node, props);
@@ -6144,21 +5860,6 @@ function createPersistentSpreads() {
6144
5860
  };
6145
5861
  }
6146
5862
 
6147
- // src/browser/views/render/detail.ts
6148
- import {
6149
- ArchiveX,
6150
- Camera,
6151
- ChevronDown,
6152
- Copy,
6153
- Highlighter as Highlighter2,
6154
- Inbox,
6155
- MessageCircleWarning,
6156
- StickyNote as StickyNote2,
6157
- TicketPlus,
6158
- View,
6159
- createElement as createLucideElement5
6160
- } from "lucide";
6161
-
6162
5863
  // src/browser/internal/arrow-nav.ts
6163
5864
  var NAV_KEYS = /* @__PURE__ */ new Set(["ArrowDown", "ArrowUp", "Home", "End"]);
6164
5865
  function bindArrowNav(options) {
@@ -6230,30 +5931,6 @@ function focusItem(items, idx) {
6230
5931
  items[idx].focus();
6231
5932
  }
6232
5933
 
6233
- // src/browser/views/builder/filter.ts
6234
- function normalizeQuery(query) {
6235
- return query.trim().toLowerCase();
6236
- }
6237
- function matchesQuery(haystack, query) {
6238
- const q = normalizeQuery(query);
6239
- if (!q) return true;
6240
- return haystack.toLowerCase().includes(q);
6241
- }
6242
- function filterEntities(entities, query) {
6243
- const q = normalizeQuery(query);
6244
- if (!q) return entities;
6245
- return entities.filter((e) => {
6246
- const name = displayName(e).toLowerCase();
6247
- const id = entityKey(e).toLowerCase();
6248
- return name.includes(q) || id.includes(q) || e.kind.toLowerCase().includes(q);
6249
- });
6250
- }
6251
- function filterIds(ids, query) {
6252
- const q = normalizeQuery(query);
6253
- if (!q) return ids;
6254
- return ids.filter((id) => id.toLowerCase().includes(q));
6255
- }
6256
-
6257
5934
  // src/browser/views/labels.ts
6258
5935
  var SECTION_LABELS = {
6259
5936
  acceptance: "Acceptance criteria",
@@ -6278,171 +5955,6 @@ var LIST_ITEM_STATE_CLASS = "data-[disabled]:pointer-events-none data-[disabled]
6278
5955
  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";
6279
5956
  var LIST_GROUP_LABEL_CLASS = "text-muted-foreground px-2 py-1.5 text-xs font-medium";
6280
5957
 
6281
- // src/browser/ui/button.ts
6282
- 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";
6283
- var buttonVariants = cva(buttonBase, {
6284
- defaultVariants: { size: "default", variant: "default" },
6285
- variants: {
6286
- size: {
6287
- default: "h-8 px-3",
6288
- sm: "h-7 gap-1.5 px-2.5",
6289
- xs: "h-6 gap-1 rounded-md px-2 text-xs",
6290
- lg: "h-9 px-3.5",
6291
- xl: "h-10 px-4 text-base",
6292
- icon: "size-8",
6293
- "icon-sm": "size-7",
6294
- "icon-lg": "size-9",
6295
- "icon-xs": "size-6 rounded-md"
6296
- },
6297
- variant: {
6298
- default: "border-primary bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
6299
- destructive: "border-destructive bg-destructive shadow-xs hover:bg-destructive/90 text-white",
6300
- "destructive-outline": "border-input bg-popover text-destructive-foreground shadow-xs/5 hover:border-destructive/30 hover:bg-destructive/5 dark:bg-input/30",
6301
- ghost: "text-foreground hover:bg-accent hover:text-accent-foreground border-transparent",
6302
- link: "text-foreground border-transparent underline-offset-4 hover:underline",
6303
- outline: "border-input bg-popover text-foreground shadow-xs/5 hover:bg-accent hover:text-accent-foreground dark:bg-input/30",
6304
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90 border-transparent"
6305
- }
6306
- }
6307
- });
6308
-
6309
- // src/browser/views/primitives/entity-presence.ts
6310
- var DOM_BACKED_KINDS = /* @__PURE__ */ new Set([
6311
- "element",
6312
- "region",
6313
- "widget",
6314
- "primitive"
6315
- ]);
6316
- var TOUCH_RESOLVE_KINDS = [
6317
- "element",
6318
- "widget",
6319
- "region",
6320
- "primitive"
6321
- ];
6322
- function isAbsentFromPage(ref2, registry) {
6323
- if (DOM_BACKED_KINDS.has(ref2.kind)) {
6324
- return !resolveEntityElement(ref2);
6325
- }
6326
- if (ref2.kind === "flow" && registry) {
6327
- const flow = registry.get("flow", ref2.id);
6328
- if (!flow) return true;
6329
- for (const touchId of flow.touches) {
6330
- for (const kind of TOUCH_RESOLVE_KINDS) {
6331
- const entity = registry.get(kind, touchId);
6332
- if (entity && resolveEntityElement({ kind, id: touchId })) return false;
6333
- }
6334
- }
6335
- return true;
6336
- }
6337
- return false;
6338
- }
6339
-
6340
- // src/browser/ui/kbd.ts
6341
- 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";
6342
- var COMMAND_SHORTCUT_CLASS = "text-muted-foreground/70 ms-auto font-sans text-xs font-medium tracking-widest";
6343
- function createCommandShortcut(options = {}, children = []) {
6344
- const { class: extra, attrs, ...rest } = options;
6345
- return el(
6346
- "kbd",
6347
- {
6348
- ...rest,
6349
- class: cn(COMMAND_SHORTCUT_CLASS, extra),
6350
- attrs: { "data-slot": "command-shortcut", ...attrs }
6351
- },
6352
- children
6353
- );
6354
- }
6355
- function kbdTpl(text, className) {
6356
- return html`<kbd class=${cn(KBD_CLASS, className)} data-slot="kbd"
6357
- >${text}</kbd
6358
- >`;
6359
- }
6360
- function commandShortcutTpl(text, className) {
6361
- return html`<kbd
6362
- class=${cn(COMMAND_SHORTCUT_CLASS, className)}
6363
- data-slot="command-shortcut"
6364
- >${text}</kbd
6365
- >`;
6366
- }
6367
-
6368
- // src/browser/views/primitives/row.ts
6369
- var LABEL_CLASS = "min-w-0 flex-1 truncate";
6370
- var SUBTITLE_CLASS = "truncate text-xs text-muted-foreground";
6371
- function fillRowWithHandle(host, content) {
6372
- if (content.leading) host.append(content.leading);
6373
- const label = el("span", { class: LABEL_CLASS, text: content.label });
6374
- host.append(label);
6375
- if (content.subtitle) {
6376
- host.append(
6377
- el("span", {
6378
- class: SUBTITLE_CLASS,
6379
- attrs: { "data-uidex-row-subtitle": "" },
6380
- text: content.subtitle
6381
- })
6382
- );
6383
- }
6384
- if (content.trailing != null) {
6385
- host.append(
6386
- typeof content.trailing === "string" ? createCommandShortcut({ text: content.trailing }) : content.trailing
6387
- );
6388
- }
6389
- return { label };
6390
- }
6391
- function rowTpl(content) {
6392
- return html`
6393
- ${content.leading ?? nothing}
6394
- <span class=${LABEL_CLASS}>${content.label}</span>
6395
- ${content.subtitle ? html`<span class=${SUBTITLE_CLASS} data-uidex-row-subtitle
6396
- >${content.subtitle}</span
6397
- >` : nothing}
6398
- ${content.trailing != null ? typeof content.trailing === "string" ? commandShortcutTpl(content.trailing) : content.trailing : nothing}
6399
- `;
6400
- }
6401
-
6402
- // src/browser/views/primitives/entity-link.ts
6403
- var ACTION_CLASS = cn(
6404
- LIST_ITEM_CLASS,
6405
- LIST_ITEM_INTERACTIVE_CLASS,
6406
- "w-full cursor-pointer text-left font-normal"
6407
- );
6408
- function entityLinkTpl(options) {
6409
- const { ctx, target, label, leading, class: extraClass } = options;
6410
- const absent = isAbsentFromPage(target);
6411
- const resolvedLabel = label ?? `${target.kind}: ${target.id}`;
6412
- const onClick = () => ctx.views.navigate(target);
6413
- const preview = () => {
6414
- ctx.highlight.show(target, { color: KIND_STYLE[target.kind].color });
6415
- };
6416
- const restoreParent = () => {
6417
- if (ctx.ref) {
6418
- ctx.highlight.show(ctx.ref, { color: KIND_STYLE[ctx.ref.kind].color });
6419
- } else {
6420
- ctx.highlight.hide();
6421
- }
6422
- };
6423
- return html`
6424
- <button
6425
- type="button"
6426
- class=${cn(ACTION_CLASS, "w-full", absent && "opacity-50", extraClass)}
6427
- data-slot="list-item-button"
6428
- data-uidex-entity-link
6429
- data-uidex-ref-kind=${target.kind}
6430
- data-uidex-ref-id=${target.id}
6431
- title=${absent ? "Not on this page" : ""}
6432
- @click=${onClick}
6433
- @mouseenter=${preview}
6434
- @mouseleave=${restoreParent}
6435
- @focus=${preview}
6436
- @blur=${restoreParent}
6437
- >
6438
- ${rowTpl({
6439
- leading,
6440
- label: resolvedLabel
6441
- })}
6442
- </button>
6443
- `;
6444
- }
6445
-
6446
5958
  // src/browser/views/primitives/text.ts
6447
5959
  var MUTED_CLASS = "text-sm text-muted-foreground";
6448
5960
  var HEADING_CLASS = LIST_GROUP_LABEL_CLASS;
@@ -6454,7 +5966,20 @@ function headingTpl(text, className) {
6454
5966
  return html`<h3 class=${cn(HEADING_CLASS, className)}>${text}</h3>`;
6455
5967
  }
6456
5968
 
6457
- // src/browser/views/render/detail.ts
5969
+ // src/browser/views/render/detail-actions.ts
5970
+ import {
5971
+ ArchiveX,
5972
+ Camera,
5973
+ ChevronDown,
5974
+ Copy,
5975
+ Highlighter as Highlighter2,
5976
+ Inbox,
5977
+ MessageCircleWarning,
5978
+ StickyNote as StickyNote2,
5979
+ TicketPlus,
5980
+ View,
5981
+ createElement as createLucideElement5
5982
+ } from "lucide";
6458
5983
  var ICON_MAP = {
6459
5984
  "archive-x": ArchiveX,
6460
5985
  copy: Copy,
@@ -6470,23 +5995,6 @@ var ICON_MAP = {
6470
5995
  function iconFor(icon2) {
6471
5996
  return icon2 ? ICON_MAP[icon2] : null;
6472
5997
  }
6473
- function subtitleTpl(subtitle) {
6474
- const text = subtitle.extra ? `${subtitle.rawId} \xB7 ${subtitle.extra}` : subtitle.rawId;
6475
- return html`<p
6476
- class=${cn("text-muted-foreground font-mono text-xs", "px-2")}
6477
- data-uidex-detail-subtitle
6478
- >
6479
- ${text}
6480
- </p>`;
6481
- }
6482
- function notFoundTpl(ref2) {
6483
- return html`<p
6484
- class=${cn("text-muted-foreground text-sm", "p-4")}
6485
- data-uidex-detail-missing
6486
- >
6487
- ${ref2.kind}: ${ref2.id} not found in registry
6488
- </p>`;
6489
- }
6490
5998
  function renderActions(actions, ctx, root, heading) {
6491
5999
  const cleanups = [];
6492
6000
  const buttons = [];
@@ -6597,6 +6105,169 @@ function renderActions(actions, ctx, root, heading) {
6597
6105
  }
6598
6106
  return { node: section, buttons, cleanup: composeCleanups(cleanups) };
6599
6107
  }
6108
+
6109
+ // src/browser/views/render/detail-sections.ts
6110
+ import {
6111
+ html as staticHtml,
6112
+ literal
6113
+ } from "lit-html/static.js";
6114
+
6115
+ // src/browser/views/builder/filter.ts
6116
+ function normalizeQuery(query) {
6117
+ return query.trim().toLowerCase();
6118
+ }
6119
+ function matchesQuery(haystack, query) {
6120
+ const q = normalizeQuery(query);
6121
+ if (!q) return true;
6122
+ return haystack.toLowerCase().includes(q);
6123
+ }
6124
+ function filterEntities(entities, query) {
6125
+ const q = normalizeQuery(query);
6126
+ if (!q) return entities;
6127
+ return entities.filter((e) => {
6128
+ const name = displayName(e).toLowerCase();
6129
+ const id = entityKey(e).toLowerCase();
6130
+ return name.includes(q) || id.includes(q) || e.kind.toLowerCase().includes(q);
6131
+ });
6132
+ }
6133
+ function filterIds(ids, query) {
6134
+ const q = normalizeQuery(query);
6135
+ if (!q) return ids;
6136
+ return ids.filter((id) => id.toLowerCase().includes(q));
6137
+ }
6138
+
6139
+ // src/browser/views/primitives/entity-presence.ts
6140
+ var TOUCH_RESOLVE_KINDS = [
6141
+ "element",
6142
+ "widget",
6143
+ "region",
6144
+ "primitive"
6145
+ ];
6146
+ function isAbsentFromPage(ref2, registry) {
6147
+ if (DOM_BACKED_KINDS.has(ref2.kind)) {
6148
+ return !resolveEntityElement(ref2);
6149
+ }
6150
+ if (ref2.kind === "flow" && registry) {
6151
+ const flow = registry.get("flow", ref2.id);
6152
+ if (!flow) return true;
6153
+ for (const touchId of flow.touches) {
6154
+ for (const kind of TOUCH_RESOLVE_KINDS) {
6155
+ const entity = registry.get(kind, touchId);
6156
+ if (entity && resolveEntityElement({ kind, id: touchId })) return false;
6157
+ }
6158
+ }
6159
+ return true;
6160
+ }
6161
+ return false;
6162
+ }
6163
+
6164
+ // src/browser/ui/kbd.ts
6165
+ 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";
6166
+ var COMMAND_SHORTCUT_CLASS = "text-muted-foreground/70 ms-auto font-sans text-xs font-medium tracking-widest";
6167
+ function createCommandShortcut(options = {}, children = []) {
6168
+ const { class: extra, attrs, ...rest } = options;
6169
+ return el(
6170
+ "kbd",
6171
+ {
6172
+ ...rest,
6173
+ class: cn(COMMAND_SHORTCUT_CLASS, extra),
6174
+ attrs: { "data-slot": "command-shortcut", ...attrs }
6175
+ },
6176
+ children
6177
+ );
6178
+ }
6179
+ function kbdTpl(text, className) {
6180
+ return html`<kbd class=${cn(KBD_CLASS, className)} data-slot="kbd"
6181
+ >${text}</kbd
6182
+ >`;
6183
+ }
6184
+ function commandShortcutTpl(text, className) {
6185
+ return html`<kbd
6186
+ class=${cn(COMMAND_SHORTCUT_CLASS, className)}
6187
+ data-slot="command-shortcut"
6188
+ >${text}</kbd
6189
+ >`;
6190
+ }
6191
+
6192
+ // src/browser/views/primitives/row.ts
6193
+ var LABEL_CLASS = "min-w-0 flex-1 truncate";
6194
+ var SUBTITLE_CLASS = "truncate text-xs text-muted-foreground";
6195
+ function fillRowWithHandle(host, content) {
6196
+ if (content.leading) host.append(content.leading);
6197
+ const label = el("span", { class: LABEL_CLASS, text: content.label });
6198
+ host.append(label);
6199
+ if (content.subtitle) {
6200
+ host.append(
6201
+ el("span", {
6202
+ class: SUBTITLE_CLASS,
6203
+ attrs: { "data-uidex-row-subtitle": "" },
6204
+ text: content.subtitle
6205
+ })
6206
+ );
6207
+ }
6208
+ if (content.trailing != null) {
6209
+ host.append(
6210
+ typeof content.trailing === "string" ? createCommandShortcut({ text: content.trailing }) : content.trailing
6211
+ );
6212
+ }
6213
+ return { label };
6214
+ }
6215
+ function rowTpl(content) {
6216
+ return html`
6217
+ ${content.leading ?? nothing}
6218
+ <span class=${LABEL_CLASS}>${content.label}</span>
6219
+ ${content.subtitle ? html`<span class=${SUBTITLE_CLASS} data-uidex-row-subtitle
6220
+ >${content.subtitle}</span
6221
+ >` : nothing}
6222
+ ${content.trailing != null ? typeof content.trailing === "string" ? commandShortcutTpl(content.trailing) : content.trailing : nothing}
6223
+ `;
6224
+ }
6225
+
6226
+ // src/browser/views/primitives/entity-link.ts
6227
+ var ACTION_CLASS = cn(
6228
+ LIST_ITEM_CLASS,
6229
+ LIST_ITEM_INTERACTIVE_CLASS,
6230
+ "w-full cursor-pointer text-left font-normal"
6231
+ );
6232
+ function entityLinkTpl(options) {
6233
+ const { ctx, target, label, leading, class: extraClass } = options;
6234
+ const absent = isAbsentFromPage(target);
6235
+ const resolvedLabel = label ?? `${target.kind}: ${target.id}`;
6236
+ const onClick = () => ctx.views.navigate(target);
6237
+ const preview = () => {
6238
+ ctx.highlight.show(target, { color: KIND_STYLE[target.kind].color });
6239
+ };
6240
+ const restoreParent = () => {
6241
+ if (ctx.ref) {
6242
+ ctx.highlight.show(ctx.ref, { color: KIND_STYLE[ctx.ref.kind].color });
6243
+ } else {
6244
+ ctx.highlight.hide();
6245
+ }
6246
+ };
6247
+ return html`
6248
+ <button
6249
+ type="button"
6250
+ class=${cn(ACTION_CLASS, "w-full", absent && "opacity-50", extraClass)}
6251
+ data-slot="list-item-button"
6252
+ data-uidex-entity-link
6253
+ data-uidex-ref-kind=${target.kind}
6254
+ data-uidex-ref-id=${target.id}
6255
+ title=${absent ? "Not on this page" : ""}
6256
+ @click=${onClick}
6257
+ @mouseenter=${preview}
6258
+ @mouseleave=${restoreParent}
6259
+ @focus=${preview}
6260
+ @blur=${restoreParent}
6261
+ >
6262
+ ${rowTpl({
6263
+ leading,
6264
+ label: resolvedLabel
6265
+ })}
6266
+ </button>
6267
+ `;
6268
+ }
6269
+
6270
+ // src/browser/views/render/detail-sections.ts
6600
6271
  function entityListItems(ctx, entities) {
6601
6272
  return html`${entities.map((entity) => {
6602
6273
  const eRef = { kind: entity.kind, id: entityKey(entity) };
@@ -6610,71 +6281,29 @@ function entityListItems(ctx, entities) {
6610
6281
  </li>`;
6611
6282
  })}`;
6612
6283
  }
6613
- function composesListTpl(ctx, label, entities) {
6614
- if (entities.length === 0) return null;
6615
- return html`
6616
- <section class="flex flex-col" data-uidex-detail-composes>
6617
- ${headingTpl(label)}
6618
- <ul class="flex flex-col">
6619
- ${entityListItems(ctx, entities)}
6620
- </ul>
6621
- </section>
6622
- `;
6623
- }
6624
- function usedByListTpl(ctx, label, entities) {
6625
- if (entities.length === 0) return null;
6626
- return html`
6627
- <section class="flex flex-col" data-uidex-detail-used-by>
6628
- ${headingTpl(label)}
6629
- <ul class="flex flex-col">
6630
- ${entityListItems(ctx, entities)}
6631
- </ul>
6632
- </section>
6633
- `;
6634
- }
6635
- function flowListTpl(ctx, flows) {
6636
- if (flows.length === 0) return null;
6637
- return html`
6638
- <section class="flex flex-col" data-uidex-detail-flows>
6639
- ${headingTpl(SECTION_LABELS.flows)}
6640
- <ul class="flex flex-col">
6641
- ${flows.map(
6642
- (flow) => html`<li>
6643
- ${entityLinkTpl({
6644
- ctx,
6645
- target: { kind: "flow", id: flow.id },
6646
- label: displayName(flow),
6647
- leading: kindIconTileTpl("flow")
6648
- })}
6649
- </li>`
6650
- )}
6651
- </ul>
6652
- </section>
6653
- `;
6654
- }
6655
- function touchesTpl(ctx, entities, query) {
6284
+ var DETAIL_SECTION_ATTRS = {
6285
+ composes: literal`data-uidex-detail-composes`,
6286
+ "used-by": literal`data-uidex-detail-used-by`,
6287
+ flows: literal`data-uidex-detail-flows`,
6288
+ touches: literal`data-uidex-detail-touches`
6289
+ };
6290
+ function entitySectionTpl(ctx, opts) {
6291
+ const { label, entities, dataAttr, emptyText } = opts;
6292
+ const attr = DETAIL_SECTION_ATTRS[dataAttr];
6656
6293
  if (entities.length === 0) {
6657
- return html`
6658
- <section class="flex flex-col gap-2" data-uidex-detail-touches>
6659
- ${headingTpl(SECTION_LABELS.touches)}
6660
- ${mutedTextTpl(query ? "No matches" : "No entities touched")}
6294
+ if (emptyText === void 0) return null;
6295
+ return staticHtml`
6296
+ <section class="flex flex-col gap-2" ${attr}>
6297
+ ${headingTpl(label)}
6298
+ ${mutedTextTpl(emptyText)}
6661
6299
  </section>
6662
6300
  `;
6663
6301
  }
6664
- return html`
6665
- <section class="flex flex-col" data-uidex-detail-touches>
6666
- ${headingTpl(SECTION_LABELS.touches)}
6302
+ return staticHtml`
6303
+ <section class="flex flex-col" ${attr}>
6304
+ ${headingTpl(label)}
6667
6305
  <ul class="flex flex-col">
6668
- ${entities.map(
6669
- (entity) => html`<li>
6670
- ${entityLinkTpl({
6671
- ctx,
6672
- target: { kind: entity.kind, id: entityKey(entity) },
6673
- label: displayName(entity),
6674
- leading: kindIconTileTpl(entity.kind)
6675
- })}
6676
- </li>`
6677
- )}
6306
+ ${entityListItems(ctx, entities)}
6678
6307
  </ul>
6679
6308
  </section>
6680
6309
  `;
@@ -6831,6 +6460,20 @@ function screenshotTpl(url) {
6831
6460
  </section>
6832
6461
  `;
6833
6462
  }
6463
+ function lazyScreenshotEl(load) {
6464
+ const holder = document.createElement("div");
6465
+ holder.hidden = true;
6466
+ void load().then(
6467
+ (url) => {
6468
+ if (!url) return;
6469
+ render(screenshotTpl(url), holder);
6470
+ holder.hidden = false;
6471
+ },
6472
+ () => {
6473
+ }
6474
+ );
6475
+ return holder;
6476
+ }
6834
6477
  function metadataListTpl(entries) {
6835
6478
  return html`
6836
6479
  <div
@@ -6861,28 +6504,30 @@ function sectionTpl(section, ctx, query) {
6861
6504
  return acceptanceChecklistTpl(section.items);
6862
6505
  }
6863
6506
  case "composes":
6864
- return composesListTpl(
6865
- ctx,
6866
- section.label,
6867
- section.filterable ? filterEntities(section.entities, query) : section.entities
6868
- );
6507
+ return entitySectionTpl(ctx, {
6508
+ label: section.label,
6509
+ entities: section.filterable ? filterEntities(section.entities, query) : section.entities,
6510
+ dataAttr: "composes"
6511
+ });
6869
6512
  case "used-by":
6870
- return usedByListTpl(
6871
- ctx,
6872
- section.label,
6873
- section.filterable ? filterEntities(section.entities, query) : section.entities
6874
- );
6513
+ return entitySectionTpl(ctx, {
6514
+ label: section.label,
6515
+ entities: section.filterable ? filterEntities(section.entities, query) : section.entities,
6516
+ dataAttr: "used-by"
6517
+ });
6875
6518
  case "flows":
6876
- return flowListTpl(
6877
- ctx,
6878
- section.filterable ? filterEntities(section.flows, query) : section.flows
6879
- );
6519
+ return entitySectionTpl(ctx, {
6520
+ label: SECTION_LABELS.flows,
6521
+ entities: section.filterable ? filterEntities(section.flows, query) : section.flows,
6522
+ dataAttr: "flows"
6523
+ });
6880
6524
  case "touches":
6881
- return touchesTpl(
6882
- ctx,
6883
- section.filterable ? filterEntities(section.entities, query) : section.entities,
6884
- query
6885
- );
6525
+ return entitySectionTpl(ctx, {
6526
+ label: SECTION_LABELS.touches,
6527
+ entities: section.filterable ? filterEntities(section.entities, query) : section.entities,
6528
+ dataAttr: "touches",
6529
+ emptyText: query ? "No matches" : "No entities touched"
6530
+ });
6886
6531
  case "steps":
6887
6532
  return stepsTpl(
6888
6533
  ctx,
@@ -6894,11 +6539,32 @@ function sectionTpl(section, ctx, query) {
6894
6539
  section.filterable ? filterIds(section.paths, query) : section.paths
6895
6540
  );
6896
6541
  case "screenshot":
6897
- return screenshotTpl(section.url);
6542
+ if (section.url) return screenshotTpl(section.url);
6543
+ if (section.load) return html`${lazyScreenshotEl(section.load)}`;
6544
+ return null;
6898
6545
  case "metadata":
6899
6546
  return section.entries.length > 0 ? metadataListTpl(section.entries) : null;
6900
6547
  }
6901
6548
  }
6549
+
6550
+ // src/browser/views/render/detail.ts
6551
+ function subtitleTpl(subtitle) {
6552
+ const text = subtitle.extra ? `${subtitle.rawId} \xB7 ${subtitle.extra}` : subtitle.rawId;
6553
+ return html`<p
6554
+ class=${cn("text-muted-foreground font-mono text-xs", "px-2")}
6555
+ data-uidex-detail-subtitle
6556
+ >
6557
+ ${text}
6558
+ </p>`;
6559
+ }
6560
+ function notFoundTpl(ref2) {
6561
+ return html`<p
6562
+ class=${cn("text-muted-foreground text-sm", "p-4")}
6563
+ data-uidex-detail-missing
6564
+ >
6565
+ ${ref2.kind}: ${ref2.id} not found in registry
6566
+ </p>`;
6567
+ }
6902
6568
  function hasFilterableSections(sections) {
6903
6569
  return sections.some((s) => "filterable" in s && s.filterable === true);
6904
6570
  }
@@ -7259,6 +6925,34 @@ function createScreenshotLightbox(trigger, dataUrl, options) {
7259
6925
  };
7260
6926
  }
7261
6927
 
6928
+ // src/browser/ui/button.ts
6929
+ 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";
6930
+ var buttonVariants = cva(buttonBase, {
6931
+ defaultVariants: { size: "default", variant: "default" },
6932
+ variants: {
6933
+ size: {
6934
+ default: "h-8 px-3",
6935
+ sm: "h-7 gap-1.5 px-2.5",
6936
+ xs: "h-6 gap-1 rounded-md px-2 text-xs",
6937
+ lg: "h-9 px-3.5",
6938
+ xl: "h-10 px-4 text-base",
6939
+ icon: "size-8",
6940
+ "icon-sm": "size-7",
6941
+ "icon-lg": "size-9",
6942
+ "icon-xs": "size-6 rounded-md"
6943
+ },
6944
+ variant: {
6945
+ default: "border-primary bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
6946
+ destructive: "border-destructive bg-destructive shadow-xs hover:bg-destructive/90 text-white",
6947
+ "destructive-outline": "border-input bg-popover text-destructive-foreground shadow-xs/5 hover:border-destructive/30 hover:bg-destructive/5 dark:bg-input/30",
6948
+ ghost: "text-foreground hover:bg-accent hover:text-accent-foreground border-transparent",
6949
+ link: "text-foreground border-transparent underline-offset-4 hover:underline",
6950
+ outline: "border-input bg-popover text-foreground shadow-xs/5 hover:bg-accent hover:text-accent-foreground dark:bg-input/30",
6951
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90 border-transparent"
6952
+ }
6953
+ }
6954
+ });
6955
+
7262
6956
  // src/browser/views/render/form.ts
7263
6957
  var fieldSeq = 0;
7264
6958
  var nextFieldId = () => `uidex-field-${++fieldSeq}`;
@@ -7455,9 +7149,9 @@ function iconMediaTpl(iconTpl) {
7455
7149
  </div>
7456
7150
  `;
7457
7151
  }
7458
- function loadingViewTpl() {
7152
+ function loadingViewTpl(rootRef) {
7459
7153
  return html`
7460
- <div class=${EMPTY_ROOT} aria-live="polite" hidden>
7154
+ <div class=${EMPTY_ROOT} aria-live="polite" hidden ${ref(rootRef)}>
7461
7155
  ${iconMediaTpl(icon(Loader2, "animate-spin"))}
7462
7156
  <div class="flex max-w-sm flex-col items-center text-center">
7463
7157
  <div class=${SKELETON + " h-6 w-36 rounded-md"}></div>
@@ -7470,42 +7164,47 @@ function loadingViewTpl() {
7470
7164
  </div>
7471
7165
  `;
7472
7166
  }
7473
- function successViewTpl() {
7167
+ function successViewTpl(refs) {
7474
7168
  return html`
7475
- <div class=${EMPTY_ROOT} hidden data-uidex-success-view>
7169
+ <div class=${EMPTY_ROOT} hidden data-uidex-success-view ${ref(refs.root)}>
7476
7170
  ${iconMediaTpl(icon(CircleCheck))}
7477
7171
  <div class="flex max-w-sm flex-col items-center text-center">
7478
7172
  <div
7479
7173
  class="font-heading text-xl font-semibold"
7480
7174
  data-uidex-success-title
7175
+ ${ref(refs.title)}
7481
7176
  ></div>
7482
7177
  </div>
7483
7178
  <div
7484
7179
  class="flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm"
7485
7180
  data-uidex-success-actions
7181
+ ${ref(refs.actions)}
7486
7182
  >
7487
7183
  <a
7488
7184
  class=${buttonVariants({ variant: "outline", size: "sm" })}
7489
7185
  target="_blank"
7490
7186
  rel="noreferrer"
7491
7187
  data-uidex-success-link
7188
+ ${ref(refs.link)}
7492
7189
  ></a>
7493
7190
  </div>
7494
7191
  </div>
7495
7192
  `;
7496
7193
  }
7497
- function errorViewTpl() {
7194
+ function errorViewTpl(refs) {
7498
7195
  return html`
7499
- <div class=${EMPTY_ROOT} hidden data-uidex-error-view>
7196
+ <div class=${EMPTY_ROOT} hidden data-uidex-error-view ${ref(refs.root)}>
7500
7197
  ${iconMediaTpl(icon(CircleX))}
7501
7198
  <div class="flex max-w-sm flex-col items-center gap-1 text-center">
7502
7199
  <div
7503
7200
  class="font-heading text-xl font-semibold"
7504
7201
  data-uidex-error-title
7202
+ ${ref(refs.title)}
7505
7203
  ></div>
7506
7204
  <p
7507
7205
  class="text-muted-foreground text-sm"
7508
7206
  data-uidex-error-description
7207
+ ${ref(refs.description)}
7509
7208
  ></p>
7510
7209
  </div>
7511
7210
  <div
@@ -7516,6 +7215,7 @@ function errorViewTpl() {
7516
7215
  class=${buttonVariants({ variant: "outline", size: "sm" })}
7517
7216
  data-slot="button"
7518
7217
  data-uidex-retry-button
7218
+ ${ref(refs.retry)}
7519
7219
  >
7520
7220
  Try Again
7521
7221
  </button>
@@ -7557,8 +7257,15 @@ function renderFormSurface(surface, ctx, root) {
7557
7257
  });
7558
7258
  }
7559
7259
  const formRef = createRef();
7560
- const statusRef = createRef();
7561
- const submitRef = createRef();
7260
+ const loadingRef = createRef();
7261
+ const successRef = createRef();
7262
+ const successTitleRef = createRef();
7263
+ const successLinkRef = createRef();
7264
+ const successActionsRef = createRef();
7265
+ const errorRef = createRef();
7266
+ const errorTitleRef = createRef();
7267
+ const errorDescriptionRef = createRef();
7268
+ const retryRef = createRef();
7562
7269
  render(
7563
7270
  html`
7564
7271
  <section
@@ -7573,7 +7280,19 @@ function renderFormSurface(surface, ctx, root) {
7573
7280
  novalidate
7574
7281
  data-uidex-form=${surface.id}
7575
7282
  ></form>
7576
- ${loadingViewTpl()} ${successViewTpl()} ${errorViewTpl()}
7283
+ ${loadingViewTpl(loadingRef)}
7284
+ ${successViewTpl({
7285
+ root: successRef,
7286
+ title: successTitleRef,
7287
+ link: successLinkRef,
7288
+ actions: successActionsRef
7289
+ })}
7290
+ ${errorViewTpl({
7291
+ root: errorRef,
7292
+ title: errorTitleRef,
7293
+ description: errorDescriptionRef,
7294
+ retry: retryRef
7295
+ })}
7577
7296
  </section>
7578
7297
  `,
7579
7298
  root
@@ -7603,32 +7322,15 @@ function renderFormSurface(surface, ctx, root) {
7603
7322
  }
7604
7323
  });
7605
7324
  form.append(status);
7606
- const sectionEl = root.querySelector(
7607
- "[data-uidex-form-surface]"
7608
- );
7609
- const loadingView = sectionEl.querySelector("[aria-live='polite'][hidden]") ?? sectionEl.children[1];
7610
- const successView = sectionEl.querySelector(
7611
- "[data-uidex-success-view]"
7612
- );
7613
- const successTitle = sectionEl.querySelector(
7614
- "[data-uidex-success-title]"
7615
- );
7616
- const successLink = sectionEl.querySelector(
7617
- "[data-uidex-success-link]"
7618
- );
7619
- const successActions = successLink.parentElement;
7620
- const errorView = sectionEl.querySelector(
7621
- "[data-uidex-error-view]"
7622
- );
7623
- const errorTitle = sectionEl.querySelector(
7624
- "[data-uidex-error-title]"
7625
- );
7626
- const errorDescription = sectionEl.querySelector(
7627
- "[data-uidex-error-description]"
7628
- );
7629
- const retryButton = sectionEl.querySelector(
7630
- "[data-uidex-retry-button]"
7631
- );
7325
+ const loadingView = loadingRef.value;
7326
+ const successView = successRef.value;
7327
+ const successTitle = successTitleRef.value;
7328
+ const successLink = successLinkRef.value;
7329
+ const successActions = successActionsRef.value;
7330
+ const errorView = errorRef.value;
7331
+ const errorTitle = errorTitleRef.value;
7332
+ const errorDescription = errorDescriptionRef.value;
7333
+ const retryButton = retryRef.value;
7632
7334
  function clearAllFieldErrors() {
7633
7335
  for (const f of fields) f.setError(null);
7634
7336
  }
@@ -8029,7 +7731,7 @@ function defaultFilter(item, query) {
8029
7731
  function rowTag(item) {
8030
7732
  return item.tag ?? item.value;
8031
7733
  }
8032
- function resolveLeadingTpl(item, ctx) {
7734
+ function resolveLeadingTpl(item) {
8033
7735
  if (item.entityChip) return kindIconTileTpl(item.entityChip.entity.kind);
8034
7736
  if (item.leading) {
8035
7737
  const node = item.leading();
@@ -8134,7 +7836,7 @@ function buildItemsDom(surface, ctx, filteredItems, allByValue, content) {
8134
7836
  data-uidex-item-value=${item.value}
8135
7837
  >
8136
7838
  ${rowTpl({
8137
- leading: resolveLeadingTpl(item, ctx),
7839
+ leading: resolveLeadingTpl(item),
8138
7840
  label: item.label,
8139
7841
  subtitle: item.subtitle,
8140
7842
  trailing: item.trailing ?? item.shortcut
@@ -8179,7 +7881,7 @@ function renderListSurface(surface, ctx, root) {
8179
7881
  return root.ownerDocument ?? document;
8180
7882
  };
8181
7883
  const hasDefault = surface.defaultHighlight !== void 0 && allByValue.has(surface.defaultHighlight);
8182
- const { section, scrollRoot, viewport, content } = renderShell(surface, root);
7884
+ const { scrollRoot, viewport, content } = renderShell(surface, root);
8183
7885
  let maps = buildItemsDom(surface, ctx, filteredItems, allByValue, content);
8184
7886
  const controller = createListController({
8185
7887
  surfaceId: surface.id,
@@ -8875,52 +8577,16 @@ function sameRef2(a, b) {
8875
8577
  if (a === null || b === null) return false;
8876
8578
  return a.kind === b.kind && a.id === b.id;
8877
8579
  }
8878
- function resolveHints(view, ctx) {
8879
- const h = view.hints;
8880
- if (!h) return [];
8881
- if (typeof h === "function") {
8882
- try {
8883
- return h(ctx) ?? [];
8884
- } catch (err) {
8885
- console.error(`[uidex] view "${view.id}" hints() threw`, err);
8886
- return [];
8887
- }
8888
- }
8889
- return h;
8890
- }
8891
- function resolveTitle(view, ctx) {
8892
- const t = view.title;
8893
- if (!t) return "";
8894
- if (typeof t === "function") {
8895
- try {
8896
- return t(ctx) ?? "";
8897
- } catch (err) {
8898
- console.error(`[uidex] view "${view.id}" title() threw`, err);
8899
- return "";
8900
- }
8901
- }
8902
- return t;
8903
- }
8904
- function resolveActions(view, ctx, globalActions) {
8905
- const result = [];
8906
- const fn = view.actions;
8907
- if (fn) {
8908
- try {
8909
- const viewActions2 = fn(ctx) ?? [];
8910
- result.push(...viewActions2);
8911
- } catch (err) {
8912
- console.error(`[uidex] view "${view.id}" actions() threw`, err);
8913
- }
8914
- }
8915
- if (globalActions) {
8580
+ function resolveProp(view, ctx, value, propName, fallback) {
8581
+ if (typeof value === "function") {
8916
8582
  try {
8917
- const globals = globalActions(ctx) ?? [];
8918
- result.push(...globals);
8583
+ return value(ctx) ?? fallback;
8919
8584
  } catch (err) {
8920
- console.error(`[uidex] globalActions() threw`, err);
8585
+ console.error(`[uidex] view "${view.id}" ${propName}() threw`, err);
8586
+ return fallback;
8921
8587
  }
8922
8588
  }
8923
- return result;
8589
+ return value ?? fallback;
8924
8590
  }
8925
8591
  function createViewStack(options) {
8926
8592
  const { container, views, session, registry, highlight } = options;
@@ -8988,39 +8654,40 @@ function createViewStack(options) {
8988
8654
  color: KIND_STYLE[top.ctx.ref.kind].color
8989
8655
  });
8990
8656
  }
8991
- function updateChrome() {
8992
- if (!shell) return;
8993
- const top = mounted[mounted.length - 1];
8994
- if (!top) return;
8657
+ function updateNavButtons(shell2, top) {
8995
8658
  const atRoot = mounted.length <= 1 && top.view.id === "command-palette";
8996
- shell.backBtn.hidden = atRoot;
8997
- shell.searchIcon.hidden = !atRoot;
8659
+ shell2.backBtn.hidden = atRoot;
8660
+ shell2.searchIcon.hidden = !atRoot;
8661
+ }
8662
+ function updateTitle(shell2, top) {
8998
8663
  const searchable = top.view.searchable !== false;
8999
- shell.searchInput.hidden = !searchable;
9000
- const titleText = searchable ? "" : resolveTitle(top.view, top.ctx);
9001
- shell.headerTitle.textContent = titleText;
9002
- shell.headerTitle.hidden = searchable || !titleText;
9003
- shell.footerLeft.replaceChildren();
8664
+ shell2.searchInput.hidden = !searchable;
8665
+ const titleText = searchable ? "" : resolveProp(top.view, top.ctx, top.view.title, "title", "");
8666
+ shell2.headerTitle.textContent = titleText;
8667
+ shell2.headerTitle.hidden = searchable || !titleText;
8668
+ }
8669
+ function updateFooterChip(shell2, top) {
8670
+ shell2.footerLeft.replaceChildren();
9004
8671
  const refEntity = top.ctx.ref ? top.ctx.registry.get(top.ctx.ref.kind, top.ctx.ref.id) : null;
9005
8672
  if (refEntity) {
9006
- shell.footerLeft.append(
8673
+ shell2.footerLeft.append(
9007
8674
  renderKindChip({
9008
8675
  entity: refEntity,
9009
8676
  withKindName: true
9010
8677
  })
9011
8678
  );
9012
8679
  } else {
9013
- shell.footerLeft.append(shell.logo);
8680
+ shell2.footerLeft.append(shell2.logo);
9014
8681
  }
9015
- hintChangeSub?.();
9016
- hintChangeSub = null;
9017
- highlightActionsSub?.();
9018
- highlightActionsSub = null;
9019
- shell.footerRight.replaceChildren();
9020
- const footerItems = [];
9021
- const enterHint = resolveHints(top.view, top.ctx).find(
9022
- (h) => h.key.includes("\u21B5")
9023
- );
8682
+ }
8683
+ function buildEnterHint(top, footerItems) {
8684
+ const enterHint = resolveProp(
8685
+ top.view,
8686
+ top.ctx,
8687
+ top.view.hints,
8688
+ "hints",
8689
+ []
8690
+ ).find((h) => h.key.includes("\u21B5"));
9024
8691
  const src = top.mounted.submitIntent;
9025
8692
  if (src) {
9026
8693
  const key = enterHint?.key ?? "\u21B5";
@@ -9041,28 +8708,52 @@ function createViewStack(options) {
9041
8708
  } else if (enterHint) {
9042
8709
  footerItems.push(createHint(enterHint.key, enterHint.label));
9043
8710
  }
8711
+ }
8712
+ function buildActions(shell2, top, footerItems) {
9044
8713
  const actionsDivider = createFooterDivider();
9045
- const viewActions2 = resolveActions(top.view, top.ctx);
8714
+ const viewActions2 = resolveProp(
8715
+ top.view,
8716
+ top.ctx,
8717
+ top.view.actions,
8718
+ "actions",
8719
+ []
8720
+ );
9046
8721
  const hlSrc = top.mounted.highlightActions;
9047
8722
  const syncActions = () => {
9048
8723
  const hlActions = hlSrc?.get() ?? [];
9049
8724
  const combined = [...hlActions, ...viewActions2];
9050
- shell.actionsPopup.setActions(combined);
8725
+ shell2.actionsPopup.setActions(combined);
9051
8726
  const visible = combined.length > 0;
9052
- shell.actionsPopup.trigger.hidden = !visible;
8727
+ shell2.actionsPopup.trigger.hidden = !visible;
9053
8728
  actionsDivider.hidden = !visible || footerItems.length === 0;
9054
8729
  };
9055
8730
  if (hlSrc) {
9056
8731
  highlightActionsSub = hlSrc.subscribe(syncActions);
9057
8732
  }
9058
8733
  for (let i = 0; i < footerItems.length; i++) {
9059
- if (i > 0) shell.footerRight.append(createFooterDivider());
9060
- shell.footerRight.append(footerItems[i]);
8734
+ if (i > 0) shell2.footerRight.append(createFooterDivider());
8735
+ shell2.footerRight.append(footerItems[i]);
9061
8736
  }
9062
- shell.footerRight.append(actionsDivider);
9063
- shell.footerRight.append(shell.actionsPopup.trigger);
8737
+ shell2.footerRight.append(actionsDivider);
8738
+ shell2.footerRight.append(shell2.actionsPopup.trigger);
9064
8739
  syncActions();
9065
8740
  }
8741
+ function updateChrome() {
8742
+ if (!shell) return;
8743
+ const top = mounted[mounted.length - 1];
8744
+ if (!top) return;
8745
+ updateNavButtons(shell, top);
8746
+ updateTitle(shell, top);
8747
+ updateFooterChip(shell, top);
8748
+ hintChangeSub?.();
8749
+ hintChangeSub = null;
8750
+ highlightActionsSub?.();
8751
+ highlightActionsSub = null;
8752
+ shell.footerRight.replaceChildren();
8753
+ const footerItems = [];
8754
+ buildEnterHint(top, footerItems);
8755
+ buildActions(shell, top, footerItems);
8756
+ }
9066
8757
  function render2() {
9067
8758
  if (!container.isConnected) return;
9068
8759
  const stack = session.getState().stack;
@@ -9159,7 +8850,7 @@ function createViewStack(options) {
9159
8850
 
9160
8851
  // src/browser/views/built-in/ids.ts
9161
8852
  var BUILT_IN_VIEW_IDS = {
9162
- archiveReason: "archive-reason",
8853
+ closeReason: "close-reason",
9163
8854
  commandPalette: "command-palette",
9164
8855
  elements: "elements",
9165
8856
  entityReports: "entity-reports",
@@ -9210,7 +8901,7 @@ function parentDetail(ref2) {
9210
8901
  return { id: DETAIL_VIEW_FOR_KIND[ref2.kind], ref: ref2 };
9211
8902
  }
9212
8903
 
9213
- // src/browser/views/built-in/archive-reason.ts
8904
+ // src/browser/views/built-in/close-reason.ts
9214
8905
  import {
9215
8906
  Ban,
9216
8907
  BugOff,
@@ -9220,10 +8911,10 @@ import {
9220
8911
  createElement as createLucideElement6
9221
8912
  } from "lucide";
9222
8913
  var pendingReportId = null;
9223
- var afterArchive = null;
9224
- function setArchiveTarget(reportId, onDone) {
8914
+ var afterClose = null;
8915
+ function setCloseTarget(reportId, onDone) {
9225
8916
  pendingReportId = reportId;
9226
- afterArchive = onDone;
8917
+ afterClose = onDone;
9227
8918
  }
9228
8919
  function leadingIcon(iconNode) {
9229
8920
  return () => {
@@ -9238,16 +8929,16 @@ function spinnerTile() {
9238
8929
  return createIconTile(spinner);
9239
8930
  }
9240
8931
  var REASONS = [
9241
- { value: "fixed", label: "Fixed", icon: CircleCheck2 },
8932
+ { value: "fixed", label: "Resolved", icon: CircleCheck2 },
9242
8933
  { value: "not_a_bug", label: "Not a bug", icon: BugOff },
9243
8934
  { value: "duplicate", label: "Duplicate", icon: Copy2 },
9244
8935
  { value: "wont_fix", label: "Won't fix", icon: Ban }
9245
8936
  ];
9246
- var archiveReasonView = {
9247
- id: BUILT_IN_VIEW_IDS.archiveReason,
8937
+ var closeReasonView = {
8938
+ id: BUILT_IN_VIEW_IDS.closeReason,
9248
8939
  matches: () => false,
9249
8940
  parent: (ref2) => ref2 ? { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 } : null,
9250
- title: "Archive reason",
8941
+ title: "Close reason",
9251
8942
  searchable: false,
9252
8943
  focusTarget: (host) => host.querySelector("[data-uidex-list-content]"),
9253
8944
  surface: () => ({ kind: "list", id: "unused", items: [] }),
@@ -9260,12 +8951,12 @@ var archiveReasonView = {
9260
8951
  }));
9261
8952
  const surface = {
9262
8953
  kind: "list",
9263
- id: "uidex-archive-reason",
8954
+ id: "uidex-close-reason",
9264
8955
  searchable: false,
9265
8956
  items,
9266
8957
  emptyLabel: "No reasons available",
9267
8958
  onSelect: async (item) => {
9268
- if (busy || !pendingReportId || !ctx.registry.archiveReport) return;
8959
+ if (busy || !pendingReportId || !ctx.registry.closeReport) return;
9269
8960
  busy = true;
9270
8961
  const row = root.querySelector(
9271
8962
  `[data-uidex-item-value="${item.value}"]`
@@ -9277,13 +8968,10 @@ var archiveReasonView = {
9277
8968
  row.style.pointerEvents = "none";
9278
8969
  }
9279
8970
  try {
9280
- await ctx.registry.archiveReport(
9281
- pendingReportId,
9282
- item.value
9283
- );
9284
- const done = afterArchive;
8971
+ await ctx.registry.closeReport(pendingReportId, item.value);
8972
+ const done = afterClose;
9285
8973
  pendingReportId = null;
9286
- afterArchive = null;
8974
+ afterClose = null;
9287
8975
  done?.();
9288
8976
  } catch {
9289
8977
  busy = false;
@@ -9301,7 +8989,7 @@ var archiveReasonView = {
9301
8989
  }
9302
8990
  const error = document.createElement("p");
9303
8991
  error.className = "text-destructive px-4 py-2 text-xs";
9304
- error.textContent = "Archive failed \u2014 try again";
8992
+ error.textContent = "Close failed \u2014 try again";
9305
8993
  root.querySelector("[data-uidex-list-content]")?.prepend(error);
9306
8994
  setTimeout(() => error.remove(), 3e3);
9307
8995
  }
@@ -9628,7 +9316,8 @@ function createCommandPaletteView(shortcut) {
9628
9316
  }
9629
9317
 
9630
9318
  // src/browser/internal/screenshot.ts
9631
- import { domToPng } from "modern-screenshot";
9319
+ import { domToPng, domToWebp } from "modern-screenshot";
9320
+ var WEBP_QUALITY = 0.85;
9632
9321
  function resolveBackgroundColor(el2) {
9633
9322
  let node = el2;
9634
9323
  while (node) {
@@ -9642,8 +9331,7 @@ function isUidexChrome(node) {
9642
9331
  if (node.nodeType !== Node.ELEMENT_NODE) return false;
9643
9332
  const el2 = node;
9644
9333
  if (el2.shadowRoot !== null) return true;
9645
- const cls = el2.classList;
9646
- return cls.contains(SURFACE_HOST_CLASS) || cls.contains(SURFACE_CONTAINER_CLASS);
9334
+ return el2.classList.contains(SURFACE_HOST_CLASS);
9647
9335
  }
9648
9336
  async function captureScreenshot(options = {}) {
9649
9337
  if (typeof document === "undefined") {
@@ -9655,8 +9343,10 @@ async function captureScreenshot(options = {}) {
9655
9343
  const scale = options.maxWidth && width > options.maxWidth ? options.maxWidth / width : 1;
9656
9344
  const padding = 16;
9657
9345
  const height = target.scrollHeight || target.clientHeight;
9658
- return domToPng(target, {
9346
+ const encode = options.format === "png" ? domToPng : domToWebp;
9347
+ return encode(target, {
9659
9348
  scale,
9349
+ quality: WEBP_QUALITY,
9660
9350
  width: width + padding * 2,
9661
9351
  height: height + padding * 2,
9662
9352
  backgroundColor: resolveBackgroundColor(target),
@@ -9712,12 +9402,6 @@ function collectFlowsTouching(ctx, targetId) {
9712
9402
  }
9713
9403
 
9714
9404
  // src/browser/views/builder/detail-builder.ts
9715
- var DOM_BACKED_KINDS2 = /* @__PURE__ */ new Set([
9716
- "element",
9717
- "region",
9718
- "widget",
9719
- "primitive"
9720
- ]);
9721
9405
  var DETAIL_HINTS = [{ key: "\u21B5", label: "Open" }];
9722
9406
  function copyPathAction(ref2, loc) {
9723
9407
  const identifier = `${ref2.kind}:${ref2.id}`;
@@ -9819,7 +9503,10 @@ function copyScreenshotAction(ref2) {
9819
9503
  const target = resolveEntityElement(ref2) ?? void 0;
9820
9504
  const dataUrl = await captureScreenshot({
9821
9505
  target,
9822
- maxWidth: 1280
9506
+ maxWidth: 1280,
9507
+ // Clipboard write requires PNG — the async Clipboard API rejects
9508
+ // image/webp (`ClipboardItem` throws on Chrome/Safari).
9509
+ format: "png"
9823
9510
  });
9824
9511
  const res = await fetch(dataUrl);
9825
9512
  const blob = await res.blob();
@@ -9870,7 +9557,7 @@ function createEntityDetailView(config) {
9870
9557
  if (cloud?.integrations.getCachedConfig()?.hasJira) {
9871
9558
  actions.push({ ...jiraAction(ctx.ref), group: "Report" });
9872
9559
  }
9873
- if (DOM_BACKED_KINDS2.has(kind) && resolveEntityElement(ctx.ref)) {
9560
+ if (DOM_BACKED_KINDS.has(kind) && resolveEntityElement(ctx.ref)) {
9874
9561
  actions.push({ ...highlightElementAction(ctx.ref), group: "Inspect" });
9875
9562
  actions.push({ ...copyScreenshotAction(ctx.ref), group: "Inspect" });
9876
9563
  }
@@ -9896,7 +9583,7 @@ function createEntityDetailView(config) {
9896
9583
  sections.push(s);
9897
9584
  }
9898
9585
  }
9899
- if (!DOM_BACKED_KINDS2.has(kind)) {
9586
+ if (!DOM_BACKED_KINDS.has(kind)) {
9900
9587
  sections.push({
9901
9588
  id: "composes",
9902
9589
  label: SECTION_LABELS.contains,
@@ -9904,7 +9591,7 @@ function createEntityDetailView(config) {
9904
9591
  filterable: true
9905
9592
  });
9906
9593
  }
9907
- if (DOM_BACKED_KINDS2.has(kind) && meta?.features?.length) {
9594
+ if (DOM_BACKED_KINDS.has(kind) && meta?.features?.length) {
9908
9595
  const featureEntities = meta.features.map((fId) => ctx.registry.get("feature", fId)).filter((e) => !!e);
9909
9596
  if (featureEntities.length > 0) {
9910
9597
  sections.push({
@@ -9934,35 +9621,14 @@ function createEntityDetailView(config) {
9934
9621
  }
9935
9622
 
9936
9623
  // src/browser/views/built-in/entity-detail.ts
9937
- function collectDomParents(ctx, ref2) {
9938
- const el2 = resolveEntityElement(ref2);
9939
- if (!el2) return [];
9940
- const parents = [];
9941
- const seen = /* @__PURE__ */ new Set();
9942
- let node = el2.parentElement;
9943
- while (node) {
9944
- if (node instanceof HTMLElement) {
9945
- for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
9946
- const id = node.getAttribute(attr);
9947
- if (id) {
9948
- const key = `${kind}:${id}`;
9949
- if (!seen.has(key)) {
9950
- seen.add(key);
9951
- const entity = ctx.registry.get(kind, id);
9952
- if (entity) parents.push(entity);
9953
- }
9954
- }
9955
- }
9956
- }
9957
- node = node.parentElement;
9958
- }
9959
- return parents;
9624
+ function collectFeatureConsumers(ctx, featureId) {
9625
+ return ctx.registry.list("page").filter((page) => page.meta?.features?.includes(featureId));
9960
9626
  }
9961
- function usedBySection(ctx, ref2) {
9627
+ function usedBySection(ctx, featureId) {
9962
9628
  return {
9963
9629
  id: "used-by",
9964
9630
  label: SECTION_LABELS.usedBy,
9965
- entities: collectDomParents(ctx, ref2),
9631
+ entities: collectFeatureConsumers(ctx, featureId),
9966
9632
  filterable: true
9967
9633
  };
9968
9634
  }
@@ -9983,9 +9649,7 @@ var featureDetailView = createEntityDetailView({
9983
9649
  id: "feature-detail",
9984
9650
  kind: "feature",
9985
9651
  fallbackTitle: "Feature",
9986
- extraSections: (ctx, entity) => [
9987
- usedBySection(ctx, { kind: "feature", id: entity.id })
9988
- ]
9652
+ extraSections: (ctx, entity) => [usedBySection(ctx, entity.id)]
9989
9653
  });
9990
9654
  var regionDetailView = createEntityDetailView({
9991
9655
  id: "region-detail",
@@ -10113,6 +9777,15 @@ var reportDetailView = {
10113
9777
  }
10114
9778
  if (report.screenshot) {
10115
9779
  sections.push({ id: "screenshot", url: report.screenshot });
9780
+ } else {
9781
+ const pins = ctx.cloud?.pins;
9782
+ const fetchScreenshot = pins?.screenshot;
9783
+ if (pins && fetchScreenshot) {
9784
+ sections.push({
9785
+ id: "screenshot",
9786
+ load: () => fetchScreenshot.call(pins, report.id)
9787
+ });
9788
+ }
10116
9789
  }
10117
9790
  const metaEntries = [];
10118
9791
  if (report.url) metaEntries.push({ label: "URL", value: report.url });
@@ -10123,14 +9796,14 @@ var reportDetailView = {
10123
9796
  sections.push({ id: "metadata", entries: metaEntries });
10124
9797
  }
10125
9798
  const actions = [];
10126
- if (ctx.registry.archiveReport) {
9799
+ if (ctx.registry.closeReport) {
10127
9800
  actions.push({
10128
- id: "archive",
10129
- label: "Archive",
9801
+ id: "close",
9802
+ label: "Close",
10130
9803
  icon: "archive-x",
10131
9804
  run: () => {
10132
- setArchiveTarget(report.id, () => ctx.pop());
10133
- ctx.push({ id: BUILT_IN_VIEW_IDS.archiveReason });
9805
+ setCloseTarget(report.id, () => ctx.pop());
9806
+ ctx.push({ id: BUILT_IN_VIEW_IDS.closeReason });
10134
9807
  }
10135
9808
  });
10136
9809
  }
@@ -10168,13 +9841,13 @@ function reportToItem(report, ctx) {
10168
9841
  const label = raw.length > 80 ? raw.slice(0, 80) + "\u2026" : raw;
10169
9842
  const kind = ctx.ref?.kind ?? "element";
10170
9843
  const actions = [];
10171
- if (ctx.registry.archiveReport) {
9844
+ if (ctx.registry.closeReport) {
10172
9845
  actions.push({
10173
- id: `archive-${report.id}`,
10174
- label: "Archive",
9846
+ id: `close-${report.id}`,
9847
+ label: "Close",
10175
9848
  perform: () => {
10176
- setArchiveTarget(report.id, () => ctx.pop());
10177
- ctx.push({ id: BUILT_IN_VIEW_IDS.archiveReason });
9849
+ setCloseTarget(report.id, () => ctx.pop());
9850
+ ctx.push({ id: BUILT_IN_VIEW_IDS.closeReason });
10178
9851
  }
10179
9852
  });
10180
9853
  }
@@ -10327,7 +10000,7 @@ function capitalize2(s) {
10327
10000
  return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
10328
10001
  }
10329
10002
  function renderPayloadMarkdown(payload) {
10330
- const heading = payload.title ?? `${capitalize2(payload.type)} Report`;
10003
+ const heading = payload.title ?? `${capitalize2(payload.type ?? "")} Report`;
10331
10004
  const ctx = payload.context;
10332
10005
  const lines = [
10333
10006
  `# ${heading}`,
@@ -10396,12 +10069,6 @@ var reportFields = [
10396
10069
  ];
10397
10070
 
10398
10071
  // src/browser/views/built-in/report/view-builder.ts
10399
- var DOM_BACKED_KINDS3 = /* @__PURE__ */ new Set([
10400
- "element",
10401
- "region",
10402
- "widget",
10403
- "primitive"
10404
- ]);
10405
10072
  var KIND_TO_ATTR = new Map(
10406
10073
  UIDEX_ATTR_TO_KIND.map(([attr, kind]) => [kind, attr])
10407
10074
  );
@@ -10472,7 +10139,7 @@ function createReportView(config) {
10472
10139
  const cloud = resolveCloud(ctx);
10473
10140
  const extra = extraFields ? extraFields(ctx) : [];
10474
10141
  const fields = [...extra, ...baseFields ?? reportFields];
10475
- const isDomBacked = ctx.ref != null && DOM_BACKED_KINDS3.has(ctx.ref.kind);
10142
+ const isDomBacked = ctx.ref != null && DOM_BACKED_KINDS.has(ctx.ref.kind);
10476
10143
  const targetEl = ctx.ref && isDomBacked ? resolveElement(ctx.ref) : null;
10477
10144
  const screenshotPromise = isDomBacked ? captureScreenshot({
10478
10145
  target: targetEl ?? void 0,
@@ -11055,7 +10722,7 @@ var pinSettingsView = {
11055
10722
  // src/browser/views/built-in/index.ts
11056
10723
  function buildDefaultViews(shortcut) {
11057
10724
  return [
11058
- archiveReasonView,
10725
+ closeReasonView,
11059
10726
  createCommandPaletteView(shortcut),
11060
10727
  explorePageView,
11061
10728
  componentDetailView,
@@ -11083,6 +10750,27 @@ function buildDefaultViews(shortcut) {
11083
10750
  }
11084
10751
 
11085
10752
  // src/browser/create-uidex.ts
10753
+ function getVisibleEntities() {
10754
+ if (typeof document === "undefined") return [];
10755
+ const selector = UIDEX_ATTR_TO_KIND.map(([a]) => `[${a}]`).join(",");
10756
+ const nodes = document.querySelectorAll(selector);
10757
+ const ids = [];
10758
+ const seen = /* @__PURE__ */ new Set();
10759
+ for (const node of nodes) {
10760
+ if (node.closest(SURFACE_IGNORE_SELECTOR)) continue;
10761
+ for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
10762
+ const id = node.getAttribute(attr);
10763
+ if (!id) continue;
10764
+ const key = `${kind}:${id}`;
10765
+ if (!seen.has(key)) {
10766
+ seen.add(key);
10767
+ ids.push(key);
10768
+ }
10769
+ break;
10770
+ }
10771
+ }
10772
+ return ids;
10773
+ }
11086
10774
  function createUidex(options = {}) {
11087
10775
  const registry = createRegistry();
11088
10776
  const inspectorRef = { current: null };
@@ -11117,7 +10805,7 @@ function createUidex(options = {}) {
11117
10805
  const views = createRouter({ session });
11118
10806
  const cloud = options.cloud ?? null;
11119
10807
  const ingestOpts = resolveIngestOptions(options.ingest, cloud !== null);
11120
- const ingest = ingestOpts ? createIngest({ session, ...ingestOpts }) : null;
10808
+ const ingest = ingestOpts ? createIngest(ingestOpts) : null;
11121
10809
  if (options.defaultViews !== false) {
11122
10810
  for (const view of buildDefaultViews(options.shortcut)) views.add(view);
11123
10811
  }
@@ -11127,6 +10815,94 @@ function createUidex(options = {}) {
11127
10815
  let shadowRoot = null;
11128
10816
  const mountCleanup = createCleanupStack();
11129
10817
  let mounted = false;
10818
+ function syncReportsToRegistry() {
10819
+ const layer = pinLayerRef.current;
10820
+ if (!layer) return;
10821
+ const byEntity = /* @__PURE__ */ new Map();
10822
+ for (const pin of layer.getAllPins()) {
10823
+ const cid = pin.entity ?? "";
10824
+ if (!cid) continue;
10825
+ const list = byEntity.get(cid);
10826
+ if (list) list.push(pin);
10827
+ else byEntity.set(cid, [pin]);
10828
+ }
10829
+ for (const [cid, pins] of byEntity) {
10830
+ const ref2 = parseComponentRef(cid);
10831
+ registry.setReports(ref2.kind, ref2.id, pins);
10832
+ }
10833
+ }
10834
+ function setupKeyBindings(root, viewStack) {
10835
+ const bindings = bindShadowKeys({
10836
+ shadowRoot: root,
10837
+ session,
10838
+ getActionsPopup: () => viewStack.getActionsPopup(),
10839
+ getPinLayer: () => pinLayerRef.current,
10840
+ shortcut: options.shortcut
10841
+ });
10842
+ mountCleanup.add(bindings);
10843
+ return bindings;
10844
+ }
10845
+ function setupPinLayer(root, shell, channel, getCurrentRoute, getMatchMode, getPathname) {
10846
+ if (cloud) {
10847
+ registry.closeReport = async (reportId, status) => {
10848
+ pinLayerRef.current?.removePin(reportId);
10849
+ await cloud.pins.close(
10850
+ reportId,
10851
+ status
10852
+ );
10853
+ syncReportsToRegistry();
10854
+ };
10855
+ }
10856
+ const pinLayer = createPinLayer({
10857
+ container: root,
10858
+ onOpenPinDetail: (componentId, reportId) => {
10859
+ const ref2 = parseComponentRef(componentId);
10860
+ setSelectedReportId(reportId);
10861
+ const entry = { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 };
10862
+ session.mode.transition.enterViewing(views.buildStack(entry));
10863
+ },
10864
+ onHoverPin: (anchor, componentId) => {
10865
+ const overlay = overlayRef.current;
10866
+ if (!overlay) return;
10867
+ if (!anchor || !componentId) {
10868
+ overlay.hide();
10869
+ return;
10870
+ }
10871
+ overlay.show(anchor, {
10872
+ color: isDarkMode() ? "#e4e4e7" : "#27272a"
10873
+ });
10874
+ },
10875
+ onPinsChanged: syncReportsToRegistry
10876
+ });
10877
+ shell.menuBar.setPinLayer(pinLayer);
10878
+ pinLayerRef.current = pinLayer;
10879
+ mountCleanup.add(() => {
10880
+ pinLayer.destroy();
10881
+ pinLayerRef.current = null;
10882
+ registry.closeReport = void 0;
10883
+ });
10884
+ if (cloud) {
10885
+ const detach = pinLayer.attachCloud({
10886
+ cloud,
10887
+ channel,
10888
+ getRoute: getCurrentRoute,
10889
+ getPathname,
10890
+ getVisibleEntities,
10891
+ getMatchMode,
10892
+ onError: (err) => console.warn("[uidex] pin fetch failed", err)
10893
+ });
10894
+ mountCleanup.add(detach);
10895
+ if (channel && typeof window !== "undefined") {
10896
+ const onRouteChange = () => {
10897
+ const route = getCurrentRoute();
10898
+ channel?.joinRoute(route);
10899
+ void pinLayer.refresh();
10900
+ };
10901
+ const detachRoute = bindRouteChange(onRouteChange);
10902
+ mountCleanup.add(detachRoute);
10903
+ }
10904
+ }
10905
+ }
11130
10906
  function mount(target) {
11131
10907
  if (mounted) return;
11132
10908
  const mountTarget = target ?? (typeof document !== "undefined" ? document.body : null);
@@ -11135,28 +10911,7 @@ function createUidex(options = {}) {
11135
10911
  }
11136
10912
  const getPathname = () => typeof location !== "undefined" ? location.pathname : "/";
11137
10913
  const getCurrentRoute = () => options.getRoute?.() ?? findCurrentRoutePath(registry) ?? getPathname();
11138
- const getVisibleEntities = () => {
11139
- if (typeof document === "undefined") return [];
11140
- const selector = UIDEX_ATTR_TO_KIND.map(([a]) => `[${a}]`).join(",");
11141
- const nodes = document.querySelectorAll(selector);
11142
- const ids = [];
11143
- const seen = /* @__PURE__ */ new Set();
11144
- for (const node of nodes) {
11145
- if (node.closest(SURFACE_IGNORE_SELECTOR)) continue;
11146
- for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
11147
- const id = node.getAttribute(attr);
11148
- if (!id) continue;
11149
- const key = `${kind}:${id}`;
11150
- if (!seen.has(key)) {
11151
- seen.add(key);
11152
- ids.push(key);
11153
- }
11154
- break;
11155
- }
11156
- }
11157
- return ids;
11158
- };
11159
- const getMatchMode = () => (typeof localStorage !== "undefined" ? localStorage.getItem("uidex:pin-match-mode") : null) ?? "route";
10914
+ const getMatchMode = () => readMode();
11160
10915
  let channel = null;
11161
10916
  if (cloud && options.user) {
11162
10917
  channel = cloud.realtime.connect({
@@ -11177,7 +10932,6 @@ function createUidex(options = {}) {
11177
10932
  onSelect: (match) => {
11178
10933
  const route = views.resolve(match.ref);
11179
10934
  if (!route) return;
11180
- session.select(match.ref);
11181
10935
  const entry = { id: route.view.id, ref: match.ref };
11182
10936
  session.mode.transition.enterViewing(views.buildStack(entry));
11183
10937
  }
@@ -11228,92 +10982,15 @@ function createUidex(options = {}) {
11228
10982
  });
11229
10983
  mountCleanup.add(viewStack);
11230
10984
  if (shadowRoot) {
11231
- keyBindings = bindShadowKeys({
10985
+ keyBindings = setupKeyBindings(shadowRoot, viewStack);
10986
+ setupPinLayer(
11232
10987
  shadowRoot,
11233
- session,
11234
- getActionsPopup: () => viewStack.getActionsPopup(),
11235
- getPinLayer: () => pinLayerRef.current,
11236
- shortcut: options.shortcut
11237
- });
11238
- mountCleanup.add(keyBindings);
11239
- }
11240
- if (shadowRoot) {
11241
- const syncReportsToRegistry = () => {
11242
- const layer = pinLayerRef.current;
11243
- if (!layer) return;
11244
- const byEntity = /* @__PURE__ */ new Map();
11245
- for (const pin of layer.getAllPins()) {
11246
- const cid = pin.entity ?? "";
11247
- if (!cid) continue;
11248
- const list = byEntity.get(cid);
11249
- if (list) list.push(pin);
11250
- else byEntity.set(cid, [pin]);
11251
- }
11252
- for (const [cid, pins] of byEntity) {
11253
- const ref2 = parseComponentRef(cid);
11254
- registry.setReports(ref2.kind, ref2.id, pins);
11255
- }
11256
- };
11257
- if (cloud) {
11258
- registry.archiveReport = async (reportId, reason) => {
11259
- pinLayerRef.current?.removePin(reportId);
11260
- await cloud.pins.archive(
11261
- reportId,
11262
- reason
11263
- );
11264
- syncReportsToRegistry();
11265
- };
11266
- }
11267
- const pinLayer = createPinLayer({
11268
- container: shadowRoot,
11269
- currentBranch: options.git?.branch ?? null,
11270
- onOpenPinDetail: (componentId, reportId) => {
11271
- const ref2 = parseComponentRef(componentId);
11272
- setSelectedReportId(reportId);
11273
- const entry = { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 };
11274
- session.mode.transition.enterViewing(views.buildStack(entry));
11275
- },
11276
- onHoverPin: (anchor, componentId) => {
11277
- const overlay = overlayRef.current;
11278
- if (!overlay) return;
11279
- if (!anchor || !componentId) {
11280
- overlay.hide();
11281
- return;
11282
- }
11283
- overlay.show(anchor, {
11284
- color: isDarkMode() ? "#e4e4e7" : "#27272a"
11285
- });
11286
- },
11287
- onPinsChanged: syncReportsToRegistry
11288
- });
11289
- shell.menuBar.setPinLayer(pinLayer);
11290
- pinLayerRef.current = pinLayer;
11291
- mountCleanup.add(() => {
11292
- pinLayer.destroy();
11293
- pinLayerRef.current = null;
11294
- registry.archiveReport = void 0;
11295
- });
11296
- if (cloud) {
11297
- const detach = pinLayer.attachCloud({
11298
- cloud,
11299
- channel,
11300
- getRoute: getCurrentRoute,
11301
- getPathname,
11302
- getVisibleEntities,
11303
- getMatchMode,
11304
- onError: (err) => console.warn("[uidex] pin fetch failed", err)
11305
- });
11306
- mountCleanup.add(detach);
11307
- if (channel && typeof window !== "undefined") {
11308
- const onRouteChange = () => {
11309
- const route = getCurrentRoute();
11310
- channel?.joinRoute(route);
11311
- void pinLayer.refresh();
11312
- };
11313
- const detachRoute = bindRouteChange(onRouteChange);
11314
- mountCleanup.add(detachRoute);
11315
- }
11316
- }
10988
+ shell,
10989
+ channel,
10990
+ getCurrentRoute,
10991
+ getMatchMode,
10992
+ getPathname
10993
+ );
11317
10994
  }
11318
10995
  if (ingest) {
11319
10996
  ingest.start();
@@ -11343,7 +11020,6 @@ function createUidex(options = {}) {
11343
11020
  export {
11344
11021
  ENTITY_KINDS,
11345
11022
  KIND_STYLE,
11346
- SURFACE_CONTAINER_CLASS,
11347
11023
  SURFACE_HOST_CLASS,
11348
11024
  SURFACE_IGNORE_SELECTOR,
11349
11025
  UnknownEntityKindError,
@@ -11358,7 +11034,6 @@ export {
11358
11034
  createCommandPaletteView,
11359
11035
  createConsoleCapture,
11360
11036
  createCursorTooltip,
11361
- createHighlightController,
11362
11037
  createIngest,
11363
11038
  createInspector,
11364
11039
  createMenuBar,
@@ -11375,13 +11050,11 @@ export {
11375
11050
  createThemeDetector,
11376
11051
  createUidex,
11377
11052
  createViewStack,
11378
- defaultResolveMatch,
11379
11053
  displayName,
11380
11054
  entityKey,
11381
11055
  featureDetailView,
11382
11056
  flowDetailView,
11383
11057
  formatShortcutLabel,
11384
- getNativeFetch,
11385
11058
  isMetaKind,
11386
11059
  nativeFetch,
11387
11060
  pageDetailView,