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
@@ -3,7 +3,6 @@
3
3
 
4
4
  // src/integrations/react/provider.tsx
5
5
  import { createContext, useEffect, useState } from "react";
6
- import { cloud as createCloud } from "uidex/cloud";
7
6
 
8
7
  // src/shared/entities/types.ts
9
8
  var ENTITY_KINDS = [
@@ -287,207 +286,6 @@ function displayName(entity, node) {
287
286
  return prettify(entity.id);
288
287
  }
289
288
 
290
- // src/browser/ingest/console.ts
291
- var DEFAULT_LIMIT = 50;
292
- var LEVELS = ["warn", "error"];
293
- function formatMessage(args) {
294
- return args.map((arg) => {
295
- if (typeof arg === "string") return arg;
296
- if (arg instanceof Error) return arg.stack ?? arg.message;
297
- try {
298
- return JSON.stringify(arg);
299
- } catch {
300
- return String(arg);
301
- }
302
- }).join(" ");
303
- }
304
- function createConsoleCapture(options = {}) {
305
- const limit = options.limit ?? DEFAULT_LIMIT;
306
- const target = options.target ?? console;
307
- const now = options.now ?? (() => /* @__PURE__ */ new Date());
308
- const buffer = [];
309
- const originals = {};
310
- let active = false;
311
- function record(level, args) {
312
- const entry = {
313
- level,
314
- message: formatMessage(args),
315
- timestamp: now().toISOString()
316
- };
317
- buffer.push(entry);
318
- if (buffer.length > limit) buffer.splice(0, buffer.length - limit);
319
- }
320
- function start() {
321
- if (active) return;
322
- for (const level of LEVELS) {
323
- const original = target[level];
324
- originals[level] = original;
325
- const wrapped = (...args) => {
326
- record(level, args);
327
- original.call(target, ...args);
328
- };
329
- target[level] = wrapped;
330
- }
331
- active = true;
332
- }
333
- function stop() {
334
- if (!active) return;
335
- for (const level of LEVELS) {
336
- const original = originals[level];
337
- if (original) {
338
- ;
339
- target[level] = original;
340
- }
341
- delete originals[level];
342
- }
343
- active = false;
344
- }
345
- return {
346
- start,
347
- stop,
348
- get isActive() {
349
- return active;
350
- },
351
- entries() {
352
- return buffer.slice();
353
- },
354
- clear() {
355
- buffer.length = 0;
356
- }
357
- };
358
- }
359
-
360
- // src/browser/ingest/native-fetch.ts
361
- var nativeFetch = typeof globalThis !== "undefined" && typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0;
362
-
363
- // src/browser/ingest/network.ts
364
- var DEFAULT_LIMIT2 = 20;
365
- function resolveMethod(input, init) {
366
- if (init?.method) return init.method.toUpperCase();
367
- if (typeof input !== "string" && !(input instanceof URL) && "method" in input) {
368
- return input.method.toUpperCase();
369
- }
370
- return "GET";
371
- }
372
- function resolveUrl(input) {
373
- if (typeof input === "string") return input;
374
- if (input instanceof URL) return input.toString();
375
- return input.url;
376
- }
377
- function createNetworkCapture(options = {}) {
378
- const limit = options.limit ?? DEFAULT_LIMIT2;
379
- const target = options.target ?? globalThis;
380
- const now = options.now ?? (() => /* @__PURE__ */ new Date());
381
- const buffer = [];
382
- let original;
383
- let active = false;
384
- function push(entry) {
385
- buffer.push(entry);
386
- if (buffer.length > limit) buffer.splice(0, buffer.length - limit);
387
- }
388
- function start() {
389
- if (active) return;
390
- const baseline = target.fetch ?? nativeFetch;
391
- if (!baseline) return;
392
- original = baseline;
393
- const wrapped = async (input, init) => {
394
- const method = resolveMethod(input, init);
395
- const url = resolveUrl(input);
396
- try {
397
- const res = await baseline(input, init);
398
- if (res.status >= 400) {
399
- push({
400
- method,
401
- url,
402
- status: res.status,
403
- timestamp: now().toISOString()
404
- });
405
- }
406
- return res;
407
- } catch (err) {
408
- push({
409
- method,
410
- url,
411
- status: 0,
412
- timestamp: now().toISOString(),
413
- error: err instanceof Error ? err.message : String(err)
414
- });
415
- throw err;
416
- }
417
- };
418
- target.fetch = wrapped;
419
- active = true;
420
- }
421
- function stop() {
422
- if (!active) return;
423
- if (original) target.fetch = original;
424
- original = void 0;
425
- active = false;
426
- }
427
- return {
428
- start,
429
- stop,
430
- get isActive() {
431
- return active;
432
- },
433
- entries() {
434
- return buffer.slice();
435
- },
436
- clear() {
437
- buffer.length = 0;
438
- }
439
- };
440
- }
441
-
442
- // src/browser/ingest/index.ts
443
- function createIngest(options = {}) {
444
- const opts = options;
445
- const wantConsole = opts.captureConsole !== false;
446
- const wantNetwork = opts.captureNetwork !== false;
447
- const consoleCapture = wantConsole ? createConsoleCapture({
448
- limit: opts.consoleLimit,
449
- target: opts.consoleTarget,
450
- now: opts.now
451
- }) : null;
452
- const networkCapture = wantNetwork ? createNetworkCapture({
453
- limit: opts.networkLimit,
454
- target: opts.networkTarget,
455
- now: opts.now
456
- }) : null;
457
- let active = false;
458
- function start() {
459
- if (active) return;
460
- consoleCapture?.start();
461
- networkCapture?.start();
462
- active = Boolean(consoleCapture?.isActive || networkCapture?.isActive);
463
- }
464
- function stop() {
465
- if (!active) return;
466
- consoleCapture?.stop();
467
- networkCapture?.stop();
468
- active = false;
469
- }
470
- return {
471
- start,
472
- stop,
473
- get isActive() {
474
- return active;
475
- },
476
- console: consoleCapture,
477
- network: networkCapture
478
- };
479
- }
480
- function resolveIngestOptions(explicit, hasCloud) {
481
- if (explicit === null) return null;
482
- if (explicit === void 0) {
483
- return hasCloud ? { captureConsole: true, captureNetwork: true } : null;
484
- }
485
- if (explicit.captureConsole === false && explicit.captureNetwork === false) {
486
- return null;
487
- }
488
- return explicit;
489
- }
490
-
491
289
  // src/browser/internal/cleanup.ts
492
290
  function createCleanupStack() {
493
291
  const stack = [];
@@ -521,60 +319,6 @@ function composeCleanups(cleanups) {
521
319
  };
522
320
  }
523
321
 
524
- // src/browser/internal/dark-mode.ts
525
- function isDarkMode() {
526
- if (typeof document !== "undefined") {
527
- const root = document.documentElement;
528
- if (root?.classList.contains("dark")) return true;
529
- if (root?.classList.contains("light")) return false;
530
- }
531
- if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
532
- return window.matchMedia("(prefers-color-scheme: dark)").matches;
533
- }
534
- return false;
535
- }
536
-
537
- // src/browser/internal/route-change.ts
538
- var ROUTE_CHANGE_EVENT = "uidex:routechange";
539
- var patched = false;
540
- function ensureHistoryPatched() {
541
- if (patched) return;
542
- if (typeof window === "undefined" || typeof history === "undefined") return;
543
- patched = true;
544
- const dispatch = () => {
545
- window.dispatchEvent(new Event(ROUTE_CHANGE_EVENT));
546
- };
547
- const origPush = history.pushState;
548
- history.pushState = function(...args) {
549
- const result = origPush.apply(
550
- this,
551
- args
552
- );
553
- dispatch();
554
- return result;
555
- };
556
- const origReplace = history.replaceState;
557
- history.replaceState = function(...args) {
558
- const result = origReplace.apply(
559
- this,
560
- args
561
- );
562
- dispatch();
563
- return result;
564
- };
565
- }
566
- function bindRouteChange(handler) {
567
- if (typeof window === "undefined") return () => {
568
- };
569
- ensureHistoryPatched();
570
- window.addEventListener("popstate", handler);
571
- window.addEventListener(ROUTE_CHANGE_EVENT, handler);
572
- return () => {
573
- window.removeEventListener("popstate", handler);
574
- window.removeEventListener(ROUTE_CHANGE_EVENT, handler);
575
- };
576
- }
577
-
578
322
  // src/browser/session/store.ts
579
323
  import { createStore as createStore3 } from "zustand/vanilla";
580
324
 
@@ -712,8 +456,7 @@ var defaultSnapshot = {
712
456
  pinnedHighlight: null,
713
457
  mode: "idle",
714
458
  theme: "auto",
715
- resolvedTheme: "light",
716
- user: null
459
+ resolvedTheme: "light"
717
460
  };
718
461
  function resolveTheme(preference, detect) {
719
462
  if (preference !== "auto") return preference;
@@ -803,8 +546,7 @@ function createSession(options = {}) {
803
546
  pinnedHighlight: null,
804
547
  mode: "idle",
805
548
  theme: initialPref,
806
- resolvedTheme: initialResolved,
807
- user: overrides.user ?? null
549
+ resolvedTheme: initialResolved
808
550
  }));
809
551
  nav.subscribe(() => {
810
552
  const { stack } = nav.getState();
@@ -848,8 +590,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
848
590
  @layer theme, base, components, utilities;
849
591
  @layer theme {
850
592
  :root, :host {
851
- --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
852
- Roboto, sans-serif;
853
593
  --color-red-400: oklch(70.4% 0.191 22.216);
854
594
  --color-red-500: oklch(63.7% 0.237 25.331);
855
595
  --color-red-700: oklch(50.5% 0.213 27.518);
@@ -902,7 +642,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
902
642
  --color-black: #000;
903
643
  --color-white: #fff;
904
644
  --spacing: 0.25rem;
905
- --container-sm: 24rem;
906
645
  --container-xl: 36rem;
907
646
  --text-xs: 0.75rem;
908
647
  --text-xs--line-height: calc(1 / 0.75);
@@ -910,28 +649,22 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
910
649
  --text-sm--line-height: calc(1.25 / 0.875);
911
650
  --text-base: 1rem;
912
651
  --text-base--line-height: calc(1.5 / 1);
913
- --text-xl: 1.25rem;
914
- --text-xl--line-height: calc(1.75 / 1.25);
915
652
  --font-weight-normal: 400;
916
653
  --font-weight-medium: 500;
917
654
  --font-weight-semibold: 600;
918
- --font-weight-bold: 700;
919
655
  --tracking-tight: -0.025em;
920
656
  --tracking-widest: 0.1em;
921
657
  --leading-relaxed: 1.625;
922
- --radius-md: calc(var(--radius) * 0.8);
923
658
  --radius-lg: var(--radius);
924
659
  --radius-xl: calc(var(--radius) * 1.4);
925
660
  --radius-2xl: calc(var(--radius) * 1.8);
926
661
  --ease-out: cubic-bezier(0, 0, 0.2, 1);
927
- --animate-spin: spin 1s linear infinite;
928
662
  --blur-sm: 8px;
929
663
  --default-transition-duration: 150ms;
930
664
  --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
931
665
  --default-font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
932
666
  Roboto, sans-serif;
933
667
  --default-mono-font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
934
- --color-muted: var(--muted);
935
668
  --color-border: var(--border);
936
669
  }
937
670
  }
@@ -1099,17 +832,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1099
832
  .visible {
1100
833
  visibility: visible;
1101
834
  }
1102
- .sr-only {
1103
- position: absolute;
1104
- width: 1px;
1105
- height: 1px;
1106
- padding: 0;
1107
- margin: -1px;
1108
- overflow: hidden;
1109
- clip-path: inset(50%);
1110
- white-space: nowrap;
1111
- border-width: 0;
1112
- }
1113
835
  .absolute {
1114
836
  position: absolute;
1115
837
  }
@@ -1131,30 +853,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1131
853
  .end {
1132
854
  inset-inline-end: var(--spacing);
1133
855
  }
1134
- .-top-1 {
1135
- top: calc(var(--spacing) * -1);
1136
- }
1137
- .-right-1 {
1138
- right: calc(var(--spacing) * -1);
1139
- }
1140
- .right-0 {
1141
- right: calc(var(--spacing) * 0);
1142
- }
1143
- .bottom-full {
1144
- bottom: 100%;
1145
- }
1146
- .bottom-px {
1147
- bottom: 1px;
1148
- }
1149
856
  .z-1 {
1150
857
  z-index: 1;
1151
858
  }
1152
859
  .z-10 {
1153
860
  z-index: 10;
1154
861
  }
1155
- .z-\\[2147483647\\] {
1156
- z-index: 2147483647;
1157
- }
1158
862
  .container {
1159
863
  width: 100%;
1160
864
  @media (width >= 40rem) {
@@ -1188,12 +892,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1188
892
  .mt-1 {
1189
893
  margin-top: calc(var(--spacing) * 1);
1190
894
  }
1191
- .mb-2 {
1192
- margin-bottom: calc(var(--spacing) * 2);
1193
- }
1194
- .mb-6 {
1195
- margin-bottom: calc(var(--spacing) * 6);
1196
- }
1197
895
  .ml-auto {
1198
896
  margin-left: auto;
1199
897
  }
@@ -1212,19 +910,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1212
910
  .inline {
1213
911
  display: inline;
1214
912
  }
1215
- .inline-block {
1216
- display: inline-block;
1217
- }
1218
913
  .inline-flex {
1219
914
  display: inline-flex;
1220
915
  }
1221
916
  .table {
1222
917
  display: table;
1223
918
  }
1224
- .size-2 {
1225
- width: calc(var(--spacing) * 2);
1226
- height: calc(var(--spacing) * 2);
1227
- }
1228
919
  .size-3\\.5 {
1229
920
  width: calc(var(--spacing) * 3.5);
1230
921
  height: calc(var(--spacing) * 3.5);
@@ -1285,21 +976,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1285
976
  .h-full {
1286
977
  height: 100%;
1287
978
  }
1288
- .max-h-32 {
1289
- max-height: calc(var(--spacing) * 32);
1290
- }
1291
- .max-h-full {
1292
- max-height: 100%;
1293
- }
1294
979
  .min-h-0 {
1295
980
  min-height: calc(var(--spacing) * 0);
1296
981
  }
1297
982
  .min-h-8 {
1298
983
  min-height: calc(var(--spacing) * 8);
1299
984
  }
1300
- .min-h-20 {
1301
- min-height: calc(var(--spacing) * 20);
1302
- }
1303
985
  .w-3\\.5 {
1304
986
  width: calc(var(--spacing) * 3.5);
1305
987
  }
@@ -1309,12 +991,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1309
991
  .w-12 {
1310
992
  width: calc(var(--spacing) * 12);
1311
993
  }
1312
- .w-20 {
1313
- width: calc(var(--spacing) * 20);
1314
- }
1315
- .w-36 {
1316
- width: calc(var(--spacing) * 36);
1317
- }
1318
994
  .w-56 {
1319
995
  width: calc(var(--spacing) * 56);
1320
996
  }
@@ -1324,12 +1000,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1324
1000
  .w-px {
1325
1001
  width: 1px;
1326
1002
  }
1327
- .max-w-full {
1328
- max-width: 100%;
1329
- }
1330
- .max-w-sm {
1331
- max-width: var(--container-sm);
1332
- }
1333
1003
  .max-w-xl {
1334
1004
  max-width: var(--container-xl);
1335
1005
  }
@@ -1357,41 +1027,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1357
1027
  .shrink-0 {
1358
1028
  flex-shrink: 0;
1359
1029
  }
1360
- .origin-bottom-left {
1361
- transform-origin: 0 100%;
1362
- }
1363
- .origin-bottom-right {
1364
- transform-origin: 100% 100%;
1365
- }
1366
- .-translate-x-0\\.5 {
1367
- --tw-translate-x: calc(var(--spacing) * -0.5);
1368
- translate: var(--tw-translate-x) var(--tw-translate-y);
1369
- }
1370
- .translate-x-0\\.5 {
1371
- --tw-translate-x: calc(var(--spacing) * 0.5);
1372
- translate: var(--tw-translate-x) var(--tw-translate-y);
1373
- }
1374
- .scale-84 {
1375
- --tw-scale-x: 84%;
1376
- --tw-scale-y: 84%;
1377
- --tw-scale-z: 84%;
1378
- scale: var(--tw-scale-x) var(--tw-scale-y);
1379
- }
1380
- .-rotate-10 {
1381
- rotate: calc(10deg * -1);
1382
- }
1383
- .rotate-10 {
1384
- rotate: 10deg;
1385
- }
1386
1030
  .transform {
1387
1031
  transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
1388
1032
  }
1389
- .animate-skeleton {
1390
- animation: skeleton 2s -1s infinite linear;
1391
- }
1392
- .animate-spin {
1393
- animation: var(--animate-spin);
1394
- }
1395
1033
  .cursor-pointer {
1396
1034
  cursor: pointer;
1397
1035
  }
@@ -1401,15 +1039,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1401
1039
  .scroll-py-2 {
1402
1040
  scroll-padding-block: calc(var(--spacing) * 2);
1403
1041
  }
1404
- .appearance-none {
1405
- appearance: none;
1406
- }
1407
1042
  .flex-col {
1408
1043
  flex-direction: column;
1409
1044
  }
1410
- .flex-row {
1411
- flex-direction: row;
1412
- }
1413
1045
  .items-center {
1414
1046
  align-items: center;
1415
1047
  }
@@ -1425,9 +1057,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1425
1057
  .gap-0 {
1426
1058
  gap: calc(var(--spacing) * 0);
1427
1059
  }
1428
- .gap-0\\.5 {
1429
- gap: calc(var(--spacing) * 0.5);
1430
- }
1431
1060
  .gap-1 {
1432
1061
  gap: calc(var(--spacing) * 1);
1433
1062
  }
@@ -1440,12 +1069,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1440
1069
  .gap-3 {
1441
1070
  gap: calc(var(--spacing) * 3);
1442
1071
  }
1443
- .gap-4 {
1444
- gap: calc(var(--spacing) * 4);
1445
- }
1446
- .gap-6 {
1447
- gap: calc(var(--spacing) * 6);
1448
- }
1449
1072
  .truncate {
1450
1073
  overflow: hidden;
1451
1074
  text-overflow: ellipsis;
@@ -1543,21 +1166,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1543
1166
  background-color: color-mix(in oklab, var(--color-black) 32%, transparent);
1544
1167
  }
1545
1168
  }
1546
- .bg-black\\/80 {
1547
- background-color: color-mix(in srgb, #000 80%, transparent);
1548
- @supports (color: color-mix(in lab, red, red)) {
1549
- background-color: color-mix(in oklab, var(--color-black) 80%, transparent);
1550
- }
1551
- }
1552
1169
  .bg-blue-50 {
1553
1170
  background-color: var(--color-blue-50);
1554
1171
  }
1555
1172
  .bg-border {
1556
1173
  background-color: var(--border);
1557
1174
  }
1558
- .bg-card {
1559
- background-color: var(--card);
1560
- }
1561
1175
  .bg-cyan-50 {
1562
1176
  background-color: var(--color-cyan-50);
1563
1177
  }
@@ -1573,15 +1187,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1573
1187
  .bg-emerald-50 {
1574
1188
  background-color: var(--color-emerald-50);
1575
1189
  }
1576
- .bg-emerald-500 {
1577
- background-color: var(--color-emerald-500);
1578
- }
1579
1190
  .bg-fuchsia-50 {
1580
1191
  background-color: var(--color-fuchsia-50);
1581
1192
  }
1582
- .bg-info {
1583
- background-color: var(--info);
1584
- }
1585
1193
  .bg-info\\/10 {
1586
1194
  background-color: var(--info);
1587
1195
  @supports (color: color-mix(in lab, red, red)) {
@@ -1633,18 +1241,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1633
1241
  .bg-clip-padding {
1634
1242
  background-clip: padding-box;
1635
1243
  }
1636
- .bg-\\[right_0\\.5rem_center\\] {
1637
- background-position: right 0.5rem center;
1638
- }
1639
- .bg-no-repeat {
1640
- background-repeat: no-repeat;
1641
- }
1642
- .object-cover {
1643
- object-fit: cover;
1644
- }
1645
- .object-top {
1646
- object-position: top;
1647
- }
1648
1244
  .p-1 {
1649
1245
  padding: calc(var(--spacing) * 1);
1650
1246
  }
@@ -1657,9 +1253,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1657
1253
  .p-4 {
1658
1254
  padding: calc(var(--spacing) * 4);
1659
1255
  }
1660
- .p-6 {
1661
- padding: calc(var(--spacing) * 6);
1662
- }
1663
1256
  .px-1 {
1664
1257
  padding-inline: calc(var(--spacing) * 1);
1665
1258
  }
@@ -1681,24 +1274,15 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1681
1274
  .px-4 {
1682
1275
  padding-inline: calc(var(--spacing) * 4);
1683
1276
  }
1684
- .px-6 {
1685
- padding-inline: calc(var(--spacing) * 6);
1686
- }
1687
1277
  .py-1 {
1688
1278
  padding-block: calc(var(--spacing) * 1);
1689
1279
  }
1690
1280
  .py-1\\.5 {
1691
1281
  padding-block: calc(var(--spacing) * 1.5);
1692
1282
  }
1693
- .py-2 {
1694
- padding-block: calc(var(--spacing) * 2);
1695
- }
1696
1283
  .py-4 {
1697
1284
  padding-block: calc(var(--spacing) * 4);
1698
1285
  }
1699
- .py-12 {
1700
- padding-block: calc(var(--spacing) * 12);
1701
- }
1702
1286
  .py-\\[max\\(1rem\\,4vh\\)\\] {
1703
1287
  padding-block: max(1rem, 4vh);
1704
1288
  }
@@ -1711,12 +1295,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1711
1295
  .pt-2 {
1712
1296
  padding-top: calc(var(--spacing) * 2);
1713
1297
  }
1714
- .pr-8 {
1715
- padding-right: calc(var(--spacing) * 8);
1716
- }
1717
- .pl-3 {
1718
- padding-left: calc(var(--spacing) * 3);
1719
- }
1720
1298
  .text-center {
1721
1299
  text-align: center;
1722
1300
  }
@@ -1734,18 +1312,10 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1734
1312
  font-size: var(--text-base);
1735
1313
  line-height: var(--tw-leading, var(--text-base--line-height));
1736
1314
  }
1737
- .text-base\\/4\\.5 {
1738
- font-size: var(--text-base);
1739
- line-height: calc(var(--spacing) * 4.5);
1740
- }
1741
1315
  .text-sm {
1742
1316
  font-size: var(--text-sm);
1743
1317
  line-height: var(--tw-leading, var(--text-sm--line-height));
1744
1318
  }
1745
- .text-xl {
1746
- font-size: var(--text-xl);
1747
- line-height: var(--tw-leading, var(--text-xl--line-height));
1748
- }
1749
1319
  .text-xs {
1750
1320
  font-size: var(--text-xs);
1751
1321
  line-height: var(--tw-leading, var(--text-xs--line-height));
@@ -1753,24 +1323,13 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1753
1323
  .text-\\[0\\.625rem\\] {
1754
1324
  font-size: 0.625rem;
1755
1325
  }
1756
- .text-\\[9px\\] {
1757
- font-size: 9px;
1758
- }
1759
1326
  .text-\\[11px\\] {
1760
1327
  font-size: 11px;
1761
1328
  }
1762
- .leading-none {
1763
- --tw-leading: 1;
1764
- line-height: 1;
1765
- }
1766
1329
  .leading-relaxed {
1767
1330
  --tw-leading: var(--leading-relaxed);
1768
1331
  line-height: var(--leading-relaxed);
1769
1332
  }
1770
- .font-bold {
1771
- --tw-font-weight: var(--font-weight-bold);
1772
- font-weight: var(--font-weight-bold);
1773
- }
1774
1333
  .font-medium {
1775
1334
  --tw-font-weight: var(--font-weight-medium);
1776
1335
  font-weight: var(--font-weight-medium);
@@ -1791,9 +1350,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1791
1350
  --tw-tracking: var(--tracking-widest);
1792
1351
  letter-spacing: var(--tracking-widest);
1793
1352
  }
1794
- .text-balance {
1795
- text-wrap: balance;
1796
- }
1797
1353
  .break-words {
1798
1354
  overflow-wrap: break-word;
1799
1355
  }
@@ -1818,9 +1374,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1818
1374
  .text-cyan-700 {
1819
1375
  color: var(--color-cyan-700);
1820
1376
  }
1821
- .text-destructive {
1822
- color: var(--destructive);
1823
- }
1824
1377
  .text-destructive-foreground {
1825
1378
  color: var(--destructive-foreground);
1826
1379
  }
@@ -1886,18 +1439,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1886
1439
  .line-through {
1887
1440
  text-decoration-line: line-through;
1888
1441
  }
1889
- .underline {
1890
- text-decoration-line: underline;
1891
- }
1892
1442
  .underline-offset-4 {
1893
1443
  text-underline-offset: 4px;
1894
1444
  }
1895
1445
  .accent-accent {
1896
1446
  accent-color: var(--accent);
1897
1447
  }
1898
- .accent-primary {
1899
- accent-color: var(--primary);
1900
- }
1901
1448
  .opacity-50 {
1902
1449
  opacity: 50%;
1903
1450
  }
@@ -1909,11 +1456,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1909
1456
  --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%));
1910
1457
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1911
1458
  }
1912
- .shadow-sm\\/5 {
1913
- --tw-shadow-alpha: 5%;
1914
- --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%));
1915
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1916
- }
1917
1459
  .shadow-xs\\/5 {
1918
1460
  --tw-shadow-alpha: 5%;
1919
1461
  --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, oklab(from rgb(0 0 0 / 0.05) l a b / 5%));
@@ -1923,14 +1465,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1923
1465
  --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));
