uidex 0.7.0 → 0.9.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 (51) hide show
  1. package/dist/cli/cli.cjs +1112 -1054
  2. package/dist/cli/cli.cjs.map +1 -1
  3. package/dist/headless/index.cjs +4 -448
  4. package/dist/headless/index.cjs.map +1 -1
  5. package/dist/headless/index.d.cts +41 -11
  6. package/dist/headless/index.d.ts +41 -11
  7. package/dist/headless/index.js +4 -450
  8. package/dist/headless/index.js.map +1 -1
  9. package/dist/index.cjs +147 -3252
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +43 -316
  12. package/dist/index.d.ts +43 -316
  13. package/dist/index.js +133 -3254
  14. package/dist/index.js.map +1 -1
  15. package/dist/playwright/index.cjs +175 -0
  16. package/dist/playwright/index.cjs.map +1 -1
  17. package/dist/playwright/index.d.cts +2 -0
  18. package/dist/playwright/index.d.ts +2 -0
  19. package/dist/playwright/index.js +167 -0
  20. package/dist/playwright/index.js.map +1 -1
  21. package/dist/playwright/states-reporter.cjs +123 -0
  22. package/dist/playwright/states-reporter.cjs.map +1 -0
  23. package/dist/playwright/states-reporter.d.cts +46 -0
  24. package/dist/playwright/states-reporter.d.ts +46 -0
  25. package/dist/playwright/states-reporter.js +88 -0
  26. package/dist/playwright/states-reporter.js.map +1 -0
  27. package/dist/playwright/states.cjs +118 -0
  28. package/dist/playwright/states.cjs.map +1 -0
  29. package/dist/playwright/states.d.cts +120 -0
  30. package/dist/playwright/states.d.ts +120 -0
  31. package/dist/playwright/states.js +88 -0
  32. package/dist/playwright/states.js.map +1 -0
  33. package/dist/react/index.cjs +163 -3255
  34. package/dist/react/index.cjs.map +1 -1
  35. package/dist/react/index.d.cts +62 -275
  36. package/dist/react/index.d.ts +62 -275
  37. package/dist/react/index.js +151 -3268
  38. package/dist/react/index.js.map +1 -1
  39. package/dist/scan/index.cjs +1292 -345
  40. package/dist/scan/index.cjs.map +1 -1
  41. package/dist/scan/index.d.cts +305 -12
  42. package/dist/scan/index.d.ts +305 -12
  43. package/dist/scan/index.js +1283 -345
  44. package/dist/scan/index.js.map +1 -1
  45. package/package.json +12 -16
  46. package/dist/cloud/index.cjs +0 -682
  47. package/dist/cloud/index.cjs.map +0 -1
  48. package/dist/cloud/index.d.cts +0 -270
  49. package/dist/cloud/index.d.ts +0 -270
  50. package/dist/cloud/index.js +0 -645
  51. package/dist/cloud/index.js.map +0 -1
@@ -44,7 +44,6 @@ module.exports = __toCommonJS(react_exports);
44
44
 
45
45
  // src/integrations/react/provider.tsx
46
46
  var import_react = require("react");
47
- var import_cloud = require("uidex/cloud");
48
47
 
49
48
  // src/shared/entities/types.ts
50
49
  var ENTITY_KINDS = [
@@ -319,207 +318,6 @@ function displayName(entity, node) {
319
318
  return prettify(entity.id);
320
319
  }
321
320
 
322
- // src/browser/ingest/console.ts
323
- var DEFAULT_LIMIT = 50;
324
- var LEVELS = ["warn", "error"];
325
- function formatMessage(args) {
326
- return args.map((arg) => {
327
- if (typeof arg === "string") return arg;
328
- if (arg instanceof Error) return arg.stack ?? arg.message;
329
- try {
330
- return JSON.stringify(arg);
331
- } catch {
332
- return String(arg);
333
- }
334
- }).join(" ");
335
- }
336
- function createConsoleCapture(options = {}) {
337
- const limit = options.limit ?? DEFAULT_LIMIT;
338
- const target = options.target ?? console;
339
- const now = options.now ?? (() => /* @__PURE__ */ new Date());
340
- const buffer = [];
341
- const originals = {};
342
- let active = false;
343
- function record(level, args) {
344
- const entry = {
345
- level,
346
- message: formatMessage(args),
347
- timestamp: now().toISOString()
348
- };
349
- buffer.push(entry);
350
- if (buffer.length > limit) buffer.splice(0, buffer.length - limit);
351
- }
352
- function start() {
353
- if (active) return;
354
- for (const level of LEVELS) {
355
- const original = target[level];
356
- originals[level] = original;
357
- const wrapped = (...args) => {
358
- record(level, args);
359
- original.call(target, ...args);
360
- };
361
- target[level] = wrapped;
362
- }
363
- active = true;
364
- }
365
- function stop() {
366
- if (!active) return;
367
- for (const level of LEVELS) {
368
- const original = originals[level];
369
- if (original) {
370
- ;
371
- target[level] = original;
372
- }
373
- delete originals[level];
374
- }
375
- active = false;
376
- }
377
- return {
378
- start,
379
- stop,
380
- get isActive() {
381
- return active;
382
- },
383
- entries() {
384
- return buffer.slice();
385
- },
386
- clear() {
387
- buffer.length = 0;
388
- }
389
- };
390
- }
391
-
392
- // src/browser/ingest/native-fetch.ts
393
- var nativeFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
394
-
395
- // src/browser/ingest/network.ts
396
- var DEFAULT_LIMIT2 = 20;
397
- function resolveMethod(input, init) {
398
- if (init?.method) return init.method.toUpperCase();
399
- if (typeof input !== "string" && !(input instanceof URL) && "method" in input) {
400
- return input.method.toUpperCase();
401
- }
402
- return "GET";
403
- }
404
- function resolveUrl(input) {
405
- if (typeof input === "string") return input;
406
- if (input instanceof URL) return input.toString();
407
- return input.url;
408
- }
409
- function createNetworkCapture(options = {}) {
410
- const limit = options.limit ?? DEFAULT_LIMIT2;
411
- const target = options.target ?? globalThis;
412
- const now = options.now ?? (() => /* @__PURE__ */ new Date());
413
- const buffer = [];
414
- let original;
415
- let active = false;
416
- function push(entry) {
417
- buffer.push(entry);
418
- if (buffer.length > limit) buffer.splice(0, buffer.length - limit);
419
- }
420
- function start() {
421
- if (active) return;
422
- const baseline = target.fetch ?? nativeFetch;
423
- if (!baseline) return;
424
- original = baseline;
425
- const wrapped = async (input, init) => {
426
- const method = resolveMethod(input, init);
427
- const url = resolveUrl(input);
428
- try {
429
- const res = await baseline(input, init);
430
- if (res.status >= 400) {
431
- push({
432
- method,
433
- url,
434
- status: res.status,
435
- timestamp: now().toISOString()
436
- });
437
- }
438
- return res;
439
- } catch (err) {
440
- push({
441
- method,
442
- url,
443
- status: 0,
444
- timestamp: now().toISOString(),
445
- error: err instanceof Error ? err.message : String(err)
446
- });
447
- throw err;
448
- }
449
- };
450
- target.fetch = wrapped;
451
- active = true;
452
- }
453
- function stop() {
454
- if (!active) return;
455
- if (original) target.fetch = original;
456
- original = void 0;
457
- active = false;
458
- }
459
- return {
460
- start,
461
- stop,
462
- get isActive() {
463
- return active;
464
- },
465
- entries() {
466
- return buffer.slice();
467
- },
468
- clear() {
469
- buffer.length = 0;
470
- }
471
- };
472
- }
473
-
474
- // src/browser/ingest/index.ts
475
- function createIngest(options = {}) {
476
- const opts = options;
477
- const wantConsole = opts.captureConsole !== false;
478
- const wantNetwork = opts.captureNetwork !== false;
479
- const consoleCapture = wantConsole ? createConsoleCapture({
480
- limit: opts.consoleLimit,
481
- target: opts.consoleTarget,
482
- now: opts.now
483
- }) : null;
484
- const networkCapture = wantNetwork ? createNetworkCapture({
485
- limit: opts.networkLimit,
486
- target: opts.networkTarget,
487
- now: opts.now
488
- }) : null;
489
- let active = false;
490
- function start() {
491
- if (active) return;
492
- consoleCapture?.start();
493
- networkCapture?.start();
494
- active = Boolean(consoleCapture?.isActive || networkCapture?.isActive);
495
- }
496
- function stop() {
497
- if (!active) return;
498
- consoleCapture?.stop();
499
- networkCapture?.stop();
500
- active = false;
501
- }
502
- return {
503
- start,
504
- stop,
505
- get isActive() {
506
- return active;
507
- },
508
- console: consoleCapture,
509
- network: networkCapture
510
- };
511
- }
512
- function resolveIngestOptions(explicit, hasCloud) {
513
- if (explicit === null) return null;
514
- if (explicit === void 0) {
515
- return hasCloud ? { captureConsole: true, captureNetwork: true } : null;
516
- }
517
- if (explicit.captureConsole === false && explicit.captureNetwork === false) {
518
- return null;
519
- }
520
- return explicit;
521
- }
522
-
523
321
  // src/browser/internal/cleanup.ts
524
322
  function createCleanupStack() {
525
323
  const stack = [];
@@ -553,60 +351,6 @@ function composeCleanups(cleanups) {
553
351
  };
554
352
  }
555
353
 
556
- // src/browser/internal/dark-mode.ts
557
- function isDarkMode() {
558
- if (typeof document !== "undefined") {
559
- const root = document.documentElement;
560
- if (root?.classList.contains("dark")) return true;
561
- if (root?.classList.contains("light")) return false;
562
- }
563
- if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
564
- return window.matchMedia("(prefers-color-scheme: dark)").matches;
565
- }
566
- return false;
567
- }
568
-
569
- // src/browser/internal/route-change.ts
570
- var ROUTE_CHANGE_EVENT = "uidex:routechange";
571
- var patched = false;
572
- function ensureHistoryPatched() {
573
- if (patched) return;
574
- if (typeof window === "undefined" || typeof history === "undefined") return;
575
- patched = true;
576
- const dispatch = () => {
577
- window.dispatchEvent(new Event(ROUTE_CHANGE_EVENT));
578
- };
579
- const origPush = history.pushState;
580
- history.pushState = function(...args) {
581
- const result = origPush.apply(
582
- this,
583
- args
584
- );
585
- dispatch();
586
- return result;
587
- };
588
- const origReplace = history.replaceState;
589
- history.replaceState = function(...args) {
590
- const result = origReplace.apply(
591
- this,
592
- args
593
- );
594
- dispatch();
595
- return result;
596
- };
597
- }
598
- function bindRouteChange(handler) {
599
- if (typeof window === "undefined") return () => {
600
- };
601
- ensureHistoryPatched();
602
- window.addEventListener("popstate", handler);
603
- window.addEventListener(ROUTE_CHANGE_EVENT, handler);
604
- return () => {
605
- window.removeEventListener("popstate", handler);
606
- window.removeEventListener(ROUTE_CHANGE_EVENT, handler);
607
- };
608
- }
609
-
610
354
  // src/browser/session/store.ts
611
355
  var import_vanilla3 = require("zustand/vanilla");
612
356
 
@@ -744,8 +488,7 @@ var defaultSnapshot = {
744
488
  pinnedHighlight: null,
745
489
  mode: "idle",
746
490
  theme: "auto",
747
- resolvedTheme: "light",
748
- user: null
491
+ resolvedTheme: "light"
749
492
  };
750
493
  function resolveTheme(preference, detect) {
751
494
  if (preference !== "auto") return preference;
@@ -835,8 +578,7 @@ function createSession(options = {}) {
835
578
  pinnedHighlight: null,
836
579
  mode: "idle",
837
580
  theme: initialPref,
838
- resolvedTheme: initialResolved,
839
- user: overrides.user ?? null
581
+ resolvedTheme: initialResolved
840
582
  }));
841
583
  nav.subscribe(() => {
842
584
  const { stack } = nav.getState();
@@ -880,8 +622,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
880
622
  @layer theme, base, components, utilities;
881
623
  @layer theme {
882
624
  :root, :host {
883
- --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
884
- Roboto, sans-serif;
885
625
  --color-red-400: oklch(70.4% 0.191 22.216);
886
626
  --color-red-500: oklch(63.7% 0.237 25.331);
887
627
  --color-red-700: oklch(50.5% 0.213 27.518);
@@ -934,7 +674,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
934
674
  --color-black: #000;
935
675
  --color-white: #fff;
936
676
  --spacing: 0.25rem;
937
- --container-sm: 24rem;
938
677
  --container-xl: 36rem;
939
678
  --text-xs: 0.75rem;
940
679
  --text-xs--line-height: calc(1 / 0.75);
@@ -942,28 +681,22 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
942
681
  --text-sm--line-height: calc(1.25 / 0.875);
943
682
  --text-base: 1rem;
944
683
  --text-base--line-height: calc(1.5 / 1);
945
- --text-xl: 1.25rem;
946
- --text-xl--line-height: calc(1.75 / 1.25);
947
684
  --font-weight-normal: 400;
948
685
  --font-weight-medium: 500;
949
686
  --font-weight-semibold: 600;
950
- --font-weight-bold: 700;
951
687
  --tracking-tight: -0.025em;
952
688
  --tracking-widest: 0.1em;
953
689
  --leading-relaxed: 1.625;
954
- --radius-md: calc(var(--radius) * 0.8);
955
690
  --radius-lg: var(--radius);
956
691
  --radius-xl: calc(var(--radius) * 1.4);
957
692
  --radius-2xl: calc(var(--radius) * 1.8);
958
693
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
959
- --animate-spin: spin 1s linear infinite;
960
694
  --blur-sm: 8px;
961
695
  --default-transition-duration: 150ms;
962
696
  --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
963
697
  --default-font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
964
698
  Roboto, sans-serif;
965
699
  --default-mono-font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
966
- --color-muted: var(--muted);
967
700
  --color-border: var(--border);
968
701
  }
969
702
  }
@@ -1131,17 +864,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1131
864
  .visible {
1132
865
  visibility: visible;
1133
866
  }
1134
- .sr-only {
1135
- position: absolute;
1136
- width: 1px;
1137
- height: 1px;
1138
- padding: 0;
1139
- margin: -1px;
1140
- overflow: hidden;
1141
- clip-path: inset(50%);
1142
- white-space: nowrap;
1143
- border-width: 0;
1144
- }
1145
867
  .absolute {
1146
868
  position: absolute;
1147
869
  }
@@ -1163,30 +885,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1163
885
  .end {
1164
886
  inset-inline-end: var(--spacing);
1165
887
  }
1166
- .-top-1 {
1167
- top: calc(var(--spacing) * -1);
1168
- }
1169
- .-right-1 {
1170
- right: calc(var(--spacing) * -1);
1171
- }
1172
- .right-0 {
1173
- right: calc(var(--spacing) * 0);
1174
- }
1175
- .bottom-full {
1176
- bottom: 100%;
1177
- }
1178
- .bottom-px {
1179
- bottom: 1px;
1180
- }
1181
888
  .z-1 {
1182
889
  z-index: 1;
1183
890
  }
1184
891
  .z-10 {
1185
892
  z-index: 10;
1186
893
  }
1187
- .z-\\[2147483647\\] {
1188
- z-index: 2147483647;
1189
- }
1190
894
  .container {
1191
895
  width: 100%;
1192
896
  @media (width >= 40rem) {
@@ -1220,12 +924,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1220
924
  .mt-1 {
1221
925
  margin-top: calc(var(--spacing) * 1);
1222
926
  }
1223
- .mb-2 {
1224
- margin-bottom: calc(var(--spacing) * 2);
1225
- }
1226
- .mb-6 {
1227
- margin-bottom: calc(var(--spacing) * 6);
1228
- }
1229
927
  .ml-auto {
1230
928
  margin-left: auto;
1231
929
  }
@@ -1244,19 +942,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1244
942
  .inline {
1245
943
  display: inline;
1246
944
  }
1247
- .inline-block {
1248
- display: inline-block;
1249
- }
1250
945
  .inline-flex {
1251
946
  display: inline-flex;
1252
947
  }
1253
948
  .table {
1254
949
  display: table;
1255
950
  }
1256
- .size-2 {
1257
- width: calc(var(--spacing) * 2);
1258
- height: calc(var(--spacing) * 2);
1259
- }
1260
951
  .size-3\\.5 {
1261
952
  width: calc(var(--spacing) * 3.5);
1262
953
  height: calc(var(--spacing) * 3.5);
@@ -1317,21 +1008,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1317
1008
  .h-full {
1318
1009
  height: 100%;
1319
1010
  }
1320
- .max-h-32 {
1321
- max-height: calc(var(--spacing) * 32);
1322
- }
1323
- .max-h-full {
1324
- max-height: 100%;
1325
- }
1326
1011
  .min-h-0 {
1327
1012
  min-height: calc(var(--spacing) * 0);
1328
1013
  }
1329
1014
  .min-h-8 {
1330
1015
  min-height: calc(var(--spacing) * 8);
1331
1016
  }
1332
- .min-h-20 {
1333
- min-height: calc(var(--spacing) * 20);
1334
- }
1335
1017
  .w-3\\.5 {
1336
1018
  width: calc(var(--spacing) * 3.5);
1337
1019
  }
@@ -1341,12 +1023,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1341
1023
  .w-12 {
1342
1024
  width: calc(var(--spacing) * 12);
1343
1025
  }
1344
- .w-20 {
1345
- width: calc(var(--spacing) * 20);
1346
- }
1347
- .w-36 {
1348
- width: calc(var(--spacing) * 36);
1349
- }
1350
1026
  .w-56 {
1351
1027
  width: calc(var(--spacing) * 56);
1352
1028
  }
@@ -1356,12 +1032,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1356
1032
  .w-px {
1357
1033
  width: 1px;
1358
1034
  }
1359
- .max-w-full {
1360
- max-width: 100%;
1361
- }
1362
- .max-w-sm {
1363
- max-width: var(--container-sm);
1364
- }
1365
1035
  .max-w-xl {
1366
1036
  max-width: var(--container-xl);
1367
1037
  }
@@ -1389,41 +1059,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1389
1059
  .shrink-0 {
1390
1060
  flex-shrink: 0;
1391
1061
  }
1392
- .origin-bottom-left {
1393
- transform-origin: 0 100%;
1394
- }
1395
- .origin-bottom-right {
1396
- transform-origin: 100% 100%;
1397
- }
1398
- .-translate-x-0\\.5 {
1399
- --tw-translate-x: calc(var(--spacing) * -0.5);
1400
- translate: var(--tw-translate-x) var(--tw-translate-y);
1401
- }
1402
- .translate-x-0\\.5 {
1403
- --tw-translate-x: calc(var(--spacing) * 0.5);
1404
- translate: var(--tw-translate-x) var(--tw-translate-y);
1405
- }
1406
- .scale-84 {
1407
- --tw-scale-x: 84%;
1408
- --tw-scale-y: 84%;
1409
- --tw-scale-z: 84%;
1410
- scale: var(--tw-scale-x) var(--tw-scale-y);
1411
- }
1412
- .-rotate-10 {
1413
- rotate: calc(10deg * -1);
1414
- }
1415
- .rotate-10 {
1416
- rotate: 10deg;
1417
- }
1418
1062
  .transform {
1419
1063
  transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
1420
1064
  }
1421
- .animate-skeleton {
1422
- animation: skeleton 2s -1s infinite linear;
1423
- }
1424
- .animate-spin {
1425
- animation: var(--animate-spin);
1426
- }
1427
1065
  .cursor-pointer {
1428
1066
  cursor: pointer;
1429
1067
  }
@@ -1433,15 +1071,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1433
1071
  .scroll-py-2 {
1434
1072
  scroll-padding-block: calc(var(--spacing) * 2);
1435
1073
  }
1436
- .appearance-none {
1437
- appearance: none;
1438
- }
1439
1074
  .flex-col {
1440
1075
  flex-direction: column;
1441
1076
  }
1442
- .flex-row {
1443
- flex-direction: row;
1444
- }
1445
1077
  .items-center {
1446
1078
  align-items: center;
1447
1079
  }
@@ -1457,9 +1089,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1457
1089
  .gap-0 {
1458
1090
  gap: calc(var(--spacing) * 0);
1459
1091
  }
1460
- .gap-0\\.5 {
1461
- gap: calc(var(--spacing) * 0.5);
1462
- }
1463
1092
  .gap-1 {
1464
1093
  gap: calc(var(--spacing) * 1);
1465
1094
  }
@@ -1472,12 +1101,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1472
1101
  .gap-3 {
1473
1102
  gap: calc(var(--spacing) * 3);
1474
1103
  }
1475
- .gap-4 {
1476
- gap: calc(var(--spacing) * 4);
1477
- }
1478
- .gap-6 {
1479
- gap: calc(var(--spacing) * 6);
1480
- }
1481
1104
  .truncate {
1482
1105
  overflow: hidden;
1483
1106
  text-overflow: ellipsis;
@@ -1575,21 +1198,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1575
1198
  background-color: color-mix(in oklab, var(--color-black) 32%, transparent);
1576
1199
  }
1577
1200
  }
1578
- .bg-black\\/80 {
1579
- background-color: color-mix(in srgb, #000 80%, transparent);
1580
- @supports (color: color-mix(in lab, red, red)) {
1581
- background-color: color-mix(in oklab, var(--color-black) 80%, transparent);
1582
- }
1583
- }
1584
1201
  .bg-blue-50 {
1585
1202
  background-color: var(--color-blue-50);
1586
1203
  }
1587
1204
  .bg-border {
1588
1205
  background-color: var(--border);
1589
1206
  }
1590
- .bg-card {
1591
- background-color: var(--card);
1592
- }
1593
1207
  .bg-cyan-50 {
1594
1208
  background-color: var(--color-cyan-50);
1595
1209
  }
@@ -1605,15 +1219,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1605
1219
  .bg-emerald-50 {
1606
1220
  background-color: var(--color-emerald-50);
1607
1221
  }
1608
- .bg-emerald-500 {
1609
- background-color: var(--color-emerald-500);
1610
- }
1611
1222
  .bg-fuchsia-50 {
1612
1223
  background-color: var(--color-fuchsia-50);
1613
1224
  }
1614
- .bg-info {
1615
- background-color: var(--info);
1616
- }
1617
1225
  .bg-info\\/10 {
1618
1226
  background-color: var(--info);
1619
1227
  @supports (color: color-mix(in lab, red, red)) {
@@ -1665,18 +1273,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1665
1273
  .bg-clip-padding {
1666
1274
  background-clip: padding-box;
1667
1275
  }
1668
- .bg-\\[right_0\\.5rem_center\\] {
1669
- background-position: right 0.5rem center;
1670
- }
1671
- .bg-no-repeat {
1672
- background-repeat: no-repeat;
1673
- }
1674
- .object-cover {
1675
- object-fit: cover;
1676
- }
1677
- .object-top {
1678
- object-position: top;
1679
- }
1680
1276
  .p-1 {
1681
1277
  padding: calc(var(--spacing) * 1);
1682
1278
  }
@@ -1689,9 +1285,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1689
1285
  .p-4 {
1690
1286
  padding: calc(var(--spacing) * 4);
1691
1287
  }
1692
- .p-6 {
1693
- padding: calc(var(--spacing) * 6);
1694
- }
1695
1288
  .px-1 {
1696
1289
  padding-inline: calc(var(--spacing) * 1);
1697
1290
  }
@@ -1713,24 +1306,15 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1713
1306
  .px-4 {
1714
1307
  padding-inline: calc(var(--spacing) * 4);
1715
1308
  }
1716
- .px-6 {
1717
- padding-inline: calc(var(--spacing) * 6);
1718
- }
1719
1309
  .py-1 {
1720
1310
  padding-block: calc(var(--spacing) * 1);
1721
1311
  }
1722
1312
  .py-1\\.5 {
1723
1313
  padding-block: calc(var(--spacing) * 1.5);
1724
1314
  }
1725
- .py-2 {
1726
- padding-block: calc(var(--spacing) * 2);
1727
- }
1728
1315
  .py-4 {
1729
1316
  padding-block: calc(var(--spacing) * 4);
1730
1317
  }
1731
- .py-12 {
1732
- padding-block: calc(var(--spacing) * 12);
1733
- }
1734
1318
  .py-\\[max\\(1rem\\,4vh\\)\\] {
1735
1319
  padding-block: max(1rem, 4vh);
1736
1320
  }
@@ -1743,12 +1327,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1743
1327
  .pt-2 {
1744
1328
  padding-top: calc(var(--spacing) * 2);
1745
1329
  }
1746
- .pr-8 {
1747
- padding-right: calc(var(--spacing) * 8);
1748
- }
1749
- .pl-3 {
1750
- padding-left: calc(var(--spacing) * 3);
1751
- }
1752
1330
  .text-center {
1753
1331
  text-align: center;
1754
1332
  }
@@ -1766,18 +1344,10 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1766
1344
  font-size: var(--text-base);
1767
1345
  line-height: var(--tw-leading, var(--text-base--line-height));
1768
1346
  }
1769
- .text-base\\/4\\.5 {
1770
- font-size: var(--text-base);
1771
- line-height: calc(var(--spacing) * 4.5);
1772
- }
1773
1347
  .text-sm {
1774
1348
  font-size: var(--text-sm);
1775
1349
  line-height: var(--tw-leading, var(--text-sm--line-height));
1776
1350
  }
1777
- .text-xl {
1778
- font-size: var(--text-xl);
1779
- line-height: var(--tw-leading, var(--text-xl--line-height));
1780
- }
1781
1351
  .text-xs {
1782
1352
  font-size: var(--text-xs);
1783
1353
  line-height: var(--tw-leading, var(--text-xs--line-height));
@@ -1785,24 +1355,13 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1785
1355
  .text-\\[0\\.625rem\\] {
1786
1356
  font-size: 0.625rem;
1787
1357
  }
1788
- .text-\\[9px\\] {
1789
- font-size: 9px;
1790
- }
1791
1358
  .text-\\[11px\\] {
1792
1359
  font-size: 11px;
1793
1360
  }
1794
- .leading-none {
1795
- --tw-leading: 1;
1796
- line-height: 1;
1797
- }
1798
1361
  .leading-relaxed {
1799
1362
  --tw-leading: var(--leading-relaxed);
1800
1363
  line-height: var(--leading-relaxed);
1801
1364
  }
1802
- .font-bold {
1803
- --tw-font-weight: var(--font-weight-bold);
1804
- font-weight: var(--font-weight-bold);
1805
- }
1806
1365
  .font-medium {
1807
1366
  --tw-font-weight: var(--font-weight-medium);
1808
1367
  font-weight: var(--font-weight-medium);
@@ -1823,9 +1382,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1823
1382
  --tw-tracking: var(--tracking-widest);
1824
1383
  letter-spacing: var(--tracking-widest);
1825
1384
  }
1826
- .text-balance {
1827
- text-wrap: balance;
1828
- }
1829
1385
  .break-words {
1830
1386
  overflow-wrap: break-word;
1831
1387
  }
@@ -1850,9 +1406,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1850
1406
  .text-cyan-700 {
1851
1407
  color: var(--color-cyan-700);
1852
1408
  }
1853
- .text-destructive {
1854
- color: var(--destructive);
1855
- }
1856
1409
  .text-destructive-foreground {
1857
1410
  color: var(--destructive-foreground);
1858
1411
  }
@@ -1918,18 +1471,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1918
1471
  .line-through {
1919
1472
  text-decoration-line: line-through;
1920
1473
  }
1921
- .underline {
1922
- text-decoration-line: underline;
1923
- }
1924
1474
  .underline-offset-4 {
1925
1475
  text-underline-offset: 4px;
1926
1476
  }
1927
1477
  .accent-accent {
1928
1478
  accent-color: var(--accent);
1929
1479
  }
1930
- .accent-primary {
1931
- accent-color: var(--primary);
1932
- }
1933
1480
  .opacity-50 {
1934
1481
  opacity: 50%;
1935
1482
  }
@@ -1941,11 +1488,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1941
1488
  --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, oklab(from rgb(0 0 0 / 0.1) l a b / 5%)), 0 4px 6px -4px var(--tw-shadow-color, oklab(from rgb(0 0 0 / 0.1) l a b / 5%));
1942
1489
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1943
1490
  }
1944
- .shadow-sm\\/5 {
1945
- --tw-shadow-alpha: 5%;
1946
- --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, oklab(from rgb(0 0 0 / 0.1) l a b / 5%)), 0 1px 2px -1px var(--tw-shadow-color, oklab(from rgb(0 0 0 / 0.1) l a b / 5%));
1947
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1948
- }
1949
1491
  .shadow-xs\\/5 {
1950
1492
  --tw-shadow-alpha: 5%;
1951
1493
  --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, oklab(from rgb(0 0 0 / 0.05) l a b / 5%));
@@ -1955,14 +1497,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1955
1497
  --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
1956
1498
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1957
1499
  }
1958
- .shadow-lg {
1959
- --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
1960
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1961
- }
1962
- .shadow-none {
1963
- --tw-shadow: 0 0 #0000;
1964
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1965
- }
1966
1500
  .shadow-xs {
1967
1501
  --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
1968
1502
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -2062,15 +1596,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2062
1596
  -webkit-user-select: none;
2063
1597
  user-select: none;
2064
1598
  }
2065
- .\\[--skeleton-highlight\\:--alpha\\(var\\(--color-white\\)\\/64\\%\\)\\] {
2066
- --skeleton-highlight: color-mix(in srgb, #fff 64%, transparent);
2067
- @supports (color: color-mix(in lab, red, red)) {
2068
- --skeleton-highlight: color-mix(in oklab, var(--color-white) 64%, transparent);
2069
- }
2070
- }
2071
- .\\[background\\:linear-gradient\\(120deg\\,transparent_40\\%\\,var\\(--skeleton-highlight\\)\\,transparent_60\\%\\)_var\\(--color-muted\\)_0_0\\/200\\%_100\\%_fixed\\] {
2072
- background: linear-gradient(120deg,transparent 40%,var(--skeleton-highlight),transparent 60%) var(--color-muted) 0 0/200% 100% fixed;
2073
- }
2074
1599
  .\\[clip-path\\:inset\\(0_1px\\)\\] {
2075
1600
  clip-path: inset(0 1px);
2076
1601
  }
@@ -2154,12 +1679,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2154
1679
  border-radius: calc(var(--radius-lg) - 1px);
2155
1680
  }
2156
1681
  }
2157
- .before\\:rounded-\\[calc\\(var\\(--radius-md\\)-1px\\)\\] {
2158
- &::before {
2159
- content: var(--tw-content);
2160
- border-radius: calc(var(--radius-md) - 1px);
2161
- }
2162
- }
2163
1682
  .before\\:rounded-\\[calc\\(var\\(--radius-xl\\)-1px\\)\\] {
2164
1683
  &::before {
2165
1684
  content: var(--tw-content);
@@ -2289,36 +1808,17 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2289
1808
  outline-style: none;
2290
1809
  }
2291
1810
  }
2292
- .focus-visible\\:border-ring {
2293
- &:focus-visible {
2294
- border-color: var(--ring);
2295
- }
2296
- }
2297
1811
  .focus-visible\\:ring-2 {
2298
1812
  &:focus-visible {
2299
1813
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
2300
1814
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2301
1815
  }
2302
1816
  }
2303
- .focus-visible\\:ring-\\[3px\\] {
2304
- &:focus-visible {
2305
- --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
2306
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2307
- }
2308
- }
2309
1817
  .focus-visible\\:ring-ring {
2310
1818
  &:focus-visible {
2311
1819
  --tw-ring-color: var(--ring);
2312
1820
  }
2313
1821
  }
2314
- .focus-visible\\:ring-ring\\/30 {
2315
- &:focus-visible {
2316
- --tw-ring-color: var(--ring);
2317
- @supports (color: color-mix(in lab, red, red)) {
2318
- --tw-ring-color: color-mix(in oklab, var(--ring) 30%, transparent);
2319
- }
2320
- }
2321
- }
2322
1822
  .focus-visible\\:ring-offset-1 {
2323
1823
  &:focus-visible {
2324
1824
  --tw-ring-offset-width: 1px;
@@ -2345,24 +1845,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2345
1845
  opacity: 60%;
2346
1846
  }
2347
1847
  }
2348
- .aria-invalid\\:border-destructive\\/40 {
2349
- &[aria-invalid="true"] {
2350
- border-color: var(--destructive);
2351
- @supports (color: color-mix(in lab, red, red)) {
2352
- border-color: color-mix(in oklab, var(--destructive) 40%, transparent);
2353
- }
2354
- }
2355
- }
2356
- .aria-invalid\\:focus-visible\\:ring-destructive\\/20 {
2357
- &[aria-invalid="true"] {
2358
- &:focus-visible {
2359
- --tw-ring-color: var(--destructive);
2360
- @supports (color: color-mix(in lab, red, red)) {
2361
- --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent);
2362
- }
2363
- }
2364
- }
2365
- }
2366
1848
  .data-\\[disabled\\]\\:pointer-events-none {
2367
1849
  &[data-disabled] {
2368
1850
  pointer-events: none;
@@ -2399,12 +1881,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2399
1881
  line-height: var(--tw-leading, var(--text-sm--line-height));
2400
1882
  }
2401
1883
  }
2402
- .sm\\:text-sm\\/4 {
2403
- @media (width >= 40rem) {
2404
- font-size: var(--text-sm);
2405
- line-height: calc(var(--spacing) * 4);
2406
- }
2407
- }
2408
1884
  .dark\\:bg-amber-400\\/10 {
2409
1885
  &:is(.dark *, :host(.dark) *) {
2410
1886
  background-color: color-mix(in srgb, oklch(82.8% 0.189 84.429) 10%, transparent);
@@ -2618,14 +2094,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2618
2094
  }
2619
2095
  }
2620
2096
  }