1924
1466
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1925
1467
  }
1926
- .shadow-lg {
1927
- --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));
1928
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1929
- }
1930
- .shadow-none {
1931
- --tw-shadow: 0 0 #0000;
1932
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1933
- }
1934
1468
  .shadow-xs {
1935
1469
  --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.05));
1936
1470
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -2030,15 +1564,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2030
1564
  -webkit-user-select: none;
2031
1565
  user-select: none;
2032
1566
  }
2033
- .\\[--skeleton-highlight\\:--alpha\\(var\\(--color-white\\)\\/64\\%\\)\\] {
2034
- --skeleton-highlight: color-mix(in srgb, #fff 64%, transparent);
2035
- @supports (color: color-mix(in lab, red, red)) {
2036
- --skeleton-highlight: color-mix(in oklab, var(--color-white) 64%, transparent);
2037
- }
2038
- }
2039
- .\\[background\\:linear-gradient\\(120deg\\,transparent_40\\%\\,var\\(--skeleton-highlight\\)\\,transparent_60\\%\\)_var\\(--color-muted\\)_0_0\\/200\\%_100\\%_fixed\\] {
2040
- background: linear-gradient(120deg,transparent 40%,var(--skeleton-highlight),transparent 60%) var(--color-muted) 0 0/200% 100% fixed;
2041
- }
2042
1567
  .\\[clip-path\\:inset\\(0_1px\\)\\] {
2043
1568
  clip-path: inset(0 1px);
2044
1569
  }
@@ -2122,12 +1647,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2122
1647
  border-radius: calc(var(--radius-lg) - 1px);
2123
1648
  }
2124
1649
  }
2125
- .before\\:rounded-\\[calc\\(var\\(--radius-md\\)-1px\\)\\] {
2126
- &::before {
2127
- content: var(--tw-content);
2128
- border-radius: calc(var(--radius-md) - 1px);
2129
- }
2130
- }
2131
1650
  .before\\:rounded-\\[calc\\(var\\(--radius-xl\\)-1px\\)\\] {
2132
1651
  &::before {
2133
1652
  content: var(--tw-content);
@@ -2257,36 +1776,17 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2257
1776
  outline-style: none;
2258
1777
  }
2259
1778
  }
2260
- .focus-visible\\:border-ring {
2261
- &:focus-visible {
2262
- border-color: var(--ring);
2263
- }
2264
- }
2265
1779
  .focus-visible\\:ring-2 {
2266
1780
  &:focus-visible {
2267
1781
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
2268
1782
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2269
1783
  }
2270
1784
  }
2271
- .focus-visible\\:ring-\\[3px\\] {
2272
- &:focus-visible {
2273
- --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
2274
- box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
2275
- }
2276
- }
2277
1785
  .focus-visible\\:ring-ring {
2278
1786
  &:focus-visible {
2279
1787
  --tw-ring-color: var(--ring);
2280
1788
  }
2281
1789
  }
2282
- .focus-visible\\:ring-ring\\/30 {
2283
- &:focus-visible {
2284
- --tw-ring-color: var(--ring);
2285
- @supports (color: color-mix(in lab, red, red)) {
2286
- --tw-ring-color: color-mix(in oklab, var(--ring) 30%, transparent);
2287
- }
2288
- }
2289
- }
2290
1790
  .focus-visible\\:ring-offset-1 {
2291
1791
  &:focus-visible {
2292
1792
  --tw-ring-offset-width: 1px;
@@ -2313,24 +1813,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2313
1813
  opacity: 60%;
2314
1814
  }
2315
1815
  }
2316
- .aria-invalid\\:border-destructive\\/40 {
2317
- &[aria-invalid="true"] {
2318
- border-color: var(--destructive);
2319
- @supports (color: color-mix(in lab, red, red)) {
2320
- border-color: color-mix(in oklab, var(--destructive) 40%, transparent);
2321
- }
2322
- }
2323
- }
2324
- .aria-invalid\\:focus-visible\\:ring-destructive\\/20 {
2325
- &[aria-invalid="true"] {
2326
- &:focus-visible {
2327
- --tw-ring-color: var(--destructive);
2328
- @supports (color: color-mix(in lab, red, red)) {
2329
- --tw-ring-color: color-mix(in oklab, var(--destructive) 20%, transparent);
2330
- }
2331
- }
2332
- }
2333
- }
2334
1816
  .data-\\[disabled\\]\\:pointer-events-none {
2335
1817
  &[data-disabled] {
2336
1818
  pointer-events: none;
@@ -2367,12 +1849,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2367
1849
  line-height: var(--tw-leading, var(--text-sm--line-height));
2368
1850
  }
2369
1851
  }
2370
- .sm\\:text-sm\\/4 {
2371
- @media (width >= 40rem) {
2372
- font-size: var(--text-sm);
2373
- line-height: calc(var(--spacing) * 4);
2374
- }
2375
- }
2376
1852
  .dark\\:bg-amber-400\\/10 {
2377
1853
  &:is(.dark *, :host(.dark) *) {
2378
1854
  background-color: color-mix(in srgb, oklch(82.8% 0.189 84.429) 10%, transparent);
@@ -2586,14 +2062,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2586
2062
  }
2587
2063
  }
2588
2064
  }
2589
- .dark\\:\\[--skeleton-highlight\\:--alpha\\(var\\(--color-white\\)\\/4\\%\\)\\] {
2590
- &:is(.dark *, :host(.dark) *) {
2591
- --skeleton-highlight: color-mix(in srgb, #fff 4%, transparent);
2592
- @supports (color: color-mix(in lab, red, red)) {
2593
- --skeleton-highlight: color-mix(in oklab, var(--color-white) 4%, transparent);
2594
- }
2595
- }
2596
- }
2597
2065
  .dark\\:group-data-hover\\:bg-amber-400\\/20 {
2598
2066
  &:is(.dark *, :host(.dark) *) {
2599
2067
  &:is(:where(.group)[data-hover] *) {
@@ -2713,12 +2181,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2713
2181
  height: calc(var(--spacing) * 4);
2714
2182
  }
2715
2183
  }
2716
- .\\[\\&_svg\\:not\\(\\[class\\*\\=\\'size-\\'\\]\\)\\]\\:size-4\\.5 {
2717
- & svg:not([class*='size-']) {
2718
- width: calc(var(--spacing) * 4.5);
2719
- height: calc(var(--spacing) * 4.5);
2720
- }
2721
- }
2722
2184
  .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:bg-accent {
2723
2185
  [data-kbd-nav] & {
2724
2186
  &:focus-within {
@@ -2951,36 +2413,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2951
2413
  --warning: var(--color-amber-500);
2952
2414
  --warning-foreground: var(--color-amber-700);
2953
2415
  }
2954
- @property --tw-translate-x {
2955
- syntax: "*";
2956
- inherits: false;
2957
- initial-value: 0;
2958
- }
2959
- @property --tw-translate-y {
2960
- syntax: "*";
2961
- inherits: false;
2962
- initial-value: 0;
2963
- }
2964
- @property --tw-translate-z {
2965
- syntax: "*";
2966
- inherits: false;
2967
- initial-value: 0;
2968
- }
2969
- @property --tw-scale-x {
2970
- syntax: "*";
2971
- inherits: false;
2972
- initial-value: 1;
2973
- }
2974
- @property --tw-scale-y {
2975
- syntax: "*";
2976
- inherits: false;
2977
- initial-value: 1;
2978
- }
2979
- @property --tw-scale-z {
2980
- syntax: "*";
2981
- inherits: false;
2982
- initial-value: 1;
2983
- }
2984
2416
  @property --tw-rotate-x {
2985
2417
  syntax: "*";
2986
2418
  inherits: false;
@@ -3206,20 +2638,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3206
2638
  initial-value: "";
3207
2639
  inherits: false;
3208
2640
  }
3209
- @keyframes spin {
3210
- to {
3211
- transform: rotate(360deg);
3212
- }
3213
- }
3214
2641
  @layer properties {
3215
2642
  @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
3216
2643
  *, ::before, ::after, ::backdrop {
3217
- --tw-translate-x: 0;
3218
- --tw-translate-y: 0;
3219
- --tw-translate-z: 0;
3220
- --tw-scale-x: 1;
3221
- --tw-scale-y: 1;
3222
- --tw-scale-z: 1;
3223
2644
  --tw-rotate-x: initial;
3224
2645
  --tw-rotate-y: initial;
3225
2646
  --tw-rotate-z: initial;
@@ -3282,7 +2703,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3282
2703
  var SURFACE_HOST_CLASS = "uidex-surface-host";
3283
2704
  var Z_BASE = 2147483630;
3284
2705
  var Z_OVERLAY = 2147483635;
3285
- var Z_PIN_LAYER = 2147483640;
3286
2706
  var Z_CHROME = 2147483645;
3287
2707
  var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS}`;
3288
2708
  var UIDEX_ATTR_TO_KIND = [
@@ -3636,9 +3056,7 @@ function createInspector(options) {
3636
3056
  import {
3637
3057
  Command,
3638
3058
  Highlighter,
3639
- MapPin,
3640
3059
  MousePointerClick as MousePointerClick2,
3641
- Users,
3642
3060
  createElement as createLucideElement2
3643
3061
  } from "lucide";
3644
3062
 
@@ -3688,7 +3106,6 @@ function el(tag, options = {}, children = []) {
3688
3106
  var DEFAULT_SHORTCUT = { key: "k" };
3689
3107
  var ACTIONS_SHORTCUT = { key: "j" };
3690
3108
  var INSPECT_SHORTCUT = { key: "i" };
3691
- var PINS_SHORTCUT = { key: "u" };
3692
3109
  function formatShortcutLabel(shortcut) {
3693
3110
  const parts = [];
3694
3111
  if (shortcut.mod !== false) parts.push("\u2318");
@@ -3703,19 +3120,17 @@ function matchesShortcut(e, shortcut) {
3703
3120
  return hasMod === wantMod && e.shiftKey === wantShift && !e.altKey && e.key.toLowerCase() === shortcut.key.toLowerCase();
3704
3121
  }
3705
3122
  function bindShadowKeys(options) {
3706
- const { shadowRoot, session, getActionsPopup, getPinLayer } = options;
3123
+ const { shadowRoot, session, getActionsPopup } = options;
3707
3124
  const shortcut = options.shortcut ?? DEFAULT_SHORTCUT;
3708
- const escapeStack = [];
3709
3125
  const onKeydown = (e) => {
3710
3126
  const isMod = e.metaKey || e.ctrlKey;
3711
3127
  const isCmdK = matchesShortcut(e, shortcut);
3712
3128
  const isCmdDot = matchesShortcut(e, ACTIONS_SHORTCUT);
3713
3129
  const isCmdI = matchesShortcut(e, INSPECT_SHORTCUT);
3714
- const isCmdU = matchesShortcut(e, PINS_SHORTCUT);
3715
3130
  const isCmdBracket = isMod && !e.altKey && !e.shiftKey && e.key === "[";
3716
3131
  const isEsc = e.key === "Escape";
3717
3132
  const isPopKey = isEsc || isCmdBracket;
3718
- if (!isCmdK && !isCmdDot && !isCmdI && !isCmdU && !isPopKey) return;
3133
+ if (!isCmdK && !isCmdDot && !isCmdI && !isPopKey) return;
3719
3134
  const popup = getActionsPopup();
3720
3135
  if (popup?.isOpen()) {
3721
3136
  if (isPopKey || isCmdK || isCmdDot) {
@@ -3733,15 +3148,6 @@ function bindShadowKeys(options) {
3733
3148
  }
3734
3149
  return;
3735
3150
  }
3736
- if (isCmdU) {
3737
- const pinLayer = getPinLayer?.();
3738
- if (pinLayer) {
3739
- e.preventDefault();
3740
- e.stopPropagation();
3741
- pinLayer.setVisible(!pinLayer.visible);
3742
- }
3743
- return;
3744
- }
3745
3151
  if (isCmdI) {
3746
3152
  e.preventDefault();
3747
3153
  e.stopPropagation();
@@ -3763,14 +3169,6 @@ function bindShadowKeys(options) {
3763
3169
  }
3764
3170
  return;
3765
3171
  }
3766
- if (escapeStack.length > 0) {
3767
- const top = escapeStack[escapeStack.length - 1];
3768
- if (top()) {
3769
- e.preventDefault();
3770
- e.stopPropagation();
3771
- return;
3772
- }
3773
- }
3774
3172
  const { mode } = session.mode.getState();
3775
3173
  if (mode === "viewing") {
3776
3174
  e.preventDefault();
@@ -3805,13 +3203,6 @@ function bindShadowKeys(options) {
3805
3203
  capture: true
3806
3204
  }
3807
3205
  );
3808
- },
3809
- pushEscapeLayer(handler) {
3810
- escapeStack.push(handler);
3811
- return () => {
3812
- const idx = escapeStack.indexOf(handler);
3813
- if (idx >= 0) escapeStack.splice(idx, 1);
3814
- };
3815
3206
  }
3816
3207
  };
3817
3208
  }
@@ -3827,11 +3218,8 @@ function createMenuBar(options) {
3827
3218
  container,
3828
3219
  session,
3829
3220
  initialCorner = "bottom-right",
3830
- appTitle,
3831
- channel = null,
3832
- pinLayer: initialPinLayer = null
3221
+ appTitle
3833
3222
  } = options;
3834
- let activePinLayer = initialPinLayer;
3835
3223
  const root = el("div", {
3836
3224
  class: ROOT_CLASS,
3837
3225
  attrs: {
@@ -3896,108 +3284,6 @@ function createMenuBar(options) {
3896
3284
  inspectBtn.blur();
3897
3285
  });
3898
3286
  root.appendChild(inspectBtn);
3899
- const pinIcon = createLucideElement2(MapPin);
3900
- pinIcon.setAttribute("class", "size-3.5");
3901
- pinIcon.setAttribute("aria-hidden", "true");
3902
- const pinBtn = el(
3903
- "button",
3904
- {
3905
- class: BUTTON_CLASS,
3906
- attrs: {
3907
- type: "button",
3908
- "data-uidex-menubar-pins": "",
3909
- "aria-label": "Report pins"
3910
- }
3911
- },
3912
- pinIcon
3913
- );
3914
- const pinWrapper = el("div", {
3915
- class: "relative z-1 inline-flex items-center gap-0.5",
3916
- attrs: { "data-uidex-menubar-pin-wrapper": "" }
3917
- });
3918
- pinWrapper.hidden = true;
3919
- pinWrapper.appendChild(pinBtn);
3920
- root.appendChild(pinWrapper);
3921
- const updatePinUI = () => {
3922
- if (!activePinLayer) {
3923
- pinWrapper.hidden = true;
3924
- return;
3925
- }
3926
- const pinsVisible = activePinLayer.visible;
3927
- pinWrapper.hidden = false;
3928
- pinBtn.className = cn(BUTTON_CLASS, pinsVisible && BUTTON_ACTIVE_CLASS);
3929
- };
3930
- pinBtn.addEventListener("click", (e) => {
3931
- e.stopPropagation();
3932
- if (activePinLayer) {
3933
- activePinLayer.setVisible(!activePinLayer.visible);
3934
- }
3935
- });
3936
- let unsubscribePinFilter = activePinLayer?.onFilterChange(() => updatePinUI());
3937
- const presenceIcon = createLucideElement2(Users);
3938
- presenceIcon.setAttribute("class", "size-3.5");
3939
- presenceIcon.setAttribute("aria-hidden", "true");
3940
- const presenceBtn = el(
3941
- "button",
3942
- {
3943
- class: BUTTON_CLASS,
3944
- attrs: {
3945
- type: "button",
3946
- "data-uidex-menubar-presence": "",
3947
- "aria-label": "Online users"
3948
- }
3949
- },
3950
- presenceIcon
3951
- );
3952
- presenceBtn.hidden = true;
3953
- const presenceBadge = el("span", {
3954
- 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"
3955
- });
3956
- presenceBtn.appendChild(presenceBadge);
3957
- const presencePopover = el("div", {
3958
- 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"
3959
- });
3960
- presencePopover.hidden = true;
3961
- let presencePopoverVisible = false;
3962
- presenceBtn.addEventListener("click", (e) => {
3963
- e.stopPropagation();
3964
- presencePopoverVisible = !presencePopoverVisible;
3965
- presencePopover.hidden = !presencePopoverVisible;
3966
- });
3967
- const presenceWrapper = el("div", { class: "relative z-1 inline-flex" }, [
3968
- presenceBtn,
3969
- presencePopover
3970
- ]);
3971
- presenceWrapper.hidden = true;
3972
- root.appendChild(presenceWrapper);
3973
- let presenceUsers = [];
3974
- const updatePresenceUI = () => {
3975
- const localUserId = session.getState().user?.id ?? null;
3976
- const peers = localUserId ? presenceUsers.filter((u) => u.userId !== localUserId) : presenceUsers;
3977
- const count = peers.length;
3978
- presenceWrapper.hidden = count === 0;
3979
- presenceBadge.textContent = String(count);
3980
- presenceBtn.setAttribute(
3981
- "aria-label",
3982
- count === 1 ? "1 other user online" : `${count} other users online`
3983
- );
3984
- presencePopover.innerHTML = "";
3985
- for (const user of peers) {
3986
- const row = el("div", {
3987
- class: "flex items-center gap-2 rounded px-1.5 py-1"
3988
- });
3989
- const dot = el("span", {
3990
- class: "inline-block size-2 shrink-0 rounded-full bg-emerald-500"
3991
- });
3992
- const name = el("span", {
3993
- class: "truncate",
3994
- text: user.name || "Anonymous"
3995
- });
3996
- row.appendChild(dot);
3997
- row.appendChild(name);
3998
- presencePopover.appendChild(row);
3999
- }
4000
- };
4001
3287
  const paletteIcon = createLucideElement2(Command);
4002
3288
  paletteIcon.setAttribute("class", "size-3.5");
4003
3289
  paletteIcon.setAttribute("aria-hidden", "true");
@@ -4112,28 +3398,14 @@ function createMenuBar(options) {
4112
3398
  const vertical = y / (window.innerHeight || 1) < 0.5 ? "top" : "bottom";
4113
3399
  snapTo(`${vertical}-${horizontal}`);
4114
3400
  }
4115
- const unsubscribePresence = channel?.onPresence(
4116
- (users) => {
4117
- presenceUsers = users;
4118
- updatePresenceUI();
4119
- }
4120
- );
4121
3401
  return {
4122
3402
  destroy() {
4123
- unsubscribePinFilter?.();
4124
- unsubscribePresence?.();
4125
3403
  unsubscribeSession();
4126
3404
  root.removeEventListener("mousedown", onMouseDown);
4127
3405
  document.removeEventListener("mousemove", onMouseMove);
4128
3406
  document.removeEventListener("mouseup", onMouseUp);
4129
3407
  root.remove();
4130
3408
  },
4131
- setPinLayer(layer) {
4132
- unsubscribePinFilter?.();
4133
- activePinLayer = layer;
4134
- unsubscribePinFilter = layer.onFilterChange(() => updatePinUI());
4135
- updatePinUI();
4136
- },
4137
3409
  snapTo,
4138
3410
  snapToNearest,
4139
3411
  get corner() {
@@ -4491,9 +3763,7 @@ function createSurfaceShell(options) {
4491
3763
  container: host.chromeEl,
4492
3764
  session: options.session,
4493
3765
  initialCorner: options.initialCorner,
4494
- appTitle: options.appTitle,
4495
- channel: options.channel ?? null,
4496
- pinLayer: options.pinLayer ?? null
3766
+ appTitle: options.appTitle
4497
3767
  });
4498
3768
  cleanup.add(menuBar);
4499
3769
  return {
@@ -4508,547 +3778,6 @@ function createSurfaceShell(options) {
4508
3778
  };
4509
3779
  }
4510
3780
 
4511
- // src/browser/report/constants.ts
4512
- var TYPE_LABELS = {
4513
- bug: "Bug",
4514
- request: "Request",
4515
- suggestion: "Suggestion"
4516
- };
4517
- var SEVERITY_LABELS = {
4518
- critical: "Critical",
4519
- high: "High",
4520
- medium: "Medium",
4521
- low: "Low"
4522
- };
4523
- function authorLabel(rec) {
4524
- const name = rec.reporter?.name ?? rec.reporterName;
4525
- const email = rec.reporter?.email ?? rec.reporterEmail;
4526
- return name?.trim() || email?.trim() || "Anonymous";
4527
- }
4528
- function relativeTime(iso) {
4529
- const ms = Date.parse(iso);
4530
- if (!Number.isFinite(ms)) return "";
4531
- const s = Math.floor((Date.now() - ms) / 1e3);
4532
- if (s < 60) return "just now";
4533
- const m = Math.floor(s / 60);
4534
- if (m < 60) return `${m}m ago`;
4535
- const h = Math.floor(m / 60);
4536
- if (h < 24) return `${h}h ago`;
4537
- const d = Math.floor(h / 24);
4538
- if (d < 30) return `${d}d ago`;
4539
- return `${Math.floor(d / 30)}mo ago`;
4540
- }
4541
-
4542
- // src/browser/surface/pin-layer.ts
4543
- import {
4544
- Bug,
4545
- CircleHelp,
4546
- Lightbulb,
4547
- MessageSquarePlus,
4548
- Sparkles as Sparkles2,
4549
- StickyNote,
4550
- createElement as createLucideElement3
4551
- } from "lucide";
4552
- var KNOWN_KINDS = new Set(UIDEX_ATTR_TO_KIND.map(([, k]) => k));
4553
- function parseComponentRef(componentId) {
4554
- const colon = componentId.indexOf(":");
4555
- if (colon > 0) {
4556
- const maybeKind = componentId.slice(0, colon);
4557
- if (KNOWN_KINDS.has(maybeKind)) {
4558
- return { kind: maybeKind, id: componentId.slice(colon + 1) };
4559
- }
4560
- }
4561
- return { kind: "element", id: componentId };
4562
- }
4563
- var DOT = 28;
4564
- var BORDER = 2;
4565
- var ICON = 16;
4566
- var PAD = 10;
4567
- var CARD_W = 280;
4568
- var LINE_H = 17;
4569
- var HEADER_H = 16;
4570
- var BODY_MT = 2;
4571
- var CYCLE_H = 18;
4572
- var CARD_H = PAD + HEADER_H + BODY_MT + LINE_H * 2 + CYCLE_H + PAD;
4573
- var INSET = 4;
4574
- var TYPE_ICON = {
4575
- bug: Bug,
4576
- request: MessageSquarePlus,
4577
- suggestion: Lightbulb,
4578
- feature: Lightbulb,
4579
- improvement: Sparkles2,
4580
- question: CircleHelp,
4581
- note: StickyNote
4582
- };
4583
- var TYPE_COLOR = {
4584
- bug: {
4585
- bg: "color-mix(in srgb, var(--destructive) 10%, var(--background))",
4586
- fg: "var(--destructive-foreground)"
4587
- },
4588
- request: {
4589
- bg: "color-mix(in srgb, var(--info) 10%, var(--background))",
4590
- fg: "var(--info-foreground)"
4591
- },
4592
- suggestion: {
4593
- bg: "color-mix(in srgb, var(--warning) 10%, var(--background))",
4594
- fg: "var(--warning-foreground)"
4595
- },
4596
- feature: { bg: "var(--secondary)", fg: "var(--secondary-foreground)" },
4597
- improvement: {
4598
- bg: "color-mix(in srgb, var(--info) 10%, var(--background))",
4599
- fg: "var(--info-foreground)"
4600
- },
4601
- question: {
4602
- bg: "color-mix(in srgb, var(--warning) 10%, var(--background))",
4603
- fg: "var(--warning-foreground)"
4604
- },
4605
- note: {
4606
- bg: "color-mix(in srgb, var(--primary) 10%, var(--background))",
4607
- fg: "var(--primary)"
4608
- }
4609
- };
4610
- var FALLBACK_COLOR = {
4611
- bg: "var(--secondary)",
4612
- fg: "var(--secondary-foreground)"
4613
- };
4614
- function typeColors(t) {
4615
- return TYPE_COLOR[t] ?? FALLBACK_COLOR;
4616
- }
4617
- function typeIcon(t) {
4618
- return TYPE_ICON[t] ?? StickyNote;
4619
- }
4620
- var EASE_OUT = "cubic-bezier(0.16, 1, 0.3, 1)";
4621
- var DUR_FADE = 120;
4622
- var DUR_EXPAND = 250;
4623
- var T_FADE = `opacity ${DUR_FADE}ms ease`;
4624
- function createPinLayer(options) {
4625
- const { container, onOpenPinDetail, onHoverPin, onPinsChanged } = options;
4626
- const layerEl = document.createElement("div");
4627
- layerEl.setAttribute("data-uidex-pin-layer", "");
4628
- Object.assign(layerEl.style, {
4629
- position: "fixed",
4630
- inset: "0",
4631
- zIndex: String(Z_PIN_LAYER),
4632
- pointerEvents: "none"
4633
- });
4634
- container.appendChild(layerEl);
4635
- let layerVisible = true;
4636
- const allPins = [];
4637
- const seenIds = /* @__PURE__ */ new Set();
4638
- const byComp = /* @__PURE__ */ new Map();
4639
- const indicators = /* @__PURE__ */ new Map();
4640
- const filterCbs = /* @__PURE__ */ new Set();
4641
- const notifyFilter = () => {
4642
- for (const cb of filterCbs) cb();
4643
- };
4644
- const rebuildFiltered = () => {
4645
- byComp.clear();
4646
- for (const p of allPins) {
4647
- const cid = p.entity ?? "";
4648
- const list = byComp.get(cid);
4649
- if (list) list.push(p);
4650
- else byComp.set(cid, [p]);
4651
- }
4652
- };
4653
- const rerender = () => {
4654
- rebuildFiltered();
4655
- for (const id of Array.from(indicators.keys())) {
4656
- if (!byComp.has(id)) removeIndicator(id);
4657
- }
4658
- for (const id of byComp.keys()) renderIndicator(id);
4659
- notifyFilter();
4660
- onPinsChanged?.();
4661
- };
4662
- let obs = null;
4663
- const repositioner = createRepositioner(() => posAll());
4664
- const attachObs = () => {
4665
- if (obs || typeof MutationObserver === "undefined") return;
4666
- obs = new MutationObserver((recs) => {
4667
- for (const r of recs) {
4668
- if (r.type === "childList" && (r.addedNodes.length || r.removedNodes.length)) {
4669
- refreshAnchors();
4670
- return;
4671
- }
4672
- }
4673
- });
4674
- obs.observe(document.body, { childList: true, subtree: true });
4675
- };
4676
- const detachObs = () => {
4677
- obs?.disconnect();
4678
- obs = null;
4679
- };
4680
- const refreshAnchors = () => {
4681
- let changed = false;
4682
- for (const s of indicators.values()) {
4683
- if (s.anchor && s.anchor.isConnected) continue;
4684
- s.anchor = resolveEntityElement(parseComponentRef(s.componentId));
4685
- changed = true;
4686
- }
4687
- if (changed) repositioner.schedule();
4688
- };
4689
- const expand = (st) => {
4690
- if (st.expanded) return;
4691
- st.expanded = true;
4692
- fillContent(st);
4693
- st.wrap.style.zIndex = "1";
4694
- st.iconEl.style.transition = T_FADE;
4695
- st.iconEl.style.opacity = "0";
4696
- st.badgeEl.style.visibility = "hidden";
4697
- st.el.style.transition = `width ${DUR_EXPAND}ms ${EASE_OUT} ${DUR_FADE}ms, height ${DUR_EXPAND}ms ${EASE_OUT} ${DUR_FADE}ms`;
4698
- st.el.style.width = `${CARD_W}px`;
4699
- st.el.style.height = `${CARD_H}px`;
4700
- st.contentEl.style.transition = `opacity ${DUR_FADE}ms ease ${DUR_FADE + DUR_EXPAND}ms`;
4701
- st.contentEl.style.opacity = "1";
4702
- if (st.anchor) onHoverPin?.(st.anchor, st.componentId);
4703
- };
4704
- const collapse = (st) => {
4705
- if (!st.expanded) return;
4706
- st.expanded = false;
4707
- st.contentEl.style.transition = T_FADE;
4708
- st.contentEl.style.opacity = "0";
4709
- st.el.style.transition = `width ${DUR_EXPAND}ms ${EASE_OUT} ${DUR_FADE}ms, height ${DUR_EXPAND}ms ${EASE_OUT} ${DUR_FADE}ms`;
4710
- st.el.style.width = `${DOT}px`;
4711
- st.el.style.height = `${DOT}px`;
4712
- st.iconEl.style.transition = `opacity ${DUR_FADE}ms ease ${DUR_FADE + DUR_EXPAND}ms`;
4713
- st.iconEl.style.opacity = "1";
4714
- st.badgeEl.style.visibility = "";
4715
- st.iconEl.addEventListener(
4716
- "transitionend",
4717
- () => {
4718
- if (!st.expanded) st.wrap.style.zIndex = "";
4719
- },
4720
- { once: true }
4721
- );
4722
- onHoverPin?.(null, null);
4723
- };
4724
- const fillContent = (st) => {
4725
- const pins = byComp.get(st.componentId);
4726
- if (!pins?.length) return;
4727
- const idx = Math.max(0, Math.min(st.pinIndex, pins.length - 1));
4728
- st.pinIndex = idx;
4729
- const pin = pins[idx];
4730
- st.contentEl.innerHTML = "";
4731
- const hdr = document.createElement("div");
4732
- hdr.style.display = "flex";
4733
- hdr.style.alignItems = "baseline";
4734
- hdr.style.gap = "6px";
4735
- const nm = document.createElement("span");
4736
- nm.style.fontWeight = "600";
4737
- nm.style.fontSize = "12px";
4738
- nm.textContent = authorLabel(pin);
4739
- hdr.appendChild(nm);
4740
- const tm = document.createElement("span");
4741
- tm.style.fontSize = "11px";
4742
- tm.style.opacity = "0.75";
4743
- tm.textContent = relativeTime(pin.createdAt);
4744
- hdr.appendChild(tm);
4745
- st.contentEl.appendChild(hdr);
4746
- const body = document.createElement("div");
4747
- Object.assign(body.style, {
4748
- fontSize: "12px",
4749
- lineHeight: "1.4",
4750
- marginTop: "2px",
4751
- whiteSpace: "pre-wrap",
4752
- overflow: "hidden",
4753
- display: "-webkit-box"
4754
- });
4755
- body.style.setProperty("-webkit-line-clamp", "2");
4756
- body.style.setProperty("-webkit-box-orient", "vertical");
4757
- body.textContent = pin.body;
4758
- st.contentEl.appendChild(body);
4759
- const ctr = document.createElement("div");
4760
- Object.assign(ctr.style, {
4761
- fontSize: "10px",
4762
- opacity: "0.5",
4763
- marginTop: "4px"
4764
- });
4765
- ctr.textContent = pins.length > 1 ? `${idx + 1} of ${pins.length} \xB7 right-click to cycle` : "click to open";
4766
- st.contentEl.appendChild(ctr);
4767
- };
4768
- const buildIndicator = (componentId) => {
4769
- const wrap = document.createElement("div");
4770
- wrap.setAttribute("data-uidex-pin-wrap", "");
4771
- Object.assign(wrap.style, {
4772
- position: "fixed",
4773
- display: "none",
4774
- pointerEvents: "none"
4775
- });
4776
- const el2 = document.createElement("button");
4777
- el2.type = "button";
4778
- el2.setAttribute("data-uidex-pin", "");
4779
- el2.setAttribute("data-uidex-pin-component", componentId);
4780
- el2.setAttribute("aria-label", `Open report for ${componentId}`);
4781
- Object.assign(el2.style, {
4782
- position: "relative",
4783
- width: `${DOT}px`,
4784
- height: `${DOT}px`,
4785
- borderRadius: `${DOT / 2}px`,
4786
- border: `${BORDER}px solid var(--background)`,
4787
- padding: "0",
4788
- margin: "0",
4789
- cursor: "pointer",
4790
- pointerEvents: "auto",
4791
- overflow: "hidden",
4792
- boxShadow: "0 2px 8px rgba(0,0,0,0.15), 0 0 0 1px rgba(0,0,0,0.05)",
4793
- fontFamily: "var(--font-sans)",
4794
- textAlign: "left",
4795
- float: "right"
4796
- });
4797
- wrap.appendChild(el2);
4798
- const iconEl = document.createElement("span");
4799
- iconEl.setAttribute("data-uidex-pin-icon", "");
4800
- Object.assign(iconEl.style, {
4801
- position: "absolute",
4802
- inset: "0",
4803
- display: "flex",
4804
- alignItems: "center",
4805
- justifyContent: "center"
4806
- });
4807
- el2.appendChild(iconEl);
4808
- const badgeEl = document.createElement("span");
4809
- badgeEl.setAttribute("data-uidex-pin-badge", "");
4810
- Object.assign(badgeEl.style, {
4811
- position: "absolute",
4812
- top: "-4px",
4813
- right: "-4px",
4814
- minWidth: "14px",
4815
- height: "14px",
4816
- borderRadius: "999px",
4817
- background: "var(--foreground)",
4818
- color: "var(--background)",
4819
- fontSize: "9px",
4820
- fontWeight: "600",
4821
- display: "none",
4822
- alignItems: "center",
4823
- justifyContent: "center",
4824
- padding: "0 3px",
4825
- lineHeight: "1",
4826
- zIndex: "2",
4827
- pointerEvents: "none"
4828
- });
4829
- wrap.appendChild(badgeEl);
4830
- const contentEl = document.createElement("div");
4831
- contentEl.setAttribute("data-uidex-pin-content", "");
4832
- Object.assign(contentEl.style, {
4833
- position: "absolute",
4834
- top: `${PAD}px`,
4835
- left: `${PAD}px`,
4836
- right: `${PAD}px`,
4837
- opacity: "0",
4838
- minWidth: "0"
4839
- });
4840
- el2.appendChild(contentEl);
4841
- const st = {
4842
- componentId,
4843
- wrap,
4844
- el: el2,
4845
- iconEl,
4846
- badgeEl,
4847
- contentEl,
4848
- anchor: null,
4849
- pinIndex: 0,
4850
- expanded: false,
4851
- lastType: null
4852
- };
4853
- el2.addEventListener("mouseenter", () => expand(st));
4854
- el2.addEventListener("mouseleave", () => collapse(st));
4855
- el2.addEventListener("click", (e) => {
4856
- e.stopPropagation();
4857
- e.preventDefault();
4858
- const pins = byComp.get(componentId);
4859
- const pin = pins?.[st.pinIndex];
4860
- if (pin) onOpenPinDetail?.(componentId, pin.id);
4861
- });
4862
- el2.addEventListener("contextmenu", (e) => {
4863
- e.stopPropagation();
4864
- e.preventDefault();
4865
- const pins = byComp.get(componentId);
4866
- if (!pins || pins.length <= 1) return;
4867
- st.pinIndex = (st.pinIndex + 1) % pins.length;
4868
- applyStyle(st);
4869
- if (st.expanded) fillContent(st);
4870
- });
4871
- layerEl.appendChild(wrap);
4872
- return st;
4873
- };
4874
- const applyStyle = (st) => {
4875
- const pins = byComp.get(st.componentId);
4876
- if (!pins?.length) return;
4877
- const idx = Math.max(0, Math.min(st.pinIndex, pins.length - 1));
4878
- st.pinIndex = idx;
4879
- const pin = pins[idx];
4880
- const c = typeColors(pin.type);
4881
- st.el.style.backgroundColor = "var(--background)";
4882
- st.el.style.backgroundImage = `linear-gradient(${c.bg}, ${c.bg})`;
4883
- st.el.style.color = c.fg;
4884
- if (st.lastType !== pin.type) {
4885
- st.lastType = pin.type;
4886
- st.iconEl.innerHTML = "";
4887
- const svg2 = createLucideElement3(typeIcon(pin.type));
4888
- svg2.setAttribute("width", String(ICON));
4889
- svg2.setAttribute("height", String(ICON));
4890
- st.iconEl.appendChild(svg2);
4891
- }
4892
- if (pins.length > 1) {
4893
- st.badgeEl.textContent = String(pins.length);
4894
- st.badgeEl.style.display = "flex";
4895
- } else {
4896
- st.badgeEl.textContent = "";
4897
- st.badgeEl.style.display = "none";
4898
- }
4899
- };
4900
- const posIndicator = (st) => {
4901
- const a = st.anchor;
4902
- if (!a || !a.isConnected) {
4903
- st.wrap.style.display = "none";
4904
- return;
4905
- }
4906
- const r = a.getBoundingClientRect();
4907
- if (r.width === 0 && r.height === 0) {
4908
- st.wrap.style.display = "none";
4909
- return;
4910
- }
4911
- const vw = window.innerWidth;
4912
- st.wrap.style.top = `${r.top + INSET}px`;
4913
- st.wrap.style.right = `${vw - r.right + INSET}px`;
4914
- st.wrap.style.display = "";
4915
- };
4916
- const posAll = () => {
4917
- for (const st of indicators.values()) posIndicator(st);
4918
- };
4919
- const renderIndicator = (componentId) => {
4920
- const pins = byComp.get(componentId);
4921
- if (!pins?.length) {
4922
- removeIndicator(componentId);
4923
- return;
4924
- }
4925
- let st = indicators.get(componentId);
4926
- if (!st) {
4927
- st = buildIndicator(componentId);
4928
- indicators.set(componentId, st);
4929
- repositioner.attach();
4930
- attachObs();
4931
- }
4932
- if (st.pinIndex >= pins.length) st.pinIndex = 0;
4933
- applyStyle(st);
4934
- st.anchor = resolveEntityElement(parseComponentRef(componentId));
4935
- posIndicator(st);
4936
- };
4937
- const removeIndicator = (componentId) => {
4938
- const st = indicators.get(componentId);
4939
- if (!st) return;
4940
- st.wrap.remove();
4941
- indicators.delete(componentId);
4942
- if (indicators.size === 0) {
4943
- repositioner.detach();
4944
- detachObs();
4945
- }
4946
- };
4947
- const ingest = (pin) => {
4948
- const cid = pin.entity ?? "";
4949
- if (!cid || seenIds.has(pin.id)) return false;
4950
- seenIds.add(pin.id);
4951
- allPins.push(pin);
4952
- return true;
4953
- };
4954
- let activeRefresh = null;
4955
- const layer = {
4956
- setPins(pins) {
4957
- allPins.length = 0;
4958
- seenIds.clear();
4959
- for (const p of pins) ingest(p);
4960
- rerender();
4961
- },
4962
- addPin(pin) {
4963
- if (!ingest(pin)) return;
4964
- rerender();
4965
- },
4966
- removePin(reportId) {
4967
- const i = allPins.findIndex((p) => p.id === reportId);
4968
- if (i === -1) return;
4969
- allPins.splice(i, 1);
4970
- seenIds.delete(reportId);
4971
- rerender();
4972
- },
4973
- clear() {
4974
- allPins.length = 0;
4975
- seenIds.clear();
4976
- byComp.clear();
4977
- for (const id of Array.from(indicators.keys())) removeIndicator(id);
4978
- notifyFilter();
4979
- },
4980
- getPinsForElement: (id) => byComp.get(id) ?? [],
4981
- getAllPins: () => allPins.slice(),
4982
- attachChannel(channel) {
4983
- const offPin = channel.onPin((pin) => {
4984
- layer.addPin(pin);
4985
- });
4986
- const offArchived = channel.onPinArchived?.((reportId) => {
4987
- layer.removePin(reportId);
4988
- }) ?? (() => {
4989
- });
4990
- return () => {
4991
- offPin();
4992
- offArchived();
4993
- };
4994
- },
4995
- attachCloud(opts) {
4996
- const offCh = opts.channel ? layer.attachChannel(opts.channel) : () => {
4997
- };
4998
- const fetch2 = async () => {
4999
- try {
5000
- const mode = opts.getMatchMode();
5001
- let params;
5002
- if (mode === "component") {
5003
- params = { entities: opts.getVisibleEntities().join(",") };
5004
- } else if (mode === "pathname") {
5005
- params = { route: opts.getPathname() };
5006
- } else {
5007
- params = { route: opts.getRoute() };
5008
- }
5009
- layer.setPins(await opts.cloud.pins.list(params));
5010
- } catch (err) {
5011
- opts.onError?.(err);
5012
- }
5013
- };
5014
- void fetch2();
5015
- activeRefresh = fetch2;
5016
- return () => {
5017
- offCh();
5018
- if (activeRefresh === fetch2) activeRefresh = null;
5019
- };
5020
- },
5021
- async refresh() {
5022
- if (activeRefresh) await activeRefresh();
5023
- },
5024
- onFilterChange(cb) {
5025
- filterCbs.add(cb);
5026
- return () => {
5027
- filterCbs.delete(cb);
5028
- };
5029
- },
5030
- get visible() {
5031
- return layerVisible;
5032
- },
5033
- setVisible(v) {
5034
- if (layerVisible === v) return;
5035
- layerVisible = v;
5036
- layerEl.style.display = v ? "" : "none";
5037
- if (v) posAll();
5038
- notifyFilter();
5039
- },
5040
- destroy() {
5041
- layer.clear();
5042
- repositioner.detach();
5043
- detachObs();
5044
- layerEl.remove();
5045
- activeRefresh = null;
5046
- filterCbs.clear();
5047
- }
5048
- };
5049
- return layer;
5050
- }
5051
-
5052
3781
  // src/browser/views/core/types.ts
5053
3782
  var ViewValidationError = class extends Error {
5054
3783
  constructor(message) {
@@ -5244,7 +3973,7 @@ function createChip(options = {}, children = []) {
5244
3973
  }
5245
3974
 
5246
3975
  // src/browser/views/primitives/kind-icon.ts
5247
- import { createElement as createLucideElement4 } from "lucide";
3976
+ import { createElement as createLucideElement3 } from "lucide";
5248
3977
 
5249
3978
  // src/browser/ui/cva.ts
5250
3979
  function cva(base, config = {}) {
@@ -5332,7 +4061,7 @@ function iconTileTpl(inner, options = {}) {
5332
4061
  // src/browser/views/primitives/kind-icon.ts
5333
4062
  function renderKindIcon(kind) {
5334
4063
  const style = KIND_STYLE[kind];
5335
- const svg2 = createLucideElement4(style.icon);
4064
+ const svg2 = createLucideElement3(style.icon);
5336
4065
  svg2.setAttribute("aria-hidden", "true");
5337
4066
  svg2.setAttribute("class", cn("h-4 w-4 shrink-0", style.tone));
5338
4067
  return svg2;
@@ -5444,7 +4173,7 @@ function createBindable(propsFn) {
5444
4173
  }
5445
4174
  };
5446
4175
  }
5447
- function createMachineHandle(zagMachine, connect4, normalize, props = {}) {
4176
+ function createMachineHandle(zagMachine, connect3, normalize, props = {}) {
5448
4177
  const runner = createMachineRunner(
5449
4178
  zagMachine,
5450
4179
  props
@@ -5452,12 +4181,12 @@ function createMachineHandle(zagMachine, connect4, normalize, props = {}) {
5452
4181
  runner.start();
5453
4182
  return {
5454
4183
  runner,
5455
- api: () => connect4(runner.service, normalize),
4184
+ api: () => connect3(runner.service, normalize),
5456
4185
  destroy: () => runner.stop()
5457
4186
  };
5458
4187
  }
5459
4188
  function createMachineRunner(machineArg, initialProps = {}) {
5460
- const machine4 = machineArg;
4189
+ const machine3 = machineArg;
5461
4190
  let userProps = initialProps;
5462
4191
  const unmountFns = [];
5463
4192
  const listeners = /* @__PURE__ */ new Set();
@@ -5474,7 +4203,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5474
4203
  };
5475
4204
  };
5476
4205
  const debug = (...args) => {
5477
- if (machine4.debug) {
4206
+ if (machine3.debug) {
5478
4207
  console.log(...args);
5479
4208
  }
5480
4209
  };
@@ -5482,7 +4211,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5482
4211
  const prop = (key) => propsRef.current[key];
5483
4212
  const refreshProps = () => {
5484
4213
  const scope = scopeRef.current;
5485
- const computedProps = machine4.props?.({
4214
+ const computedProps = machine3.props?.({
5486
4215
  props: compact(userProps),
5487
4216
  scope
5488
4217
  }) ?? userProps;
@@ -5516,7 +4245,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5516
4245
  return values.some((v) => matchesState(state.ref.current, v));
5517
4246
  },
5518
4247
  hasTag(tag) {
5519
- return hasTagFn(machine4, state.ref.current, tag);
4248
+ return hasTagFn(machine3, state.ref.current, tag);
5520
4249
  }
5521
4250
  });
5522
4251
  const getParams = () => ({
@@ -5538,7 +4267,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5538
4267
  const strs = isFunction(keys) ? keys(getParams()) : keys;
5539
4268
  if (!strs) return;
5540
4269
  const fns = strs.map((s) => {
5541
- const fn = machine4.implementations?.actions?.[s];
4270
+ const fn = machine3.implementations?.actions?.[s];
5542
4271
  if (!fn) warn(`[uidex] No implementation found for action "${s}"`);
5543
4272
  return fn;
5544
4273
  });
@@ -5546,13 +4275,13 @@ function createMachineRunner(machineArg, initialProps = {}) {
5546
4275
  };
5547
4276
  const guard = (str) => {
5548
4277
  if (isFunction(str)) return str(getParams());
5549
- return machine4.implementations?.guards?.[str](getParams());
4278
+ return machine3.implementations?.guards?.[str](getParams());
5550
4279
  };
5551
4280
  const effect = (keys) => {
5552
4281
  const strs = isFunction(keys) ? keys(getParams()) : keys;
5553
4282
  if (!strs) return;
5554
4283
  const fns = strs.map((s) => {
5555
- const fn = machine4.implementations?.effects?.[s];
4284
+ const fn = machine3.implementations?.effects?.[s];
5556
4285
  if (!fn) warn(`[uidex] No implementation found for effect "${s}"`);
5557
4286
  return fn;
5558
4287
  });
@@ -5573,10 +4302,10 @@ function createMachineRunner(machineArg, initialProps = {}) {
5573
4302
  });
5574
4303
  const computed = (key) => {
5575
4304
  ensure(
5576
- machine4.computed,
4305
+ machine3.computed,
5577
4306
  () => `[uidex] No computed object found on machine`
5578
4307
  );
5579
- const fn = machine4.computed[key];
4308
+ const fn = machine3.computed[key];
5580
4309
  return fn({
5581
4310
  context: ctx,
5582
4311
  event: getEvent(),
@@ -5609,7 +4338,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5609
4338
  queueMicrotask(() => fn());
5610
4339
  };
5611
4340
  let contextFields = {};
5612
- const ctxContextFn = machine4.context;
4341
+ const ctxContextFn = machine3.context;
5613
4342
  if (ctxContextFn) {
5614
4343
  contextFields = ctxContextFn({
5615
4344
  prop,
@@ -5645,7 +4374,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5645
4374
  return contextFields[key]?.hash(current) ?? "";
5646
4375
  }
5647
4376
  };
5648
- const refsInit = machine4.refs?.({ prop, context: ctx }) ?? {};
4377
+ const refsInit = machine3.refs?.({ prop, context: ctx }) ?? {};
5649
4378
  const refsStore = { ...refsInit };
5650
4379
  const refs = {
5651
4380
  get(key) {
@@ -5656,14 +4385,14 @@ function createMachineRunner(machineArg, initialProps = {}) {
5656
4385
  }
5657
4386
  };
5658
4387
  const initialState = resolveStateValue(
5659
- machine4,
5660
- machine4.initialState({ prop })
4388
+ machine3,
4389
+ machine3.initialState({ prop })
5661
4390
  );
5662
4391
  const state = createBindable(() => ({
5663
4392
  defaultValue: initialState,
5664
4393
  onChange(nextState, prevState) {
5665
4394
  const { exiting, entering } = getExitEnterStates(
5666
- machine4,
4395
+ machine3,
5667
4396
  prevState,
5668
4397
  nextState,
5669
4398
  transitionRef?.reenter
@@ -5682,8 +4411,8 @@ function createMachineRunner(machineArg, initialProps = {}) {
5682
4411
  if (cleanup) effectsMap.set(item.path, cleanup);
5683
4412
  });
5684
4413
  if (prevState === INIT_STATE) {
5685
- action(machine4.entry);
5686
- const cleanup = effect(machine4.effects);
4414
+ action(machine3.entry);
4415
+ const cleanup = effect(machine3.effects);
5687
4416
  if (cleanup) effectsMap.set(INIT_STATE, cleanup);
5688
4417
  }
5689
4418
  entering.forEach((item) => {
@@ -5702,7 +4431,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5702
4431
  currentEvent = event;
5703
4432
  const currentState = getCurrentState();
5704
4433
  const { transitions, source } = findTransition(
5705
- machine4,
4434
+ machine3,
5706
4435
  currentState,
5707
4436
  event.type
5708
4437
  );
@@ -5710,7 +4439,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5710
4439
  if (!transition) return;
5711
4440
  transitionRef = transition;
5712
4441
  const target = resolveStateValue(
5713
- machine4,
4442
+ machine3,
5714
4443
  transition.target ?? currentState,
5715
4444
  source
5716
4445
  );
@@ -5730,7 +4459,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5730
4459
  }
5731
4460
  });
5732
4461
  };
5733
- machine4.watch?.(getParams());
4462
+ machine3.watch?.(getParams());
5734
4463
  const service = {
5735
4464
  state: getState(),
5736
4465
  send,
@@ -5771,7 +4500,7 @@ function createMachineRunner(machineArg, initialProps = {}) {
5771
4500
  while (unmountFns.length) unmountFns.pop()?.();
5772
4501
  listeners.clear();
5773
4502
  queueMicrotask(() => {
5774
- action(machine4.exit);
4503
+ action(machine3.exit);
5775
4504
  });
5776
4505
  },
5777
4506
  subscribe(listener) {
@@ -5849,9 +4578,6 @@ function applyProps(node, props) {
5849
4578
  }
5850
4579
 
5851
4580
  // src/browser/views/builder/spread-props.ts
5852
- function spreadProps(node, props) {
5853
- return applyProps(node, props);
5854
- }
5855
4581
  function createPersistentSpreads() {
5856
4582
  const map = /* @__PURE__ */ new Map();
5857
4583
  return {
@@ -5982,10 +4708,10 @@ import {
5982
4708
  Highlighter as Highlighter2,
5983
4709
  Inbox,
5984
4710
  MessageCircleWarning,
5985
- StickyNote as StickyNote2,
4711
+ StickyNote,
5986
4712
  TicketPlus,
5987
4713
  View,
5988
- createElement as createLucideElement5
4714
+ createElement as createLucideElement4
5989
4715
  } from "lucide";
5990
4716
  var ICON_MAP = {
5991
4717
  "archive-x": ArchiveX,
@@ -5995,7 +4721,7 @@ var ICON_MAP = {
5995
4721
  "message-circle-warning": MessageCircleWarning,
5996
4722
  "chevron-down": ChevronDown,
5997
4723
  highlighter: Highlighter2,
5998
- "sticky-note": StickyNote2,
4724
+ "sticky-note": StickyNote,
5999
4725
  "ticket-plus": TicketPlus,
6000
4726
  view: View
6001
4727
  };
@@ -6036,7 +4762,7 @@ function renderActions(actions, ctx, root, heading) {
6036
4762
  let leading;
6037
4763
  const iconNode = iconFor(action.icon);
6038
4764
  if (iconNode) {
6039
- const svg2 = createLucideElement5(iconNode);
4765
+ const svg2 = createLucideElement4(iconNode);
6040
4766
  svg2.setAttribute("aria-hidden", "true");
6041
4767
  svg2.setAttribute("class", "h-4 w-4 shrink-0");
6042
4768
  const tile = el(
@@ -6775,858 +5501,84 @@ function renderDetailSurface(surface, ctx, root) {
6775
5501
  return mounted;
6776
5502
  }
6777
5503
 
6778
- // src/browser/views/render/form.ts
6779
- import { CircleCheck, CircleX, Loader2 } from "lucide";
6780
-
6781
- // src/browser/machines/dialog.ts
6782
- import * as dialog from "@zag-js/dialog";
6783
- function createDialog(props = {}) {
6784
- return createMachineHandle(
6785
- dialog.machine,
6786
- dialog.connect,
6787
- normalizeProps,
6788
- props
6789
- );
6790
- }
5504
+ // src/browser/views/render/list.ts
5505
+ import { Circle as Circle2 } from "lucide";
6791
5506
 
6792
- // src/browser/ui/screenshot-lightbox.ts
6793
- function createScreenshotLightbox(trigger, dataUrl, options) {
6794
- const cleanups = [];
6795
- const lightbox = createDialog({
6796
- id: "uidex-screenshot-lightbox",
6797
- modal: true,
6798
- closeOnInteractOutside: false,
6799
- closeOnEscape: false,
6800
- trapFocus: true,
6801
- preventScroll: true,
6802
- restoreFocus: true,
6803
- "aria-label": "Screenshot preview",
6804
- getRootNode: () => trigger.getRootNode()
6805
- });
6806
- cleanups.push(() => lightbox.destroy());
6807
- const backdropRef = createRef();
6808
- const positionerRef = createRef();
6809
- const contentRef = createRef();
6810
- const fullImgRef = createRef();
6811
- const mountTarget = (() => {
6812
- const rn = trigger.getRootNode();
6813
- return rn instanceof ShadowRoot ? rn : rn.body;
6814
- })();
6815
- const fragment = document.createDocumentFragment();
6816
- render(
6817
- html`
6818
- <div
6819
- ${ref(backdropRef)}
6820
- class="fixed inset-0 z-[2147483647] cursor-pointer bg-black/80"
6821
- data-uidex-lightbox
6822
- hidden
6823
- ></div>
6824
- <div
6825
- ${ref(positionerRef)}
6826
- class="fixed inset-0 z-[2147483647] flex cursor-pointer items-center justify-center p-6"
6827
- hidden
6828
- >
6829
- <div
6830
- ${ref(contentRef)}
6831
- class="flex items-center justify-center outline-none"
6832
- >
6833
- <img
6834
- ${ref(fullImgRef)}
6835
- class="max-h-full max-w-full rounded-lg"
6836
- src=${dataUrl}
6837
- alt="Screenshot"
6838
- />
6839
- </div>
6840
- </div>
6841
- `,
6842
- fragment
6843
- );
6844
- const backdropEl = backdropRef.value;
6845
- const positioner = positionerRef.value;
6846
- const content = contentRef.value;
6847
- const fullImg = fullImgRef.value;
6848
- mountTarget.append(fragment);
6849
- let spreadCleanups = [];
6850
- function applyLightboxProps() {
6851
- for (const c of spreadCleanups) c();
6852
- spreadCleanups = [];
6853
- const api = lightbox.api();
6854
- spreadCleanups.push(
6855
- spreadProps(
6856
- backdropEl,
6857
- api.getBackdropProps()
6858
- ),
6859
- spreadProps(
6860
- positioner,
6861
- api.getPositionerProps()
6862
- ),
6863
- spreadProps(content, api.getContentProps())
6864
- );
6865
- backdropEl.hidden = !api.open;
6866
- positioner.hidden = !api.open;
6867
- }
6868
- applyLightboxProps();
6869
- const unsubLightbox = lightbox.runner.subscribe(applyLightboxProps);
6870
- cleanups.push(() => {
6871
- unsubLightbox();
6872
- for (const c of spreadCleanups) c();
6873
- backdropEl.remove();
6874
- positioner.remove();
6875
- });
6876
- const closeLightbox = () => lightbox.api().setOpen(false);
6877
- if (options?.pushEscapeLayer) {
6878
- cleanups.push(
6879
- options.pushEscapeLayer(() => {
6880
- if (!lightbox.api().open) return false;
6881
- closeLightbox();
6882
- return true;
6883
- })
6884
- );
6885
- } else {
6886
- const onEscapeCapture = (e) => {
6887
- if (e.key !== "Escape") return;
6888
- if (!lightbox.api().open) return;
6889
- e.preventDefault();
6890
- e.stopPropagation();
6891
- e.stopImmediatePropagation();
6892
- closeLightbox();
6893
- };
6894
- window.addEventListener("keydown", onEscapeCapture, true);
6895
- cleanups.push(
6896
- () => window.removeEventListener("keydown", onEscapeCapture, true)
6897
- );
5507
+ // src/browser/views/render/list-controller.ts
5508
+ function createListController(opts) {
5509
+ let items = opts.items;
5510
+ let itemNodes = opts.itemNodes;
5511
+ let highlighted = null;
5512
+ let kbdMode = false;
5513
+ const subs = /* @__PURE__ */ new Set();
5514
+ const { contentEl, onSelect, onHighlightChange } = opts;
5515
+ contentEl.setAttribute("role", "listbox");
5516
+ contentEl.tabIndex = 0;
5517
+ contentEl.style.outline = "none";
5518
+ applyItemAria();
5519
+ if (opts.defaultHighlight && itemNodes.has(opts.defaultHighlight)) {
5520
+ setHighlight(opts.defaultHighlight, false);
6898
5521
  }
6899
- let suppressClickUntil = 0;
6900
- const onClickOutsideCapture = (e) => {
6901
- if (Date.now() < suppressClickUntil) {
6902
- e.preventDefault();
6903
- e.stopPropagation();
6904
- e.stopImmediatePropagation();
6905
- return;
6906
- }
6907
- if (!lightbox.api().open) return;
6908
- if (fullImg.contains(e.target)) return;
6909
- e.preventDefault();
6910
- e.stopPropagation();
6911
- e.stopImmediatePropagation();
6912
- closeLightbox();
6913
- suppressClickUntil = Date.now() + 100;
6914
- };
6915
- window.addEventListener("pointerdown", onClickOutsideCapture, true);
6916
- window.addEventListener("mousedown", onClickOutsideCapture, true);
6917
- window.addEventListener("click", onClickOutsideCapture, true);
6918
- cleanups.push(() => {
6919
- window.removeEventListener("pointerdown", onClickOutsideCapture, true);
6920
- window.removeEventListener("mousedown", onClickOutsideCapture, true);
6921
- window.removeEventListener("click", onClickOutsideCapture, true);
6922
- });
6923
- spreadProps(
6924
- trigger,
6925
- lightbox.api().getTriggerProps()
6926
- );
6927
- return {
6928
- destroy() {
6929
- for (const c of cleanups) c();
6930
- cleanups.length = 0;
6931
- }
6932
- };
6933
- }
6934
-
6935
- // src/browser/ui/button.ts
6936
- 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";
6937
- var buttonVariants = cva(buttonBase, {
6938
- defaultVariants: { size: "default", variant: "default" },
6939
- variants: {
6940
- size: {
6941
- default: "h-8 px-3",
6942
- sm: "h-7 gap-1.5 px-2.5",
6943
- xs: "h-6 gap-1 rounded-md px-2 text-xs",
6944
- lg: "h-9 px-3.5",
6945
- xl: "h-10 px-4 text-base",
6946
- icon: "size-8",
6947
- "icon-sm": "size-7",
6948
- "icon-lg": "size-9",
6949
- "icon-xs": "size-6 rounded-md"
6950
- },
6951
- variant: {
6952
- default: "border-primary bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
6953
- destructive: "border-destructive bg-destructive shadow-xs hover:bg-destructive/90 text-white",
6954
- "destructive-outline": "border-input bg-popover text-destructive-foreground shadow-xs/5 hover:border-destructive/30 hover:bg-destructive/5 dark:bg-input/30",
6955
- ghost: "text-foreground hover:bg-accent hover:text-accent-foreground border-transparent",
6956
- link: "text-foreground border-transparent underline-offset-4 hover:underline",
6957
- outline: "border-input bg-popover text-foreground shadow-xs/5 hover:bg-accent hover:text-accent-foreground dark:bg-input/30",
6958
- secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/90 border-transparent"
5522
+ function values() {
5523
+ return items.map((i) => i.value);
5524
+ }
5525
+ function notify() {
5526
+ for (const cb of subs) cb();
5527
+ }
5528
+ function applyItemAria() {
5529
+ for (const [value, el2] of itemNodes) {
5530
+ el2.setAttribute("role", "option");
5531
+ el2.id = `${opts.surfaceId}-item-${value}`;
6959
5532
  }
6960
5533
  }
6961
- });
6962
-
6963
- // src/browser/views/render/form.ts
6964
- var fieldSeq = 0;
6965
- var nextFieldId = () => `uidex-field-${++fieldSeq}`;
6966
- function issuePathRoot(issue) {
6967
- const seg = issue.path?.[0];
6968
- if (seg === void 0) return void 0;
6969
- if (typeof seg === "object" && seg !== null && "key" in seg) {
6970
- return String(seg.key);
6971
- }
6972
- return String(seg);
6973
- }
6974
- async function validateWithSchema(schema, values) {
6975
- const result = await schema["~standard"].validate(values);
6976
- if (!("issues" in result) || !result.issues) return null;
6977
- const out = {};
6978
- for (const issue of result.issues) {
6979
- const key = issuePathRoot(issue);
6980
- if (!key) continue;
6981
- if (out[key]) continue;
6982
- out[key] = issue.message;
6983
- }
6984
- return Object.keys(out).length > 0 ? out : null;
6985
- }
6986
- 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";
6987
- 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";
6988
- var FIELD_ROOT_CLASS = "flex flex-col items-start gap-2";
6989
- var FIELD_LABEL_CLASS = "text-base/4.5 text-foreground inline-flex items-center gap-2 font-medium sm:text-sm/4";
6990
- var FIELD_ERROR_CLASS = "text-destructive-foreground text-xs";
6991
- var CHECKBOX_CLASS = "border-input bg-popover size-4 cursor-pointer rounded-[0.25rem] border accent-primary";
6992
- function renderField(field, initial) {
6993
- const controlId = nextFieldId();
6994
- let control;
6995
- let read;
6996
- let reset;
6997
- switch (field.kind) {
6998
- case "select": {
6999
- const node = el("select", {
7000
- class: NATIVE_SELECT_CLASS,
7001
- attrs: {
7002
- "data-slot": "select",
7003
- name: field.name,
7004
- id: controlId,
7005
- required: field.required ?? false
7006
- }
7007
- });
7008
- for (const opt of field.options) {
7009
- node.append(
7010
- el("option", { attrs: { value: opt.value }, text: opt.label })
7011
- );
5534
+ function setHighlight(value, scroll = true) {
5535
+ if (value === highlighted) return;
5536
+ if (highlighted) {
5537
+ const prev = itemNodes.get(highlighted);
5538
+ if (prev) {
5539
+ prev.removeAttribute("data-highlighted");
5540
+ prev.removeAttribute("data-kbd-highlighted");
5541
+ prev.removeAttribute("aria-selected");
7012
5542
  }
7013
- const initialVal = typeof initial === "string" ? initial : typeof field.value === "string" ? field.value : field.options[0]?.value;
7014
- if (initialVal !== void 0) node.value = initialVal;
7015
- control = node;
7016
- read = () => node.value;
7017
- reset = () => {
7018
- if (initialVal !== void 0) node.value = initialVal;
7019
- };
7020
- break;
7021
- }
7022
- case "text":
7023
- case "email": {
7024
- const node = el("input", {
7025
- class: cn(FIELD_INPUT_CLASS, "h-8 px-3"),
7026
- attrs: {
7027
- "data-slot": "input",
7028
- type: field.kind === "email" ? "email" : "text",
7029
- name: field.name,
7030
- id: controlId,
7031
- placeholder: field.placeholder ?? "",
7032
- required: field.required ?? false
7033
- }
7034
- });
7035
- const initialVal = typeof initial === "string" ? initial : typeof field.value === "string" ? field.value : "";
7036
- node.value = initialVal;
7037
- control = node;
7038
- read = () => node.value;
7039
- reset = () => {
7040
- node.value = initialVal;
7041
- };
7042
- break;
7043
- }
7044
- case "textarea": {
7045
- const node = el("textarea", {
7046
- class: cn(FIELD_INPUT_CLASS, "min-h-20 px-3 py-2"),
7047
- attrs: {
7048
- "data-slot": "textarea",
7049
- name: field.name,
7050
- id: controlId,
7051
- placeholder: field.placeholder ?? "",
7052
- required: field.required ?? false,
7053
- rows: field.rows
7054
- }
7055
- });
7056
- const initialVal = typeof initial === "string" ? initial : typeof field.value === "string" ? field.value : "";
7057
- node.value = initialVal;
7058
- control = node;
7059
- read = () => node.value;
7060
- reset = () => {
7061
- node.value = initialVal;
7062
- };
7063
- break;
7064
- }
7065
- case "checkbox": {
7066
- const input = el("input", {
7067
- attrs: {
7068
- type: "checkbox",
7069
- name: field.name,
7070
- id: controlId
7071
- },
7072
- class: CHECKBOX_CLASS
7073
- });
7074
- const initialVal = typeof initial === "boolean" ? initial : typeof field.value === "boolean" ? field.value : false;
7075
- input.checked = initialVal;
7076
- control = input;
7077
- read = () => input.checked;
7078
- reset = () => {
7079
- input.checked = initialVal;
7080
- };
7081
- break;
7082
5543
  }
5544
+ highlighted = value;
5545
+ if (value) {
5546
+ const el2 = itemNodes.get(value);
5547
+ if (el2) {
5548
+ el2.setAttribute("data-highlighted", "");
5549
+ el2.setAttribute("aria-selected", "true");
5550
+ if (kbdMode) el2.setAttribute("data-kbd-highlighted", "");
5551
+ contentEl.setAttribute("aria-activedescendant", el2.id);
5552
+ if (scroll) el2.scrollIntoView({ block: "nearest" });
5553
+ }
5554
+ } else {
5555
+ contentEl.removeAttribute("aria-activedescendant");
5556
+ }
5557
+ onHighlightChange?.(value);
5558
+ notify();
7083
5559
  }
7084
- const errorNode = el("p", {
7085
- class: FIELD_ERROR_CLASS,
7086
- attrs: {
7087
- "data-slot": "field-error",
7088
- role: "alert",
7089
- "aria-live": "polite"
5560
+ function enterKbd() {
5561
+ if (kbdMode) return;
5562
+ kbdMode = true;
5563
+ contentEl.setAttribute("data-kbd-nav", "");
5564
+ }
5565
+ function exitKbd() {
5566
+ if (!kbdMode) return;
5567
+ kbdMode = false;
5568
+ contentEl.removeAttribute("data-kbd-nav");
5569
+ if (highlighted) {
5570
+ itemNodes.get(highlighted)?.removeAttribute("data-kbd-highlighted");
7090
5571
  }
7091
- });
7092
- errorNode.hidden = true;
7093
- const field$ = el("div", {
7094
- class: cn(
7095
- FIELD_ROOT_CLASS,
7096
- field.kind === "checkbox" ? "flex-row items-center gap-2" : ""
7097
- ),
7098
- attrs: {
7099
- "data-slot": "field",
7100
- "data-uidex-primitive": "field",
7101
- "data-uidex-field-name": field.name
7102
- }
7103
- });
7104
- const label = el("label", {
7105
- class: FIELD_LABEL_CLASS,
7106
- attrs: {
7107
- "data-slot": "field-label",
7108
- for: controlId
7109
- },
7110
- text: field.label
7111
- });
7112
- if (field.kind === "checkbox") {
7113
- field$.append(control, label);
7114
- } else {
7115
- field$.append(label, control);
7116
- }
7117
- field$.append(errorNode);
7118
- const setError = (message) => {
7119
- if (!message) {
7120
- errorNode.hidden = true;
7121
- errorNode.textContent = "";
7122
- control.removeAttribute("aria-invalid");
7123
- return;
7124
- }
7125
- errorNode.hidden = false;
7126
- errorNode.textContent = message;
7127
- control.setAttribute("aria-invalid", "true");
7128
- };
7129
- return {
7130
- name: field.name,
7131
- node: field$,
7132
- control,
7133
- errorNode,
7134
- read,
7135
- reset,
7136
- setError
7137
- };
7138
- }
7139
- 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%)]";
7140
- var ICON_TILE = ICON_TILE_BASE + " relative";
7141
- var ICON_TILE_GHOST = ICON_TILE_BASE + " pointer-events-none absolute bottom-px shadow-none scale-84";
7142
- 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";
7143
- 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%)]";
7144
- function iconMediaTpl(iconTpl) {
7145
- return html`
7146
- <div class="relative mb-6">
7147
- <div
7148
- class=${ICON_TILE_GHOST + " -rotate-10 origin-bottom-left -translate-x-0.5"}
7149
- aria-hidden="true"
7150
- ></div>
7151
- <div
7152
- class=${ICON_TILE_GHOST + " rotate-10 origin-bottom-right translate-x-0.5"}
7153
- aria-hidden="true"
7154
- ></div>
7155
- <div class=${ICON_TILE}>${iconTpl}</div>
7156
- </div>
7157
- `;
7158
- }
7159
- function loadingViewTpl(rootRef) {
7160
- return html`
7161
- <div class=${EMPTY_ROOT} aria-live="polite" hidden ${ref(rootRef)}>
7162
- ${iconMediaTpl(icon(Loader2, "animate-spin"))}
7163
- <div class="flex max-w-sm flex-col items-center text-center">
7164
- <div class=${SKELETON + " h-6 w-36 rounded-md"}></div>
7165
- </div>
7166
- <div
7167
- class="flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm"
7168
- >
7169
- <div class=${SKELETON + " h-7 w-20 rounded-lg"}></div>
7170
- </div>
7171
- </div>
7172
- `;
7173
- }
7174
- function successViewTpl(refs) {
7175
- return html`
7176
- <div class=${EMPTY_ROOT} hidden data-uidex-success-view ${ref(refs.root)}>
7177
- ${iconMediaTpl(icon(CircleCheck))}
7178
- <div class="flex max-w-sm flex-col items-center text-center">
7179
- <div
7180
- class="font-heading text-xl font-semibold"
7181
- data-uidex-success-title
7182
- ${ref(refs.title)}
7183
- ></div>
7184
- </div>
7185
- <div
7186
- class="flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm"
7187
- data-uidex-success-actions
7188
- ${ref(refs.actions)}
7189
- >
7190
- <a
7191
- class=${buttonVariants({ variant: "outline", size: "sm" })}
7192
- target="_blank"
7193
- rel="noreferrer"
7194
- data-uidex-success-link
7195
- ${ref(refs.link)}
7196
- ></a>
7197
- </div>
7198
- </div>
7199
- `;
7200
- }
7201
- function errorViewTpl(refs) {
7202
- return html`
7203
- <div class=${EMPTY_ROOT} hidden data-uidex-error-view ${ref(refs.root)}>
7204
- ${iconMediaTpl(icon(CircleX))}
7205
- <div class="flex max-w-sm flex-col items-center gap-1 text-center">
7206
- <div
7207
- class="font-heading text-xl font-semibold"
7208
- data-uidex-error-title
7209
- ${ref(refs.title)}
7210
- ></div>
7211
- <p
7212
- class="text-muted-foreground text-sm"
7213
- data-uidex-error-description
7214
- ${ref(refs.description)}
7215
- ></p>
7216
- </div>
7217
- <div
7218
- class="flex w-full min-w-0 max-w-sm flex-col items-center gap-4 text-balance text-sm"
7219
- >
7220
- <button
7221
- type="button"
7222
- class=${buttonVariants({ variant: "outline", size: "sm" })}
7223
- data-slot="button"
7224
- data-uidex-retry-button
7225
- ${ref(refs.retry)}
7226
- >
7227
- Try Again
7228
- </button>
7229
- </div>
7230
- </div>
7231
- `;
7232
- }
7233
- function renderFormSurface(surface, ctx, root) {
7234
- const cleanups = [];
7235
- const fields = surface.fields.map(
7236
- (field) => renderField(field, surface.initial?.[field.name])
7237
- );
7238
- const fieldByName = new Map(fields.map((f) => [f.name, f]));
7239
- const screenshotField = el("div", {
7240
- class: "flex flex-col gap-1.5",
7241
- attrs: { hidden: true }
7242
- });
7243
- const screenshotLabel = el("span", {
7244
- class: "text-muted-foreground text-xs font-medium",
7245
- text: "Screenshot"
7246
- });
7247
- const screenshotPreview = el("div", {
7248
- class: "cursor-pointer overflow-hidden rounded-md border"
7249
- });
7250
- screenshotField.append(screenshotLabel, screenshotPreview);
7251
- if (surface.screenshotPreview) {
7252
- surface.screenshotPreview.then((dataUrl) => {
7253
- if (!dataUrl) return;
7254
- const img = el("img", {
7255
- class: "max-h-32 w-full object-cover object-top",
7256
- attrs: { src: dataUrl, alt: "Screenshot preview" }
7257
- });
7258
- screenshotPreview.append(img);
7259
- screenshotField.hidden = false;
7260
- const handle = createScreenshotLightbox(screenshotPreview, dataUrl, {
7261
- pushEscapeLayer: ctx.pushEscapeLayer
7262
- });
7263
- cleanups.push(() => handle.destroy());
7264
- });
7265
- }
7266
- const formRef = createRef();
7267
- const loadingRef = createRef();
7268
- const successRef = createRef();
7269
- const successTitleRef = createRef();
7270
- const successLinkRef = createRef();
7271
- const successActionsRef = createRef();
7272
- const errorRef = createRef();
7273
- const errorTitleRef = createRef();
7274
- const errorDescriptionRef = createRef();
7275
- const retryRef = createRef();
7276
- render(
7277
- html`
7278
- <section
7279
- class="flex min-h-0 flex-1 flex-col p-4"
7280
- data-uidex-form-surface=${surface.id}
7281
- >
7282
- <form
7283
- ${ref(formRef)}
7284
- class="flex w-full flex-col gap-4"
7285
- data-slot="form"
7286
- data-uidex-primitive="form"
7287
- novalidate
7288
- data-uidex-form=${surface.id}
7289
- ></form>
7290
- ${loadingViewTpl(loadingRef)}
7291
- ${successViewTpl({
7292
- root: successRef,
7293
- title: successTitleRef,
7294
- link: successLinkRef,
7295
- actions: successActionsRef
7296
- })}
7297
- ${errorViewTpl({
7298
- root: errorRef,
7299
- title: errorTitleRef,
7300
- description: errorDescriptionRef,
7301
- retry: retryRef
7302
- })}
7303
- </section>
7304
- `,
7305
- root
7306
- );
7307
- const form = formRef.value;
7308
- if (surface.screenshotPreview) {
7309
- form.append(screenshotField);
7310
- }
7311
- for (const f of fields) {
7312
- form.append(f.node);
7313
- }
7314
- const submit = el("button", {
7315
- class: cn(buttonVariants({ variant: "default" }), "sr-only"),
7316
- attrs: {
7317
- type: "submit",
7318
- "data-slot": "button",
7319
- "data-uidex-form-submit": ""
7320
- },
7321
- text: surface.submit.label
7322
- });
7323
- form.append(submit);
7324
- const status = el("p", {
7325
- class: "text-muted-foreground text-xs",
7326
- attrs: {
7327
- "data-uidex-form-status": "",
7328
- "aria-live": "polite"
7329
- }
7330
- });
7331
- form.append(status);
7332
- const loadingView = loadingRef.value;
7333
- const successView = successRef.value;
7334
- const successTitle = successTitleRef.value;
7335
- const successLink = successLinkRef.value;
7336
- const successActions = successActionsRef.value;
7337
- const errorView = errorRef.value;
7338
- const errorTitle = errorTitleRef.value;
7339
- const errorDescription = errorDescriptionRef.value;
7340
- const retryButton = retryRef.value;
7341
- function clearAllFieldErrors() {
7342
- for (const f of fields) f.setError(null);
7343
- }
7344
- function applyFieldErrors(errs) {
7345
- clearAllFieldErrors();
7346
- if (!errs) return;
7347
- for (const [name, message] of Object.entries(errs)) {
7348
- fieldByName.get(name)?.setError(message);
7349
- }
7350
- }
7351
- function setStatus(result) {
7352
- status.replaceChildren();
7353
- status.classList.remove("text-destructive-foreground");
7354
- status.classList.add("text-muted-foreground");
7355
- if (!result) return;
7356
- if (result.status === "error") {
7357
- status.classList.remove("text-muted-foreground");
7358
- status.classList.add("text-destructive-foreground");
7359
- if (!result.fieldErrors) {
7360
- status.textContent = result.message;
7361
- }
7362
- return;
7363
- }
7364
- if (result.link) {
7365
- status.append(
7366
- document.createTextNode(`${result.message ?? "Submitted."} `),
7367
- el("a", {
7368
- class: "text-foreground underline underline-offset-4",
7369
- attrs: {
7370
- href: result.link.url,
7371
- target: "_blank",
7372
- rel: "noreferrer"
7373
- },
7374
- text: result.link.label ?? "Open link"
7375
- })
7376
- );
7377
- } else {
7378
- status.textContent = result.message ?? "Submitted.";
7379
- }
7380
- }
7381
- function showView(state) {
7382
- form.hidden = state !== "form";
7383
- loadingView.hidden = state !== "loading";
7384
- successView.hidden = state !== "success";
7385
- errorView.hidden = state !== "error";
7386
- }
7387
- function populateSuccess(result) {
7388
- if (!result || result.status !== "success") return;
7389
- successTitle.textContent = result.message ?? "Submitted";
7390
- if (result.link) {
7391
- successLink.setAttribute("href", result.link.url);
7392
- successLink.textContent = result.link.label ?? "Open link";
7393
- successActions.hidden = false;
7394
- } else {
7395
- successActions.hidden = true;
7396
- }
7397
- }
7398
- function populateError(result) {
7399
- if (!result || result.status !== "error") return;
7400
- errorTitle.textContent = "Something went wrong";
7401
- errorDescription.textContent = result.message;
7402
- }
7403
- let formState = "idle";
7404
- let lastResult = null;
7405
- let lastFieldErrors = null;
7406
- const subscribers = /* @__PURE__ */ new Set();
7407
- const notify = () => {
7408
- for (const cb of subscribers) cb();
7409
- };
7410
- async function doSubmit(values) {
7411
- if (formState === "submitting") return;
7412
- formState = "submitting";
7413
- setStatus(null);
7414
- clearAllFieldErrors();
7415
- showView("form");
7416
- lastResult = null;
7417
- lastFieldErrors = null;
7418
- if (surface.schema) {
7419
- const errors = await validateWithSchema(surface.schema, values);
7420
- if (errors) {
7421
- lastFieldErrors = errors;
7422
- lastResult = {
7423
- status: "error",
7424
- message: "Please fix the errors above.",
7425
- fieldErrors: errors
7426
- };
7427
- formState = "error";
7428
- applyFieldErrors(errors);
7429
- setStatus(lastResult);
7430
- showView("form");
7431
- notify();
7432
- return;
7433
- }
7434
- }
7435
- submit.disabled = true;
7436
- submit.setAttribute("data-loading", "");
7437
- showView("loading");
7438
- notify();
7439
- try {
7440
- const result = await surface.submit.onSubmit(
7441
- values
7442
- ) ?? { status: "success" };
7443
- submit.disabled = false;
7444
- submit.removeAttribute("data-loading");
7445
- if (result.status === "success") {
7446
- lastResult = result;
7447
- formState = "success";
7448
- populateSuccess(result);
7449
- showView("success");
7450
- clearAllFieldErrors();
7451
- if (result.resetFields) {
7452
- for (const name of result.resetFields) {
7453
- fieldByName.get(name)?.reset();
7454
- }
7455
- }
7456
- } else {
7457
- lastResult = result;
7458
- lastFieldErrors = result.fieldErrors ?? null;
7459
- formState = "error";
7460
- const hasFieldErrors = lastFieldErrors && Object.keys(lastFieldErrors).length > 0;
7461
- if (hasFieldErrors) {
7462
- applyFieldErrors(lastFieldErrors ?? void 0);
7463
- setStatus(result);
7464
- showView("form");
7465
- } else {
7466
- populateError(result);
7467
- showView("error");
7468
- }
7469
- }
7470
- } catch (err) {
7471
- submit.disabled = false;
7472
- submit.removeAttribute("data-loading");
7473
- const message = err instanceof Error ? err.message : String(err);
7474
- lastResult = { status: "error", message };
7475
- formState = "error";
7476
- populateError(lastResult);
7477
- showView("error");
7478
- }
7479
- notify();
7480
- }
7481
- function retryFromError() {
7482
- formState = "idle";
7483
- lastResult = null;
7484
- lastFieldErrors = null;
7485
- setStatus(null);
7486
- clearAllFieldErrors();
7487
- showView("form");
7488
- notify();
7489
- }
7490
- const onSubmit = (e) => {
7491
- e.preventDefault();
7492
- if (formState === "success") return;
7493
- if (formState === "error") retryFromError();
7494
- const values = {};
7495
- surface.fields.forEach((field, i) => {
7496
- values[field.name] = fields[i].read();
7497
- });
7498
- void doSubmit(values);
7499
- };
7500
- form.addEventListener("submit", onSubmit);
7501
- cleanups.push(() => form.removeEventListener("submit", onSubmit));
7502
- const triggerSubmit = () => {
7503
- if (typeof form.requestSubmit === "function") form.requestSubmit();
7504
- else form.dispatchEvent(new Event("submit", { cancelable: true }));
7505
- };
7506
- const onFormKeyDown = (e) => {
7507
- if (e.key !== "Enter") return;
7508
- const isMod = e.metaKey || e.ctrlKey;
7509
- if (isMod) {
7510
- e.preventDefault();
7511
- if (formState === "success") {
7512
- ctx.close();
7513
- return;
7514
- }
7515
- if (formState !== "idle" && formState !== "error") return;
7516
- triggerSubmit();
7517
- return;
7518
- }
7519
- if (e.target instanceof HTMLInputElement && e.target.type !== "checkbox" && e.target.type !== "radio") {
7520
- e.preventDefault();
7521
- }
7522
- };
7523
- form.addEventListener("keydown", onFormKeyDown);
7524
- cleanups.push(() => form.removeEventListener("keydown", onFormKeyDown));
7525
- retryButton.addEventListener("click", retryFromError);
7526
- cleanups.push(() => retryButton.removeEventListener("click", retryFromError));
7527
- const resolveIntent = () => {
7528
- if (formState === "submitting") {
7529
- return { label: "Submitting\u2026", disabled: true, perform: () => {
7530
- } };
7531
- }
7532
- if (formState === "success") {
7533
- return { label: "Close", perform: () => ctx.close() };
7534
- }
7535
- if (formState === "error" && !lastFieldErrors) {
7536
- return { label: "Try Again", perform: retryFromError };
7537
- }
7538
- return { label: surface.submit.label, perform: triggerSubmit };
7539
- };
7540
- return {
7541
- cleanup: composeCleanups([...cleanups, () => root.replaceChildren()]),
7542
- submitIntent: {
7543
- get: resolveIntent,
7544
- subscribe: (cb) => {
7545
- subscribers.add(cb);
7546
- return () => subscribers.delete(cb);
7547
- }
7548
- }
7549
- };
7550
- }
7551
-
7552
- // src/browser/views/render/list.ts
7553
- import { Circle as Circle2 } from "lucide";
7554
-
7555
- // src/browser/views/render/list-controller.ts
7556
- function createListController(opts) {
7557
- let items = opts.items;
7558
- let itemNodes = opts.itemNodes;
7559
- let highlighted = null;
7560
- let kbdMode = false;
7561
- const subs = /* @__PURE__ */ new Set();
7562
- const { contentEl, onSelect, onHighlightChange } = opts;
7563
- contentEl.setAttribute("role", "listbox");
7564
- contentEl.tabIndex = 0;
7565
- contentEl.style.outline = "none";
7566
- applyItemAria();
7567
- if (opts.defaultHighlight && itemNodes.has(opts.defaultHighlight)) {
7568
- setHighlight(opts.defaultHighlight, false);
7569
- }
7570
- function values() {
7571
- return items.map((i) => i.value);
7572
- }
7573
- function notify() {
7574
- for (const cb of subs) cb();
7575
- }
7576
- function applyItemAria() {
7577
- for (const [value, el2] of itemNodes) {
7578
- el2.setAttribute("role", "option");
7579
- el2.id = `${opts.surfaceId}-item-${value}`;
7580
- }
7581
- }
7582
- function setHighlight(value, scroll = true) {
7583
- if (value === highlighted) return;
7584
- if (highlighted) {
7585
- const prev = itemNodes.get(highlighted);
7586
- if (prev) {
7587
- prev.removeAttribute("data-highlighted");
7588
- prev.removeAttribute("data-kbd-highlighted");
7589
- prev.removeAttribute("aria-selected");
7590
- }
7591
- }
7592
- highlighted = value;
7593
- if (value) {
7594
- const el2 = itemNodes.get(value);
7595
- if (el2) {
7596
- el2.setAttribute("data-highlighted", "");
7597
- el2.setAttribute("aria-selected", "true");
7598
- if (kbdMode) el2.setAttribute("data-kbd-highlighted", "");
7599
- contentEl.setAttribute("aria-activedescendant", el2.id);
7600
- if (scroll) el2.scrollIntoView({ block: "nearest" });
7601
- }
7602
- } else {
7603
- contentEl.removeAttribute("aria-activedescendant");
7604
- }
7605
- onHighlightChange?.(value);
7606
- notify();
7607
- }
7608
- function enterKbd() {
7609
- if (kbdMode) return;
7610
- kbdMode = true;
7611
- contentEl.setAttribute("data-kbd-nav", "");
7612
- }
7613
- function exitKbd() {
7614
- if (!kbdMode) return;
7615
- kbdMode = false;
7616
- contentEl.removeAttribute("data-kbd-nav");
7617
- if (highlighted) {
7618
- itemNodes.get(highlighted)?.removeAttribute("data-kbd-highlighted");
7619
- }
7620
- }
7621
- function move(delta) {
7622
- const vals = values();
7623
- if (vals.length === 0) return;
7624
- enterKbd();
7625
- if (highlighted === null) {
7626
- setHighlight(delta > 0 ? vals[0] : vals[vals.length - 1]);
7627
- if (highlighted)
7628
- itemNodes.get(highlighted)?.setAttribute("data-kbd-highlighted", "");
7629
- return;
5572
+ }
5573
+ function move(delta) {
5574
+ const vals = values();
5575
+ if (vals.length === 0) return;
5576
+ enterKbd();
5577
+ if (highlighted === null) {
5578
+ setHighlight(delta > 0 ? vals[0] : vals[vals.length - 1]);
5579
+ if (highlighted)
5580
+ itemNodes.get(highlighted)?.setAttribute("data-kbd-highlighted", "");
5581
+ return;
7630
5582
  }
7631
5583
  const idx = vals.indexOf(highlighted);
7632
5584
  const next = (idx + delta + vals.length) % vals.length;
@@ -7646,7 +5598,7 @@ function createListController(opts) {
7646
5598
  e.preventDefault();
7647
5599
  move(-1);
7648
5600
  break;
7649
- case "Home":
5601
+ case "Home": {
7650
5602
  e.preventDefault();
7651
5603
  enterKbd();
7652
5604
  const first = values()[0];
@@ -7656,7 +5608,8 @@ function createListController(opts) {
7656
5608
  itemNodes.get(highlighted)?.setAttribute("data-kbd-highlighted", "");
7657
5609
  }
7658
5610
  break;
7659
- case "End":
5611
+ }
5612
+ case "End": {
7660
5613
  e.preventDefault();
7661
5614
  enterKbd();
7662
5615
  const vals = values();
@@ -7667,6 +5620,7 @@ function createListController(opts) {
7667
5620
  itemNodes.get(highlighted)?.setAttribute("data-kbd-highlighted", "");
7668
5621
  }
7669
5622
  break;
5623
+ }
7670
5624
  case "Enter":
7671
5625
  if (highlighted) {
7672
5626
  e.preventDefault();
@@ -7996,8 +5950,6 @@ function renderSurface(surface, ctx, root) {
7996
5950
  return renderListSurface(surface, ctx, root);
7997
5951
  case "detail":
7998
5952
  return renderDetailSurface(surface, ctx, root);
7999
- case "form":
8000
- return renderFormSurface(surface, ctx, root);
8001
5953
  }
8002
5954
  }
8003
5955
 
@@ -8056,8 +6008,6 @@ function mountEntry(entry, view, deps) {
8056
6008
  const ctx = {
8057
6009
  ref: entry.ref,
8058
6010
  registry: deps.registry,
8059
- cloud: deps.cloud,
8060
- user: deps.session.getState().user,
8061
6011
  views: deps.views,
8062
6012
  push: (target) => {
8063
6013
  if (!deps.views.get(target.id)) return;
@@ -8071,10 +6021,7 @@ function mountEntry(entry, view, deps) {
8071
6021
  pinHighlight: (r) => deps.session.highlight.pin(r),
8072
6022
  searchInput: deps.searchInput,
8073
6023
  highlight: deps.highlight,
8074
- getStack: () => deps.session.getState().stack,
8075
- pushEscapeLayer: deps.pushEscapeLayer,
8076
- onAfterSubmit: deps.onAfterSubmit,
8077
- getRoute: deps.getRoute
6024
+ getStack: () => deps.session.getState().stack
8078
6025
  };
8079
6026
  let surfaceResult;
8080
6027
  try {
@@ -8597,9 +6544,7 @@ function resolveProp(view, ctx, value, propName, fallback) {
8597
6544
  }
8598
6545
  function createViewStack(options) {
8599
6546
  const { container, views, session, registry, highlight } = options;
8600
- const cloud = options.cloud ?? null;
8601
6547
  const dev = options.dev ?? detectDev();
8602
- const onAfterSubmit = options.onAfterSubmit;
8603
6548
  const mounted = [];
8604
6549
  let shell = null;
8605
6550
  let warnedForDepth = 0;
@@ -8796,13 +6741,8 @@ function createViewStack(options) {
8796
6741
  views,
8797
6742
  session,
8798
6743
  registry,
8799
- cloud,
8800
6744
  highlight,
8801
- nextScrollSeq: () => ++scrollAreaSeq,
8802
- pushEscapeLayer: options.pushEscapeLayer ?? (() => () => {
8803
- }),
8804
- onAfterSubmit,
8805
- getRoute: options.getRoute
6745
+ nextScrollSeq: () => ++scrollAreaSeq
8806
6746
  });
8807
6747
  if (record) mounted.push(record);
8808
6748
  }
@@ -8857,20 +6797,13 @@ function createViewStack(options) {
8857
6797
 
8858
6798
  // src/browser/views/built-in/ids.ts
8859
6799
  var BUILT_IN_VIEW_IDS = {
8860
- closeReason: "close-reason",
8861
6800
  commandPalette: "command-palette",
8862
6801
  elements: "elements",
8863
- entityReports: "entity-reports",
8864
6802
  explorePage: "explore-page",
8865
6803
  features: "features",
8866
- report: "report",
8867
6804
  flows: "flows",
8868
6805
  glossary: "glossary",
8869
- jiraReport: "jira-report",
8870
6806
  pages: "pages",
8871
- pageReports: "page-reports",
8872
- reportDetail: "report-detail",
8873
- pinSettings: "pin-settings",
8874
6807
  primitives: "primitives",
8875
6808
  regions: "regions",
8876
6809
  widgets: "widgets"
@@ -8885,16 +6818,6 @@ var LIST_VIEW_FOR_KIND = {
8885
6818
  flow: BUILT_IN_VIEW_IDS.flows,
8886
6819
  route: BUILT_IN_VIEW_IDS.pages
8887
6820
  };
8888
- var DETAIL_VIEW_FOR_KIND = {
8889
- element: "component-detail",
8890
- feature: "feature-detail",
8891
- page: "page-detail",
8892
- widget: "widget-detail",
8893
- region: "region-detail",
8894
- primitive: "primitive-detail",
8895
- flow: "flow-detail",
8896
- route: "page-detail"
8897
- };
8898
6821
  var COMMAND_PALETTE_ENTRY2 = {
8899
6822
  id: BUILT_IN_VIEW_IDS.commandPalette,
8900
6823
  ref: null
@@ -8903,120 +6826,15 @@ function parentList(ref2) {
8903
6826
  if (!ref2) return COMMAND_PALETTE_ENTRY2;
8904
6827
  return { id: LIST_VIEW_FOR_KIND[ref2.kind], ref: null };
8905
6828
  }
8906
- function parentDetail(ref2) {
8907
- if (!ref2) return COMMAND_PALETTE_ENTRY2;
8908
- return { id: DETAIL_VIEW_FOR_KIND[ref2.kind], ref: ref2 };
8909
- }
8910
-
8911
- // src/browser/views/built-in/close-reason.ts
8912
- import {
8913
- Ban,
8914
- BugOff,
8915
- CircleCheck as CircleCheck2,
8916
- Copy as Copy2,
8917
- LoaderCircle,
8918
- createElement as createLucideElement6
8919
- } from "lucide";
8920
- var pendingReportId = null;
8921
- var afterClose = null;
8922
- function setCloseTarget(reportId, onDone) {
8923
- pendingReportId = reportId;
8924
- afterClose = onDone;
8925
- }
8926
- function leadingIcon(iconNode) {
8927
- return () => {
8928
- const svg2 = createLucideElement6(iconNode);
8929
- svg2.setAttribute("aria-hidden", "true");
8930
- return createIconTile(svg2);
8931
- };
8932
- }
8933
- function spinnerTile() {
8934
- const spinner = createLucideElement6(LoaderCircle);
8935
- spinner.setAttribute("class", "animate-spin");
8936
- return createIconTile(spinner);
8937
- }
8938
- var REASONS = [
8939
- { value: "fixed", label: "Resolved", icon: CircleCheck2 },
8940
- { value: "not_a_bug", label: "Not a bug", icon: BugOff },
8941
- { value: "duplicate", label: "Duplicate", icon: Copy2 },
8942
- { value: "wont_fix", label: "Won't fix", icon: Ban }
8943
- ];
8944
- var closeReasonView = {
8945
- id: BUILT_IN_VIEW_IDS.closeReason,
8946
- matches: () => false,
8947
- parent: (ref2) => ref2 ? { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 } : null,
8948
- title: "Close reason",
8949
- searchable: false,
8950
- focusTarget: (host) => host.querySelector("[data-uidex-list-content]"),
8951
- surface: () => ({ kind: "list", id: "unused", items: [] }),
8952
- render(ctx, root) {
8953
- let busy = false;
8954
- const items = REASONS.map((r) => ({
8955
- value: r.value,
8956
- label: r.label,
8957
- leading: leadingIcon(r.icon)
8958
- }));
8959
- const surface = {
8960
- kind: "list",
8961
- id: "uidex-close-reason",
8962
- searchable: false,
8963
- items,
8964
- emptyLabel: "No reasons available",
8965
- onSelect: async (item) => {
8966
- if (busy || !pendingReportId || !ctx.registry.closeReport) return;
8967
- busy = true;
8968
- const row = root.querySelector(
8969
- `[data-uidex-item-value="${item.value}"]`
8970
- );
8971
- const existingTile = row?.querySelector("[data-slot='icon-tile']");
8972
- if (row && existingTile) {
8973
- existingTile.replaceWith(spinnerTile());
8974
- row.style.opacity = "0.6";
8975
- row.style.pointerEvents = "none";
8976
- }
8977
- try {
8978
- await ctx.registry.closeReport(pendingReportId, item.value);
8979
- const done = afterClose;
8980
- pendingReportId = null;
8981
- afterClose = null;
8982
- done?.();
8983
- } catch {
8984
- busy = false;
8985
- if (row) {
8986
- row.style.opacity = "";
8987
- row.style.pointerEvents = "";
8988
- const reason = REASONS.find((r) => r.value === item.value);
8989
- if (reason) {
8990
- const tile = row.querySelector("[data-slot='icon-tile']");
8991
- if (tile)
8992
- tile.replaceWith(
8993
- createIconTile(createLucideElement6(reason.icon))
8994
- );
8995
- }
8996
- }
8997
- const error = document.createElement("p");
8998
- error.className = "text-destructive px-4 py-2 text-xs";
8999
- error.textContent = "Close failed \u2014 try again";
9000
- root.querySelector("[data-uidex-list-content]")?.prepend(error);
9001
- setTimeout(() => error.remove(), 3e3);
9002
- }
9003
- }
9004
- };
9005
- const mounted = renderListSurface(surface, ctx, root);
9006
- return mounted.cleanup;
9007
- }
9008
- };
9009
6829
 
9010
6830
  // src/browser/views/built-in/command-palette.ts
9011
6831
  import {
9012
6832
  Compass,
9013
- Copy as Copy3,
6833
+ Copy as Copy2,
9014
6834
  ExternalLink,
9015
- MessageCircleWarning as MessageCircleWarning2,
9016
6835
  Star,
9017
6836
  StarOff,
9018
- TicketPlus as TicketPlus2,
9019
- createElement as createLucideElement7
6837
+ createElement as createLucideElement5
9020
6838
  } from "lucide";
9021
6839
 
9022
6840
  // src/browser/views/builder/format-location.ts
@@ -9056,9 +6874,6 @@ function findCurrentRoute(registry) {
9056
6874
  function findCurrentPageId(ctx) {
9057
6875
  return findCurrentRoute(ctx.registry)?.page ?? null;
9058
6876
  }
9059
- function findCurrentRoutePath(registry) {
9060
- return findCurrentRoute(registry)?.path ?? null;
9061
- }
9062
6877
  function buildPageRouteMap(ctx) {
9063
6878
  const map = /* @__PURE__ */ new Map();
9064
6879
  for (const route of ctx.registry.list("route")) {
@@ -9109,7 +6924,7 @@ function openAction(onSelect) {
9109
6924
  return {
9110
6925
  id: "open",
9111
6926
  label: "Open",
9112
- icon: () => createLucideElement7(ExternalLink),
6927
+ icon: () => createLucideElement5(ExternalLink),
9113
6928
  perform: onSelect
9114
6929
  };
9115
6930
  }
@@ -9119,7 +6934,7 @@ function favoriteAction(ref2, ctx) {
9119
6934
  id: "toggle-favorite",
9120
6935
  label: isFav ? "Remove from Favorites" : "Add to Favorites",
9121
6936
  shortcut: "\u21E7\u2318F",
9122
- icon: () => createLucideElement7(isFav ? StarOff : Star),
6937
+ icon: () => createLucideElement5(isFav ? StarOff : Star),
9123
6938
  perform: () => {
9124
6939
  ctx.views.toggleFavorite(ref2);
9125
6940
  ctx.close();
@@ -9135,7 +6950,7 @@ function entityActions(ref2, entity, ctx, onSelect) {
9135
6950
  actions.push({
9136
6951
  id: "copy-path",
9137
6952
  label: "Copy Source Path",
9138
- icon: () => createLucideElement7(Copy3),
6953
+ icon: () => createLucideElement5(Copy2),
9139
6954
  async perform() {
9140
6955
  try {
9141
6956
  await navigator.clipboard.writeText(path);
@@ -9145,20 +6960,6 @@ function entityActions(ref2, entity, ctx, onSelect) {
9145
6960
  });
9146
6961
  }
9147
6962
  }
9148
- actions.push({
9149
- id: "submit-report",
9150
- label: "Report",
9151
- icon: () => createLucideElement7(MessageCircleWarning2),
9152
- perform: () => ctx.push({ id: BUILT_IN_VIEW_IDS.report, ref: ref2 })
9153
- });
9154
- if (ctx.cloud?.integrations.getCachedConfig()?.hasJira) {
9155
- actions.push({
9156
- id: "create-jira-issue",
9157
- label: "Create Jira Ticket",
9158
- icon: () => createLucideElement7(TicketPlus2),
9159
- perform: () => ctx.push({ id: BUILT_IN_VIEW_IDS.jiraReport, ref: ref2 })
9160
- });
9161
- }
9162
6963
  actions.push(favoriteAction(ref2, ctx));
9163
6964
  return actions;
9164
6965
  }
@@ -9191,7 +6992,7 @@ function explorePageRow(totalOnPage, ctx) {
9191
6992
  subtitle: totalOnPage === 1 ? "1 item" : `${totalOnPage} items`,
9192
6993
  group: CURRENT_PAGE_GROUP,
9193
6994
  tag: BUILT_IN_VIEW_IDS.explorePage,
9194
- leading: () => createLucideElement7(Compass),
6995
+ leading: () => createLucideElement5(Compass),
9195
6996
  actions: viewActions(onSelect),
9196
6997
  payload: { type: "view", id: BUILT_IN_VIEW_IDS.explorePage }
9197
6998
  };
@@ -9256,7 +7057,7 @@ function createCommandPaletteView(shortcut) {
9256
7057
  group: PALETTE_GROUPS.favorites,
9257
7058
  preview: ref2,
9258
7059
  entityChip: { entity },
9259
- leading: () => createLucideElement7(style.icon),
7060
+ leading: () => createLucideElement5(style.icon),
9260
7061
  actions: entityActions(ref2, entity, ctx, onSelect),
9261
7062
  payload: { type: "entity", ref: ref2 }
9262
7063
  });
@@ -9281,7 +7082,7 @@ function createCommandPaletteView(shortcut) {
9281
7082
  group: PALETTE_GROUPS.recents,
9282
7083
  preview: ref2,
9283
7084
  entityChip: { entity },
9284
- leading: () => createLucideElement7(style.icon),
7085
+ leading: () => createLucideElement5(style.icon),
9285
7086
  actions: entityActions(ref2, entity, ctx, onSelect),
9286
7087
  payload: { type: "entity", ref: ref2 }
9287
7088
  });
@@ -9363,14 +7164,14 @@ async function captureScreenshot(options = {}) {
9363
7164
  }
9364
7165
 
9365
7166
  // src/browser/report/capture.ts
9366
- function captureReportContext(opts) {
7167
+ function captureReportContext() {
9367
7168
  const nav = typeof navigator !== "undefined" ? navigator : void 0;
9368
7169
  const loc = typeof location !== "undefined" ? location : void 0;
9369
7170
  const win = typeof window !== "undefined" ? window : void 0;
9370
7171
  const doc = typeof document !== "undefined" ? document : void 0;
9371
7172
  return {
9372
7173
  url: loc?.href ?? "",
9373
- route: opts?.getRoute?.() ?? loc?.pathname,
7174
+ route: loc?.pathname,
9374
7175
  userAgent: nav?.userAgent ?? "",
9375
7176
  pageTitle: doc?.title,
9376
7177
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -9422,46 +7223,6 @@ function copyPathAction(ref2, loc) {
9422
7223
  intent: { kind: "external", describe: "clipboard" }
9423
7224
  };
9424
7225
  }
9425
- function reportAction(ref2) {
9426
- return {
9427
- id: "submit-report",
9428
- label: "Report",
9429
- icon: "message-circle-warning",
9430
- push: { id: BUILT_IN_VIEW_IDS.report, ref: ref2 },
9431
- intent: {
9432
- kind: "push",
9433
- viewId: BUILT_IN_VIEW_IDS.report,
9434
- refKind: ref2.kind
9435
- }
9436
- };
9437
- }
9438
- function viewReportsAction(ref2, count) {
9439
- return {
9440
- id: "view-reports",
9441
- label: "View Reports",
9442
- icon: "inbox",
9443
- hint: count != null ? count === 1 ? "1 item" : `${count} items` : void 0,
9444
- push: { id: BUILT_IN_VIEW_IDS.entityReports, ref: ref2 },
9445
- intent: {
9446
- kind: "push",
9447
- viewId: BUILT_IN_VIEW_IDS.entityReports,
9448
- refKind: ref2.kind
9449
- }
9450
- };
9451
- }
9452
- function jiraAction(ref2) {
9453
- return {
9454
- id: "create-jira-issue",
9455
- label: "Create Jira Ticket",
9456
- icon: "ticket-plus",
9457
- push: { id: BUILT_IN_VIEW_IDS.jiraReport, ref: ref2 },
9458
- intent: {
9459
- kind: "push",
9460
- viewId: BUILT_IN_VIEW_IDS.jiraReport,
9461
- refKind: ref2.kind
9462
- }
9463
- };
9464
- }
9465
7226
  function buildSnapshotMarkdown(ref2, loc) {
9466
7227
  const snapshot = captureReportContext();
9467
7228
  const sourcePath = formatLocation(loc);
@@ -9552,18 +7313,6 @@ function createEntityDetailView(config) {
9552
7313
  const metaEntity = entity ? entity : null;
9553
7314
  const meta = metaEntity?.meta;
9554
7315
  const actions = [];
9555
- const cloud = ctx.cloud;
9556
- actions.push({ ...reportAction(ctx.ref), group: "Report" });
9557
- const reportCount = ctx.registry.getReports(kind, ctx.ref.id).length;
9558
- if (reportCount > 0) {
9559
- actions.push({
9560
- ...viewReportsAction(ctx.ref, reportCount),
9561
- group: "Report"
9562
- });
9563
- }
9564
- if (cloud?.integrations.getCachedConfig()?.hasJira) {
9565
- actions.push({ ...jiraAction(ctx.ref), group: "Report" });
9566
- }
9567
7316
  if (DOM_BACKED_KINDS.has(kind) && resolveEntityElement(ctx.ref)) {
9568
7317
  actions.push({ ...highlightElementAction(ctx.ref), group: "Inspect" });
9569
7318
  actions.push({ ...copyScreenshotAction(ctx.ref), group: "Inspect" });
@@ -9749,157 +7498,8 @@ var primitivesView = createEntityKindListView(
9749
7498
  BUILT_IN_VIEW_IDS.primitives
9750
7499
  );
9751
7500
 
9752
- // src/browser/views/built-in/report-detail.ts
9753
- var selectedId = null;
9754
- function setSelectedReportId(id) {
9755
- selectedId = id;
9756
- }
9757
- function findReport(ctx) {
9758
- if (!ctx.ref || !selectedId) return null;
9759
- const reports = ctx.registry.getReports(ctx.ref.kind, ctx.ref.id);
9760
- return reports.find((r) => r.id === selectedId) ?? null;
9761
- }
9762
- var reportDetailView = {
9763
- id: BUILT_IN_VIEW_IDS.reportDetail,
9764
- matches: () => false,
9765
- parent: (ref2) => ({ id: BUILT_IN_VIEW_IDS.entityReports, ref: ref2 }),
9766
- searchable: false,
9767
- focusTarget: (host) => host.querySelector(
9768
- "[data-uidex-detail-actions] [data-uidex-detail-action]"
9769
- ),
9770
- surface: (ctx) => {
9771
- const report = findReport(ctx);
9772
- if (!report) {
9773
- return {
9774
- kind: "detail",
9775
- entityKind: ctx.ref?.kind ?? "element",
9776
- notFound: ctx.ref ?? void 0
9777
- };
9778
- }
9779
- const typeLabel = TYPE_LABELS[report.type] ?? report.type;
9780
- const sevLabel = SEVERITY_LABELS[report.severity] ?? report.severity;
9781
- const sections = [];
9782
- if (report.body) {
9783
- sections.push({ id: "description", text: report.body });
9784
- }
9785
- if (report.screenshot) {
9786
- sections.push({ id: "screenshot", url: report.screenshot });
9787
- } else {
9788
- const pins = ctx.cloud?.pins;
9789
- const fetchScreenshot = pins?.screenshot;
9790
- if (pins && fetchScreenshot) {
9791
- sections.push({
9792
- id: "screenshot",
9793
- load: () => fetchScreenshot.call(pins, report.id)
9794
- });
9795
- }
9796
- }
9797
- const metaEntries = [];
9798
- if (report.url) metaEntries.push({ label: "URL", value: report.url });
9799
- if (report.route) metaEntries.push({ label: "Route", value: report.route });
9800
- if (report.pageTitle)
9801
- metaEntries.push({ label: "Page", value: report.pageTitle });
9802
- if (metaEntries.length > 0) {
9803
- sections.push({ id: "metadata", entries: metaEntries });
9804
- }
9805
- const actions = [];
9806
- if (ctx.registry.closeReport) {
9807
- actions.push({
9808
- id: "close",
9809
- label: "Close",
9810
- icon: "archive-x",
9811
- run: () => {
9812
- setCloseTarget(report.id, () => ctx.pop());
9813
- ctx.push({ id: BUILT_IN_VIEW_IDS.closeReason });
9814
- }
9815
- });
9816
- }
9817
- if (report.url) {
9818
- actions.push({
9819
- id: "open-url",
9820
- label: "Open URL",
9821
- icon: "copy",
9822
- copy: report.url
9823
- });
9824
- }
9825
- return {
9826
- kind: "detail",
9827
- entityKind: ctx.ref?.kind ?? "element",
9828
- title: report.title || "(no title)",
9829
- subtitle: {
9830
- rawId: [
9831
- typeLabel,
9832
- sevLabel,
9833
- authorLabel(report),
9834
- relativeTime(report.createdAt)
9835
- ].filter(Boolean).join(" \xB7 ")
9836
- },
9837
- actions,
9838
- sections
9839
- };
9840
- }
9841
- };
9842
-
9843
- // src/browser/views/built-in/entity-reports.ts
9844
- function reportToItem(report, ctx) {
9845
- const typeLabel = TYPE_LABELS[report.type] ?? report.type;
9846
- const sevLabel = SEVERITY_LABELS[report.severity] ?? report.severity;
9847
- const raw = report.title || report.body;
9848
- const label = raw.length > 80 ? raw.slice(0, 80) + "\u2026" : raw;
9849
- const kind = ctx.ref?.kind ?? "element";
9850
- const actions = [];
9851
- if (ctx.registry.closeReport) {
9852
- actions.push({
9853
- id: `close-${report.id}`,
9854
- label: "Close",
9855
- perform: () => {
9856
- setCloseTarget(report.id, () => ctx.pop());
9857
- ctx.push({ id: BUILT_IN_VIEW_IDS.closeReason });
9858
- }
9859
- });
9860
- }
9861
- return {
9862
- value: `report:${report.id}`,
9863
- label: label || "(no description)",
9864
- subtitle: [
9865
- authorLabel(report),
9866
- typeLabel,
9867
- sevLabel,
9868
- relativeTime(report.createdAt)
9869
- ].filter(Boolean).join(" \xB7 "),
9870
- tag: `report:${report.id}`,
9871
- leading: () => renderKindIcon(kind),
9872
- actions
9873
- };
9874
- }
9875
- var entityReportsView = {
9876
- id: BUILT_IN_VIEW_IDS.entityReports,
9877
- matches: () => false,
9878
- parent: parentDetail,
9879
- title: "Reports",
9880
- searchable: true,
9881
- hints: [{ key: "\u21B5", label: "Select" }],
9882
- focusTarget: () => null,
9883
- surface: (ctx) => {
9884
- const ref2 = ctx.ref;
9885
- const reports = ref2 ? ctx.registry.getReports(ref2.kind, ref2.id) : [];
9886
- const items = reports.map((r) => reportToItem(r, ctx));
9887
- return {
9888
- kind: "list",
9889
- id: "uidex-entity-reports",
9890
- items,
9891
- emptyLabel: "No reports for this element",
9892
- filter: (item, query) => matchesQuery(`${item.label} ${item.subtitle ?? ""}`, query),
9893
- onSelect: (item) => {
9894
- setSelectedReportId(item.value.replace(/^report:/, ""));
9895
- ctx.push({ id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 });
9896
- }
9897
- };
9898
- }
9899
- };
9900
-
9901
7501
  // src/browser/views/built-in/explore-page.ts
9902
- import { Compass as Compass2, createElement as createLucideElement8 } from "lucide";
7502
+ import { Compass as Compass2, createElement as createLucideElement6 } from "lucide";
9903
7503
  var KIND_ORDER = new Map(
9904
7504
  ENTITY_KINDS.map((kind, index) => [kind, index])
9905
7505
  );
@@ -9941,7 +7541,7 @@ var explorePageView = {
9941
7541
  palette: {
9942
7542
  label: "Explore Page",
9943
7543
  shortcut: "",
9944
- icon: () => createLucideElement8(Compass2)
7544
+ icon: () => createLucideElement6(Compass2)
9945
7545
  },
9946
7546
  title: "Explore Page",
9947
7547
  hints: [{ key: "\u21B5", label: "Select" }],
@@ -9976,332 +7576,6 @@ var explorePageView = {
9976
7576
  }
9977
7577
  };
9978
7578
 
9979
- // src/browser/internal/clipboard.ts
9980
- async function copyToClipboard(text) {
9981
- try {
9982
- if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
9983
- await navigator.clipboard.writeText(text);
9984
- return true;
9985
- }
9986
- } catch {
9987
- }
9988
- try {
9989
- if (typeof document === "undefined") return false;
9990
- const textarea = document.createElement("textarea");
9991
- textarea.value = text;
9992
- textarea.setAttribute("readonly", "");
9993
- textarea.style.position = "fixed";
9994
- textarea.style.opacity = "0";
9995
- document.body.appendChild(textarea);
9996
- textarea.select();
9997
- const ok = document.execCommand("copy");
9998
- document.body.removeChild(textarea);
9999
- return ok;
10000
- } catch {
10001
- return false;
10002
- }
10003
- }
10004
-
10005
- // src/browser/views/built-in/report/markdown.ts
10006
- function capitalize2(s) {
10007
- return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
10008
- }
10009
- function renderPayloadMarkdown(payload) {
10010
- const heading = payload.title ?? `${capitalize2(payload.type ?? "")} Report`;
10011
- const ctx = payload.context;
10012
- const lines = [
10013
- `# ${heading}`,
10014
- "",
10015
- `- **Type:** ${payload.type}`,
10016
- ...payload.severity ? [`- **Severity:** ${payload.severity}`] : [],
10017
- `- **Entity:** \`${payload.entity}\``,
10018
- "",
10019
- "#### Description",
10020
- payload.body || "_(no description)_",
10021
- "",
10022
- "#### Context",
10023
- `- **URL:** ${ctx?.url}`,
10024
- ...ctx?.pageTitle ? [`- **Page title:** ${ctx.pageTitle}`] : [],
10025
- `- **Viewport:** ${ctx?.viewport.w}\xD7${ctx?.viewport.h}`,
10026
- `- **User agent:** ${ctx?.userAgent}`,
10027
- `- **Timestamp:** ${ctx?.timestamp}`
10028
- ];
10029
- return lines.join("\n") + "\n";
10030
- }
10031
-
10032
- // src/browser/views/built-in/report/schema.ts
10033
- import { z } from "zod";
10034
- var reportSchema = z.object({
10035
- type: z.enum(["bug", "request", "suggestion"]),
10036
- severity: z.enum(["low", "medium", "high", "critical"]).optional(),
10037
- title: z.string().optional(),
10038
- body: z.string().trim().min(1, { message: "Description is required." })
10039
- });
10040
- var reportFields = [
10041
- {
10042
- kind: "select",
10043
- name: "type",
10044
- label: "Type",
10045
- options: [
10046
- { value: "bug", label: "Bug" },
10047
- { value: "request", label: "Request" },
10048
- { value: "suggestion", label: "Suggestion" }
10049
- ]
10050
- },
10051
- {
10052
- kind: "select",
10053
- name: "severity",
10054
- label: "Severity",
10055
- value: "medium",
10056
- options: [
10057
- { value: "low", label: "Low" },
10058
- { value: "medium", label: "Medium" },
10059
- { value: "high", label: "High" },
10060
- { value: "critical", label: "Critical" }
10061
- ]
10062
- },
10063
- {
10064
- kind: "text",
10065
- name: "title",
10066
- label: "Title",
10067
- placeholder: "Short summary (optional)"
10068
- },
10069
- {
10070
- kind: "textarea",
10071
- name: "body",
10072
- label: "Description",
10073
- placeholder: "Describe what happened\u2026",
10074
- required: true
10075
- }
10076
- ];
10077
-
10078
- // src/browser/views/built-in/report/view-builder.ts
10079
- var KIND_TO_ATTR = new Map(
10080
- UIDEX_ATTR_TO_KIND.map(([attr, kind]) => [kind, attr])
10081
- );
10082
- function resolveElement(ref2) {
10083
- const attr = KIND_TO_ATTR.get(ref2.kind);
10084
- if (!attr) return null;
10085
- return document.querySelector(
10086
- `[${attr}="${CSS.escape(ref2.id)}"]`
10087
- );
10088
- }
10089
- function buildPayload(ctx, componentId, values, prefix, augmentPayload, screenshot) {
10090
- const context = captureReportContext({ getRoute: ctx.getRoute });
10091
- const title = String(values.title ?? "").trim();
10092
- const body = String(values.body ?? "").trim();
10093
- const reporter = ctx.user ? { id: ctx.user.id, name: ctx.user.name || void 0 } : void 0;
10094
- const payload = {
10095
- type: values.type ?? "bug",
10096
- severity: values.severity ?? "medium",
10097
- title: title || void 0,
10098
- body: prefix ? `${prefix}${body}` : body,
10099
- entity: componentId,
10100
- screenshot,
10101
- context,
10102
- reporter,
10103
- metadata: ctx.user ? { userId: ctx.user.id } : void 0
10104
- };
10105
- return augmentPayload ? augmentPayload(payload, ctx, values) : payload;
10106
- }
10107
- async function copyFallback(payload) {
10108
- const markdown = renderPayloadMarkdown(payload);
10109
- const copied = await copyToClipboard(markdown);
10110
- if (!copied) console.log("[uidex] report markdown:\n" + markdown);
10111
- return {
10112
- status: copied ? "success" : "error",
10113
- message: copied ? "Copied to clipboard" : "Copy failed"
10114
- };
10115
- }
10116
- function createReportView(config) {
10117
- const {
10118
- id,
10119
- resolveCloud,
10120
- componentId,
10121
- submitLabels,
10122
- successToast,
10123
- failureToast,
10124
- prependDescription,
10125
- baseFields,
10126
- extraFields,
10127
- augmentPayload,
10128
- successResult,
10129
- schema: schemaOverride
10130
- } = config;
10131
- return {
10132
- id,
10133
- matches: () => false,
10134
- parent: parentDetail,
10135
- searchable: false,
10136
- hints: (ctx) => [
10137
- {
10138
- key: "\u2318\u21B5",
10139
- label: resolveCloud(ctx) ? submitLabels.ready : submitLabels.noCloud
10140
- }
10141
- ],
10142
- focusTarget: (host) => host.querySelector(
10143
- "[data-uidex-form='report'] select, [data-uidex-form='report'] input, [data-uidex-form='report'] textarea"
10144
- ),
10145
- surface: (ctx) => {
10146
- const cloud = resolveCloud(ctx);
10147
- const extra = extraFields ? extraFields(ctx) : [];
10148
- const fields = [...extra, ...baseFields ?? reportFields];
10149
- const isDomBacked = ctx.ref != null && DOM_BACKED_KINDS.has(ctx.ref.kind);
10150
- const targetEl = ctx.ref && isDomBacked ? resolveElement(ctx.ref) : null;
10151
- const screenshotPromise = isDomBacked ? captureScreenshot({
10152
- target: targetEl ?? void 0,
10153
- maxWidth: 1280
10154
- }).catch(() => null) : null;
10155
- return {
10156
- kind: "form",
10157
- id: "report",
10158
- fields,
10159
- schema: schemaOverride ?? reportSchema,
10160
- screenshotPreview: screenshotPromise ? screenshotPromise : void 0,
10161
- submit: {
10162
- label: cloud ? submitLabels.ready : submitLabels.noCloud,
10163
- onSubmit: async (values) => {
10164
- const screenshot = await screenshotPromise ?? void 0;
10165
- const prefix = prependDescription ? prependDescription() : "";
10166
- const payload = buildPayload(
10167
- ctx,
10168
- componentId(ctx),
10169
- values,
10170
- prefix,
10171
- augmentPayload,
10172
- screenshot
10173
- );
10174
- if (!cloud) {
10175
- return copyFallback(payload);
10176
- }
10177
- try {
10178
- const result = await cloud.reports.submit(
10179
- payload
10180
- );
10181
- ctx.onAfterSubmit?.();
10182
- if (successResult) return successResult(result);
10183
- return {
10184
- status: "success",
10185
- message: successToast(result)
10186
- };
10187
- } catch (err) {
10188
- const message = err instanceof Error ? err.message : String(err);
10189
- return {
10190
- status: "error",
10191
- message: failureToast(err) ?? message
10192
- };
10193
- }
10194
- }
10195
- }
10196
- };
10197
- }
10198
- };
10199
- }
10200
-
10201
- // src/browser/views/built-in/report/host-report.ts
10202
- var reportView = createReportView({
10203
- id: BUILT_IN_VIEW_IDS.report,
10204
- resolveCloud: (ctx) => ctx.cloud,
10205
- componentId: (ctx) => ctx.ref ? `${ctx.ref.kind}:${ctx.ref.id}` : "unknown",
10206
- submitLabels: { ready: "Report", noCloud: "Copy Report" },
10207
- successToast: (result) => {
10208
- const ticketKey = result.externalLink?.ok && result.externalLink.key ? result.externalLink.key : null;
10209
- return ticketKey ? `Report submitted (${ticketKey})` : "Report submitted";
10210
- },
10211
- failureToast: (err) => {
10212
- const message = err instanceof Error ? err.message : String(err);
10213
- return `Failed: ${message}`;
10214
- }
10215
- });
10216
-
10217
- // src/browser/views/built-in/report/jira-report.ts
10218
- var jiraSchema = reportSchema.omit({ type: true });
10219
- var jiraBaseFields = reportFields.filter(
10220
- (f) => f.name !== "type" && f.name !== "severity"
10221
- );
10222
- function buildParentIssueField(config) {
10223
- const issues = config.parentIssues ?? [];
10224
- const options = [
10225
- { value: "", label: "None" }
10226
- ];
10227
- const byType = /* @__PURE__ */ new Map();
10228
- for (const issue of issues) {
10229
- const group = byType.get(issue.issueType) ?? [];
10230
- group.push(issue);
10231
- byType.set(issue.issueType, group);
10232
- }
10233
- for (const [type, group] of byType) {
10234
- for (const issue of group) {
10235
- options.push({
10236
- value: issue.key,
10237
- label: `[${type}] ${issue.key}: ${issue.summary}`
10238
- });
10239
- }
10240
- }
10241
- return {
10242
- kind: "select",
10243
- name: "parentIssue",
10244
- label: "Parent Issue",
10245
- options
10246
- };
10247
- }
10248
- var jiraReportView = createReportView({
10249
- id: BUILT_IN_VIEW_IDS.jiraReport,
10250
- resolveCloud: (ctx) => ctx.cloud,
10251
- componentId: (ctx) => ctx.ref ? `${ctx.ref.kind}:${ctx.ref.id}` : "unknown",
10252
- submitLabels: { ready: "Create Jira Ticket", noCloud: "Copy report" },
10253
- schema: jiraSchema,
10254
- baseFields: jiraBaseFields,
10255
- extraFields: (ctx) => {
10256
- const cloud = ctx.cloud;
10257
- const config = cloud?.integrations.getCachedConfig() ?? null;
10258
- if (!config?.hasJira) return [];
10259
- return [buildParentIssueField(config)];
10260
- },
10261
- augmentPayload: (payload, ctx, values) => {
10262
- const cloud = ctx.cloud;
10263
- const config = cloud?.integrations.getCachedConfig() ?? null;
10264
- if (!config?.hasJira || !config.integrationId) return payload;
10265
- const targetConfig = {};
10266
- const parentKey = values.parentIssue;
10267
- if (parentKey) {
10268
- const issue = config.parentIssues?.find((i) => i.key === parentKey);
10269
- if (issue) targetConfig.epicId = issue.id;
10270
- }
10271
- return {
10272
- ...payload,
10273
- type: "bug",
10274
- suggestedTarget: {
10275
- integrationId: config.integrationId,
10276
- targetConfig
10277
- }
10278
- };
10279
- },
10280
- successResult: (result) => {
10281
- const link = result.externalLink;
10282
- if (link?.ok && link.key) {
10283
- return {
10284
- status: "success",
10285
- message: `Created ${link.key}`,
10286
- link: link.url ? { url: link.url, label: link.key } : void 0
10287
- };
10288
- }
10289
- return {
10290
- status: "error",
10291
- message: "Report saved \u2014 Jira issue creation failed"
10292
- };
10293
- },
10294
- successToast: (result) => {
10295
- const link = result.externalLink;
10296
- if (link?.ok && link.key) return `Created ${link.key}`;
10297
- return "Report saved \u2014 Jira Bug failed";
10298
- },
10299
- failureToast: (err) => {
10300
- const message = err instanceof Error ? err.message : String(err);
10301
- return `Failed to create issue: ${message}`;
10302
- }
10303
- });
10304
-
10305
7579
  // src/browser/views/built-in/flow-detail.ts
10306
7580
  var STEP_KINDS = [
10307
7581
  "element",
@@ -10333,8 +7607,7 @@ var flowDetailView = {
10333
7607
  }
10334
7608
  const actions = [
10335
7609
  copyPathAction(ctx.ref, flow.loc),
10336
- copySnapshotAction(ctx.ref, flow.loc),
10337
- reportAction(ctx.ref)
7610
+ copySnapshotAction(ctx.ref, flow.loc)
10338
7611
  ];
10339
7612
  const sections = [
10340
7613
  flow.steps.length > 0 ? buildStepsSection(flow.steps, ctx.registry) : buildTouchesSection(flow, ctx.registry)
@@ -10424,7 +7697,7 @@ var flowsView = {
10424
7697
  };
10425
7698
 
10426
7699
  // src/browser/views/built-in/glossary.ts
10427
- import { BookOpen, createElement as createLucideElement9 } from "lucide";
7700
+ import { BookOpen, createElement as createLucideElement7 } from "lucide";
10428
7701
  var KIND_TO_VIEW = {
10429
7702
  element: BUILT_IN_VIEW_IDS.elements,
10430
7703
  widget: BUILT_IN_VIEW_IDS.widgets,
@@ -10461,7 +7734,7 @@ var glossaryView = {
10461
7734
  palette: {
10462
7735
  label: "Glossary",
10463
7736
  group: PALETTE_GROUPS.commands,
10464
- icon: () => createLucideElement9(BookOpen)
7737
+ icon: () => createLucideElement7(BookOpen)
10465
7738
  },
10466
7739
  title: "Glossary",
10467
7740
  hints: [{ key: "\u21B5", label: "Select" }],
@@ -10490,246 +7763,9 @@ var glossaryView = {
10490
7763
  }
10491
7764
  };
10492
7765
 
10493
- // src/browser/views/built-in/page-reports.ts
10494
- import { CircleOff, Inbox as Inbox2, createElement as createLucideElement10 } from "lucide";
10495
- function collectPageReports(ctx) {
10496
- const items = [];
10497
- const seenReportIds = /* @__PURE__ */ new Set();
10498
- for (const kind of ENTITY_KINDS) {
10499
- for (const entity of ctx.registry.list(kind)) {
10500
- const id = "id" in entity ? entity.id : "";
10501
- if (!id) continue;
10502
- const reports = ctx.registry.getReports(kind, id);
10503
- seenReportIds.add(`${kind}:${id}`);
10504
- const ref2 = { kind, id };
10505
- for (const report of reports) {
10506
- const typeLabel = TYPE_LABELS[report.type] ?? report.type;
10507
- const sevLabel = SEVERITY_LABELS[report.severity] ?? report.severity;
10508
- const raw = report.title || report.body;
10509
- const label = raw.length > 80 ? raw.slice(0, 80) + "\u2026" : raw;
10510
- const entityName = displayName(entity);
10511
- items.push({
10512
- value: `report:${report.id}`,
10513
- label: label || "(no description)",
10514
- subtitle: [
10515
- entityName,
10516
- authorLabel(report),
10517
- typeLabel,
10518
- sevLabel,
10519
- relativeTime(report.createdAt)
10520
- ].filter(Boolean).join(" \xB7 "),
10521
- tag: `report:${report.id}`,
10522
- group: `${kind}: ${entityName}`,
10523
- leading: () => renderKindIcon(kind),
10524
- ref: ref2
10525
- });
10526
- }
10527
- }
10528
- }
10529
- for (const key of ctx.registry.listReportKeys()) {
10530
- if (seenReportIds.has(key)) continue;
10531
- const parsed = parseComponentRef(key);
10532
- if (ctx.registry.get(parsed.kind, parsed.id)) continue;
10533
- const reports = ctx.registry.getReports(parsed.kind, parsed.id);
10534
- const ref2 = parsed;
10535
- for (const report of reports) {
10536
- const typeLabel = TYPE_LABELS[report.type] ?? report.type;
10537
- const sevLabel = SEVERITY_LABELS[report.severity] ?? report.severity;
10538
- const raw = report.title || report.body;
10539
- const label = raw.length > 80 ? raw.slice(0, 80) + "\u2026" : raw;
10540
- items.push({
10541
- value: `report:${report.id}`,
10542
- label: label || "(no description)",
10543
- subtitle: [
10544
- `${parsed.kind}:${parsed.id}`,
10545
- authorLabel(report),
10546
- typeLabel,
10547
- sevLabel,
10548
- relativeTime(report.createdAt)
10549
- ].filter(Boolean).join(" \xB7 "),
10550
- tag: `report:${report.id}`,
10551
- group: "Orphaned",
10552
- leading: () => createLucideElement10(CircleOff),
10553
- ref: ref2
10554
- });
10555
- }
10556
- }
10557
- return items;
10558
- }
10559
- var pageReportsView = {
10560
- id: BUILT_IN_VIEW_IDS.pageReports,
10561
- matches: () => false,
10562
- parent: () => COMMAND_PALETTE_ENTRY2,
10563
- palette: {
10564
- label: "Page Reports",
10565
- group: "Current Page",
10566
- icon: () => createLucideElement10(Inbox2)
10567
- },
10568
- title: "Page Reports",
10569
- searchable: true,
10570
- hints: [{ key: "\u21B5", label: "Select" }],
10571
- focusTarget: () => null,
10572
- surface: (ctx) => {
10573
- const items = collectPageReports(ctx);
10574
- return {
10575
- kind: "list",
10576
- id: "uidex-page-reports",
10577
- items,
10578
- emptyLabel: "No reports on this page",
10579
- filter: (item, query) => matchesQuery(
10580
- `${item.label} ${item.subtitle ?? ""} ${item.group ?? ""}`,
10581
- query
10582
- ),
10583
- onSelect: (item) => {
10584
- setSelectedReportId(item.value.replace(/^report:/, ""));
10585
- ctx.push({
10586
- id: BUILT_IN_VIEW_IDS.reportDetail,
10587
- ref: item.ref
10588
- });
10589
- }
10590
- };
10591
- }
10592
- };
10593
-
10594
- // src/browser/views/built-in/pin-settings.ts
10595
- import {
10596
- Check,
10597
- Globe,
10598
- MapPin as MapPin2,
10599
- Settings,
10600
- createElement as createLucideElement11
10601
- } from "lucide";
10602
-
10603
- // src/browser/ui/toast.ts
10604
- function showCopiedToast(message) {
10605
- if (typeof document === "undefined") return;
10606
- const dark = isDarkMode();
10607
- const toast = document.createElement("div");
10608
- toast.setAttribute("data-uidex-toast", "");
10609
- Object.assign(toast.style, {
10610
- position: "fixed",
10611
- top: "50%",
10612
- left: "50%",
10613
- transform: "translate(-50%, -50%) scale(0.96)",
10614
- padding: "12px 20px",
10615
- fontSize: "14px",
10616
- fontFamily: "ui-sans-serif, system-ui, sans-serif",
10617
- fontWeight: "500",
10618
- color: dark ? "#0a0a0a" : "#fafafa",
10619
- background: dark ? "rgba(255, 255, 255, 0.98)" : "rgba(28, 28, 30, 0.95)",
10620
- border: dark ? "1px solid rgba(0, 0, 0, 0.08)" : "1px solid rgba(255, 255, 255, 0.08)",
10621
- borderRadius: "10px",
10622
- 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)",
10623
- zIndex: "2147483647",
10624
- pointerEvents: "none",
10625
- opacity: "0",
10626
- transition: "opacity 120ms ease-out, transform 160ms ease-out",
10627
- whiteSpace: "nowrap"
10628
- });
10629
- toast.textContent = message;
10630
- document.body.appendChild(toast);
10631
- requestAnimationFrame(() => {
10632
- toast.style.opacity = "1";
10633
- toast.style.transform = "translate(-50%, -50%) scale(1)";
10634
- });
10635
- setTimeout(() => {
10636
- toast.style.opacity = "0";
10637
- toast.style.transform = "translate(-50%, -50%) scale(0.96)";
10638
- setTimeout(() => toast.remove(), 200);
10639
- }, 1200);
10640
- }
10641
-
10642
- // src/browser/views/built-in/pin-settings.ts
10643
- var MATCH_MODE_KEY = "uidex:pin-match-mode";
10644
- function readMode() {
10645
- try {
10646
- const v = localStorage.getItem(MATCH_MODE_KEY);
10647
- if (v === "route" || v === "pathname" || v === "component") return v;
10648
- } catch {
10649
- }
10650
- return "route";
10651
- }
10652
- function writeMode(mode) {
10653
- try {
10654
- localStorage.setItem(MATCH_MODE_KEY, mode);
10655
- } catch {
10656
- }
10657
- }
10658
- var MODES = [
10659
- {
10660
- value: "route",
10661
- label: "By route",
10662
- description: "Pins from any URL matching this route pattern",
10663
- icon: Globe
10664
- },
10665
- {
10666
- value: "pathname",
10667
- label: "By pathname",
10668
- description: "Pins from this exact URL path",
10669
- icon: MapPin2
10670
- },
10671
- {
10672
- value: "component",
10673
- label: "By component",
10674
- description: "All pins for components on this page",
10675
- icon: Settings
10676
- }
10677
- ];
10678
- function leadingIcon2(iconNode, active) {
10679
- return () => {
10680
- const svg2 = createLucideElement11(active ? Check : iconNode);
10681
- svg2.setAttribute("aria-hidden", "true");
10682
- return createIconTile(svg2);
10683
- };
10684
- }
10685
- var pinSettingsView = {
10686
- id: BUILT_IN_VIEW_IDS.pinSettings,
10687
- matches: () => false,
10688
- parent: () => COMMAND_PALETTE_ENTRY2,
10689
- palette: {
10690
- label: "Pin settings",
10691
- group: PALETTE_GROUPS.commands,
10692
- icon: () => createLucideElement11(Settings)
10693
- },
10694
- title: "Pin settings",
10695
- searchable: false,
10696
- focusTarget: (host) => host.querySelector("[data-uidex-list-content]"),
10697
- surface: (ctx) => {
10698
- const current = readMode();
10699
- const items = MODES.map((m) => {
10700
- const active = m.value === current;
10701
- return {
10702
- value: m.value,
10703
- label: m.label,
10704
- subtitle: m.description,
10705
- leading: leadingIcon2(m.icon, active),
10706
- trailing: active ? html`<span class="text-muted-foreground ms-auto text-xs font-medium"
10707
- >Active</span
10708
- >` : void 0
10709
- };
10710
- });
10711
- return {
10712
- kind: "list",
10713
- id: "uidex-pin-settings",
10714
- searchable: false,
10715
- items,
10716
- emptyLabel: "No options available",
10717
- onSelect: (item) => {
10718
- const mode = item.value;
10719
- writeMode(mode);
10720
- ctx.onAfterSubmit?.();
10721
- const modeLabel = MODES.find((m) => m.value === mode).label;
10722
- showCopiedToast(`Pin mode set to "${modeLabel}"`);
10723
- ctx.pop();
10724
- }
10725
- };
10726
- }
10727
- };
10728
-
10729
7766
  // src/browser/views/built-in/index.ts
10730
7767
  function buildDefaultViews(shortcut) {
10731
7768
  return [
10732
- closeReasonView,
10733
7769
  createCommandPaletteView(shortcut),
10734
7770
  explorePageView,
10735
7771
  componentDetailView,
@@ -10746,43 +7782,15 @@ function buildDefaultViews(shortcut) {
10746
7782
  primitivesView,
10747
7783
  primitiveDetailView,
10748
7784
  regionDetailView,
10749
- entityReportsView,
10750
- reportView,
10751
- jiraReportView,
10752
- glossaryView,
10753
- pageReportsView,
10754
- pinSettingsView,
10755
- reportDetailView
7785
+ glossaryView
10756
7786
  ];
10757
7787
  }
10758
7788
 
10759
7789
  // src/browser/create-uidex.ts
10760
- function getVisibleEntities() {
10761
- if (typeof document === "undefined") return [];
10762
- const selector = UIDEX_ATTR_TO_KIND.map(([a]) => `[${a}]`).join(",");
10763
- const nodes = document.querySelectorAll(selector);
10764
- const ids = [];
10765
- const seen = /* @__PURE__ */ new Set();
10766
- for (const node of nodes) {
10767
- if (node.closest(SURFACE_IGNORE_SELECTOR)) continue;
10768
- for (const [attr, kind] of UIDEX_ATTR_TO_KIND) {
10769
- const id = node.getAttribute(attr);
10770
- if (!id) continue;
10771
- const key = `${kind}:${id}`;
10772
- if (!seen.has(key)) {
10773
- seen.add(key);
10774
- ids.push(key);
10775
- }
10776
- break;
10777
- }
10778
- }
10779
- return ids;
10780
- }
10781
7790
  function createUidex(options = {}) {
10782
7791
  const registry = createRegistry();
10783
7792
  const inspectorRef = { current: null };
10784
7793
  const overlayRef = { current: null };
10785
- const pinLayerRef = { current: null };
10786
7794
  const applyOverlay = (ctx) => {
10787
7795
  const overlay = overlayRef.current;
10788
7796
  if (!overlay) return;
@@ -10802,7 +7810,6 @@ function createUidex(options = {}) {
10802
7810
  const session = createSession({
10803
7811
  theme: options.theme,
10804
7812
  resolvedTheme: options.resolvedTheme,
10805
- user: options.user,
10806
7813
  onMountInspector: () => inspectorRef.current?.mount(),
10807
7814
  onDestroyInspector: () => inspectorRef.current?.destroy(),
10808
7815
  onShowOverlay: applyOverlay,
@@ -10810,9 +7817,6 @@ function createUidex(options = {}) {
10810
7817
  onUpdateOverlay: applyOverlay
10811
7818
  });
10812
7819
  const views = createRouter({ session });
10813
- const cloud = options.cloud ?? null;
10814
- const ingestOpts = resolveIngestOptions(options.ingest, cloud !== null);
10815
- const ingest = ingestOpts ? createIngest(ingestOpts) : null;
10816
7820
  if (options.defaultViews !== false) {
10817
7821
  for (const view of buildDefaultViews(options.shortcut)) views.add(view);
10818
7822
  }
@@ -10822,111 +7826,12 @@ function createUidex(options = {}) {
10822
7826
  let shadowRoot = null;
10823
7827
  const mountCleanup = createCleanupStack();
10824
7828
  let mounted = false;
10825
- function syncReportsToRegistry() {
10826
- const layer = pinLayerRef.current;
10827
- if (!layer) return;
10828
- const byEntity = /* @__PURE__ */ new Map();
10829
- for (const pin of layer.getAllPins()) {
10830
- const cid = pin.entity ?? "";
10831
- if (!cid) continue;
10832
- const list = byEntity.get(cid);
10833
- if (list) list.push(pin);
10834
- else byEntity.set(cid, [pin]);
10835
- }
10836
- for (const [cid, pins] of byEntity) {
10837
- const ref2 = parseComponentRef(cid);
10838
- registry.setReports(ref2.kind, ref2.id, pins);
10839
- }
10840
- }
10841
- function setupKeyBindings(root, viewStack) {
10842
- const bindings = bindShadowKeys({
10843
- shadowRoot: root,
10844
- session,
10845
- getActionsPopup: () => viewStack.getActionsPopup(),
10846
- getPinLayer: () => pinLayerRef.current,
10847
- shortcut: options.shortcut
10848
- });
10849
- mountCleanup.add(bindings);
10850
- return bindings;
10851
- }
10852
- function setupPinLayer(root, shell, channel, getCurrentRoute, getMatchMode, getPathname) {
10853
- if (cloud) {
10854
- registry.closeReport = async (reportId, status) => {
10855
- pinLayerRef.current?.removePin(reportId);
10856
- await cloud.pins.close(
10857
- reportId,
10858
- status
10859
- );
10860
- syncReportsToRegistry();
10861
- };
10862
- }
10863
- const pinLayer = createPinLayer({
10864
- container: root,
10865
- onOpenPinDetail: (componentId, reportId) => {
10866
- const ref2 = parseComponentRef(componentId);
10867
- setSelectedReportId(reportId);
10868
- const entry = { id: BUILT_IN_VIEW_IDS.reportDetail, ref: ref2 };
10869
- session.mode.transition.enterViewing(views.buildStack(entry));
10870
- },
10871
- onHoverPin: (anchor, componentId) => {
10872
- const overlay = overlayRef.current;
10873
- if (!overlay) return;
10874
- if (!anchor || !componentId) {
10875
- overlay.hide();
10876
- return;
10877
- }
10878
- overlay.show(anchor, {
10879
- color: isDarkMode() ? "#e4e4e7" : "#27272a"
10880
- });
10881
- },
10882
- onPinsChanged: syncReportsToRegistry
10883
- });
10884
- shell.menuBar.setPinLayer(pinLayer);
10885
- pinLayerRef.current = pinLayer;
10886
- mountCleanup.add(() => {
10887
- pinLayer.destroy();
10888
- pinLayerRef.current = null;
10889
- registry.closeReport = void 0;
10890
- });
10891
- if (cloud) {
10892
- const detach = pinLayer.attachCloud({
10893
- cloud,
10894
- channel,
10895
- getRoute: getCurrentRoute,
10896
- getPathname,
10897
- getVisibleEntities,
10898
- getMatchMode,
10899
- onError: (err) => console.warn("[uidex] pin fetch failed", err)
10900
- });
10901
- mountCleanup.add(detach);
10902
- if (channel && typeof window !== "undefined") {
10903
- const onRouteChange = () => {
10904
- const route = getCurrentRoute();
10905
- channel?.joinRoute(route);
10906
- void pinLayer.refresh();
10907
- };
10908
- const detachRoute = bindRouteChange(onRouteChange);
10909
- mountCleanup.add(detachRoute);
10910
- }
10911
- }
10912
- }
10913
7829
  function mount(target) {
10914
7830
  if (mounted) return;
10915
7831
  const mountTarget = target ?? (typeof document !== "undefined" ? document.body : null);
10916
7832
  if (!mountTarget) {
10917
7833
  throw new Error("createUidex: no mount target available");
10918
7834
  }
10919
- const getPathname = () => typeof location !== "undefined" ? location.pathname : "/";
10920
- const getCurrentRoute = () => options.getRoute?.() ?? findCurrentRoutePath(registry) ?? getPathname();
10921
- const getMatchMode = () => readMode();
10922
- let channel = null;
10923
- if (cloud && options.user) {
10924
- channel = cloud.realtime.connect({
10925
- user: options.user,
10926
- route: getCurrentRoute()
10927
- });
10928
- mountCleanup.add(() => channel?.disconnect());
10929
- }
10930
7835
  const shell = createSurfaceShell({
10931
7836
  mount: mountTarget,
10932
7837
  registry,
@@ -10934,7 +7839,6 @@ function createUidex(options = {}) {
10934
7839
  stylesheets: options.stylesheets,
10935
7840
  initialCorner: options.initialCorner,
10936
7841
  appTitle: options.appTitle,
10937
- channel,
10938
7842
  inspector: {
10939
7843
  onSelect: (match) => {
10940
7844
  const route = views.resolve(match.ref);
@@ -10970,38 +7874,23 @@ function createUidex(options = {}) {
10970
7874
  viewContainer.setAttribute("data-uidex-views", "");
10971
7875
  shell.host.chromeEl.appendChild(viewContainer);
10972
7876
  mountCleanup.add(() => viewContainer.remove());
10973
- let keyBindings = null;
10974
7877
  const viewStack = createViewStack({
10975
7878
  container: viewContainer,
10976
7879
  views,
10977
7880
  session,
10978
7881
  registry,
10979
- cloud,
10980
7882
  highlight,
10981
- shortcut: options.shortcut,
10982
- pushEscapeLayer: (handler) => {
10983
- if (!keyBindings) return () => {
10984
- };
10985
- return keyBindings.pushEscapeLayer(handler);
10986
- },
10987
- onAfterSubmit: () => pinLayerRef.current?.refresh(),
10988
- getRoute: getCurrentRoute
7883
+ shortcut: options.shortcut
10989
7884
  });
10990
7885
  mountCleanup.add(viewStack);
10991
7886
  if (shadowRoot) {
10992
- keyBindings = setupKeyBindings(shadowRoot, viewStack);
10993
- setupPinLayer(
7887
+ const keyBindings = bindShadowKeys({
10994
7888
  shadowRoot,
10995
- shell,
10996
- channel,
10997
- getCurrentRoute,
10998
- getMatchMode,
10999
- getPathname
11000
- );
11001
- }
11002
- if (ingest) {
11003
- ingest.start();
11004
- mountCleanup.add(() => ingest.stop());
7889
+ session,
7890
+ getActionsPopup: () => viewStack.getActionsPopup(),
7891
+ shortcut: options.shortcut
7892
+ });
7893
+ mountCleanup.add(keyBindings);
11005
7894
  }
11006
7895
  mounted = true;
11007
7896
  }
@@ -11017,8 +7906,6 @@ function createUidex(options = {}) {
11017
7906
  registry,
11018
7907
  session,
11019
7908
  views,
11020
- cloud,
11021
- ingest,
11022
7909
  get shadowRoot() {
11023
7910
  return shadowRoot;
11024
7911
  }
@@ -11030,27 +7917,18 @@ import { jsx } from "react/jsx-runtime";
11030
7917
  var UidexContext = createContext(null);
11031
7918
  function UidexProvider({
11032
7919
  instance: externalInstance,
11033
- projectKey,
11034
- cloud,
11035
- user,
11036
7920
  config,
11037
7921
  children
11038
7922
  }) {
11039
7923
  const [owned] = useState(() => {
11040
7924
  if (externalInstance) return null;
11041
- const ownedCloud = cloud === void 0 && projectKey && typeof window !== "undefined" ? createCloud({ projectKey }) : null;
11042
- const resolvedCloud = cloud !== void 0 ? cloud : ownedCloud;
11043
- return {
11044
- instance: createUidex({ ...config, cloud: resolvedCloud, user }),
11045
- cloud: ownedCloud
11046
- };
7925
+ return { instance: createUidex({ ...config }) };
11047
7926
  });
11048
- const instance = externalInstance ?? owned.instance;
7927
+ const instance = externalInstance ?? owned?.instance ?? null;
11049
7928
  useEffect(() => {
11050
7929
  if (!owned) return;
11051
7930
  return () => {
11052
7931
  owned.instance.unmount();
11053
- owned.cloud?.dispose?.();
11054
7932
  };
11055
7933
  }, [owned]);
11056
7934
  return /* @__PURE__ */ jsx(UidexContext.Provider, { value: instance, children });
@@ -11119,7 +7997,7 @@ import {
11119
7997
  LayoutPanelTop as LayoutPanelTop2,
11120
7998
  MousePointerClick as MousePointerClick3,
11121
7999
  Route as RouteIcon2,
11122
- Sparkles as Sparkles3,
8000
+ Sparkles as Sparkles2,
11123
8001
  Workflow as Workflow2
11124
8002
  } from "lucide-react";
11125
8003
  import { twMerge as twMerge2 } from "tailwind-merge";
@@ -11127,7 +8005,7 @@ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
11127
8005
  var REACT_ICONS = {
11128
8006
  route: RouteIcon2,
11129
8007
  page: FileText2,
11130
- feature: Sparkles3,
8008
+ feature: Sparkles2,
11131
8009
  widget: LayoutGrid2,
11132
8010
  region: LayoutPanelTop2,
11133
8011
  element: MousePointerClick3,
@@ -11169,27 +8047,32 @@ function KindChip({
11169
8047
 
11170
8048
  // src/integrations/react/devtools.tsx
11171
8049
  import { useEffect as useEffect3, useState as useState2 } from "react";
11172
- import { cloud as createCloud2 } from "uidex/cloud";
11173
8050
  import { jsx as jsx4 } from "react/jsx-runtime";
11174
8051
  function UidexDevtools({
11175
8052
  loadRegistry,
11176
- projectKey,
11177
- endpoint,
11178
- user,
11179
- git,
8053
+ loadDeferred,
11180
8054
  config
11181
8055
  }) {
11182
8056
  const [instance, setInstance] = useState2(null);
11183
8057
  useEffect3(() => {
11184
- const cloud = projectKey ? createCloud2({ projectKey, endpoint, git }) : null;
11185
- const inst = createUidex({ ...config, cloud, user, git });
8058
+ const inst = createUidex({ ...config });
11186
8059
  loadRegistry(inst.registry);
11187
8060
  setInstance(inst);
8061
+ let cancelled = false;
8062
+ if (loadDeferred) {
8063
+ const run = () => {
8064
+ if (!cancelled)
8065
+ Promise.resolve(loadDeferred(inst.registry)).catch(() => {
8066
+ });
8067
+ };
8068
+ if (typeof requestIdleCallback === "function") requestIdleCallback(run);
8069
+ else setTimeout(run, 0);
8070
+ }
11188
8071
  return () => {
8072
+ cancelled = true;
11189
8073
  inst.unmount();
11190
- cloud?.dispose?.();
11191
8074
  };
11192
- }, [projectKey, user?.id]);
8075
+ }, []);
11193
8076
  if (!instance) return null;
11194
8077
  return /* @__PURE__ */ jsx4(UidexContext.Provider, { value: instance, children: /* @__PURE__ */ jsx4(UidexMount, {}) });
11195
8078
  }