2621
- .dark\\:\\[--skeleton-highlight\\:--alpha\\(var\\(--color-white\\)\\/4\\%\\)\\] {
2622
- &:is(.dark *, :host(.dark) *) {
2623
- --skeleton-highlight: color-mix(in srgb, #fff 4%, transparent);
2624
- @supports (color: color-mix(in lab, red, red)) {
2625
- --skeleton-highlight: color-mix(in oklab, var(--color-white) 4%, transparent);
2626
- }
2627
- }
2628
- }
2629
2097
  .dark\\:group-data-hover\\:bg-amber-400\\/20 {
2630
2098
  &:is(.dark *, :host(.dark) *) {
2631
2099
  &:is(:where(.group)[data-hover] *) {
@@ -2745,12 +2213,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2745
2213
  height: calc(var(--spacing) * 4);
2746
2214
  }
2747
2215
  }
2748
- .\\[\\&_svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-4\\.5 {
2749
- & svg:not([class*='size-']) {
2750
- width: calc(var(--spacing) * 4.5);
2751
- height: calc(var(--spacing) * 4.5);
2752
- }
2753
- }
2754
2216
  .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:bg-accent {
2755
2217
  [data-kbd-nav] & {
2756
2218
  &:focus-within {
@@ -2983,36 +2445,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2983
2445
  --warning: var(--color-amber-500);
2984
2446
  --warning-foreground: var(--color-amber-700);
2985
2447
  }
2986
- @property --tw-translate-x {
2987
- syntax: "*";
2988
- inherits: false;
2989
- initial-value: 0;
2990
- }
2991
- @property --tw-translate-y {
2992
- syntax: "*";
2993
- inherits: false;
2994
- initial-value: 0;
2995
- }
2996
- @property --tw-translate-z {
2997
- syntax: "*";
2998
- inherits: false;
2999
- initial-value: 0;
3000
- }
3001
- @property --tw-scale-x {
3002
- syntax: "*";
3003
- inherits: false;
3004
- initial-value: 1;
3005
- }
3006
- @property --tw-scale-y {
3007
- syntax: "*";
3008
- inherits: false;
3009
- initial-value: 1;
3010
- }
3011
- @property --tw-scale-z {
3012
- syntax: "*";
3013
- inherits: false;
3014
- initial-value: 1;
3015
- }
3016
2448
  @property --tw-rotate-x {
3017
2449
  syntax: "*";
3018
2450
  inherits: false;
@@ -3238,20 +2670,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3238
2670
  initial-value: "";
3239
2671
  inherits: false;
3240
2672
  }
3241
- @keyframes spin {
3242
- to {
3243
- transform: rotate(360deg);
3244
- }
3245
- }
3246
2673
  @layer properties {
3247
2674
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
3248
2675
  *, ::before, ::after, ::backdrop {
3249
- --tw-translate-x: 0;
3250
- --tw-translate-y: 0;
3251
- --tw-translate-z: 0;
3252
- --tw-scale-x: 1;
3253
- --tw-scale-y: 1;
3254
- --tw-scale-z: 1;
3255
2676
  --tw-rotate-x: initial;
3256
2677
  --tw-rotate-y: initial;
3257
2678
  --tw-rotate-z: initial;
@@ -3314,7 +2735,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3314
2735
  var SURFACE_HOST_CLASS = "uidex-surface-host";
3315
2736
  var Z_BASE = 2147483630;
3316
2737
  var Z_OVERLAY = 2147483635;
3317
- var Z_PIN_LAYER = 2147483640;
3318
2738
  var Z_CHROME = 2147483645;
3319
2739
  var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS}`;
3320
2740
  var UIDEX_ATTR_TO_KIND = [
@@ -3713,7 +3133,6 @@ function el(tag, options = {}, children = []) {
3713
3133
  var DEFAULT_SHORTCUT = { key: "k" };
3714
3134
  var ACTIONS_SHORTCUT = { key: "j" };
3715
3135
  var INSPECT_SHORTCUT = { key: "i" };
3716
- var PINS_SHORTCUT = { key: "u" };
3717
3136
  function formatShortcutLabel(shortcut) {
3718
3137
  const parts = [];
3719
3138
  if (shortcut.mod !== false) parts.push("\u2318");
@@ -3728,19 +3147,17 @@ function matchesShortcut(e, shortcut) {
3728
3147
  return hasMod === wantMod && e.shiftKey === wantShift && !e.altKey && e.key.toLowerCase() === shortcut.key.toLowerCase();
3729
3148
  }
3730
3149
  function bindShadowKeys(options) {
3731
- const { shadowRoot, session, getActionsPopup, getPinLayer } = options;
3150
+ const { shadowRoot, session, getActionsPopup } = options;
3732
3151
  const shortcut = options.shortcut ?? DEFAULT_SHORTCUT;
3733
- const escapeStack = [];
3734
3152
  const onKeydown = (e) => {
3735
3153
  const isMod = e.metaKey || e.ctrlKey;
3736
3154
  const isCmdK = matchesShortcut(e, shortcut);
3737
3155
  const isCmdDot = matchesShortcut(e, ACTIONS_SHORTCUT);
3738
3156
  const isCmdI = matchesShortcut(e, INSPECT_SHORTCUT);
3739
- const isCmdU = matchesShortcut(e, PINS_SHORTCUT);
3740
3157
  const isCmdBracket = isMod && !e.altKey && !e.shiftKey && e.key === "[";
3741
3158
  const isEsc = e.key === "Escape";
3742
3159
  const isPopKey = isEsc || isCmdBracket;
3743
- if (!isCmdK && !isCmdDot && !isCmdI && !isCmdU && !isPopKey) return;
3160
+ if (!isCmdK && !isCmdDot && !isCmdI && !isPopKey) return;
3744
3161
  const popup = getActionsPopup();
3745
3162
  if (popup?.isOpen()) {
3746
3163
  if (isPopKey || isCmdK || isCmdDot) {
@@ -3758,15 +3175,6 @@ function bindShadowKeys(options) {
3758
3175
  }
3759
3176
  return;
3760
3177
  }
3761
- if (isCmdU) {
3762
- const pinLayer = getPinLayer?.();
3763
- if (pinLayer) {
3764
- e.preventDefault();
3765
- e.stopPropagation();
3766
- pinLayer.setVisible(!pinLayer.visible);
3767
- }
3768
- return;
3769
- }
3770
3178
  if (isCmdI) {
3771
3179
  e.preventDefault();
3772
3180
  e.stopPropagation();
@@ -3788,14 +3196,6 @@ function bindShadowKeys(options) {
3788
3196
  }
3789
3197
  return;
3790
3198
  }
3791
- if (escapeStack.length > 0) {
3792
- const top = escapeStack[escapeStack.length - 1];
3793
- if (top()) {
3794
- e.preventDefault();
3795
- e.stopPropagation();
3796
- return;
3797
- }
3798
- }
3799
3199
  const { mode } = session.mode.getState();
3800
3200
  if (mode === "viewing") {
3801
3201
  e.preventDefault();
@@ -3830,13 +3230,6 @@ function bindShadowKeys(options) {
3830
3230
  capture: true
3831
3231
  }
3832
3232
  );
3833
- },
3834
- pushEscapeLayer(handler) {
3835
- escapeStack.push(handler);
3836
- return () => {
3837
- const idx = escapeStack.indexOf(handler);
3838
- if (idx >= 0) escapeStack.splice(idx, 1);
3839
- };
3840
3233
  }
3841
3234
  };
3842
3235
  }
@@ -3852,11 +3245,8 @@ function createMenuBar(options) {
3852
3245
  container,
3853
3246
  session,
3854
3247
  initialCorner = "bottom-right",
3855
- appTitle,
3856
- channel = null,
3857
- pinLayer: initialPinLayer = null
3248
+ appTitle
3858
3249
  } = options;
3859
- let activePinLayer = initialPinLayer;
3860
3250
  const root = el("div", {
3861
3251
  class: ROOT_CLASS,
3862
3252
  attrs: {
@@ -3921,108 +3311,6 @@ function createMenuBar(options) {
3921
3311
  inspectBtn.blur();
3922
3312
  });
3923
3313
  root.appendChild(inspectBtn);
3924
- const pinIcon = (0, import_lucide3.createElement)(import_lucide3.MapPin);
3925
- pinIcon.setAttribute("class", "size-3.5");
3926
- pinIcon.setAttribute("aria-hidden", "true");
3927
- const pinBtn = el(
3928
- "button",
3929
- {
3930
- class: BUTTON_CLASS,
3931
- attrs: {
3932
- type: "button",
3933
- "data-uidex-menubar-pins": "",
3934
- "aria-label": "Report pins"
3935
- }
3936
- },
3937
- pinIcon
3938
- );
3939
- const pinWrapper = el("div", {
3940
- class: "relative z-1 inline-flex items-center gap-0.5",
3941
- attrs: { "data-uidex-menubar-pin-wrapper": "" }
3942
- });
3943
- pinWrapper.hidden = true;
3944
- pinWrapper.appendChild(pinBtn);
3945
- root.appendChild(pinWrapper);
3946
- const updatePinUI = () => {
3947
- if (!activePinLayer) {
3948
- pinWrapper.hidden = true;
3949
- return;
3950
- }
3951
- const pinsVisible = activePinLayer.visible;
3952
- pinWrapper.hidden = false;
3953
- pinBtn.className = cn(BUTTON_CLASS, pinsVisible && BUTTON_ACTIVE_CLASS);
3954
- };
3955
- pinBtn.addEventListener("click", (e) => {
3956
- e.stopPropagation();
3957
- if (activePinLayer) {
3958
- activePinLayer.setVisible(!activePinLayer.visible);
3959
- }
3960
- });
3961
- let unsubscribePinFilter = activePinLayer?.onFilterChange(() => updatePinUI());
3962
- const presenceIcon = (0, import_lucide3.createElement)(import_lucide3.Users);
3963
- presenceIcon.setAttribute("class", "size-3.5");
3964
- presenceIcon.setAttribute("aria-hidden", "true");
3965
- const presenceBtn = el(
3966
- "button",
3967
- {
3968
- class: BUTTON_CLASS,
3969
- attrs: {
3970
- type: "button",
3971
- "data-uidex-menubar-presence": "",
3972
- "aria-label": "Online users"
3973
- }
3974
- },
3975
- presenceIcon
3976
- );
3977
- presenceBtn.hidden = true;
3978
- const presenceBadge = el("span", {
3979
- class: "absolute -top-1 -right-1 z-1 inline-flex size-3.5 items-center justify-center rounded-full bg-info text-[9px] font-bold leading-none text-info-foreground"
3980
- });
3981
- presenceBtn.appendChild(presenceBadge);
3982
- const presencePopover = el("div", {
3983
- class: "absolute bottom-full mb-2 right-0 min-w-32 rounded-lg border border-border bg-popover p-2 text-xs text-popover-foreground shadow-lg"
3984
- });
3985
- presencePopover.hidden = true;
3986
- let presencePopoverVisible = false;
3987
- presenceBtn.addEventListener("click", (e) => {
3988
- e.stopPropagation();
3989
- presencePopoverVisible = !presencePopoverVisible;
3990
- presencePopover.hidden = !presencePopoverVisible;
3991
- });
3992
- const presenceWrapper = el("div", { class: "relative z-1 inline-flex" }, [
3993
- presenceBtn,
3994
- presencePopover
3995
- ]);
3996
- presenceWrapper.hidden = true;
3997
- root.appendChild(presenceWrapper);
3998
- let presenceUsers = [];
3999
- const updatePresenceUI = () => {
4000
- const localUserId = session.getState().user?.id ?? null;
4001
- const peers = localUserId ? presenceUsers.filter((u) => u.userId !== localUserId) : presenceUsers;
4002
- const count = peers.length;
4003
- presenceWrapper.hidden = count === 0;
4004
- presenceBadge.textContent = String(count);
4005
- presenceBtn.setAttribute(
4006
- "aria-label",
4007
- count === 1 ? "1 other user online" : `${count} other users online`
4008
- );
4009
- presencePopover.innerHTML = "";
4010
- for (const user of peers) {
4011
- const row = el("div", {
4012
- class: "flex items-center gap-2 rounded px-1.5 py-1"
4013
- });
4014
- const dot = el("span", {
4015
- class: "inline-block size-2 shrink-0 rounded-full bg-emerald-500"
4016
- });
4017
- const name = el("span", {
4018
- class: "truncate",
4019
- text: user.name || "Anonymous"
4020
- });
4021
- row.appendChild(dot);
4022
- row.appendChild(name);
4023
- presencePopover.appendChild(row);
4024
- }
4025
- };
4026
3314
  const paletteIcon = (0, import_lucide3.createElement)(import_lucide3.Command);
4027
3315
  paletteIcon.setAttribute("class", "size-3.5");
4028
3316
  paletteIcon.setAttribute("aria-hidden", "true");
@@ -4137,28 +3425,14 @@ function createMenuBar(options) {
4137
3425
  const vertical = y / (window.innerHeight || 1) < 0.5 ? "top" : "bottom";
4138
3426
  snapTo(`${vertical}-${horizontal}`);
4139
3427
  }
4140
- const unsubscribePresence = channel?.onPresence(
4141
- (users) => {
4142
- presenceUsers = users;
4143
- updatePresenceUI();
4144
- }
4145
- );
4146
3428
  return {
4147
3429
  destroy() {
4148
- unsubscribePinFilter?.();
4149
- unsubscribePresence?.();
4150
3430
  unsubscribeSession();
4151
3431
  root.removeEventListener("mousedown", onMouseDown);
4152
3432
  document.removeEventListener("mousemove", onMouseMove);
4153
3433
  document.removeEventListener("mouseup", onMouseUp);
4154
3434
  root.remove();
4155
3435
  },
4156
- setPinLayer(layer) {
4157
- unsubscribePinFilter?.();
4158
- activePinLayer = layer;
4159
- unsubscribePinFilter = layer.onFilterChange(() => updatePinUI());
4160
- updatePinUI();
4161
- },
4162
3436
  snapTo,
4163
3437
  snapToNearest,
4164
3438
  get corner() {
@@ -4516,9 +3790,7 @@ function createSurfaceShell(options) {
4516
3790
  container: host.chromeEl,
4517
3791
  session: options.session,
4518
3792
  initialCorner: options.initialCorner,
4519
- appTitle: options.appTitle,
4520
- channel: options.channel ?? null,
4521
- pinLayer: options.pinLayer ?? null
3793
+ appTitle: options.appTitle
4522
3794
  });
4523
3795
  cleanup.add(menuBar);
4524
3796
  return {
@@ -4533,539 +3805,6 @@ function createSurfaceShell(options) {
4533
3805
  };
4534
3806
  }
4535
3807
 
4536
- // src/browser/report/constants.ts
4537
- var TYPE_LABELS = {
4538
- bug: "Bug",
4539
- request: "Request",
4540
- suggestion: "Suggestion"
4541
- };
4542
- var SEVERITY_LABELS = {
4543
- critical: "Critical",
4544
- high: "High",
4545
- medium: "Medium",
4546
- low: "Low"
4547
- };
4548
- function authorLabel(rec) {
4549
- const name = rec.reporter?.name ?? rec.reporterName;
4550
- const email = rec.reporter?.email ?? rec.reporterEmail;
4551
- return name?.trim() || email?.trim() || "Anonymous";
4552
- }
4553
- function relativeTime(iso) {
4554
- const ms = Date.parse(iso);
4555
- if (!Number.isFinite(ms)) return "";
4556
- const s = Math.floor((Date.now() - ms) / 1e3);
4557
- if (s < 60) return "just now";
4558
- const m = Math.floor(s / 60);
4559
- if (m < 60) return `${m}m ago`;
4560
- const h = Math.floor(m / 60);
4561
- if (h < 24) return `${h}h ago`;
4562
- const d = Math.floor(h / 24);
4563
- if (d < 30) return `${d}d ago`;
4564
- return `${Math.floor(d / 30)}mo ago`;
4565
- }
4566
-
4567
- // src/browser/surface/pin-layer.ts
4568
- var import_lucide4 = require("lucide");
4569
- var KNOWN_KINDS = new Set(UIDEX_ATTR_TO_KIND.map(([, k]) => k));
4570
- function parseComponentRef(componentId) {
4571
- const colon = componentId.indexOf(":");
4572
- if (colon > 0) {
4573
- const maybeKind = componentId.slice(0, colon);
4574
- if (KNOWN_KINDS.has(maybeKind)) {
4575
- return { kind: maybeKind, id: componentId.slice(colon + 1) };
4576
- }
4577
- }
4578
- return { kind: "element", id: componentId };
4579
- }
4580
- var DOT = 28;
4581
- var BORDER = 2;
4582
- var ICON = 16;
4583
- var PAD = 10;
4584
- var CARD_W = 280;
4585
- var LINE_H = 17;
4586
- var HEADER_H = 16;
4587
- var BODY_MT = 2;
4588
- var CYCLE_H = 18;
4589
- var CARD_H = PAD + HEADER_H + BODY_MT + LINE_H * 2 + CYCLE_H + PAD;
4590
- var INSET = 4;
4591
- var TYPE_ICON = {
4592
- bug: import_lucide4.Bug,
4593
- request: import_lucide4.MessageSquarePlus,
4594
- suggestion: import_lucide4.Lightbulb,
4595
- feature: import_lucide4.Lightbulb,
4596
- improvement: import_lucide4.Sparkles,
4597
- question: import_lucide4.CircleHelp,
4598
- note: import_lucide4.StickyNote
4599
- };
4600
- var TYPE_COLOR = {
4601
- bug: {
4602
- bg: "color-mix(in srgb, var(--destructive) 10%, var(--background))",
4603
- fg: "var(--destructive-foreground)"
4604
- },
4605
- request: {
4606
- bg: "color-mix(in srgb, var(--info) 10%, var(--background))",
4607
- fg: "var(--info-foreground)"
4608
- },
4609
- suggestion: {
4610
- bg: "color-mix(in srgb, var(--warning) 10%, var(--background))",
4611
- fg: "var(--warning-foreground)"
4612
- },
4613
- feature: { bg: "var(--secondary)", fg: "var(--secondary-foreground)" },
4614
- improvement: {
4615
- bg: "color-mix(in srgb, var(--info) 10%, var(--background))",
4616
- fg: "var(--info-foreground)"
4617
- },
4618
- question: {
4619
- bg: "color-mix(in srgb, var(--warning) 10%, var(--background))",
4620
- fg: "var(--warning-foreground)"
4621
- },
4622
- note: {
4623
- bg: "color-mix(in srgb, var(--primary) 10%, var(--background))",
4624
- fg: "var(--primary)"
4625
- }
4626
- };
4627
- var FALLBACK_COLOR = {
4628
- bg: "var(--secondary)",
4629
- fg: "var(--secondary-foreground)"
4630
- };
4631
- function typeColors(t) {
4632
- return TYPE_COLOR[t] ?? FALLBACK_COLOR;
4633
- }
4634
- function typeIcon(t) {
4635
- return TYPE_ICON[t] ?? import_lucide4.StickyNote;
4636
- }
4637
- var EASE_OUT = "cubic-bezier(0.16, 1, 0.3, 1)";
4638
- var DUR_FADE = 120;
4639
- var DUR_EXPAND = 250;
4640
- var T_FADE = `opacity ${DUR_FADE}ms ease`;
4641
- function createPinLayer(options) {
4642
- const { container, onOpenPinDetail, onHoverPin, onPinsChanged } = options;
4643
- const layerEl = document.createElement("div");
4644
- layerEl.setAttribute("data-uidex-pin-layer", "");
4645
- Object.assign(layerEl.style, {
4646
- position: "fixed",
4647
- inset: "0",
4648
- zIndex: String(Z_PIN_LAYER),
4649
- pointerEvents: "none"
4650
- });
4651
- container.appendChild(layerEl);
4652
- let layerVisible = true;
4653
- const allPins = [];
4654
- const seenIds = /* @__PURE__ */ new Set();
4655
- const byComp = /* @__PURE__ */ new Map();
4656
- const indicators = /* @__PURE__ */ new Map();
4657
- const filterCbs = /* @__PURE__ */ new Set();
4658
- const notifyFilter = () => {
4659
- for (const cb of filterCbs) cb();
4660
- };
4661
- const rebuildFiltered = () => {
4662
- byComp.clear();
4663
- for (const p of allPins) {
4664
- const cid = p.entity ?? "";
4665
- const list = byComp.get(cid);
4666
- if (list) list.push(p);
4667
- else byComp.set(cid, [p]);
4668
- }
4669
- };
4670
- const rerender = () => {
4671
- rebuildFiltered();
4672
- for (const id of Array.from(indicators.keys())) {
4673
- if (!byComp.has(id)) removeIndicator(id);
4674
- }
4675
- for (const id of byComp.keys()) renderIndicator(id);
4676
- notifyFilter();
4677
- onPinsChanged?.();
4678
- };
4679
- let obs = null;
4680
- const repositioner = createRepositioner(() => posAll());
4681
- const attachObs = () => {
4682
- if (obs || typeof MutationObserver === "undefined") return;
4683
- obs = new MutationObserver((recs) => {
4684
- for (const r of recs) {
4685
- if (r.type === "childList" && (r.addedNodes.length || r.removedNodes.length)) {
4686
- refreshAnchors();
4687
- return;
4688
- }
4689
- }
4690
- });
4691
- obs.observe(document.body, { childList: true, subtree: true });
4692
- };
4693
- const detachObs = () => {
4694
- obs?.disconnect();
4695
- obs = null;
4696
- };
4697
- const refreshAnchors = () => {
4698
- let changed = false;
4699
- for (const s of indicators.values()) {
4700
- if (s.anchor && s.anchor.isConnected) continue;
4701
- s.anchor = resolveEntityElement(parseComponentRef(s.componentId));
4702
- changed = true;
4703
- }
4704
- if (changed) repositioner.schedule();
4705
- };
4706
- const expand = (st) => {
4707
- if (st.expanded) return;
4708
- st.expanded = true;
4709
- fillContent(st);
4710
- st.wrap.style.zIndex = "1";
4711
- st.iconEl.style.transition = T_FADE;
4712
- st.iconEl.style.opacity = "0";
4713
- st.badgeEl.style.visibility = "hidden";
4714
- st.el.style.transition = `width ${DUR_EXPAND}ms ${EASE_OUT} ${DUR_FADE}ms, height ${DUR_EXPAND}ms ${EASE_OUT} ${DUR_FADE}ms`;
4715
- st.el.style.width = `${CARD_W}px`;
4716
- st.el.style.height = `${CARD_H}px`;
4717
- st.contentEl.style.transition = `opacity ${DUR_FADE}ms ease ${DUR_FADE + DUR_EXPAND}ms`;
4718
- st.contentEl.style.opacity = "1";
4719
- if (st.anchor) onHoverPin?.(st.anchor, st.componentId);
4720
- };
4721
- const collapse = (st) => {
4722
- if (!st.expanded) return;
4723
- st.expanded = false;
4724
- st.contentEl.style.transition = T_FADE;
4725
- st.contentEl.style.opacity = "0";
4726
- st.el.style.transition = `width ${DUR_EXPAND}ms ${EASE_OUT} ${DUR_FADE}ms, height ${DUR_EXPAND}ms ${EASE_OUT} ${DUR_FADE}ms`;
4727
- st.el.style.width = `${DOT}px`;
4728
- st.el.style.height = `${DOT}px`;
4729
- st.iconEl.style.transition = `opacity ${DUR_FADE}ms ease ${DUR_FADE + DUR_EXPAND}ms`;
4730
- st.iconEl.style.opacity = "1";
4731
- st.badgeEl.style.visibility = "";
4732
- st.iconEl.addEventListener(
4733
- "transitionend",
4734
- () => {
4735
- if (!st.expanded) st.wrap.style.zIndex = "";
4736
- },
4737
- { once: true }
4738
- );
4739
- onHoverPin?.(null, null);
4740
- };
4741
- const fillContent = (st) => {
4742
- const pins = byComp.get(st.componentId);
4743
- if (!pins?.length) return;
4744
- const idx = Math.max(0, Math.min(st.pinIndex, pins.length - 1));
4745
- st.pinIndex = idx;
4746
- const pin = pins[idx];
4747
- st.contentEl.innerHTML = "";
4748
- const hdr = document.createElement("div");
4749
- hdr.style.display = "flex";
4750
- hdr.style.alignItems = "baseline";
4751
- hdr.style.gap = "6px";
4752
- const nm = document.createElement("span");
4753
- nm.style.fontWeight = "600";
4754
- nm.style.fontSize = "12px";
4755
- nm.textContent = authorLabel(pin);
4756
- hdr.appendChild(nm);
4757
- const tm = document.createElement("span");
4758
- tm.style.fontSize = "11px";
4759
- tm.style.opacity = "0.75";
4760
- tm.textContent = relativeTime(pin.createdAt);
4761
- hdr.appendChild(tm);
4762
- st.contentEl.appendChild(hdr);
4763
- const body = document.createElement("div");
4764
- Object.assign(body.style, {
4765
- fontSize: "12px",
4766
- lineHeight: "1.4",
4767
- marginTop: "2px",
4768
- whiteSpace: "pre-wrap",
4769
- overflow: "hidden",
4770
- display: "-webkit-box"
4771
- });
4772
- body.style.setProperty("-webkit-line-clamp", "2");
4773
- body.style.setProperty("-webkit-box-orient", "vertical");
4774
- body.textContent = pin.body;
4775
- st.contentEl.appendChild(body);
4776
- const ctr = document.createElement("div");
4777
- Object.assign(ctr.style, {
4778
- fontSize: "10px",
4779
- opacity: "0.5",
4780
- marginTop: "4px"
4781
- });
4782
- ctr.textContent = pins.length > 1 ? `${idx + 1} of ${pins.length} \xB7 right-click to cycle` : "click to open";
4783
- st.contentEl.appendChild(ctr);
4784
- };
4785
- const buildIndicator = (componentId) => {
4786
- const wrap = document.createElement("div");
4787
- wrap.setAttribute("data-uidex-pin-wrap", "");
4788
- Object.assign(wrap.style, {
4789
- position: "fixed",
4790
- display: "none",
4791
- pointerEvents: "none"
4792
- });
4793
- const el2 = document.createElement("button");
4794
- el2.type = "button";
4795
- el2.setAttribute("data-uidex-pin", "");
4796
- el2.setAttribute("data-uidex-pin-component", componentId);
4797
- el2.setAttribute("aria-label", `Open report for ${componentId}`);
4798
- Object.assign(el2.style, {
4799
- position: "relative",
4800
- width: `${DOT}px`,
4801
- height: `${DOT}px`,
4802
- borderRadius: `${DOT / 2}px`,
4803
- border: `${BORDER}px solid var(--background)`,
4804
- padding: "0",
4805
- margin: "0",
4806
- cursor: "pointer",
4807
- pointerEvents: "auto",
4808
- overflow: "hidden",
4809
- boxShadow: "0 2px 8px rgba(0,0,0,0.15), 0 0 0 1px rgba(0,0,0,0.05)",
4810
- fontFamily: "var(--font-sans)",
4811
- textAlign: "left",
4812
- float: "right"
4813
- });
4814
- wrap.appendChild(el2);
4815
- const iconEl = document.createElement("span");
4816
- iconEl.setAttribute("data-uidex-pin-icon", "");
4817
- Object.assign(iconEl.style, {
4818
- position: "absolute",
4819
- inset: "0",
4820
- display: "flex",
4821
- alignItems: "center",
4822
- justifyContent: "center"
4823
- });
4824
- el2.appendChild(iconEl);
4825
- const badgeEl = document.createElement("span");
4826
- badgeEl.setAttribute("data-uidex-pin-badge", "");
4827
- Object.assign(badgeEl.style, {
4828
- position: "absolute",
4829
- top: "-4px",
4830
- right: "-4px",
4831
- minWidth: "14px",
4832
- height: "14px",
4833
- borderRadius: "999px",
4834
- background: "var(--foreground)",
4835
- color: "var(--background)",
4836
- fontSize: "9px",
4837
- fontWeight: "600",
4838
- display: "none",
4839
- alignItems: "center",
4840
- justifyContent: "center",
4841
- padding: "0 3px",
4842
- lineHeight: "1",
4843
- zIndex: "2",
4844
- pointerEvents: "none"
4845
- });
4846
- wrap.appendChild(badgeEl);
4847
- const contentEl = document.createElement("div");
4848
- contentEl.setAttribute("data-uidex-pin-content", "");
4849
- Object.assign(contentEl.style, {
4850
- position: "absolute",
4851
- top: `${PAD}px`,
4852
- left: `${PAD}px`,
4853
- right: `${PAD}px`,
4854
- opacity: "0",
4855
- minWidth: "0"
4856
- });
4857
- el2.appendChild(contentEl);
4858
- const st = {
4859
- componentId,
4860
- wrap,
4861
- el: el2,
4862
- iconEl,
4863
- badgeEl,
4864
- contentEl,
4865
- anchor: null,
4866
- pinIndex: 0,
4867
- expanded: false,
4868
- lastType: null
4869
- };
4870
- el2.addEventListener("mouseenter", () => expand(st));
4871
- el2.addEventListener("mouseleave", () => collapse(st));
4872
- el2.addEventListener("click", (e) => {
4873
- e.stopPropagation();
4874
- e.preventDefault();
4875
- const pins = byComp.get(componentId);
4876
- const pin = pins?.[st.pinIndex];
4877
- if (pin) onOpenPinDetail?.(componentId, pin.id);
4878
- });
4879
- el2.addEventListener("contextmenu", (e) => {
4880
- e.stopPropagation();
4881
- e.preventDefault();
4882
- const pins = byComp.get(componentId);
4883
- if (!pins || pins.length <= 1) return;
4884
- st.pinIndex = (st.pinIndex + 1) % pins.length;
4885
- applyStyle(st);
4886
- if (st.expanded) fillContent(st);
4887
- });
4888
- layerEl.appendChild(wrap);
4889
- return st;
4890
- };
4891
- const applyStyle = (st) => {
4892
- const pins = byComp.get(st.componentId);
4893
- if (!pins?.length) return;
4894
- const idx = Math.max(0, Math.min(st.pinIndex, pins.length - 1));
4895
- st.pinIndex = idx;
4896
- const pin = pins[idx];
4897
- const c = typeColors(pin.type);
4898
- st.el.style.backgroundColor = "var(--background)";
4899
- st.el.style.backgroundImage = `linear-gradient(${c.bg}, ${c.bg})`;
4900
- st.el.style.color = c.fg;
4901
- if (st.lastType !== pin.type) {
4902
- st.lastType = pin.type;
4903
- st.iconEl.innerHTML = "";
4904
- const svg2 = (0, import_lucide4.createElement)(typeIcon(pin.type));
4905
- svg2.setAttribute("width", String(ICON));
4906
- svg2.setAttribute("height", String(ICON));
4907
- st.iconEl.appendChild(svg2);
4908
- }
4909
- if (pins.length > 1) {
4910
- st.badgeEl.textContent = String(pins.length);
4911
- st.badgeEl.style.display = "flex";
4912
- } else {
4913
- st.badgeEl.textContent = "";
4914
- st.badgeEl.style.display = "none";
4915
- }
4916
- };
4917
- const posIndicator = (st) => {
4918
- const a = st.anchor;
4919
- if (!a || !a.isConnected) {
4920
- st.wrap.style.display = "none";
4921
- return;
4922
- }
4923
- const r = a.getBoundingClientRect();
4924
- if (r.width === 0 && r.height === 0) {
4925
- st.wrap.style.display = "none";
4926
- return;
4927
- }
4928
- const vw = window.innerWidth;
4929
- st.wrap.style.top = `${r.top + INSET}px`;
4930
- st.wrap.style.right = `${vw - r.right + INSET}px`;
4931
- st.wrap.style.display = "";
4932
- };
4933
- const posAll = () => {
4934
- for (const st of indicators.values()) posIndicator(st);
4935
- };
4936
- const renderIndicator = (componentId) => {
4937
- const pins = byComp.get(componentId);
4938
- if (!pins?.length) {
4939
- removeIndicator(componentId);
4940
- return;
4941
- }
4942
- let st = indicators.get(componentId);
4943
- if (!st) {
4944
- st = buildIndicator(componentId);
4945
- indicators.set(componentId, st);
4946
- repositioner.attach();
4947
- attachObs();
4948
- }
4949
- if (st.pinIndex >= pins.length) st.pinIndex = 0;
4950
- applyStyle(st);
4951
- st.anchor = resolveEntityElement(parseComponentRef(componentId));
4952
- posIndicator(st);
4953
- };
4954
- const removeIndicator = (componentId) => {
4955
- const st = indicators.get(componentId);
4956
- if (!st) return;
4957
- st.wrap.remove();
4958
- indicators.delete(componentId);
4959
- if (indicators.size === 0) {
4960
- repositioner.detach();
4961
- detachObs();
4962
- }
4963
- };
4964
- const ingest = (pin) => {
4965
- const cid = pin.entity ?? "";
4966
- if (!cid || seenIds.has(pin.id)) return false;
4967
- seenIds.add(pin.id);
4968
- allPins.push(pin);
4969
- return true;
4970
- };
4971
- let activeRefresh = null;
4972
- const layer = {
4973
- setPins(pins) {
4974
- allPins.length = 0;
4975
- seenIds.clear();
4976
- for (const p of pins) ingest(p);
4977
- rerender();
4978
- },
4979
- addPin(pin) {
4980
- if (!ingest(pin)) return;
4981
- rerender();
4982
- },
4983
- removePin(reportId) {
4984
- const i = allPins.findIndex((p) => p.id === reportId);
4985
- if (i === -1) return;
4986
- allPins.splice(i, 1);
4987
- seenIds.delete(reportId);
4988
- rerender();
4989
- },
4990
- clear() {
4991
- allPins.length = 0;
4992
- seenIds.clear();
4993
- byComp.clear();
4994
- for (const id of Array.from(indicators.keys())) removeIndicator(id);
4995
- notifyFilter();
4996
- },
4997
- getPinsForElement: (id) => byComp.get(id) ?? [],
4998
- getAllPins: () => allPins.slice(),
4999
- attachChannel(channel) {
5000
- const offPin = channel.onPin((pin) => {
5001
- layer.addPin(pin);
5002
- });
5003
- const offArchived = channel.onPinArchived?.((reportId) => {
5004
- layer.removePin(reportId);
5005
- }) ?? (() => {
5006
- });
5007
- return () => {
5008
- offPin();
5009
- offArchived();
5010
- };
5011
- },
5012
- attachCloud(opts) {
5013
- const offCh = opts.channel ? layer.attachChannel(opts.channel) : () => {
5014
- };
5015
- const fetch2 = async () => {
5016
- try {
5017
- const mode = opts.getMatchMode();
5018
- let params;
5019
- if (mode === "component") {
5020
- params = { entities: opts.getVisibleEntities().join(",") };
5021
- } else if (mode === "pathname") {
5022
- params = { route: opts.getPathname() };
5023
- } else {
5024
- params = { route: opts.getRoute() };
5025
- }
5026
- layer.setPins(await opts.cloud.pins.list(params));
5027
- } catch (err) {
5028
- opts.onError?.(err);
5029
- }
5030
- };
5031
- void fetch2();
5032
- activeRefresh = fetch2;
5033
- return () => {
5034
- offCh();
5035
- if (activeRefresh === fetch2) activeRefresh = null;
5036
- };
5037
- },
5038
- async refresh() {
5039
- if (activeRefresh) await activeRefresh();
5040
- },
5041
- onFilterChange(cb) {
5042
- filterCbs.add(cb);
5043
- return () => {
5044
- filterCbs.delete(cb);
5045
- };
5046
- },
5047
- get visible() {
5048
- return layerVisible;
5049
- },
5050
- setVisible(v) {
5051
- if (layerVisible === v) return;
5052
- layerVisible = v;
5053
- layerEl.style.display = v ? "" : "none";
5054
- if (v) posAll();
5055
- notifyFilter();
5056
- },
5057
- destroy() {
5058
- layer.clear();
5059
- repositioner.detach();
5060
- detachObs();
5061
- layerEl.remove();
5062
- activeRefresh = null;
5063
- filterCbs.clear();
5064
- }
5065
- };
5066
- return layer;
5067
- }
5068
-
5069
3808
  // src/browser/views/core/types.ts
5070
3809
  var ViewValidationError = class extends Error {
5071
3810
  constructor(message) {
@@ -5220,7 +3959,7 @@ var import_ref = require("lit-html/directives/ref.js");
5220
3959
  // src/browser/internal/lit-icon.ts
5221
3960
  var import_lit_html = require("lit-html");
5222
3961
  var import_directive = require("lit-html/directive.js");
5223
- var import_lucide5 = require("lucide");
3962
+ var import_lucide4 = require("lucide");
5224
3963
  var LucideIconDirective = class extends import_directive.Directive {
5225
3964
  _icon;
5226
3965
  _class;
@@ -5235,13 +3974,13 @@ var LucideIconDirective = class extends import_directive.Directive {
5235
3974
  if (iconNode === this._icon && className === this._class) return import_lit_html.noChange;
5236
3975
  this._icon = iconNode;
5237
3976
  this._class = className;
5238
- this._el = (0, import_lucide5.createElement)(iconNode);
3977
+ this._el = (0, import_lucide4.createElement)(iconNode);
5239
3978
  this._el.setAttribute("aria-hidden", "true");
5240
3979
  if (className) this._el.setAttribute("class", className);
5241
3980
  return this._el;
5242
3981
  }
5243
3982
  render(iconNode, className) {
5244
- const el2 = (0, import_lucide5.createElement)(iconNode);
3983
+ const el2 = (0, import_lucide4.createElement)(iconNode);
5245
3984
  el2.setAttribute("aria-hidden", "true");
5246
3985
  if (className) el2.setAttribute("class", className);
5247
3986
  return el2;
@@ -5257,7 +3996,7 @@ function createChip(options = {}, children = []) {
5257
3996
  }
5258
3997
 
5259
3998
  // src/browser/views/primitives/kind-icon.ts
5260
- var import_lucide6 = require("lucide");
3999
+ var import_lucide5 = require("lucide");
5261
4000
 
5262
4001
  // src/browser/ui/cva.ts
5263
4002
  function cva(base, config = {}) {
@@ -5345,7 +4084,7 @@ function iconTileTpl(inner, options = {}) {
5345
4084
  // src/browser/views/primitives/kind-icon.ts
5346
4085
  function renderKindIcon(kind) {
5347
4086
  const style = KIND_STYLE[kind];
5348
- const svg2 = (0, import_lucide6.createElement)(style.icon);
4087
+ const svg2 = (0, import_lucide5.createElement)(style.icon);
5349
4088
  svg2.setAttribute("aria-hidden", "true");
5350
4089
  svg2.setAttribute("class", cn("h-4 w-4 shrink-0", style.tone));
5351
4090
  return svg2;
@@ -5441,7 +4180,7 @@ function createBindable(propsFn) {
5441
4180
  }
5442
4181
  };
5443
4182
  }
5444
- function createMachineHandle(zagMachine, connect4, normalize, props = {}) {
4183
+ function createMachineHandle(zagMachine, connect3, normalize, props = {}) {
5445
4184
  const runner = createMachineRunner(
5446
4185
  zagMachine,
5447
4186
  props
@@ -5449,12 +4188,12 @@ function createMachineHandle(zagMachine, connect4, normalize, props = {}) {
5449
4188
  runner.start();
5450
4189
  return {
5451
4190
  runner,
5452
- api: () => connect4(runner.service, normalize),
4191
+ api: () => connect3(runner.service, normalize),
5453
4192
  destroy: () => runner.stop()
5454
4193
  };
5455
4194
  }
5456
4195
  function createMachineRunner(machineArg, initialProps = {}) {
5457
- const machine4 = machineArg;
4196
+ const machine3 = machineArg;
5458
4197
  let userProps = initialProps;
5459
4198
  const unmountFns = [];
5460
4199
  const listeners = /* @__PURE__ */ new Set();
@@ -5471,7 +4210,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5471
4210
  };
5472
4211
  };
5473
4212
  const debug = (...args) => {
5474
- if (machine4.debug) {
4213
+ if (machine3.debug) {
5475
4214
  console.log(...args);
5476
4215
  }
5477
4216
  };
@@ -5479,7 +4218,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5479
4218
  const prop = (key) => propsRef.current[key];
5480
4219
  const refreshProps = () => {
5481
4220
  const scope = scopeRef.current;
5482
- const computedProps = machine4.props?.({
4221
+ const computedProps = machine3.props?.({
5483
4222
  props: (0, import_utils.compact)(userProps),
5484
4223
  scope
5485
4224
  }) ?? userProps;
@@ -5513,7 +4252,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5513
4252
  return values.some((v) => (0, import_core.matchesState)(state.ref.current, v));
5514
4253
  },
5515
4254
  hasTag(tag) {
5516
- return (0, import_core.hasTag)(machine4, state.ref.current, tag);
4255
+ return (0, import_core.hasTag)(machine3, state.ref.current, tag);
5517
4256
  }
5518
4257
  });
5519
4258
  const getParams = () => ({
@@ -5535,7 +4274,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5535
4274
  const strs = (0, import_utils.isFunction)(keys) ? keys(getParams()) : keys;
5536
4275
  if (!strs) return;
5537
4276
  const fns = strs.map((s) => {
5538
- const fn = machine4.implementations?.actions?.[s];
4277
+ const fn = machine3.implementations?.actions?.[s];
5539
4278
  if (!fn) (0, import_utils.warn)(`[uidex] No implementation found for action "${s}"`);
5540
4279
  return fn;
5541
4280
  });
@@ -5543,13 +4282,13 @@ function createMachineRunner(machineArg, initialProps = {}) {
5543
4282
  };
5544
4283
  const guard = (str) => {
5545
4284
  if ((0, import_utils.isFunction)(str)) return str(getParams());
5546
- return machine4.implementations?.guards?.[str](getParams());
4285
+ return machine3.implementations?.guards?.[str](getParams());
5547
4286
  };
5548
4287
  const effect = (keys) => {
5549
4288
  const strs = (0, import_utils.isFunction)(keys) ? keys(getParams()) : keys;
5550
4289
  if (!strs) return;
5551
4290
  const fns = strs.map((s) => {
5552
- const fn = machine4.implementations?.effects?.[s];
4291
+ const fn = machine3.implementations?.effects?.[s];
5553
4292
  if (!fn) (0, import_utils.warn)(`[uidex] No implementation found for effect "${s}"`);
5554
4293
  return fn;
5555
4294
  });
@@ -5570,10 +4309,10 @@ function createMachineRunner(machineArg, initialProps = {}) {
5570
4309
  });
5571
4310
  const computed = (key) => {
5572
4311
  (0, import_utils.ensure)(
5573
- machine4.computed,
4312
+ machine3.computed,
5574
4313
  () => `[uidex] No computed object found on machine`
5575
4314
  );
5576
- const fn = machine4.computed[key];
4315
+ const fn = machine3.computed[key];
5577
4316
  return fn({
5578
4317
  context: ctx,
5579
4318
  event: getEvent(),
@@ -5606,7 +4345,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5606
4345
  queueMicrotask(() => fn());
5607
4346
  };
5608
4347
  let contextFields = {};
5609
- const ctxContextFn = machine4.context;
4348
+ const ctxContextFn = machine3.context;
5610
4349
  if (ctxContextFn) {
5611
4350
  contextFields = ctxContextFn({
5612
4351
  prop,
@@ -5642,7 +4381,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5642
4381
  return contextFields[key]?.hash(current) ?? "";
5643
4382
  }
5644
4383
  };
5645
- const refsInit = machine4.refs?.({ prop, context: ctx }) ?? {};
4384
+ const refsInit = machine3.refs?.({ prop, context: ctx }) ?? {};
5646
4385
  const refsStore = { ...refsInit };
5647
4386
  const refs = {
5648
4387
  get(key) {
@@ -5653,14 +4392,14 @@ function createMachineRunner(machineArg, initialProps = {}) {
5653
4392
  }
5654
4393
  };
5655
4394
  const initialState = (0, import_core.resolveStateValue)(
5656
- machine4,
5657
- machine4.initialState({ prop })
4395
+ machine3,
4396
+ machine3.initialState({ prop })
5658
4397
  );
5659
4398
  const state = createBindable(() => ({
5660
4399
  defaultValue: initialState,
5661
4400
  onChange(nextState, prevState) {
5662
4401
  const { exiting, entering } = (0, import_core.getExitEnterStates)(
5663
- machine4,
4402
+ machine3,
5664
4403
  prevState,
5665
4404
  nextState,
5666
4405
  transitionRef?.reenter
@@ -5679,8 +4418,8 @@ function createMachineRunner(machineArg, initialProps = {}) {
5679
4418
  if (cleanup) effectsMap.set(item.path, cleanup);
5680
4419
  });
5681
4420
  if (prevState === import_core.INIT_STATE) {
5682
- action(machine4.entry);
5683
- const cleanup = effect(machine4.effects);
4421
+ action(machine3.entry);
4422
+ const cleanup = effect(machine3.effects);
5684
4423
  if (cleanup) effectsMap.set(import_core.INIT_STATE, cleanup);
5685
4424
  }
5686
4425
  entering.forEach((item) => {
@@ -5699,7 +4438,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5699
4438
  currentEvent = event;
5700
4439
  const currentState = getCurrentState();
5701
4440
  const { transitions, source } = (0, import_core.findTransition)(
5702
- machine4,
4441
+ machine3,
5703
4442
  currentState,
5704
4443
  event.type
5705
4444
  );
@@ -5707,7 +4446,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5707
4446
  if (!transition) return;
5708
4447
  transitionRef = transition;
5709
4448
  const target = (0, import_core.resolveStateValue)(
5710
- machine4,
4449
+ machine3,
5711
4450
  transition.target ?? currentState,
5712
4451
  source
5713
4452
  );
@@ -5727,7 +4466,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5727
4466
  }
5728
4467
  });
5729
4468
  };
5730
- machine4.watch?.(getParams());
4469
+ machine3.watch?.(getParams());
5731
4470
  const service = {
5732
4471
  state: getState(),
5733
4472
  send,
@@ -5768,7 +4507,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5768
4507
  while (unmountFns.length) unmountFns.pop()?.();
5769
4508
  listeners.clear();
5770
4509
  queueMicrotask(() => {
5771
- action(machine4.exit);
4510
+ action(machine3.exit);
5772
4511
  });
5773
4512
  },
5774
4513
  subscribe(listener) {
@@ -5846,9 +4585,6 @@ function applyProps(node, props) {
5846
4585
  }
5847
4586
 
5848
4587
  // src/browser/views/builder/spread-props.ts
5849
- function spreadProps(node, props) {
5850
- return applyProps(node, props);
5851
- }
5852
4588
  function createPersistentSpreads() {
5853
4589
  const map = /* @__PURE__ */ new Map();
5854
4590
  return {
@@ -5971,18 +4707,18 @@ function headingTpl(text, className) {
5971
4707
  }
5972
4708
 
5973
4709
  // src/browser/views/render/detail-actions.ts
5974
- var import_lucide7 = require("lucide");
4710
+ var import_lucide6 = require("lucide");
5975
4711
  var ICON_MAP = {
5976
- "archive-x": import_lucide7.ArchiveX,
5977
- copy: import_lucide7.Copy,
5978
- camera: import_lucide7.Camera,
5979
- inbox: import_lucide7.Inbox,
5980
- "message-circle-warning": import_lucide7.MessageCircleWarning,
5981
- "chevron-down": import_lucide7.ChevronDown,
5982
- highlighter: import_lucide7.Highlighter,
5983
- "sticky-note": import_lucide7.StickyNote,
5984
- "ticket-plus": import_lucide7.TicketPlus,
5985
- view: import_lucide7.View
4712
+ "archive-x": import_lucide6.ArchiveX,
4713
+ copy: import_lucide6.Copy,
4714
+ camera: import_lucide6.Camera,
4715
+ inbox: import_lucide6.Inbox,
4716
+ "message-circle-warning": import_lucide6.MessageCircleWarning,
4717
+ "chevron-down": import_lucide6.ChevronDown,
4718
+ highlighter: import_lucide6.Highlighter,
4719
+ "sticky-note": import_lucide6.StickyNote,
4720
+ "ticket-plus": import_lucide6.TicketPlus,
4721
+ view: import_lucide6.View
5986
4722
  };
5987
4723
  function iconFor(icon2) {
5988
4724
  return icon2 ? ICON_MAP[icon2] : null;
@@ -6021,7 +4757,7 @@ function renderActions(actions, ctx, root, heading) {
6021
4757
  let leading;
6022
4758
  const iconNode = iconFor(action.icon);
6023
4759
  if (iconNode) {
6024
- const svg2 = (0, import_lucide7.createElement)(iconNode);
4760
+ const svg2 = (0, import_lucide6.createElement)(iconNode);
6025
4761
  svg2.setAttribute("aria-hidden", "true");
6026
4762
  svg2.setAttribute("class", "h-4 w-4 shrink-0");
6027
4763
  const tile = el(
@@ -6757,858 +5493,84 @@ function renderDetailSurface(surface, ctx, root) {
6757
5493
  return mounted;
6758
5494
  }
6759
5495
 
6760
- // src/browser/views/render/form.ts
6761
- var import_lucide8 = require("lucide");
6762
-
6763
- // src/browser/machines/dialog.ts
6764
- var dialog = __toESM(require("@zag-js/dialog"), 1);
6765
- function createDialog(props = {}) {
6766
- return createMachineHandle(
6767
- dialog.machine,
6768
- dialog.connect,
6769
- normalizeProps,
6770
- props
6771
- );
6772
- }
5496
+ // src/browser/views/render/list.ts
5497
+ var import_lucide7 = require("lucide");
6773
5498
 
6774
- // src/browser/ui/screenshot-lightbox.ts
6775
- function createScreenshotLightbox(trigger, dataUrl, options) {
6776
- const cleanups = [];
6777
- const lightbox = createDialog({
6778
- id: "uidex-screenshot-lightbox",
6779
- modal: true,
6780
- closeOnInteractOutside: false,
6781
- closeOnEscape: false,
6782
- trapFocus: true,
6783
- preventScroll: true,
6784
- restoreFocus: true,
6785
- "aria-label": "Screenshot preview",
6786
- getRootNode: () => trigger.getRootNode()
6787
- });
6788
- cleanups.push(() => lightbox.destroy());
6789
- const backdropRef = (0, import_ref.createRef)();
6790
- const positionerRef = (0, import_ref.createRef)();
6791
- const contentRef = (0, import_ref.createRef)();
6792
- const fullImgRef = (0, import_ref.createRef)();
6793
- const mountTarget = (() => {
6794
- const rn = trigger.getRootNode();
6795
- return rn instanceof ShadowRoot ? rn : rn.body;
6796
- })();
6797
- const fragment = document.createDocumentFragment();
6798
- (0, import_lit_html2.render)(
6799
- import_lit_html2.html`
6800
- <div
6801
- ${(0, import_ref.ref)(backdropRef)}
6802
- class="fixed inset-0 z-[2147483647] cursor-pointer bg-black/80"
6803
- data-uidex-lightbox
6804
- hidden
6805
- ></div>
6806
- <div
6807
- ${(0, import_ref.ref)(positionerRef)}
6808
- class="fixed inset-0 z-[2147483647] flex cursor-pointer items-center justify-center p-6"
6809
- hidden
6810
- >
6811
- <div
6812
- ${(0, import_ref.ref)(contentRef)}
6813
- class="flex items-center justify-center outline-none"
6814
- >
6815
- <img
6816
- ${(0, import_ref.ref)(fullImgRef)}
6817
- class="max-h-full max-w-full rounded-lg"
6818
- src=${dataUrl}
6819
- alt="Screenshot"
6820
- />
6821
- </div>
6822
- </div>
6823
- `,
6824
- fragment
6825
- );
6826
- const backdropEl = backdropRef.value;
6827
- const positioner = positionerRef.value;
6828
- const content = contentRef.value;
6829
- const fullImg = fullImgRef.value;
6830
- mountTarget.append(fragment);
6831
- let spreadCleanups = [];
6832
- function applyLightboxProps() {
6833
- for (const c of spreadCleanups) c();
6834
- spreadCleanups = [];
6835
- const api = lightbox.api();
6836
- spreadCleanups.push(
6837
- spreadProps(
6838
- backdropEl,
6839
- api.getBackdropProps()
6840
- ),
6841
- spreadProps(
6842
- positioner,
6843
- api.getPositionerProps()
6844
- ),
6845
- spreadProps(content, api.getContentProps())
6846
- );
6847
- backdropEl.hidden = !api.open;
6848
- positioner.hidden = !api.open;
6849
- }
6850
- applyLightboxProps();
6851
- const unsubLightbox = lightbox.runner.subscribe(applyLightboxProps);
6852
- cleanups.push(() => {
6853
- unsubLightbox();
6854
- for (const c of spreadCleanups) c();
6855
- backdropEl.remove();
6856
- positioner.remove();
6857
- });
6858
- const closeLightbox = () => lightbox.api().setOpen(false);
6859
- if (options?.pushEscapeLayer) {
6860
- cleanups.push(
6861
- options.pushEscapeLayer(() => {
6862
- if (!lightbox.api().open) return false;
6863
- closeLightbox();
6864
- return true;
6865
- })
6866
- );
6867
- } else {
6868
- const onEscapeCapture = (e) => {
6869
- if (e.key !== "Escape") return;
6870
- if (!lightbox.api().open) return;
6871
- e.preventDefault();
6872
- e.stopPropagation();
6873
- e.stopImmediatePropagation();
6874
- closeLightbox();
6875
- };
6876
- window.addEventListener("keydown", onEscapeCapture, true);
6877
- cleanups.push(
6878
- () => window.removeEventListener("keydown", onEscapeCapture, true)
6879
- );
5499
+ // src/browser/views/render/list-controller.ts
5500
+ function createListController(opts) {
5501
+ let items = opts.items;
5502
+ let itemNodes = opts.itemNodes;
5503
+ let highlighted = null;
5504
+ let kbdMode = false;
5505
+ const subs = /* @__PURE__ */ new Set();
5506
+ const { contentEl, onSelect, onHighlightChange } = opts;
5507
+ contentEl.setAttribute("role", "listbox");
5508
+ contentEl.tabIndex = 0;
5509
+ contentEl.style.outline = "none";
5510
+ applyItemAria();
5511
+ if (opts.defaultHighlight && itemNodes.has(opts.defaultHighlight)) {
5512
+ setHighlight(opts.defaultHighlight, false);
6880
5513
  }
6881
- let suppressClickUntil = 0;
6882
- const onClickOutsideCapture = (e) => {
6883
- if (Date.now() < suppressClickUntil) {
6884
- e.preventDefault();
6885
- e.stopPropagation();
6886
- e.stopImmediatePropagation();
6887
- return;
6888
- }
6889
- if (!lightbox.api().open) return;
6890
- if (fullImg.contains(e.target)) return;
6891
- e.preventDefault();
6892
- e.stopPropagation();
6893
- e.stopImmediatePropagation();
6894
- closeLightbox();
6895
- suppressClickUntil = Date.now() + 100;
6896
- };
6897
- window.addEventListener("pointerdown", onClickOutsideCapture, true);
6898
- window.addEventListener("mousedown", onClickOutsideCapture, true);
6899
- window.addEventListener("click", onClickOutsideCapture, true);
6900
- cleanups.push(() => {
6901
- window.removeEventListener("pointerdown", onClickOutsideCapture, true);
6902
- window.removeEventListener("mousedown", onClickOutsideCapture, true);
6903
- window.removeEventListener("click", onClickOutsideCapture, true);
6904
- });
6905
- spreadProps(
6906
- trigger,
6907
- lightbox.api().getTriggerProps()
6908
- );
6909
- return {
6910
- destroy() {
6911
- for (const c of cleanups) c();
6912
- cleanups.length = 0;
6913
- }
6914
- };
6915
- }
6916
-
6917
- // src/browser/ui/button.ts
6918
- 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";
6919
- var buttonVariants = cva(buttonBase, {
6920
- defaultVariants: { size: "default", variant: "default" },
6921
- variants: {
6922
- size: {
6923
- default: "h-8 px-3",
6924
- sm: "h-7 gap-1.5 px-2.5",
6925
- xs: "h-6 gap-1 rounded-md px-2 text-xs",
6926
- lg: "h-9 px-3.5",
6927
- xl: "h-10 px-4 text-base",
6928
- icon: "size-8",
6929
- "icon-sm": "size-7",
6930
- "icon-lg": "size-9",
6931
- "icon-xs": "size-6 rounded-md"
6932
- },
6933
- variant: {
6934
- default: "border-primary bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
6935
- destructive: "border-destructive bg-destructive shadow-xs hover:bg-destructive/90 text-white",
6936
- "destructive-outline": "border-input bg-popover text-destructive-foreground shadow-xs/5 hover:border-destructive/30 hover:bg-destructive/5 dark:bg-input/30",
6937
- ghost: "text-foreground hover:bg-accent hover:text-accent-foreground border-transparent",
6938
- link: "text-foreground border-transparent underline-offset-4 hover:underline",
6939
- outline: "border-input bg-popover text-foreground shadow-xs/5 hover:bg-accent hover:text-accent-foreground dark:bg-input/30",
6940
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90 border-transparent"
5514
+ function values() {
5515
+ return items.map((i) => i.value);
5516
+ }
5517
+ function notify() {
5518
+ for (const cb of subs) cb();
5519
+ }
5520
+ function applyItemAria() {
5521
+ for (const [value, el2] of itemNodes) {
5522
+ el2.setAttribute("role", "option");
5523
+ el2.id = `${opts.surfaceId}-item-${value}`;
6941
5524
  }
6942
5525
  }
6943
- });
6944
-
6945
- // src/browser/views/render/form.ts
6946
- var fieldSeq = 0;
6947
- var nextFieldId = () => `uidex-field-${++fieldSeq}`;
6948
- function issuePathRoot(issue) {
6949
- const seg = issue.path?.[0];
6950
- if (seg === void 0) return void 0;
6951
- if (typeof seg === "object" && seg !== null && "key" in seg) {
6952
- return String(seg.key);
6953
- }
6954
- return String(seg);
6955
- }
6956
- async function validateWithSchema(schema, values) {
6957
- const result = await schema["~standard"].validate(values);
6958
- if (!("issues" in result) || !result.issues) return null;
6959
- const out = {};
6960
- for (const issue of result.issues) {
6961
- const key = issuePathRoot(issue);
6962
- if (!key) continue;
6963
- if (out[key]) continue;
6964
- out[key] = issue.message;
6965
- }
6966
- return Object.keys(out).length > 0 ? out : null;
6967
- }
6968
- var FIELD_INPUT_CLASS = "border-input bg-popover text-foreground shadow-xs/5 placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:border-destructive/40 aria-invalid:focus-visible:ring-destructive/20 dark:bg-input/30 inline-flex w-full rounded-lg border text-sm outline-none transition-shadow focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-60";
6969
- var NATIVE_SELECT_CLASS = "border-input bg-popover text-foreground shadow-xs/5 focus-visible:border-ring focus-visible:ring-ring/30 aria-invalid:border-destructive/40 dark:bg-input/30 inline-flex h-8 w-full cursor-pointer appearance-none rounded-lg border bg-[right_0.5rem_center] bg-no-repeat pl-3 pr-8 text-sm outline-none transition-shadow focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-60";
6970
- var FIELD_ROOT_CLASS = "flex flex-col items-start gap-2";
6971
- var FIELD_LABEL_CLASS = "text-base/4.5 text-foreground inline-flex items-center gap-2 font-medium sm:text-sm/4";
6972
- var FIELD_ERROR_CLASS = "text-destructive-foreground text-xs";
6973
- var CHECKBOX_CLASS = "border-input bg-popover size-4 cursor-pointer rounded-[0.25rem] border accent-primary";
6974
- function renderField(field, initial) {
6975
- const controlId = nextFieldId();
6976
- let control;
6977
- let read;
6978
- let reset;
6979
- switch (field.kind) {
6980
- case "select": {
6981
- const node = el("select", {
6982
- class: NATIVE_SELECT_CLASS,
6983
- attrs: {
6984
- "data-slot": "select",
6985
- name: field.name,
6986
- id: controlId,
6987
- required: field.required ?? false
6988
- }
6989
- });
6990
- for (const opt of field.options) {
6991
- node.append(
6992
- el("option", { attrs: { value: opt.value }, text: opt.label })
6993
- );
5526
+ function setHighlight(value, scroll = true) {
5527
+ if (value === highlighted) return;
5528
+ if (highlighted) {
5529
+ const prev = itemNodes.get(highlighted);
5530
+ if (prev) {
5531
+ prev.removeAttribute("data-highlighted");
5532
+ prev.removeAttribute("data-kbd-highlighted");
5533
+ prev.removeAttribute("aria-selected");
6994
5534
  }
6995
- const initialVal = typeof initial === "string" ? initial : typeof field.value === "string" ? field.value : field.options[0]?.value;
6996
- if (initialVal !== void 0) node.value = initialVal;
6997
- control = node;
6998
- read = () => node.value;
6999
- reset = () => {
7000
- if (initialVal !== void 0) node.value = initialVal;
7001
- };
7002
- break;
7003
- }
7004
- case "text":
7005
- case "email": {
7006
- const node = el("input", {
7007
- class: cn(FIELD_INPUT_CLASS, "h-8 px-3"),
7008
- attrs: {
7009
- "data-slot": "input",
7010
- type: field.kind === "email" ? "email" : "text",
7011
- name: field.name,
7012
- id: controlId,
7013
- placeholder: field.placeholder ?? "",
7014
- required: field.required ?? false
7015
- }
7016
- });
7017
- const initialVal = typeof initial === "string" ? initial : typeof field.value === "string" ? field.value : "";
7018
- node.value = initialVal;
7019
- control = node;
7020
- read = () => node.value;
7021
- reset = () => {
7022
- node.value = initialVal;
7023
- };
7024
- break;
7025
- }
7026
- case "textarea": {
7027
- const node = el("textarea", {
7028
- class: cn(FIELD_INPUT_CLASS, "min-h-20 px-3 py-2"),
7029
- attrs: {
7030
- "data-slot": "textarea",
7031
- name: field.name,
7032
- id: controlId,
7033
- placeholder: field.placeholder ?? "",
7034
- required: field.required ?? false,
7035
- rows: field.rows
7036
- }
7037
- });
7038
- const initialVal = typeof initial === "string" ? initial : typeof field.value === "string" ? field.value : "";
7039
- node.value = initialVal;
7040
- control = node;
7041
- read = () => node.value;
7042
- reset = () => {
7043
- node.value = initialVal;
7044
- };
7045
- break;
7046
- }
7047
- case "checkbox": {
7048
- const input = el("input", {
7049
- attrs: {
7050
- type: "checkbox",
7051
- name: field.name,
7052
- id: controlId
7053
- },
7054
- class: CHECKBOX_CLASS
7055
- });
7056
- const initialVal = typeof initial === "boolean" ? initial : typeof field.value === "boolean" ? field.value : false;
7057
- input.checked = initialVal;
7058
- control = input;
7059
- read = () => input.checked;
7060
- reset = () => {
7061
- input.checked = initialVal;
7062
- };
7063
- break;
7064
5535
  }
5536
+ highlighted = value;
5537
+ if (value) {
5538
+ const el2 = itemNodes.get(value);
5539
+ if (el2) {
5540
+ el2.setAttribute("data-highlighted", "");
5541
+ el2.setAttribute("aria-selected", "true");
5542
+ if (kbdMode) el2.setAttribute("data-kbd-highlighted", "");
5543
+ contentEl.setAttribute("aria-activedescendant", el2.id);
5544
+ if (scroll) el2.scrollIntoView({ block: "nearest" });
5545
+ }
5546
+ } else {
5547
+ contentEl.removeAttribute("aria-activedescendant");
5548
+ }
5549
+ onHighlightChange?.(value);
5550
+ notify();
7065
5551
  }
7066
- const errorNode = el("p", {
7067
- class: FIELD_ERROR_CLASS,
7068
- attrs: {
7069
- "data-slot": "field-error",
7070
- role: "alert",
7071
- "aria-live": "polite"
5552
+ function enterKbd() {
5553
+ if (kbdMode) return;
5554
+ kbdMode = true;
5555
+ contentEl.setAttribute("data-kbd-nav", "");
5556
+ }
5557
+ function exitKbd() {
5558
+ if (!kbdMode) return;
5559
+ kbdMode = false;
5560
+ contentEl.removeAttribute("data-kbd-nav");
5561
+ if (highlighted) {
5562
+ itemNodes.get(highlighted)?.removeAttribute("data-kbd-highlighted");
7072
5563
  }
7073
- });
7074
- errorNode.hidden = true;
7075
- const field$ = el("div", {
7076
- class: cn(
7077
- FIELD_ROOT_CLASS,
7078
- field.kind === "checkbox" ? "flex-row items-center gap-2" : ""
7079
- ),
7080
- attrs: {
7081
- "data-slot": "field",
7082
- "data-uidex-primitive": "field",
7083
- "data-uidex-field-name": field.name
7084
- }
7085
- });
7086
- const label = el("label", {
7087
- class: FIELD_LABEL_CLASS,
7088
- attrs: {
7089
- "data-slot": "field-label",
7090
- for: controlId
7091
- },
7092
- text: field.label
7093
- });
7094
- if (field.kind === "checkbox") {
7095
- field$.append(control, label);
7096
- } else {
7097
- field$.append(label, control);
7098
- }
7099
- field$.append(errorNode);
7100
- const setError = (message) => {
7101
- if (!message) {
7102
- errorNode.hidden = true;
7103
- errorNode.textContent = "";
7104
- control.removeAttribute("aria-invalid");
7105
- return;
7106
- }
7107
- errorNode.hidden = false;
7108
- errorNode.textContent = message;
7109
- control.setAttribute("aria-invalid", "true");
7110
- };
7111
- return {
7112
- name: field.name,
7113
- node: field$,
7114
- control,
7115
- errorNode,
7116
- read,
7117
- reset,
7118
- setError
7119
- };
7120
- }
7121
- var ICON_TILE_BASE = "bg-card text-foreground shadow-sm/5 not-dark:bg-clip-padding [&_svg:not([class*='size-'])]:size-4.5 flex size-9 shrink-0 items-center justify-center rounded-md border before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-md)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]";
7122
- var ICON_TILE = ICON_TILE_BASE + " relative";
7123
- var ICON_TILE_GHOST = ICON_TILE_BASE + " pointer-events-none absolute bottom-px shadow-none scale-84";
7124
- var EMPTY_ROOT = "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 text-balance px-6 py-12 text-center";
7125
- var SKELETON = "animate-skeleton rounded-sm [--skeleton-highlight:--alpha(var(--color-white)/64%)] [background:linear-gradient(120deg,transparent_40%,var(--skeleton-highlight),transparent_60%)_var(--color-muted)_0_0/200%_100%_fixed] dark:[--skeleton-highlight:--alpha(var(--color-white)/4%)]";
7126
- function iconMediaTpl(iconTpl) {
7127
- return import_lit_html2.html`
7128
- <div class="relative mb-6">
7129
- <div
7130
- class=${ICON_TILE_GHOST + " -rotate-10 origin-bottom-left -translate-x-0.5"}
7131
- aria-hidden="true"
7132
- ></div>
7133
- <div
7134
- class=${ICON_TILE_GHOST + " rotate-10 origin-bottom-right translate-x-0.5"}
7135
- aria-hidden="true"
7136
- ></div>
7137
- <div class=${ICON_TILE}>${iconTpl}</div>
7138
- </div>
7139
- `;
7140
- }
7141
- function loadingViewTpl(rootRef) {
7142
- return import_lit_html2.html`
7143
- <div class=${EMPTY_ROOT} aria-live="polite" hidden ${(0, import_ref.ref)(rootRef)}>
7144
- ${iconMediaTpl(icon(import_lucide8.Loader2, "animate-spin"))}
7145
- <div class="flex max-w-sm flex-col items-center text-center">
7146
- <div class=${SKELETON + " h-6 w-36 rounded-md"}></div>
7147
- </div>
7148
- <div
7149
- class="flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm"
7150
- >
7151
- <div class=${SKELETON + " h-7 w-20 rounded-lg"}></div>
7152
- </div>
7153
- </div>
7154
- `;
7155
- }
7156
- function successViewTpl(refs) {
7157
- return import_lit_html2.html`
7158
- <div class=${EMPTY_ROOT} hidden data-uidex-success-view ${(0, import_ref.ref)(refs.root)}>
7159
- ${iconMediaTpl(icon(import_lucide8.CircleCheck))}
7160
- <div class="flex max-w-sm flex-col items-center text-center">
7161
- <div
7162
- class="font-heading text-xl font-semibold"
7163
- data-uidex-success-title
7164
- ${(0, import_ref.ref)(refs.title)}
7165
- ></div>
7166
- </div>
7167
- <div
7168
- class="flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm"
7169
- data-uidex-success-actions
7170
- ${(0, import_ref.ref)(refs.actions)}
7171
- >
7172
- <a
7173
- class=${buttonVariants({ variant: "outline", size: "sm" })}
7174
- target="_blank"
7175
- rel="noreferrer"
7176
- data-uidex-success-link
7177
- ${(0, import_ref.ref)(refs.link)}
7178
- ></a>
7179
- </div>
7180
- </div>
7181
- `;
7182
- }
7183
- function errorViewTpl(refs) {
7184
- return import_lit_html2.html`
7185
- <div class=${EMPTY_ROOT} hidden data-uidex-error-view ${(0, import_ref.ref)(refs.root)}>
7186
- ${iconMediaTpl(icon(import_lucide8.CircleX))}
7187
- <div class="flex max-w-sm flex-col items-center gap-1 text-center">
7188
- <div
7189
- class="font-heading text-xl font-semibold"
7190
- data-uidex-error-title
7191
- ${(0, import_ref.ref)(refs.title)}
7192
- ></div>
7193
- <p
7194
- class="text-muted-foreground text-sm"
7195
- data-uidex-error-description
7196
- ${(0, import_ref.ref)(refs.description)}
7197
- ></p>
7198
- </div>
7199
- <div
7200
- class="flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm"
7201
- >
7202
- <button
7203
- type="button"
7204
- class=${buttonVariants({ variant: "outline", size: "sm" })}
7205
- data-slot="button"
7206
- data-uidex-retry-button
7207
- ${(0, import_ref.ref)(refs.retry)}
7208
- >
7209
- Try Again
7210
- </button>
7211
- </div>
7212
- </div>
7213
- `;
7214
- }
7215
- function renderFormSurface(surface, ctx, root) {
7216
- const cleanups = [];
7217
- const fields = surface.fields.map(
7218
- (field) => renderField(field, surface.initial?.[field.name])
7219
- );
7220
- const fieldByName = new Map(fields.map((f) => [f.name, f]));
7221
- const screenshotField = el("div", {
7222
- class: "flex flex-col gap-1.5",
7223
- attrs: { hidden: true }
7224
- });
7225
- const screenshotLabel = el("span", {
7226
- class: "text-muted-foreground text-xs font-medium",
7227
- text: "Screenshot"
7228
- });
7229
- const screenshotPreview = el("div", {
7230
- class: "cursor-pointer overflow-hidden rounded-md border"
7231
- });
7232
- screenshotField.append(screenshotLabel, screenshotPreview);
7233
- if (surface.screenshotPreview) {
7234
- surface.screenshotPreview.then((dataUrl) => {
7235
- if (!dataUrl) return;
7236
- const img = el("img", {
7237
- class: "max-h-32 w-full object-cover object-top",
7238
- attrs: { src: dataUrl, alt: "Screenshot preview" }
7239
- });
7240
- screenshotPreview.append(img);
7241
- screenshotField.hidden = false;
7242
- const handle = createScreenshotLightbox(screenshotPreview, dataUrl, {
7243
- pushEscapeLayer: ctx.pushEscapeLayer
7244
- });
7245
- cleanups.push(() => handle.destroy());
7246
- });
7247
- }
7248
- const formRef = (0, import_ref.createRef)();
7249
- const loadingRef = (0, import_ref.createRef)();
7250
- const successRef = (0, import_ref.createRef)();
7251
- const successTitleRef = (0, import_ref.createRef)();
7252
- const successLinkRef = (0, import_ref.createRef)();
7253
- const successActionsRef = (0, import_ref.createRef)();
7254
- const errorRef = (0, import_ref.createRef)();
7255
- const errorTitleRef = (0, import_ref.createRef)();
7256
- const errorDescriptionRef = (0, import_ref.createRef)();
7257
- const retryRef = (0, import_ref.createRef)();
7258
- (0, import_lit_html2.render)(
7259
- import_lit_html2.html`
7260
- <section
7261
- class="flex min-h-0 flex-1 flex-col p-4"
7262
- data-uidex-form-surface=${surface.id}
7263
- >
7264
- <form
7265
- ${(0, import_ref.ref)(formRef)}
7266
- class="flex w-full flex-col gap-4"
7267
- data-slot="form"
7268
- data-uidex-primitive="form"
7269
- novalidate
7270
- data-uidex-form=${surface.id}
7271
- ></form>
7272
- ${loadingViewTpl(loadingRef)}
7273
- ${successViewTpl({
7274
- root: successRef,
7275
- title: successTitleRef,
7276
- link: successLinkRef,
7277
- actions: successActionsRef
7278
- })}
7279
- ${errorViewTpl({
7280
- root: errorRef,
7281
- title: errorTitleRef,
7282
- description: errorDescriptionRef,
7283
- retry: retryRef
7284
- })}
7285
- </section>
7286
- `,
7287
- root
7288
- );
7289
- const form = formRef.value;
7290
- if (surface.screenshotPreview) {
7291
- form.append(screenshotField);
7292
- }
7293
- for (const f of fields) {
7294
- form.append(f.node);
7295
- }
7296
- const submit = el("button", {
7297
- class: cn(buttonVariants({ variant: "default" }), "sr-only"),
7298
- attrs: {
7299
- type: "submit",
7300
- "data-slot": "button",
7301
- "data-uidex-form-submit": ""
7302
- },
7303
- text: surface.submit.label
7304
- });
7305
- form.append(submit);
7306
- const status = el("p", {
7307
- class: "text-muted-foreground text-xs",
7308
- attrs: {
7309
- "data-uidex-form-status": "",
7310
- "aria-live": "polite"
7311
- }
7312
- });
7313
- form.append(status);
7314
- const loadingView = loadingRef.value;
7315
- const successView = successRef.value;
7316
- const successTitle = successTitleRef.value;
7317
- const successLink = successLinkRef.value;
7318
- const successActions = successActionsRef.value;
7319
- const errorView = errorRef.value;
7320
- const errorTitle = errorTitleRef.value;
7321
- const errorDescription = errorDescriptionRef.value;
7322
- const retryButton = retryRef.value;
7323
- function clearAllFieldErrors() {
7324
- for (const f of fields) f.setError(null);
7325
- }
7326
- function applyFieldErrors(errs) {
7327
- clearAllFieldErrors();
7328
- if (!errs) return;
7329
- for (const [name, message] of Object.entries(errs)) {
7330
- fieldByName.get(name)?.setError(message);
7331
- }
7332
- }
7333
- function setStatus(result) {
7334
- status.replaceChildren();
7335
- status.classList.remove("text-destructive-foreground");
7336
- status.classList.add("text-muted-foreground");
7337
- if (!result) return;
7338
- if (result.status === "error") {
7339
- status.classList.remove("text-muted-foreground");
7340
- status.classList.add("text-destructive-foreground");
7341
- if (!result.fieldErrors) {
7342
- status.textContent = result.message;
7343
- }
7344
- return;
7345
- }
7346
- if (result.link) {
7347
- status.append(
7348
- document.createTextNode(`${result.message ?? "Submitted."} `),
7349
- el("a", {
7350
- class: "text-foreground underline underline-offset-4",
7351
- attrs: {
7352
- href: result.link.url,
7353
- target: "_blank",
7354
- rel: "noreferrer"
7355
- },
7356
- text: result.link.label ?? "Open link"
7357
- })
7358
- );
7359
- } else {
7360
- status.textContent = result.message ?? "Submitted.";
7361
- }
7362
- }
7363
- function showView(state) {
7364
- form.hidden = state !== "form";
7365
- loadingView.hidden = state !== "loading";
7366
- successView.hidden = state !== "success";
7367
- errorView.hidden = state !== "error";
7368
- }
7369
- function populateSuccess(result) {
7370
- if (!result || result.status !== "success") return;
7371
- successTitle.textContent = result.message ?? "Submitted";
7372
- if (result.link) {
7373
- successLink.setAttribute("href", result.link.url);
7374
- successLink.textContent = result.link.label ?? "Open link";
7375
- successActions.hidden = false;
7376
- } else {
7377
- successActions.hidden = true;
7378
- }
7379
- }
7380
- function populateError(result) {
7381
- if (!result || result.status !== "error") return;
7382
- errorTitle.textContent = "Something went wrong";
7383
- errorDescription.textContent = result.message;
7384
- }
7385
- let formState = "idle";
7386
- let lastResult = null;
7387
- let lastFieldErrors = null;
7388
- const subscribers = /* @__PURE__ */ new Set();
7389
- const notify = () => {
7390
- for (const cb of subscribers) cb();
7391
- };
7392
- async function doSubmit(values) {
7393
- if (formState === "submitting") return;
7394
- formState = "submitting";
7395
- setStatus(null);
7396
- clearAllFieldErrors();
7397
- showView("form");
7398
- lastResult = null;
7399
- lastFieldErrors = null;
7400
- if (surface.schema) {
7401
- const errors = await validateWithSchema(surface.schema, values);
7402
- if (errors) {
7403
- lastFieldErrors = errors;
7404
- lastResult = {
7405
- status: "error",
7406
- message: "Please fix the errors above.",
7407
- fieldErrors: errors
7408
- };
7409
- formState = "error";
7410
- applyFieldErrors(errors);
7411
- setStatus(lastResult);
7412
- showView("form");
7413
- notify();
7414
- return;
7415
- }
7416
- }
7417
- submit.disabled = true;
7418
- submit.setAttribute("data-loading", "");
7419
- showView("loading");
7420
- notify();
7421
- try {
7422
- const result = await surface.submit.onSubmit(
7423
- values
7424
- ) ?? { status: "success" };
7425
- submit.disabled = false;
7426
- submit.removeAttribute("data-loading");
7427
- if (result.status === "success") {
7428
- lastResult = result;
7429
- formState = "success";
7430
- populateSuccess(result);
7431
- showView("success");
7432
- clearAllFieldErrors();
7433
- if (result.resetFields) {
7434
- for (const name of result.resetFields) {
7435
- fieldByName.get(name)?.reset();
7436
- }
7437
- }
7438
- } else {
7439
- lastResult = result;
7440
- lastFieldErrors = result.fieldErrors ?? null;
7441
- formState = "error";
7442
- const hasFieldErrors = lastFieldErrors && Object.keys(lastFieldErrors).length > 0;
7443
- if (hasFieldErrors) {
7444
- applyFieldErrors(lastFieldErrors ?? void 0);
7445
- setStatus(result);
7446
- showView("form");
7447
- } else {
7448
- populateError(result);
7449
- showView("error");
7450
- }
7451
- }
7452
- } catch (err) {
7453
- submit.disabled = false;
7454
- submit.removeAttribute("data-loading");
7455
- const message = err instanceof Error ? err.message : String(err);
7456
- lastResult = { status: "error", message };
7457
- formState = "error";
7458
- populateError(lastResult);
7459
- showView("error");
7460
- }
7461
- notify();
7462
- }
7463
- function retryFromError() {
7464
- formState = "idle";
7465
- lastResult = null;
7466
- lastFieldErrors = null;
7467
- setStatus(null);
7468
- clearAllFieldErrors();
7469
- showView("form");
7470
- notify();
7471
- }
7472
- const onSubmit = (e) => {
7473
- e.preventDefault();
7474
- if (formState === "success") return;
7475
- if (formState === "error") retryFromError();
7476
- const values = {};
7477
- surface.fields.forEach((field, i) => {
7478
- values[field.name] = fields[i].read();
7479
- });
7480
- void doSubmit(values);
7481
- };
7482
- form.addEventListener("submit", onSubmit);
7483
- cleanups.push(() => form.removeEventListener("submit", onSubmit));
7484
- const triggerSubmit = () => {
7485
- if (typeof form.requestSubmit === "function") form.requestSubmit();
7486
- else form.dispatchEvent(new Event("submit", { cancelable: true }));
7487
- };
7488
- const onFormKeyDown = (e) => {
7489
- if (e.key !== "Enter") return;
7490
- const isMod = e.metaKey || e.ctrlKey;
7491
- if (isMod) {
7492
- e.preventDefault();
7493
- if (formState === "success") {
7494
- ctx.close();
7495
- return;
7496
- }
7497
- if (formState !== "idle" && formState !== "error") return;
7498
- triggerSubmit();
7499
- return;
7500
- }
7501
- if (e.target instanceof HTMLInputElement && e.target.type !== "checkbox" && e.target.type !== "radio") {
7502
- e.preventDefault();
7503
- }
7504
- };
7505
- form.addEventListener("keydown", onFormKeyDown);
7506
- cleanups.push(() => form.removeEventListener("keydown", onFormKeyDown));
7507
- retryButton.addEventListener("click", retryFromError);
7508
- cleanups.push(() => retryButton.removeEventListener("click", retryFromError));
7509
- const resolveIntent = () => {
7510
- if (formState === "submitting") {
7511
- return { label: "Submitting\u2026", disabled: true, perform: () => {
7512
- } };
7513
- }
7514
- if (formState === "success") {
7515
- return { label: "Close", perform: () => ctx.close() };
7516
- }
7517
- if (formState === "error" && !lastFieldErrors) {
7518
- return { label: "Try Again", perform: retryFromError };
7519
- }
7520
- return { label: surface.submit.label, perform: triggerSubmit };
7521
- };
7522
- return {
7523
- cleanup: composeCleanups([...cleanups, () => root.replaceChildren()]),
7524
- submitIntent: {
7525
- get: resolveIntent,
7526
- subscribe: (cb) => {
7527
- subscribers.add(cb);
7528
- return () => subscribers.delete(cb);
7529
- }
7530
- }
7531
- };
7532
- }
7533
-
7534
- // src/browser/views/render/list.ts
7535
- var import_lucide9 = require("lucide");
7536
-
7537
- // src/browser/views/render/list-controller.ts
7538
- function createListController(opts) {
7539
- let items = opts.items;
7540
- let itemNodes = opts.itemNodes;
7541
- let highlighted = null;
7542
- let kbdMode = false;
7543
- const subs = /* @__PURE__ */ new Set();
7544
- const { contentEl, onSelect, onHighlightChange } = opts;
7545
- contentEl.setAttribute("role", "listbox");
7546
- contentEl.tabIndex = 0;
7547
- contentEl.style.outline = "none";
7548
- applyItemAria();
7549
- if (opts.defaultHighlight && itemNodes.has(opts.defaultHighlight)) {
7550
- setHighlight(opts.defaultHighlight, false);
7551
- }
7552
- function values() {
7553
- return items.map((i) => i.value);
7554
- }
7555
- function notify() {
7556
- for (const cb of subs) cb();
7557
- }
7558
- function applyItemAria() {
7559
- for (const [value, el2] of itemNodes) {
7560
- el2.setAttribute("role", "option");
7561
- el2.id = `${opts.surfaceId}-item-${value}`;
7562
- }
7563
- }
7564
- function setHighlight(value, scroll = true) {
7565
- if (value === highlighted) return;
7566
- if (highlighted) {
7567
- const prev = itemNodes.get(highlighted);
7568
- if (prev) {
7569
- prev.removeAttribute("data-highlighted");
7570
- prev.removeAttribute("data-kbd-highlighted");
7571
- prev.removeAttribute("aria-selected");
7572
- }
7573
- }
7574
- highlighted = value;
7575
- if (value) {
7576
- const el2 = itemNodes.get(value);
7577
- if (el2) {
7578
- el2.setAttribute("data-highlighted", "");
7579
- el2.setAttribute("aria-selected", "true");
7580
- if (kbdMode) el2.setAttribute("data-kbd-highlighted", "");
7581
- contentEl.setAttribute("aria-activedescendant", el2.id);
7582
- if (scroll) el2.scrollIntoView({ block: "nearest" });
7583
- }
7584
- } else {
7585
- contentEl.removeAttribute("aria-activedescendant");
7586
- }
7587
- onHighlightChange?.(value);
7588
- notify();
7589
- }
7590
- function enterKbd() {
7591
- if (kbdMode) return;
7592
- kbdMode = true;
7593
- contentEl.setAttribute("data-kbd-nav", "");
7594
- }
7595
- function exitKbd() {
7596
- if (!kbdMode) return;
7597
- kbdMode = false;
7598
- contentEl.removeAttribute("data-kbd-nav");
7599
- if (highlighted) {
7600
- itemNodes.get(highlighted)?.removeAttribute("data-kbd-highlighted");
7601
- }
7602
- }
7603
- function move(delta) {
7604
- const vals = values();
7605
- if (vals.length === 0) return;
7606
- enterKbd();
7607
- if (highlighted === null) {
7608
- setHighlight(delta > 0 ? vals[0] : vals[vals.length - 1]);
7609
- if (highlighted)
7610
- itemNodes.get(highlighted)?.setAttribute("data-kbd-highlighted", "");
7611
- return;
5564
+ }
5565
+ function move(delta) {
5566
+ const vals = values();
5567
+ if (vals.length === 0) return;
5568
+ enterKbd();
5569
+ if (highlighted === null) {
5570
+ setHighlight(delta > 0 ? vals[0] : vals[vals.length - 1]);
5571
+ if (highlighted)
5572
+ itemNodes.get(highlighted)?.setAttribute("data-kbd-highlighted", "");
5573
+ return;
7612
5574
  }
7613
5575
  const idx = vals.indexOf(highlighted);
7614
5576
  const next = (idx + delta + vals.length) % vals.length;
@@ -7628,7 +5590,7 @@ function createListController(opts) {
7628
5590
  e.preventDefault();
7629
5591
  move(-1);
7630
5592
  break;
7631
- case "Home":
5593
+ case "Home": {
7632
5594
  e.preventDefault();
7633
5595
  enterKbd();
7634
5596
  const first = values()[0];
@@ -7638,7 +5600,8 @@ function createListController(opts) {
7638
5600
  itemNodes.get(highlighted)?.setAttribute("data-kbd-highlighted", "");
7639
5601
  }
7640
5602
  break;
7641
- case "End":
5603
+ }
5604
+ case "End": {
7642
5605
  e.preventDefault();
7643
5606
  enterKbd();
7644
5607
  const vals = values();
@@ -7649,6 +5612,7 @@ function createListController(opts) {
7649
5612
  itemNodes.get(highlighted)?.setAttribute("data-kbd-highlighted", "");
7650
5613
  }
7651
5614
  break;
5615
+ }
7652
5616
  case "Enter":
7653
5617
  if (highlighted) {
7654
5618
  e.preventDefault();
@@ -7736,7 +5700,7 @@ function resolveLeadingTpl(item) {
7736
5700
  >${node}</span
7737
5701
  >`;
7738
5702
  }
7739
- return iconTileTpl(icon(import_lucide9.Circle, "size-3.5 opacity-80"));
5703
+ return iconTileTpl(icon(import_lucide7.Circle, "size-3.5 opacity-80"));
7740
5704
  }
7741
5705
  function renderShell(surface, root) {
7742
5706
  const scrollRootRef = (0, import_ref.createRef)();
@@ -7978,8 +5942,6 @@ function renderSurface(surface, ctx, root) {
7978
5942
  return renderListSurface(surface, ctx, root);
7979
5943
  case "detail":
7980
5944
  return renderDetailSurface(surface, ctx, root);
7981
- case "form":
7982
- return renderFormSurface(surface, ctx, root);
7983
5945
  }
7984
5946
  }
7985
5947
 
@@ -8038,8 +6000,6 @@ function mountEntry(entry, view, deps) {
8038
6000
  const ctx = {
8039
6001
  ref: entry.ref,
8040
6002
  registry: deps.registry,
8041
- cloud: deps.cloud,
8042
- user: deps.session.getState().user,
8043
6003
  views: deps.views,
8044
6004
  push: (target) => {
8045
6005
  if (!deps.views.get(target.id)) return;
@@ -8053,10 +6013,7 @@ function mountEntry(entry, view, deps) {
8053
6013
  pinHighlight: (r) => deps.session.highlight.pin(r),
8054
6014
  searchInput: deps.searchInput,
8055
6015
  highlight: deps.highlight,
8056
- getStack: () => deps.session.getState().stack,
8057
- pushEscapeLayer: deps.pushEscapeLayer,
8058
- onAfterSubmit: deps.onAfterSubmit,
8059
- getRoute: deps.getRoute
6016
+ getStack: () => deps.session.getState().stack
8060
6017
  };
8061
6018
  let surfaceResult;
8062
6019
  try {
@@ -8119,7 +6076,7 @@ function setEntryVisibility(entry, visible) {
8119
6076
  }
8120
6077
 
8121
6078
  // src/browser/views/core/shell.ts
8122
- var import_lucide10 = require("lucide");
6079
+ var import_lucide8 = require("lucide");
8123
6080
 
8124
6081
  // src/browser/machines/menu.ts
8125
6082
  var menu = __toESM(require("@zag-js/menu"), 1);
@@ -8384,14 +6341,14 @@ function createStackShell(container, callbacks, _opts) {
8384
6341
  hidden
8385
6342
  @click=${onBackClick}
8386
6343
  >
8387
- ${icon(import_lucide10.ArrowLeft, BACK_ICON_CLASS)}
6344
+ ${icon(import_lucide8.ArrowLeft, BACK_ICON_CLASS)}
8388
6345
  </button>
8389
6346
  <span
8390
6347
  ${(0, import_ref.ref)(searchIconRef)}
8391
6348
  data-uidex-view-stack-search-icon
8392
6349
  aria-hidden="true"
8393
6350
  class=${SEARCH_ICON_WRAP_CLASS}
8394
- >${icon(import_lucide10.Search, SEARCH_ICON_CLASS)}</span
6351
+ >${icon(import_lucide8.Search, SEARCH_ICON_CLASS)}</span
8395
6352
  >
8396
6353
  <input
8397
6354
  ${(0, import_ref.ref)(searchInputRef)}
@@ -8579,9 +6536,7 @@ function resolveProp(view, ctx, value, propName, fallback) {
8579
6536
  }
8580
6537
  function createViewStack(options) {
8581
6538
  const { container, views, session, registry, highlight } = options;
8582
- const cloud = options.cloud ?? null;
8583
6539
  const dev = options.dev ?? detectDev();
8584
- const onAfterSubmit = options.onAfterSubmit;
8585
6540
  const mounted = [];
8586
6541
  let shell = null;
8587
6542
  let warnedForDepth = 0;
@@ -8778,13 +6733,8 @@ function createViewStack(options) {
8778
6733
  views,
8779
6734
  session,
8780
6735
  registry,
8781
- cloud,
8782
6736
  highlight,
8783
- nextScrollSeq: () => ++scrollAreaSeq,
8784
- pushEscapeLayer: options.pushEscapeLayer ?? (() => () => {
8785
- }),
8786
- onAfterSubmit,
8787
- getRoute: options.getRoute
6737
+ nextScrollSeq: () => ++scrollAreaSeq
8788
6738
  });
8789
6739
  if (record) mounted.push(record);
8790
6740
  }
@@ -8839,20 +6789,13 @@ function createViewStack(options) {
8839
6789
 
8840
6790
  // src/browser/views/built-in/ids.ts
8841
6791
  var BUILT_IN_VIEW_IDS = {
8842
- closeReason: "close-reason",
8843
6792
  commandPalette: "command-palette",
8844
6793
  elements: "elements",
8845
- entityReports: "entity-reports",
8846
6794
  explorePage: "explore-page",
8847
6795
  features: "features",
8848
- report: "report",
8849
6796
  flows: "flows",
8850
6797
  glossary: "glossary",
8851
- jiraReport: "jira-report",
8852
6798
  pages: "pages",
8853
- pageReports: "page-reports",
8854
- reportDetail: "report-detail",
8855
- pinSettings: "pin-settings",
8856
6799
  primitives: "primitives",
8857
6800
  regions: "regions",
8858
6801
  widgets: "widgets"
@@ -8867,16 +6810,6 @@ var LIST_VIEW_FOR_KIND = {
8867
6810
  flow: BUILT_IN_VIEW_IDS.flows,
8868
6811
  route: BUILT_IN_VIEW_IDS.pages
8869
6812
  };
8870
- var DETAIL_VIEW_FOR_KIND = {
8871
- element: "component-detail",
8872
- feature: "feature-detail",
8873
- page: "page-detail",
8874
- widget: "widget-detail",
8875
- region: "region-detail",
8876
- primitive: "primitive-detail",
8877
- flow: "flow-detail",
8878
- route: "page-detail"
8879
- };
8880
6813
  var COMMAND_PALETTE_ENTRY2 = {
8881
6814
  id: BUILT_IN_VIEW_IDS.commandPalette,
8882
6815
  ref: null
@@ -8885,105 +6818,9 @@ function parentList(ref2) {
8885
6818
  if (!ref2) return COMMAND_PALETTE_ENTRY2;
8886
6819
  return { id: LIST_VIEW_FOR_KIND[ref2.kind], ref: null };
8887
6820
  }
8888
- function parentDetail(ref2) {
8889
- if (!ref2) return COMMAND_PALETTE_ENTRY2;
8890
- return { id: DETAIL_VIEW_FOR_KIND[ref2.kind], ref: ref2 };
8891
- }
8892
-
8893
- // src/browser/views/built-in/close-reason.ts
8894
- var import_lucide11 = require("lucide");
8895
- var pendingReportId = null;
8896
- var afterClose = null;
8897
- function setCloseTarget(reportId, onDone) {
8898
- pendingReportId = reportId;
8899
- afterClose = onDone;
8900
- }
8901
- function leadingIcon(iconNode) {
8902
- return () => {
8903
- const svg2 = (0, import_lucide11.createElement)(iconNode);
8904
- svg2.setAttribute("aria-hidden", "true");
8905
- return createIconTile(svg2);
8906
- };
8907
- }
8908
- function spinnerTile() {
8909
- const spinner = (0, import_lucide11.createElement)(import_lucide11.LoaderCircle);
8910
- spinner.setAttribute("class", "animate-spin");
8911
- return createIconTile(spinner);
8912
- }
8913
- var REASONS = [
8914
- { value: "fixed", label: "Resolved", icon: import_lucide11.CircleCheck },
8915
- { value: "not_a_bug", label: "Not a bug", icon: import_lucide11.BugOff },
8916
- { value: "duplicate", label: "Duplicate", icon: import_lucide11.Copy },
8917
- { value: "wont_fix", label: "Won't fix", icon: import_lucide11.Ban }
8918
- ];
8919
- var closeReasonView = {
8920
- id: BUILT_IN_VIEW_IDS.closeReason,
8921
- matches: () => false,
8922
- parent: (ref2) => ref2 ? { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 } : null,
8923
- title: "Close reason",
8924
- searchable: false,
8925
- focusTarget: (host) => host.querySelector("[data-uidex-list-content]"),
8926
- surface: () => ({ kind: "list", id: "unused", items: [] }),
8927
- render(ctx, root) {
8928
- let busy = false;
8929
- const items = REASONS.map((r) => ({
8930
- value: r.value,
8931
- label: r.label,
8932
- leading: leadingIcon(r.icon)
8933
- }));
8934
- const surface = {
8935
- kind: "list",
8936
- id: "uidex-close-reason",
8937
- searchable: false,
8938
- items,
8939
- emptyLabel: "No reasons available",
8940
- onSelect: async (item) => {
8941
- if (busy || !pendingReportId || !ctx.registry.closeReport) return;
8942
- busy = true;
8943
- const row = root.querySelector(
8944
- `[data-uidex-item-value="${item.value}"]`
8945
- );
8946
- const existingTile = row?.querySelector("[data-slot='icon-tile']");
8947
- if (row && existingTile) {
8948
- existingTile.replaceWith(spinnerTile());
8949
- row.style.opacity = "0.6";
8950
- row.style.pointerEvents = "none";
8951
- }
8952
- try {
8953
- await ctx.registry.closeReport(pendingReportId, item.value);
8954
- const done = afterClose;
8955
- pendingReportId = null;
8956
- afterClose = null;
8957
- done?.();
8958
- } catch {
8959
- busy = false;
8960
- if (row) {
8961
- row.style.opacity = "";
8962
- row.style.pointerEvents = "";
8963
- const reason = REASONS.find((r) => r.value === item.value);
8964
- if (reason) {
8965
- const tile = row.querySelector("[data-slot='icon-tile']");
8966
- if (tile)
8967
- tile.replaceWith(
8968
- createIconTile((0, import_lucide11.createElement)(reason.icon))
8969
- );
8970
- }
8971
- }
8972
- const error = document.createElement("p");
8973
- error.className = "text-destructive px-4 py-2 text-xs";
8974
- error.textContent = "Close failed \u2014 try again";
8975
- root.querySelector("[data-uidex-list-content]")?.prepend(error);
8976
- setTimeout(() => error.remove(), 3e3);
8977
- }
8978
- }
8979
- };
8980
- const mounted = renderListSurface(surface, ctx, root);
8981
- return mounted.cleanup;
8982
- }
8983
- };
8984
6821
 
8985
6822
  // src/browser/views/built-in/command-palette.ts
8986
- var import_lucide12 = require("lucide");
6823
+ var import_lucide9 = require("lucide");
8987
6824
 
8988
6825
  // src/browser/views/builder/format-location.ts
8989
6826
  function formatLocation(loc) {
@@ -9022,9 +6859,6 @@ function findCurrentRoute(registry) {
9022
6859
  function findCurrentPageId(ctx) {
9023
6860
  return findCurrentRoute(ctx.registry)?.page ?? null;
9024
6861
  }
9025
- function findCurrentRoutePath(registry) {
9026
- return findCurrentRoute(registry)?.path ?? null;
9027
- }
9028
6862
  function buildPageRouteMap(ctx) {
9029
6863
  const map = /* @__PURE__ */ new Map();
9030
6864
  for (const route of ctx.registry.list("route")) {
@@ -9075,7 +6909,7 @@ function openAction(onSelect) {
9075
6909
  return {
9076
6910
  id: "open",
9077
6911
  label: "Open",
9078
- icon: () => (0, import_lucide12.createElement)(import_lucide12.ExternalLink),
6912
+ icon: () => (0, import_lucide9.createElement)(import_lucide9.ExternalLink),
9079
6913
  perform: onSelect
9080
6914
  };
9081
6915
  }
@@ -9085,7 +6919,7 @@ function favoriteAction(ref2, ctx) {
9085
6919
  id: "toggle-favorite",
9086
6920
  label: isFav ? "Remove from Favorites" : "Add to Favorites",
9087
6921
  shortcut: "\u21E7\u2318F",
9088
- icon: () => (0, import_lucide12.createElement)(isFav ? import_lucide12.StarOff : import_lucide12.Star),
6922
+ icon: () => (0, import_lucide9.createElement)(isFav ? import_lucide9.StarOff : import_lucide9.Star),
9089
6923
  perform: () => {
9090
6924
  ctx.views.toggleFavorite(ref2);
9091
6925
  ctx.close();
@@ -9101,7 +6935,7 @@ function entityActions(ref2, entity, ctx, onSelect) {
9101
6935
  actions.push({
9102
6936
  id: "copy-path",
9103
6937
  label: "Copy Source Path",
9104
- icon: () => (0, import_lucide12.createElement)(import_lucide12.Copy),
6938
+ icon: () => (0, import_lucide9.createElement)(import_lucide9.Copy),
9105
6939
  async perform() {
9106
6940
  try {
9107
6941
  await navigator.clipboard.writeText(path);
@@ -9111,20 +6945,6 @@ function entityActions(ref2, entity, ctx, onSelect) {
9111
6945
  });
9112
6946
  }
9113
6947
  }
9114
- actions.push({
9115
- id: "submit-report",
9116
- label: "Report",
9117
- icon: () => (0, import_lucide12.createElement)(import_lucide12.MessageCircleWarning),
9118
- perform: () => ctx.push({ id: BUILT_IN_VIEW_IDS.report, ref: ref2 })
9119
- });
9120
- if (ctx.cloud?.integrations.getCachedConfig()?.hasJira) {
9121
- actions.push({
9122
- id: "create-jira-issue",
9123
- label: "Create Jira Ticket",
9124
- icon: () => (0, import_lucide12.createElement)(import_lucide12.TicketPlus),
9125
- perform: () => ctx.push({ id: BUILT_IN_VIEW_IDS.jiraReport, ref: ref2 })
9126
- });
9127
- }
9128
6948
  actions.push(favoriteAction(ref2, ctx));
9129
6949
  return actions;
9130
6950
  }
@@ -9157,7 +6977,7 @@ function explorePageRow(totalOnPage, ctx) {
9157
6977
  subtitle: totalOnPage === 1 ? "1 item" : `${totalOnPage} items`,
9158
6978
  group: CURRENT_PAGE_GROUP,
9159
6979
  tag: BUILT_IN_VIEW_IDS.explorePage,
9160
- leading: () => (0, import_lucide12.createElement)(import_lucide12.Compass),
6980
+ leading: () => (0, import_lucide9.createElement)(import_lucide9.Compass),
9161
6981
  actions: viewActions(onSelect),
9162
6982
  payload: { type: "view", id: BUILT_IN_VIEW_IDS.explorePage }
9163
6983
  };
@@ -9222,7 +7042,7 @@ function createCommandPaletteView(shortcut) {
9222
7042
  group: PALETTE_GROUPS.favorites,
9223
7043
  preview: ref2,
9224
7044
  entityChip: { entity },
9225
- leading: () => (0, import_lucide12.createElement)(style.icon),
7045
+ leading: () => (0, import_lucide9.createElement)(style.icon),
9226
7046
  actions: entityActions(ref2, entity, ctx, onSelect),
9227
7047
  payload: { type: "entity", ref: ref2 }
9228
7048
  });
@@ -9247,7 +7067,7 @@ function createCommandPaletteView(shortcut) {
9247
7067
  group: PALETTE_GROUPS.recents,
9248
7068
  preview: ref2,
9249
7069
  entityChip: { entity },
9250
- leading: () => (0, import_lucide12.createElement)(style.icon),
7070
+ leading: () => (0, import_lucide9.createElement)(style.icon),
9251
7071
  actions: entityActions(ref2, entity, ctx, onSelect),
9252
7072
  payload: { type: "entity", ref: ref2 }
9253
7073
  });
@@ -9329,14 +7149,14 @@ async function captureScreenshot(options = {}) {
9329
7149
  }
9330
7150
 
9331
7151
  // src/browser/report/capture.ts
9332
- function captureReportContext(opts) {
7152
+ function captureReportContext() {
9333
7153
  const nav = typeof navigator !== "undefined" ? navigator : void 0;
9334
7154
  const loc = typeof location !== "undefined" ? location : void 0;
9335
7155
  const win = typeof window !== "undefined" ? window : void 0;
9336
7156
  const doc = typeof document !== "undefined" ? document : void 0;
9337
7157
  return {
9338
7158
  url: loc?.href ?? "",
9339
- route: opts?.getRoute?.() ?? loc?.pathname,
7159
+ route: loc?.pathname,
9340
7160
  userAgent: nav?.userAgent ?? "",
9341
7161
  pageTitle: doc?.title,
9342
7162
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -9388,46 +7208,6 @@ function copyPathAction(ref2, loc) {
9388
7208
  intent: { kind: "external", describe: "clipboard" }
9389
7209
  };
9390
7210
  }
9391
- function reportAction(ref2) {
9392
- return {
9393
- id: "submit-report",
9394
- label: "Report",
9395
- icon: "message-circle-warning",
9396
- push: { id: BUILT_IN_VIEW_IDS.report, ref: ref2 },
9397
- intent: {
9398
- kind: "push",
9399
- viewId: BUILT_IN_VIEW_IDS.report,
9400
- refKind: ref2.kind
9401
- }
9402
- };
9403
- }
9404
- function viewReportsAction(ref2, count) {
9405
- return {
9406
- id: "view-reports",
9407
- label: "View Reports",
9408
- icon: "inbox",
9409
- hint: count != null ? count === 1 ? "1 item" : `${count} items` : void 0,
9410
- push: { id: BUILT_IN_VIEW_IDS.entityReports, ref: ref2 },
9411
- intent: {
9412
- kind: "push",
9413
- viewId: BUILT_IN_VIEW_IDS.entityReports,
9414
- refKind: ref2.kind
9415
- }
9416
- };
9417
- }
9418
- function jiraAction(ref2) {
9419
- return {
9420
- id: "create-jira-issue",
9421
- label: "Create Jira Ticket",
9422
- icon: "ticket-plus",
9423
- push: { id: BUILT_IN_VIEW_IDS.jiraReport, ref: ref2 },
9424
- intent: {
9425
- kind: "push",
9426
- viewId: BUILT_IN_VIEW_IDS.jiraReport,
9427
- refKind: ref2.kind
9428
- }
9429
- };
9430
- }
9431
7211
  function buildSnapshotMarkdown(ref2, loc) {
9432
7212
  const snapshot = captureReportContext();
9433
7213
  const sourcePath = formatLocation(loc);
@@ -9518,18 +7298,6 @@ function createEntityDetailView(config) {
9518
7298
  const metaEntity = entity ? entity : null;
9519
7299
  const meta = metaEntity?.meta;
9520
7300
  const actions = [];
9521
- const cloud = ctx.cloud;
9522
- actions.push({ ...reportAction(ctx.ref), group: "Report" });
9523
- const reportCount = ctx.registry.getReports(kind, ctx.ref.id).length;
9524
- if (reportCount > 0) {
9525
- actions.push({
9526
- ...viewReportsAction(ctx.ref, reportCount),
9527
- group: "Report"
9528
- });
9529
- }
9530
- if (cloud?.integrations.getCachedConfig()?.hasJira) {
9531
- actions.push({ ...jiraAction(ctx.ref), group: "Report" });
9532
- }
9533
7301
  if (DOM_BACKED_KINDS.has(kind) && resolveEntityElement(ctx.ref)) {
9534
7302
  actions.push({ ...highlightElementAction(ctx.ref), group: "Inspect" });
9535
7303
  actions.push({ ...copyScreenshotAction(ctx.ref), group: "Inspect" });
@@ -9715,157 +7483,8 @@ var primitivesView = createEntityKindListView(
9715
7483
  BUILT_IN_VIEW_IDS.primitives
9716
7484
  );
9717
7485
 
9718
- // src/browser/views/built-in/report-detail.ts
9719
- var selectedId = null;
9720
- function setSelectedReportId(id) {
9721
- selectedId = id;
9722
- }
9723
- function findReport(ctx) {
9724
- if (!ctx.ref || !selectedId) return null;
9725
- const reports = ctx.registry.getReports(ctx.ref.kind, ctx.ref.id);
9726
- return reports.find((r) => r.id === selectedId) ?? null;
9727
- }
9728
- var reportDetailView = {
9729
- id: BUILT_IN_VIEW_IDS.reportDetail,
9730
- matches: () => false,
9731
- parent: (ref2) => ({ id: BUILT_IN_VIEW_IDS.entityReports, ref: ref2 }),
9732
- searchable: false,
9733
- focusTarget: (host) => host.querySelector(
9734
- "[data-uidex-detail-actions] [data-uidex-detail-action]"
9735
- ),
9736
- surface: (ctx) => {
9737
- const report = findReport(ctx);
9738
- if (!report) {
9739
- return {
9740
- kind: "detail",
9741
- entityKind: ctx.ref?.kind ?? "element",
9742
- notFound: ctx.ref ?? void 0
9743
- };
9744
- }
9745
- const typeLabel = TYPE_LABELS[report.type] ?? report.type;
9746
- const sevLabel = SEVERITY_LABELS[report.severity] ?? report.severity;
9747
- const sections = [];
9748
- if (report.body) {
9749
- sections.push({ id: "description", text: report.body });
9750
- }
9751
- if (report.screenshot) {
9752
- sections.push({ id: "screenshot", url: report.screenshot });
9753
- } else {
9754
- const pins = ctx.cloud?.pins;
9755
- const fetchScreenshot = pins?.screenshot;
9756
- if (pins && fetchScreenshot) {
9757
- sections.push({
9758
- id: "screenshot",
9759
- load: () => fetchScreenshot.call(pins, report.id)
9760
- });
9761
- }
9762
- }
9763
- const metaEntries = [];
9764
- if (report.url) metaEntries.push({ label: "URL", value: report.url });
9765
- if (report.route) metaEntries.push({ label: "Route", value: report.route });
9766
- if (report.pageTitle)
9767
- metaEntries.push({ label: "Page", value: report.pageTitle });
9768
- if (metaEntries.length > 0) {
9769
- sections.push({ id: "metadata", entries: metaEntries });
9770
- }
9771
- const actions = [];
9772
- if (ctx.registry.closeReport) {
9773
- actions.push({
9774
- id: "close",
9775
- label: "Close",
9776
- icon: "archive-x",
9777
- run: () => {
9778
- setCloseTarget(report.id, () => ctx.pop());
9779
- ctx.push({ id: BUILT_IN_VIEW_IDS.closeReason });
9780
- }
9781
- });
9782
- }
9783
- if (report.url) {
9784
- actions.push({
9785
- id: "open-url",
9786
- label: "Open URL",
9787
- icon: "copy",
9788
- copy: report.url
9789
- });
9790
- }
9791
- return {
9792
- kind: "detail",
9793
- entityKind: ctx.ref?.kind ?? "element",
9794
- title: report.title || "(no title)",
9795
- subtitle: {
9796
- rawId: [
9797
- typeLabel,
9798
- sevLabel,
9799
- authorLabel(report),
9800
- relativeTime(report.createdAt)
9801
- ].filter(Boolean).join(" \xB7 ")
9802
- },
9803
- actions,
9804
- sections
9805
- };
9806
- }
9807
- };
9808
-
9809
- // src/browser/views/built-in/entity-reports.ts
9810
- function reportToItem(report, ctx) {
9811
- const typeLabel = TYPE_LABELS[report.type] ?? report.type;
9812
- const sevLabel = SEVERITY_LABELS[report.severity] ?? report.severity;
9813
- const raw = report.title || report.body;
9814
- const label = raw.length > 80 ? raw.slice(0, 80) + "\u2026" : raw;
9815
- const kind = ctx.ref?.kind ?? "element";
9816
- const actions = [];
9817
- if (ctx.registry.closeReport) {
9818
- actions.push({
9819
- id: `close-${report.id}`,
9820
- label: "Close",
9821
- perform: () => {
9822
- setCloseTarget(report.id, () => ctx.pop());
9823
- ctx.push({ id: BUILT_IN_VIEW_IDS.closeReason });
9824
- }
9825
- });
9826
- }
9827
- return {
9828
- value: `report:${report.id}`,
9829
- label: label || "(no description)",
9830
- subtitle: [
9831
- authorLabel(report),
9832
- typeLabel,
9833
- sevLabel,
9834
- relativeTime(report.createdAt)
9835
- ].filter(Boolean).join(" \xB7 "),
9836
- tag: `report:${report.id}`,
9837
- leading: () => renderKindIcon(kind),
9838
- actions
9839
- };
9840
- }
9841
- var entityReportsView = {
9842
- id: BUILT_IN_VIEW_IDS.entityReports,
9843
- matches: () => false,
9844
- parent: parentDetail,
9845
- title: "Reports",
9846
- searchable: true,
9847
- hints: [{ key: "\u21B5", label: "Select" }],
9848
- focusTarget: () => null,
9849
- surface: (ctx) => {
9850
- const ref2 = ctx.ref;
9851
- const reports = ref2 ? ctx.registry.getReports(ref2.kind, ref2.id) : [];
9852
- const items = reports.map((r) => reportToItem(r, ctx));
9853
- return {
9854
- kind: "list",
9855
- id: "uidex-entity-reports",
9856
- items,
9857
- emptyLabel: "No reports for this element",
9858
- filter: (item, query) => matchesQuery(`${item.label} ${item.subtitle ?? ""}`, query),
9859
- onSelect: (item) => {
9860
- setSelectedReportId(item.value.replace(/^report:/, ""));
9861
- ctx.push({ id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 });
9862
- }
9863
- };
9864
- }
9865
- };
9866
-
9867
7486
  // src/browser/views/built-in/explore-page.ts
9868
- var import_lucide13 = require("lucide");
7487
+ var import_lucide10 = require("lucide");
9869
7488
  var KIND_ORDER = new Map(
9870
7489
  ENTITY_KINDS.map((kind, index) => [kind, index])
9871
7490
  );
@@ -9907,7 +7526,7 @@ var explorePageView = {
9907
7526
  palette: {
9908
7527
  label: "Explore Page",
9909
7528
  shortcut: "",
9910
- icon: () => (0, import_lucide13.createElement)(import_lucide13.Compass)
7529
+ icon: () => (0, import_lucide10.createElement)(import_lucide10.Compass)
9911
7530
  },
9912
7531
  title: "Explore Page",
9913
7532
  hints: [{ key: "\u21B5", label: "Select" }],
@@ -9942,332 +7561,6 @@ var explorePageView = {
9942
7561
  }
9943
7562
  };
9944
7563
 
9945
- // src/browser/internal/clipboard.ts
9946
- async function copyToClipboard(text) {
9947
- try {
9948
- if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
9949
- await navigator.clipboard.writeText(text);
9950
- return true;
9951
- }
9952
- } catch {
9953
- }
9954
- try {
9955
- if (typeof document === "undefined") return false;
9956
- const textarea = document.createElement("textarea");
9957
- textarea.value = text;
9958
- textarea.setAttribute("readonly", "");
9959
- textarea.style.position = "fixed";
9960
- textarea.style.opacity = "0";
9961
- document.body.appendChild(textarea);
9962
- textarea.select();
9963
- const ok = document.execCommand("copy");
9964
- document.body.removeChild(textarea);
9965
- return ok;
9966
- } catch {
9967
- return false;
9968
- }
9969
- }
9970
-
9971
- // src/browser/views/built-in/report/markdown.ts
9972
- function capitalize2(s) {
9973
- return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
9974
- }
9975
- function renderPayloadMarkdown(payload) {
9976
- const heading = payload.title ?? `${capitalize2(payload.type ?? "")} Report`;
9977
- const ctx = payload.context;
9978
- const lines = [
9979
- `# ${heading}`,
9980
- "",
9981
- `- **Type:** ${payload.type}`,
9982
- ...payload.severity ? [`- **Severity:** ${payload.severity}`] : [],
9983
- `- **Entity:** \`${payload.entity}\``,
9984
- "",
9985
- "#### Description",
9986
- payload.body || "_(no description)_",
9987
- "",
9988
- "#### Context",
9989
- `- **URL:** ${ctx?.url}`,
9990
- ...ctx?.pageTitle ? [`- **Page title:** ${ctx.pageTitle}`] : [],
9991
- `- **Viewport:** ${ctx?.viewport.w}\xD7${ctx?.viewport.h}`,
9992
- `- **User agent:** ${ctx?.userAgent}`,
9993
- `- **Timestamp:** ${ctx?.timestamp}`
9994
- ];
9995
- return lines.join("\n") + "\n";
9996
- }
9997
-
9998
- // src/browser/views/built-in/report/schema.ts
9999
- var import_zod = require("zod");
10000
- var reportSchema = import_zod.z.object({
10001
- type: import_zod.z.enum(["bug", "request", "suggestion"]),
10002
- severity: import_zod.z.enum(["low", "medium", "high", "critical"]).optional(),
10003
- title: import_zod.z.string().optional(),
10004
- body: import_zod.z.string().trim().min(1, { message: "Description is required." })
10005
- });
10006
- var reportFields = [
10007
- {
10008
- kind: "select",
10009
- name: "type",
10010
- label: "Type",
10011
- options: [
10012
- { value: "bug", label: "Bug" },
10013
- { value: "request", label: "Request" },
10014
- { value: "suggestion", label: "Suggestion" }
10015
- ]
10016
- },
10017
- {
10018
- kind: "select",
10019
- name: "severity",
10020
- label: "Severity",
10021
- value: "medium",
10022
- options: [
10023
- { value: "low", label: "Low" },
10024
- { value: "medium", label: "Medium" },
10025
- { value: "high", label: "High" },
10026
- { value: "critical", label: "Critical" }
10027
- ]
10028
- },
10029
- {
10030
- kind: "text",
10031
- name: "title",
10032
- label: "Title",
10033
- placeholder: "Short summary (optional)"
10034
- },
10035
- {
10036
- kind: "textarea",
10037
- name: "body",
10038
- label: "Description",
10039
- placeholder: "Describe what happened\u2026",
10040
- required: true
10041
- }
10042
- ];
10043
-
10044
- // src/browser/views/built-in/report/view-builder.ts
10045
- var KIND_TO_ATTR = new Map(
10046
- UIDEX_ATTR_TO_KIND.map(([attr, kind]) => [kind, attr])
10047
- );
10048
- function resolveElement(ref2) {
10049
- const attr = KIND_TO_ATTR.get(ref2.kind);
10050
- if (!attr) return null;
10051
- return document.querySelector(
10052
- `[${attr}="${CSS.escape(ref2.id)}"]`
10053
- );
10054
- }
10055
- function buildPayload(ctx, componentId, values, prefix, augmentPayload, screenshot) {
10056
- const context = captureReportContext({ getRoute: ctx.getRoute });
10057
- const title = String(values.title ?? "").trim();
10058
- const body = String(values.body ?? "").trim();
10059
- const reporter = ctx.user ? { id: ctx.user.id, name: ctx.user.name || void 0 } : void 0;
10060
- const payload = {
10061
- type: values.type ?? "bug",
10062
- severity: values.severity ?? "medium",
10063
- title: title || void 0,
10064
- body: prefix ? `${prefix}${body}` : body,
10065
- entity: componentId,
10066
- screenshot,
10067
- context,
10068
- reporter,
10069
- metadata: ctx.user ? { userId: ctx.user.id } : void 0
10070
- };
10071
- return augmentPayload ? augmentPayload(payload, ctx, values) : payload;
10072
- }
10073
- async function copyFallback(payload) {
10074
- const markdown = renderPayloadMarkdown(payload);
10075
- const copied = await copyToClipboard(markdown);
10076
- if (!copied) console.log("[uidex] report markdown:\n" + markdown);
10077
- return {
10078
- status: copied ? "success" : "error",
10079
- message: copied ? "Copied to clipboard" : "Copy failed"
10080
- };
10081
- }
10082
- function createReportView(config) {
10083
- const {
10084
- id,
10085
- resolveCloud,
10086
- componentId,
10087
- submitLabels,
10088
- successToast,
10089
- failureToast,
10090
- prependDescription,
10091
- baseFields,
10092
- extraFields,
10093
- augmentPayload,
10094
- successResult,
10095
- schema: schemaOverride
10096
- } = config;
10097
- return {
10098
- id,
10099
- matches: () => false,
10100
- parent: parentDetail,
10101
- searchable: false,
10102
- hints: (ctx) => [
10103
- {
10104
- key: "\u2318\u21B5",
10105
- label: resolveCloud(ctx) ? submitLabels.ready : submitLabels.noCloud
10106
- }
10107
- ],
10108
- focusTarget: (host) => host.querySelector(
10109
- "[data-uidex-form='report'] select, [data-uidex-form='report'] input, [data-uidex-form='report'] textarea"
10110
- ),
10111
- surface: (ctx) => {
10112
- const cloud = resolveCloud(ctx);
10113
- const extra = extraFields ? extraFields(ctx) : [];
10114
- const fields = [...extra, ...baseFields ?? reportFields];
10115
- const isDomBacked = ctx.ref != null && DOM_BACKED_KINDS.has(ctx.ref.kind);
10116
- const targetEl = ctx.ref && isDomBacked ? resolveElement(ctx.ref) : null;
10117
- const screenshotPromise = isDomBacked ? captureScreenshot({
10118
- target: targetEl ?? void 0,
10119
- maxWidth: 1280
10120
- }).catch(() => null) : null;
10121
- return {
10122
- kind: "form",
10123
- id: "report",
10124
- fields,
10125
- schema: schemaOverride ?? reportSchema,
10126
- screenshotPreview: screenshotPromise ? screenshotPromise : void 0,
10127
- submit: {
10128
- label: cloud ? submitLabels.ready : submitLabels.noCloud,
10129
- onSubmit: async (values) => {
10130
- const screenshot = await screenshotPromise ?? void 0;
10131
- const prefix = prependDescription ? prependDescription() : "";
10132
- const payload = buildPayload(
10133
- ctx,
10134
- componentId(ctx),
10135
- values,
10136
- prefix,
10137
- augmentPayload,
10138
- screenshot
10139
- );
10140
- if (!cloud) {
10141
- return copyFallback(payload);
10142
- }
10143
- try {
10144
- const result = await cloud.reports.submit(
10145
- payload
10146
- );
10147
- ctx.onAfterSubmit?.();
10148
- if (successResult) return successResult(result);
10149
- return {
10150
- status: "success",
10151
- message: successToast(result)
10152
- };
10153
- } catch (err) {
10154
- const message = err instanceof Error ? err.message : String(err);
10155
- return {
10156
- status: "error",
10157
- message: failureToast(err) ?? message
10158
- };
10159
- }
10160
- }
10161
- }
10162
- };
10163
- }
10164
- };
10165
- }
10166
-
10167
- // src/browser/views/built-in/report/host-report.ts
10168
- var reportView = createReportView({
10169
- id: BUILT_IN_VIEW_IDS.report,
10170
- resolveCloud: (ctx) => ctx.cloud,
10171
- componentId: (ctx) => ctx.ref ? `${ctx.ref.kind}:${ctx.ref.id}` : "unknown",
10172
- submitLabels: { ready: "Report", noCloud: "Copy Report" },
10173
- successToast: (result) => {
10174
- const ticketKey = result.externalLink?.ok && result.externalLink.key ? result.externalLink.key : null;
10175
- return ticketKey ? `Report submitted (${ticketKey})` : "Report submitted";
10176
- },
10177
- failureToast: (err) => {
10178
- const message = err instanceof Error ? err.message : String(err);
10179
- return `Failed: ${message}`;
10180
- }
10181
- });
10182
-
10183
- // src/browser/views/built-in/report/jira-report.ts
10184
- var jiraSchema = reportSchema.omit({ type: true });
10185
- var jiraBaseFields = reportFields.filter(
10186
- (f) => f.name !== "type" && f.name !== "severity"
10187
- );
10188
- function buildParentIssueField(config) {
10189
- const issues = config.parentIssues ?? [];
10190
- const options = [
10191
- { value: "", label: "None" }
10192
- ];
10193
- const byType = /* @__PURE__ */ new Map();
10194
- for (const issue of issues) {
10195
- const group = byType.get(issue.issueType) ?? [];
10196
- group.push(issue);
10197
- byType.set(issue.issueType, group);
10198
- }
10199
- for (const [type, group] of byType) {
10200
- for (const issue of group) {
10201
- options.push({
10202
- value: issue.key,
10203
- label: `[${type}] ${issue.key}: ${issue.summary}`
10204
- });
10205
- }
10206
- }
10207
- return {
10208
- kind: "select",
10209
- name: "parentIssue",
10210
- label: "Parent Issue",
10211
- options
10212
- };
10213
- }
10214
- var jiraReportView = createReportView({
10215
- id: BUILT_IN_VIEW_IDS.jiraReport,
10216
- resolveCloud: (ctx) => ctx.cloud,
10217
- componentId: (ctx) => ctx.ref ? `${ctx.ref.kind}:${ctx.ref.id}` : "unknown",
10218
- submitLabels: { ready: "Create Jira Ticket", noCloud: "Copy report" },
10219
- schema: jiraSchema,
10220
- baseFields: jiraBaseFields,
10221
- extraFields: (ctx) => {
10222
- const cloud = ctx.cloud;
10223
- const config = cloud?.integrations.getCachedConfig() ?? null;
10224
- if (!config?.hasJira) return [];
10225
- return [buildParentIssueField(config)];
10226
- },
10227
- augmentPayload: (payload, ctx, values) => {
10228
- const cloud = ctx.cloud;
10229
- const config = cloud?.integrations.getCachedConfig() ?? null;
10230
- if (!config?.hasJira || !config.integrationId) return payload;
10231
- const targetConfig = {};
10232
- const parentKey = values.parentIssue;
10233
- if (parentKey) {
10234
- const issue = config.parentIssues?.find((i) => i.key === parentKey);
10235
- if (issue) targetConfig.epicId = issue.id;
10236
- }
10237
- return {
10238
- ...payload,
10239
- type: "bug",
10240
- suggestedTarget: {
10241
- integrationId: config.integrationId,
10242
- targetConfig
10243
- }
10244
- };
10245
- },
10246
- successResult: (result) => {
10247
- const link = result.externalLink;
10248
- if (link?.ok && link.key) {
10249
- return {
10250
- status: "success",
10251
- message: `Created ${link.key}`,
10252
- link: link.url ? { url: link.url, label: link.key } : void 0
10253
- };
10254
- }
10255
- return {
10256
- status: "error",
10257
- message: "Report saved \u2014 Jira issue creation failed"
10258
- };
10259
- },
10260
- successToast: (result) => {
10261
- const link = result.externalLink;
10262
- if (link?.ok && link.key) return `Created ${link.key}`;
10263
- return "Report saved \u2014 Jira Bug failed";
10264
- },
10265
- failureToast: (err) => {
10266
- const message = err instanceof Error ? err.message : String(err);
10267
- return `Failed to create issue: ${message}`;
10268
- }
10269
- });
10270
-
10271
7564
  // src/browser/views/built-in/flow-detail.ts
10272
7565
  var STEP_KINDS = [
10273
7566
  "element",
@@ -10299,8 +7592,7 @@ var flowDetailView = {
10299
7592
  }
10300
7593
  const actions = [
10301
7594
  copyPathAction(ctx.ref, flow.loc),
10302
- copySnapshotAction(ctx.ref, flow.loc),
10303
- reportAction(ctx.ref)
7595
+ copySnapshotAction(ctx.ref, flow.loc)
10304
7596
  ];
10305
7597
  const sections = [
10306
7598
  flow.steps.length > 0 ? buildStepsSection(flow.steps, ctx.registry) : buildTouchesSection(flow, ctx.registry)
@@ -10390,7 +7682,7 @@ var flowsView = {
10390
7682
  };
10391
7683
 
10392
7684
  // src/browser/views/built-in/glossary.ts
10393
- var import_lucide14 = require("lucide");
7685
+ var import_lucide11 = require("lucide");
10394
7686
  var KIND_TO_VIEW = {
10395
7687
  element: BUILT_IN_VIEW_IDS.elements,
10396
7688
  widget: BUILT_IN_VIEW_IDS.widgets,
@@ -10427,7 +7719,7 @@ var glossaryView = {
10427
7719
  palette: {
10428
7720
  label: "Glossary",
10429
7721
  group: PALETTE_GROUPS.commands,
10430
- icon: () => (0, import_lucide14.createElement)(import_lucide14.BookOpen)
7722
+ icon: () => (0, import_lucide11.createElement)(import_lucide11.BookOpen)
10431
7723
  },
10432
7724
  title: "Glossary",
10433
7725
  hints: [{ key: "\u21B5", label: "Select" }],
@@ -10456,240 +7748,9 @@ var glossaryView = {
10456
7748
  }
10457
7749
  };
10458
7750
 
10459
- // src/browser/views/built-in/page-reports.ts
10460
- var import_lucide15 = require("lucide");
10461
- function collectPageReports(ctx) {
10462
- const items = [];
10463
- const seenReportIds = /* @__PURE__ */ new Set();
10464
- for (const kind of ENTITY_KINDS) {
10465
- for (const entity of ctx.registry.list(kind)) {
10466
- const id = "id" in entity ? entity.id : "";
10467
- if (!id) continue;
10468
- const reports = ctx.registry.getReports(kind, id);
10469
- seenReportIds.add(`${kind}:${id}`);
10470
- const ref2 = { kind, id };
10471
- for (const report of reports) {
10472
- const typeLabel = TYPE_LABELS[report.type] ?? report.type;
10473
- const sevLabel = SEVERITY_LABELS[report.severity] ?? report.severity;
10474
- const raw = report.title || report.body;
10475
- const label = raw.length > 80 ? raw.slice(0, 80) + "\u2026" : raw;
10476
- const entityName = displayName(entity);
10477
- items.push({
10478
- value: `report:${report.id}`,
10479
- label: label || "(no description)",
10480
- subtitle: [
10481
- entityName,
10482
- authorLabel(report),
10483
- typeLabel,
10484
- sevLabel,
10485
- relativeTime(report.createdAt)
10486
- ].filter(Boolean).join(" \xB7 "),
10487
- tag: `report:${report.id}`,
10488
- group: `${kind}: ${entityName}`,
10489
- leading: () => renderKindIcon(kind),
10490
- ref: ref2
10491
- });
10492
- }
10493
- }
10494
- }
10495
- for (const key of ctx.registry.listReportKeys()) {
10496
- if (seenReportIds.has(key)) continue;
10497
- const parsed = parseComponentRef(key);
10498
- if (ctx.registry.get(parsed.kind, parsed.id)) continue;
10499
- const reports = ctx.registry.getReports(parsed.kind, parsed.id);
10500
- const ref2 = parsed;
10501
- for (const report of reports) {
10502
- const typeLabel = TYPE_LABELS[report.type] ?? report.type;
10503
- const sevLabel = SEVERITY_LABELS[report.severity] ?? report.severity;
10504
- const raw = report.title || report.body;
10505
- const label = raw.length > 80 ? raw.slice(0, 80) + "\u2026" : raw;
10506
- items.push({
10507
- value: `report:${report.id}`,
10508
- label: label || "(no description)",
10509
- subtitle: [
10510
- `${parsed.kind}:${parsed.id}`,
10511
- authorLabel(report),
10512
- typeLabel,
10513
- sevLabel,
10514
- relativeTime(report.createdAt)
10515
- ].filter(Boolean).join(" \xB7 "),
10516
- tag: `report:${report.id}`,
10517
- group: "Orphaned",
10518
- leading: () => (0, import_lucide15.createElement)(import_lucide15.CircleOff),
10519
- ref: ref2
10520
- });
10521
- }
10522
- }
10523
- return items;
10524
- }
10525
- var pageReportsView = {
10526
- id: BUILT_IN_VIEW_IDS.pageReports,
10527
- matches: () => false,
10528
- parent: () => COMMAND_PALETTE_ENTRY2,
10529
- palette: {
10530
- label: "Page Reports",
10531
- group: "Current Page",
10532
- icon: () => (0, import_lucide15.createElement)(import_lucide15.Inbox)
10533
- },
10534
- title: "Page Reports",
10535
- searchable: true,
10536
- hints: [{ key: "\u21B5", label: "Select" }],
10537
- focusTarget: () => null,
10538
- surface: (ctx) => {
10539
- const items = collectPageReports(ctx);
10540
- return {
10541
- kind: "list",
10542
- id: "uidex-page-reports",
10543
- items,
10544
- emptyLabel: "No reports on this page",
10545
- filter: (item, query) => matchesQuery(
10546
- `${item.label} ${item.subtitle ?? ""} ${item.group ?? ""}`,
10547
- query
10548
- ),
10549
- onSelect: (item) => {
10550
- setSelectedReportId(item.value.replace(/^report:/, ""));
10551
- ctx.push({
10552
- id: BUILT_IN_VIEW_IDS.reportDetail,
10553
- ref: item.ref
10554
- });
10555
- }
10556
- };
10557
- }
10558
- };
10559
-
10560
- // src/browser/views/built-in/pin-settings.ts
10561
- var import_lucide16 = require("lucide");
10562
-
10563
- // src/browser/ui/toast.ts
10564
- function showCopiedToast(message) {
10565
- if (typeof document === "undefined") return;
10566
- const dark = isDarkMode();
10567
- const toast = document.createElement("div");
10568
- toast.setAttribute("data-uidex-toast", "");
10569
- Object.assign(toast.style, {
10570
- position: "fixed",
10571
- top: "50%",
10572
- left: "50%",
10573
- transform: "translate(-50%, -50%) scale(0.96)",
10574
- padding: "12px 20px",
10575
- fontSize: "14px",
10576
- fontFamily: "ui-sans-serif, system-ui, sans-serif",
10577
- fontWeight: "500",
10578
- color: dark ? "#0a0a0a" : "#fafafa",
10579
- background: dark ? "rgba(255, 255, 255, 0.98)" : "rgba(28, 28, 30, 0.95)",
10580
- border: dark ? "1px solid rgba(0, 0, 0, 0.08)" : "1px solid rgba(255, 255, 255, 0.08)",
10581
- borderRadius: "10px",
10582
- boxShadow: dark ? "0 8px 24px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.2)" : "0 8px 24px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.25)",
10583
- zIndex: "2147483647",
10584
- pointerEvents: "none",
10585
- opacity: "0",
10586
- transition: "opacity 120ms ease-out, transform 160ms ease-out",
10587
- whiteSpace: "nowrap"
10588
- });
10589
- toast.textContent = message;
10590
- document.body.appendChild(toast);
10591
- requestAnimationFrame(() => {
10592
- toast.style.opacity = "1";
10593
- toast.style.transform = "translate(-50%, -50%) scale(1)";
10594
- });
10595
- setTimeout(() => {
10596
- toast.style.opacity = "0";
10597
- toast.style.transform = "translate(-50%, -50%) scale(0.96)";
10598
- setTimeout(() => toast.remove(), 200);
10599
- }, 1200);
10600
- }
10601
-
10602
- // src/browser/views/built-in/pin-settings.ts
10603
- var MATCH_MODE_KEY = "uidex:pin-match-mode";
10604
- function readMode() {
10605
- try {
10606
- const v = localStorage.getItem(MATCH_MODE_KEY);
10607
- if (v === "route" || v === "pathname" || v === "component") return v;
10608
- } catch {
10609
- }
10610
- return "route";
10611
- }
10612
- function writeMode(mode) {
10613
- try {
10614
- localStorage.setItem(MATCH_MODE_KEY, mode);
10615
- } catch {
10616
- }
10617
- }
10618
- var MODES = [
10619
- {
10620
- value: "route",
10621
- label: "By route",
10622
- description: "Pins from any URL matching this route pattern",
10623
- icon: import_lucide16.Globe
10624
- },
10625
- {
10626
- value: "pathname",
10627
- label: "By pathname",
10628
- description: "Pins from this exact URL path",
10629
- icon: import_lucide16.MapPin
10630
- },
10631
- {
10632
- value: "component",
10633
- label: "By component",
10634
- description: "All pins for components on this page",
10635
- icon: import_lucide16.Settings
10636
- }
10637
- ];
10638
- function leadingIcon2(iconNode, active) {
10639
- return () => {
10640
- const svg2 = (0, import_lucide16.createElement)(active ? import_lucide16.Check : iconNode);
10641
- svg2.setAttribute("aria-hidden", "true");
10642
- return createIconTile(svg2);
10643
- };
10644
- }
10645
- var pinSettingsView = {
10646
- id: BUILT_IN_VIEW_IDS.pinSettings,
10647
- matches: () => false,
10648
- parent: () => COMMAND_PALETTE_ENTRY2,
10649
- palette: {
10650
- label: "Pin settings",
10651
- group: PALETTE_GROUPS.commands,
10652
- icon: () => (0, import_lucide16.createElement)(import_lucide16.Settings)
10653
- },
10654
- title: "Pin settings",
10655
- searchable: false,
10656
- focusTarget: (host) => host.querySelector("[data-uidex-list-content]"),
10657
- surface: (ctx) => {
10658
- const current = readMode();
10659
- const items = MODES.map((m) => {
10660
- const active = m.value === current;
10661
- return {
10662
- value: m.value,
10663
- label: m.label,
10664
- subtitle: m.description,
10665
- leading: leadingIcon2(m.icon, active),
10666
- trailing: active ? import_lit_html2.html`<span class="text-muted-foreground ms-auto text-xs font-medium"
10667
- >Active</span
10668
- >` : void 0
10669
- };
10670
- });
10671
- return {
10672
- kind: "list",
10673
- id: "uidex-pin-settings",
10674
- searchable: false,
10675
- items,
10676
- emptyLabel: "No options available",
10677
- onSelect: (item) => {
10678
- const mode = item.value;
10679
- writeMode(mode);
10680
- ctx.onAfterSubmit?.();
10681
- const modeLabel = MODES.find((m) => m.value === mode).label;
10682
- showCopiedToast(`Pin mode set to "${modeLabel}"`);
10683
- ctx.pop();
10684
- }
10685
- };
10686
- }
10687
- };
10688
-
10689
7751
  // src/browser/views/built-in/index.ts
10690
7752
  function buildDefaultViews(shortcut) {
10691
7753
  return [
10692
- closeReasonView,
10693
7754
  createCommandPaletteView(shortcut),
10694
7755
  explorePageView,
10695
7756
  componentDetailView,
@@ -10706,43 +7767,15 @@ function buildDefaultViews(shortcut) {
10706
7767
  primitivesView,
10707
7768
  primitiveDetailView,
10708
7769
  regionDetailView,
10709
- entityReportsView,
10710
- reportView,
10711
- jiraReportView,
10712
- glossaryView,
10713
- pageReportsView,
10714
- pinSettingsView,
10715
- reportDetailView
7770
+ glossaryView
10716
7771
  ];
10717
7772
  }
10718
7773
 
10719
7774
  // src/browser/create-uidex.ts
10720
- function getVisibleEntities() {
10721
- if (typeof document === "undefined") return [];
10722
- const selector = UIDEX_ATTR_TO_KIND.map(([a]) => `[${a}]`).join(",");
10723
- const nodes = document.querySelectorAll(selector);
10724
- const ids = [];
10725
- const seen = /* @__PURE__ */ new Set();
10726
- for (const node of nodes) {
10727
- if (node.closest(SURFACE_IGNORE_SELECTOR)) continue;
10728
- for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
10729
- const id = node.getAttribute(attr);
10730
- if (!id) continue;
10731
- const key = `${kind}:${id}`;
10732
- if (!seen.has(key)) {
10733
- seen.add(key);
10734
- ids.push(key);
10735
- }
10736
- break;
10737
- }
10738
- }
10739
- return ids;
10740
- }
10741
7775
  function createUidex(options = {}) {
10742
7776
  const registry = createRegistry();
10743
7777
  const inspectorRef = { current: null };
10744
7778
  const overlayRef = { current: null };
10745
- const pinLayerRef = { current: null };
10746
7779
  const applyOverlay = (ctx) => {
10747
7780
  const overlay = overlayRef.current;
10748
7781
  if (!overlay) return;
@@ -10762,7 +7795,6 @@ function createUidex(options = {}) {
10762
7795
  const session = createSession({
10763
7796
  theme: options.theme,
10764
7797
  resolvedTheme: options.resolvedTheme,
10765
- user: options.user,
10766
7798
  onMountInspector: () => inspectorRef.current?.mount(),
10767
7799
  onDestroyInspector: () => inspectorRef.current?.destroy(),
10768
7800
  onShowOverlay: applyOverlay,
@@ -10770,9 +7802,6 @@ function createUidex(options = {}) {
10770
7802
  onUpdateOverlay: applyOverlay
10771
7803
  });
10772
7804
  const views = createRouter({ session });
10773
- const cloud = options.cloud ?? null;
10774
- const ingestOpts = resolveIngestOptions(options.ingest, cloud !== null);
10775
- const ingest = ingestOpts ? createIngest(ingestOpts) : null;
10776
7805
  if (options.defaultViews !== false) {
10777
7806
  for (const view of buildDefaultViews(options.shortcut)) views.add(view);
10778
7807
  }
@@ -10782,111 +7811,12 @@ function createUidex(options = {}) {
10782
7811
  let shadowRoot = null;
10783
7812
  const mountCleanup = createCleanupStack();
10784
7813
  let mounted = false;
10785
- function syncReportsToRegistry() {
10786
- const layer = pinLayerRef.current;
10787
- if (!layer) return;
10788
- const byEntity = /* @__PURE__ */ new Map();
10789
- for (const pin of layer.getAllPins()) {
10790
- const cid = pin.entity ?? "";
10791
- if (!cid) continue;
10792
- const list = byEntity.get(cid);
10793
- if (list) list.push(pin);
10794
- else byEntity.set(cid, [pin]);
10795
- }
10796
- for (const [cid, pins] of byEntity) {
10797
- const ref2 = parseComponentRef(cid);
10798
- registry.setReports(ref2.kind, ref2.id, pins);
10799
- }
10800
- }
10801
- function setupKeyBindings(root, viewStack) {
10802
- const bindings = bindShadowKeys({
10803
- shadowRoot: root,
10804
- session,
10805
- getActionsPopup: () => viewStack.getActionsPopup(),
10806
- getPinLayer: () => pinLayerRef.current,
10807
- shortcut: options.shortcut
10808
- });
10809
- mountCleanup.add(bindings);
10810
- return bindings;
10811
- }
10812
- function setupPinLayer(root, shell, channel, getCurrentRoute, getMatchMode, getPathname) {
10813
- if (cloud) {
10814
- registry.closeReport = async (reportId, status) => {
10815
- pinLayerRef.current?.removePin(reportId);
10816
- await cloud.pins.close(
10817
- reportId,
10818
- status
10819
- );
10820
- syncReportsToRegistry();
10821
- };
10822
- }
10823
- const pinLayer = createPinLayer({
10824
- container: root,
10825
- onOpenPinDetail: (componentId, reportId) => {
10826
- const ref2 = parseComponentRef(componentId);
10827
- setSelectedReportId(reportId);
10828
- const entry = { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 };
10829
- session.mode.transition.enterViewing(views.buildStack(entry));
10830
- },
10831
- onHoverPin: (anchor, componentId) => {
10832
- const overlay = overlayRef.current;
10833
- if (!overlay) return;
10834
- if (!anchor || !componentId) {
10835
- overlay.hide();
10836
- return;
10837
- }
10838
- overlay.show(anchor, {
10839
- color: isDarkMode() ? "#e4e4e7" : "#27272a"
10840
- });
10841
- },
10842
- onPinsChanged: syncReportsToRegistry
10843
- });
10844
- shell.menuBar.setPinLayer(pinLayer);
10845
- pinLayerRef.current = pinLayer;
10846
- mountCleanup.add(() => {
10847
- pinLayer.destroy();
10848
- pinLayerRef.current = null;
10849
- registry.closeReport = void 0;
10850
- });
10851
- if (cloud) {
10852
- const detach = pinLayer.attachCloud({
10853
- cloud,
10854
- channel,
10855
- getRoute: getCurrentRoute,
10856
- getPathname,
10857
- getVisibleEntities,
10858
- getMatchMode,
10859
- onError: (err) => console.warn("[uidex] pin fetch failed", err)
10860
- });
10861
- mountCleanup.add(detach);
10862
- if (channel && typeof window !== "undefined") {
10863
- const onRouteChange = () => {
10864
- const route = getCurrentRoute();
10865
- channel?.joinRoute(route);
10866
- void pinLayer.refresh();
10867
- };
10868
- const detachRoute = bindRouteChange(onRouteChange);
10869
- mountCleanup.add(detachRoute);
10870
- }
10871
- }
10872
- }
10873
7814
  function mount(target) {
10874
7815
  if (mounted) return;
10875
7816
  const mountTarget = target ?? (typeof document !== "undefined" ? document.body : null);
10876
7817
  if (!mountTarget) {
10877
7818
  throw new Error("createUidex: no mount target available");
10878
7819
  }
10879
- const getPathname = () => typeof location !== "undefined" ? location.pathname : "/";
10880
- const getCurrentRoute = () => options.getRoute?.() ?? findCurrentRoutePath(registry) ?? getPathname();
10881
- const getMatchMode = () => readMode();
10882
- let channel = null;
10883
- if (cloud && options.user) {
10884
- channel = cloud.realtime.connect({
10885
- user: options.user,
10886
- route: getCurrentRoute()
10887
- });
10888
- mountCleanup.add(() => channel?.disconnect());
10889
- }
10890
7820
  const shell = createSurfaceShell({
10891
7821
  mount: mountTarget,
10892
7822
  registry,
@@ -10894,7 +7824,6 @@ function createUidex(options = {}) {
10894
7824
  stylesheets: options.stylesheets,
10895
7825
  initialCorner: options.initialCorner,
10896
7826
  appTitle: options.appTitle,
10897
- channel,
10898
7827
  inspector: {
10899
7828
  onSelect: (match) => {
10900
7829
  const route = views.resolve(match.ref);
@@ -10930,38 +7859,23 @@ function createUidex(options = {}) {
10930
7859
  viewContainer.setAttribute("data-uidex-views", "");
10931
7860
  shell.host.chromeEl.appendChild(viewContainer);
10932
7861
  mountCleanup.add(() => viewContainer.remove());
10933
- let keyBindings = null;
10934
7862
  const viewStack = createViewStack({
10935
7863
  container: viewContainer,
10936
7864
  views,
10937
7865
  session,
10938
7866
  registry,
10939
- cloud,
10940
7867
  highlight,
10941
- shortcut: options.shortcut,
10942
- pushEscapeLayer: (handler) => {
10943
- if (!keyBindings) return () => {
10944
- };
10945
- return keyBindings.pushEscapeLayer(handler);
10946
- },
10947
- onAfterSubmit: () => pinLayerRef.current?.refresh(),
10948
- getRoute: getCurrentRoute
7868
+ shortcut: options.shortcut
10949
7869
  });
10950
7870
  mountCleanup.add(viewStack);
10951
7871
  if (shadowRoot) {
10952
- keyBindings = setupKeyBindings(shadowRoot, viewStack);
10953
- setupPinLayer(
7872
+ const keyBindings = bindShadowKeys({
10954
7873
  shadowRoot,
10955
- shell,
10956
- channel,
10957
- getCurrentRoute,
10958
- getMatchMode,
10959
- getPathname
10960
- );
10961
- }
10962
- if (ingest) {
10963
- ingest.start();
10964
- mountCleanup.add(() => ingest.stop());
7874
+ session,
7875
+ getActionsPopup: () => viewStack.getActionsPopup(),
7876
+ shortcut: options.shortcut
7877
+ });
7878
+ mountCleanup.add(keyBindings);
10965
7879
  }
10966
7880
  mounted = true;
10967
7881
  }
@@ -10977,8 +7891,6 @@ function createUidex(options = {}) {
10977
7891
  registry,
10978
7892
  session,
10979
7893
  views,
10980
- cloud,
10981
- ingest,
10982
7894
  get shadowRoot() {
10983
7895
  return shadowRoot;
10984
7896
  }
@@ -10990,27 +7902,18 @@ var import_jsx_runtime = require("react/jsx-runtime");
10990
7902
  var UidexContext = (0, import_react.createContext)(null);
10991
7903
  function UidexProvider({
10992
7904
  instance: externalInstance,
10993
- projectKey,
10994
- cloud,
10995
- user,
10996
7905
  config,
10997
7906
  children
10998
7907
  }) {
10999
7908
  const [owned] = (0, import_react.useState)(() => {
11000
7909
  if (externalInstance) return null;
11001
- const ownedCloud = cloud === void 0 && projectKey && typeof window !== "undefined" ? (0, import_cloud.cloud)({ projectKey }) : null;
11002
- const resolvedCloud = cloud !== void 0 ? cloud : ownedCloud;
11003
- return {
11004
- instance: createUidex({ ...config, cloud: resolvedCloud, user }),
11005
- cloud: ownedCloud
11006
- };
7910
+ return { instance: createUidex({ ...config }) };
11007
7911
  });
11008
- const instance = externalInstance ?? owned.instance;
7912
+ const instance = externalInstance ?? owned?.instance ?? null;
11009
7913
  (0, import_react.useEffect)(() => {
11010
7914
  if (!owned) return;
11011
7915
  return () => {
11012
7916
  owned.instance.unmount();
11013
- owned.cloud?.dispose?.();
11014
7917
  };
11015
7918
  }, [owned]);
11016
7919
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UidexContext.Provider, { value: instance, children });
@@ -11120,27 +8023,32 @@ function KindChip({
11120
8023
 
11121
8024
  // src/integrations/react/devtools.tsx
11122
8025
  var import_react5 = require("react");
11123
- var import_cloud2 = require("uidex/cloud");
11124
8026
  var import_jsx_runtime4 = require("react/jsx-runtime");
11125
8027
  function UidexDevtools({
11126
8028
  loadRegistry,
11127
- projectKey,
11128
- endpoint,
11129
- user,
11130
- git,
8029
+ loadDeferred,
11131
8030
  config
11132
8031
  }) {
11133
8032
  const [instance, setInstance] = (0, import_react5.useState)(null);
11134
8033
  (0, import_react5.useEffect)(() => {
11135
- const cloud = projectKey ? (0, import_cloud2.cloud)({ projectKey, endpoint, git }) : null;
11136
- const inst = createUidex({ ...config, cloud, user, git });
8034
+ const inst = createUidex({ ...config });
11137
8035
  loadRegistry(inst.registry);
11138
8036
  setInstance(inst);
8037
+ let cancelled = false;
8038
+ if (loadDeferred) {
8039
+ const run = () => {
8040
+ if (!cancelled)
8041
+ Promise.resolve(loadDeferred(inst.registry)).catch(() => {
8042
+ });
8043
+ };
8044
+ if (typeof requestIdleCallback === "function") requestIdleCallback(run);
8045
+ else setTimeout(run, 0);
8046
+ }
11139
8047
  return () => {
8048
+ cancelled = true;
11140
8049
  inst.unmount();
11141
- cloud?.dispose?.();
11142
8050
  };
11143
- }, [projectKey, user?.id]);
8051
+ }, []);
11144
8052
  if (!instance) return null;
11145
8053
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UidexContext.Provider, { value: instance, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UidexMount, {}) });
11146
8054
  }