uidex 0.4.0 → 0.5.1

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 (42) hide show
  1. package/dist/cli/cli.cjs +1111 -87
  2. package/dist/cli/cli.cjs.map +1 -1
  3. package/dist/cloud/index.cjs +375 -72
  4. package/dist/cloud/index.cjs.map +1 -1
  5. package/dist/cloud/index.d.cts +82 -0
  6. package/dist/cloud/index.d.ts +82 -0
  7. package/dist/cloud/index.js +376 -71
  8. package/dist/cloud/index.js.map +1 -1
  9. package/dist/headless/index.cjs +623 -469
  10. package/dist/headless/index.cjs.map +1 -1
  11. package/dist/headless/index.d.cts +77 -75
  12. package/dist/headless/index.d.ts +77 -75
  13. package/dist/headless/index.js +627 -469
  14. package/dist/headless/index.js.map +1 -1
  15. package/dist/index.cjs +4258 -2884
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +275 -234
  18. package/dist/index.d.ts +275 -234
  19. package/dist/index.js +4280 -2890
  20. package/dist/index.js.map +1 -1
  21. package/dist/playwright/index.cjs +4 -4
  22. package/dist/playwright/index.cjs.map +1 -1
  23. package/dist/playwright/index.js +3 -3
  24. package/dist/playwright/index.js.map +1 -1
  25. package/dist/playwright/reporter.cjs +3 -3
  26. package/dist/playwright/reporter.cjs.map +1 -1
  27. package/dist/playwright/reporter.js +3 -3
  28. package/dist/playwright/reporter.js.map +1 -1
  29. package/dist/react/index.cjs +4299 -2906
  30. package/dist/react/index.cjs.map +1 -1
  31. package/dist/react/index.d.cts +206 -200
  32. package/dist/react/index.d.ts +206 -200
  33. package/dist/react/index.js +4339 -2926
  34. package/dist/react/index.js.map +1 -1
  35. package/dist/scan/index.cjs +201 -49
  36. package/dist/scan/index.cjs.map +1 -1
  37. package/dist/scan/index.d.cts +27 -1
  38. package/dist/scan/index.d.ts +27 -1
  39. package/dist/scan/index.js +200 -48
  40. package/dist/scan/index.js.map +1 -1
  41. package/package.json +8 -14
  42. package/templates/claude/api.md +110 -0
@@ -24,7 +24,7 @@ __export(headless_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(headless_exports);
26
26
 
27
- // src/entities/types.ts
27
+ // src/shared/entities/types.ts
28
28
  var ENTITY_KINDS = [
29
29
  "route",
30
30
  "page",
@@ -62,7 +62,7 @@ function assertEntityKind(kind) {
62
62
  if (!KIND_SET.has(kind)) throw new UnknownEntityKindError(kind);
63
63
  }
64
64
 
65
- // src/entities/registry.ts
65
+ // src/shared/entities/registry.ts
66
66
  function emptyStore() {
67
67
  return {
68
68
  route: /* @__PURE__ */ new Map(),
@@ -144,10 +144,33 @@ function createRegistry() {
144
144
  return ids.has(entity.id);
145
145
  });
146
146
  };
147
- return { add, get, list, query, byScope, touchedBy };
147
+ const reports = /* @__PURE__ */ new Map();
148
+ const reportsCbs = /* @__PURE__ */ new Set();
149
+ const setReports = (kind, id, records) => {
150
+ reports.set(`${kind}:${id}`, records);
151
+ for (const cb of reportsCbs) cb();
152
+ };
153
+ const getReports = (kind, id) => reports.get(`${kind}:${id}`) ?? [];
154
+ const listReportKeys = () => Array.from(reports.keys());
155
+ const onReportsChange = (cb) => {
156
+ reportsCbs.add(cb);
157
+ return () => reportsCbs.delete(cb);
158
+ };
159
+ return {
160
+ add,
161
+ get,
162
+ list,
163
+ query,
164
+ byScope,
165
+ touchedBy,
166
+ setReports,
167
+ getReports,
168
+ listReportKeys,
169
+ onReportsChange
170
+ };
148
171
  }
149
172
 
150
- // src/entities/style.ts
173
+ // src/shared/entities/style.ts
151
174
  var import_lucide = require("lucide");
152
175
  var KIND_STYLE = {
153
176
  route: {
@@ -208,7 +231,7 @@ var KIND_STYLE = {
208
231
  }
209
232
  };
210
233
 
211
- // src/entities/display-name.ts
234
+ // src/shared/entities/display-name.ts
212
235
  function prettify(id) {
213
236
  const parts = id.split(/[-_]+/).filter(Boolean);
214
237
  const words = parts.flatMap((part) => part.split(/(?=[A-Z])/).filter(Boolean));
@@ -226,7 +249,7 @@ function displayName(entity, node) {
226
249
  return prettify(entity.id);
227
250
  }
228
251
 
229
- // src/internal/cleanup.ts
252
+ // src/browser/internal/cleanup.ts
230
253
  function createCleanupStack() {
231
254
  const stack = [];
232
255
  return {
@@ -248,293 +271,147 @@ function createCleanupStack() {
248
271
  };
249
272
  }
250
273
 
251
- // src/session/store.ts
252
- var import_xstate3 = require("xstate");
253
- var import_vanilla = require("zustand/vanilla");
274
+ // src/browser/session/store.ts
275
+ var import_vanilla3 = require("zustand/vanilla");
254
276
 
255
- // src/flows/highlight.ts
256
- var import_xstate = require("xstate");
257
- var initialContext = {
258
- ref: null,
259
- element: null,
260
- pinnedRef: null,
261
- color: null
277
+ // src/browser/session/mode.ts
278
+ var import_vanilla = require("zustand/vanilla");
279
+ var COMMAND_PALETTE_ENTRY = {
280
+ id: "command-palette",
281
+ ref: null
262
282
  };
263
- var highlightMachine = (0, import_xstate.setup)({
264
- types: {},
265
- actions: {
266
- showOverlay: () => {
283
+ function createModeStore(options) {
284
+ const { nav, bindings } = options;
285
+ const store = (0, import_vanilla.createStore)(() => ({
286
+ mode: "idle",
287
+ inspectorActive: false
288
+ }));
289
+ const transition = {
290
+ openPalette() {
291
+ const prev = store.getState();
292
+ if (prev.mode === "inspecting") {
293
+ bindings?.destroyInspector?.();
294
+ }
295
+ nav.nav.reset([COMMAND_PALETTE_ENTRY]);
296
+ store.setState({ mode: "palette", inspectorActive: false });
267
297
  },
268
- hideOverlay: () => {
298
+ openInspector() {
299
+ bindings?.mountInspector?.();
300
+ nav.nav.clear();
301
+ store.setState({ mode: "inspecting", inspectorActive: true });
269
302
  },
270
- updateOverlay: () => {
271
- }
272
- }
273
- }).createMachine({
274
- id: "highlight",
275
- initial: "none",
276
- context: initialContext,
277
- states: {
278
- none: {
279
- entry: [
280
- (0, import_xstate.assign)({
281
- ref: null,
282
- element: null,
283
- pinnedRef: null,
284
- color: null
285
- }),
286
- { type: "hideOverlay" }
287
- ],
288
- on: {
289
- HOVER: {
290
- target: "transient",
291
- actions: (0, import_xstate.assign)(({ event }) => ({
292
- ref: event.ref,
293
- element: event.element,
294
- color: event.color ?? null
295
- }))
296
- },
297
- PIN: {
298
- target: "pinned",
299
- actions: (0, import_xstate.assign)(({ context, event }) => ({
300
- ref: event.ref ?? context.ref,
301
- pinnedRef: event.ref ?? context.ref
302
- }))
303
- }
304
- }
303
+ closeInspector() {
304
+ bindings?.destroyInspector?.();
305
+ nav.nav.clear();
306
+ store.setState({ mode: "idle", inspectorActive: false });
305
307
  },
306
- transient: {
307
- entry: { type: "showOverlay" },
308
- on: {
309
- HOVER: {
310
- actions: [
311
- (0, import_xstate.assign)(({ event }) => ({
312
- ref: event.ref,
313
- element: event.element,
314
- color: event.color ?? null
315
- })),
316
- { type: "updateOverlay" }
317
- ]
318
- },
319
- UNHOVER: { target: "none" },
320
- PIN: {
321
- target: "pinned",
322
- actions: (0, import_xstate.assign)(({ context, event }) => ({
323
- pinnedRef: event.ref ?? context.ref
324
- }))
325
- }
308
+ toggleInspector() {
309
+ if (store.getState().mode === "inspecting") {
310
+ transition.closeInspector();
311
+ } else {
312
+ transition.openInspector();
326
313
  }
327
314
  },
328
- pinned: {
329
- entry: { type: "showOverlay" },
330
- on: {
331
- UNPIN: { target: "none" }
315
+ enterViewing(initialStack) {
316
+ const prev = store.getState();
317
+ if (prev.mode === "inspecting") {
318
+ bindings?.destroyInspector?.();
332
319
  }
333
- }
334
- }
335
- });
336
-
337
- // src/flows/surface.ts
338
- var import_xstate2 = require("xstate");
339
- var initialContext2 = {
340
- stack: [],
341
- hover: null,
342
- selection: null,
343
- pinnedHighlight: null,
344
- theme: "auto",
345
- resolvedTheme: "light",
346
- ingestActive: false
347
- };
348
- var surfaceMachine = (0, import_xstate2.setup)({
349
- types: {},
350
- actors: {
351
- highlight: highlightMachine
352
- },
353
- actions: {
354
- mountInspector: () => {
320
+ nav.nav.reset(initialStack);
321
+ store.setState({ mode: "viewing", inspectorActive: false });
355
322
  },
356
- destroyInspector: () => {
323
+ dismiss() {
324
+ const prev = store.getState();
325
+ if (prev.mode === "inspecting") {
326
+ bindings?.destroyInspector?.();
327
+ }
328
+ nav.nav.clear();
329
+ store.setState({ mode: "idle", inspectorActive: false });
357
330
  },
358
- openActionsPopup: () => {
331
+ popOrTransition() {
332
+ const { stack } = nav.getState();
333
+ if (stack.length > 2) {
334
+ nav.nav.pop();
335
+ } else if (stack.length === 2 && stack[0]?.id === "command-palette") {
336
+ nav.nav.reset([COMMAND_PALETTE_ENTRY]);
337
+ store.setState({ mode: "palette", inspectorActive: false });
338
+ } else if (stack.length === 2) {
339
+ nav.nav.pop();
340
+ } else {
341
+ nav.nav.clear();
342
+ store.setState({ mode: "idle", inspectorActive: false });
343
+ }
359
344
  },
360
- closePopup: () => {
345
+ pushView(entry) {
346
+ const { mode } = store.getState();
347
+ const { stack } = nav.getState();
348
+ if (entry.id === "command-palette" && stack.length === 0) {
349
+ transition.openPalette();
350
+ return;
351
+ }
352
+ switch (mode) {
353
+ case "idle":
354
+ transition.enterViewing([entry]);
355
+ break;
356
+ case "inspecting":
357
+ bindings?.destroyInspector?.();
358
+ nav.nav.reset([entry]);
359
+ store.setState({ mode: "viewing", inspectorActive: false });
360
+ break;
361
+ case "palette":
362
+ case "viewing":
363
+ nav.nav.push(entry);
364
+ if (mode === "palette") {
365
+ store.setState({ mode: "viewing" });
366
+ }
367
+ break;
368
+ }
361
369
  }
362
- },
363
- guards: {
364
- hasPinnedHighlight: ({ context }) => context.pinnedHighlight !== null,
365
- hasActions: () => false,
366
- popupOpen: () => false,
367
- stackDeeperThanTwo: ({ context }) => context.stack.length > 2,
368
- stackEqualsTwo: ({ context }) => context.stack.length === 2,
369
- stackEqualsOne: ({ context }) => context.stack.length === 1,
370
- paletteAtBottomDepthTwo: ({ context }) => context.stack.length === 2 && context.stack[0]?.id === "command-palette"
371
- }
372
- }).createMachine({
373
- id: "surface",
374
- initial: "idle",
375
- context: ({ input }) => ({
376
- ...initialContext2,
377
- ...input ?? {}
378
- }),
379
- invoke: {
380
- id: "highlight",
381
- src: "highlight"
382
- },
383
- on: {
384
- HOVER: {
385
- actions: [
386
- (0, import_xstate2.assign)(({ event }) => ({ hover: event.ref })),
387
- (0, import_xstate2.forwardTo)("highlight")
388
- ]
389
- },
390
- UNHOVER: {
391
- actions: [(0, import_xstate2.assign)({ hover: null }), (0, import_xstate2.forwardTo)("highlight")]
392
- },
393
- PIN: {
394
- actions: [
395
- (0, import_xstate2.assign)(({ context, event }) => ({
396
- pinnedHighlight: event.ref ?? context.hover
397
- })),
398
- (0, import_xstate2.forwardTo)("highlight")
399
- ]
400
- },
401
- UNPIN: {
402
- actions: [(0, import_xstate2.assign)({ pinnedHighlight: null }), (0, import_xstate2.forwardTo)("highlight")]
403
- },
404
- SET_SELECTION: {
405
- actions: (0, import_xstate2.assign)(({ event }) => ({ selection: event.ref }))
406
- },
407
- SET_THEME: {
408
- actions: (0, import_xstate2.assign)(({ event }) => ({
409
- theme: event.theme,
410
- resolvedTheme: event.resolvedTheme
411
- }))
370
+ };
371
+ const modeStore = store;
372
+ modeStore.transition = transition;
373
+ return modeStore;
374
+ }
375
+
376
+ // src/browser/session/navigation.ts
377
+ var import_vanilla2 = require("zustand/vanilla");
378
+ function createNavigationStore() {
379
+ const store = (0, import_vanilla2.createStore)(() => ({
380
+ stack: []
381
+ }));
382
+ const nav = {
383
+ push(entry) {
384
+ store.setState({ stack: [...store.getState().stack, entry] });
412
385
  },
413
- SET_INGEST: {
414
- actions: (0, import_xstate2.assign)(({ event }) => ({ ingestActive: event.active }))
415
- }
416
- },
417
- states: {
418
- idle: {
419
- entry: (0, import_xstate2.assign)({ stack: [] }),
420
- on: {
421
- TOGGLE_INSPECTOR: { target: "inspecting" },
422
- OPEN_PALETTE: { target: "palette" },
423
- PUSH_VIEW: {
424
- target: "viewing",
425
- actions: (0, import_xstate2.assign)(({ event }) => ({ stack: [event.entry] }))
426
- },
427
- ESC: {
428
- guard: "hasPinnedHighlight",
429
- actions: [
430
- (0, import_xstate2.assign)({ pinnedHighlight: null }),
431
- (0, import_xstate2.sendTo)("highlight", { type: "UNPIN" })
432
- ]
433
- }
386
+ pop() {
387
+ const s = store.getState().stack;
388
+ if (s.length <= 1) {
389
+ store.setState({ stack: [] });
390
+ } else {
391
+ store.setState({ stack: s.slice(0, -1) });
434
392
  }
435
393
  },
436
- inspecting: {
437
- entry: { type: "mountInspector" },
438
- exit: { type: "destroyInspector" },
439
- on: {
440
- SELECT: {
441
- target: "viewing",
442
- actions: (0, import_xstate2.assign)(({ context, event }) => ({
443
- selection: event.ref,
444
- stack: [...context.stack, event.entry]
445
- }))
446
- },
447
- PUSH_VIEW: {
448
- target: "viewing",
449
- actions: (0, import_xstate2.assign)(({ event }) => ({ stack: [event.entry] }))
450
- },
451
- TOGGLE_INSPECTOR: { target: "idle" },
452
- OPEN_PALETTE: { target: "palette" },
453
- ESC: { target: "idle" }
394
+ replace(entry) {
395
+ const s = store.getState().stack;
396
+ if (s.length === 0) {
397
+ store.setState({ stack: [entry] });
398
+ } else {
399
+ store.setState({ stack: [...s.slice(0, -1), entry] });
454
400
  }
455
401
  },
456
- palette: {
457
- entry: (0, import_xstate2.assign)({ stack: [{ id: "command-palette", ref: null }] }),
458
- on: {
459
- PUSH_VIEW: {
460
- target: "viewing",
461
- actions: (0, import_xstate2.assign)(({ context, event }) => ({
462
- stack: [...context.stack, event.entry]
463
- }))
464
- },
465
- CLOSE: { target: "idle" },
466
- ESC: { target: "idle" },
467
- CMD_K: [
468
- {
469
- guard: "hasActions",
470
- actions: { type: "openActionsPopup" }
471
- },
472
- { target: "idle" }
473
- ]
474
- }
402
+ clear() {
403
+ store.setState({ stack: [] });
475
404
  },
476
- viewing: {
477
- on: {
478
- PUSH_VIEW: {
479
- actions: (0, import_xstate2.assign)(({ context, event }) => ({
480
- stack: [...context.stack, event.entry]
481
- }))
482
- },
483
- POP_VIEW: [
484
- {
485
- guard: "stackDeeperThanTwo",
486
- actions: (0, import_xstate2.assign)(({ context }) => ({
487
- stack: context.stack.slice(0, -1)
488
- }))
489
- },
490
- {
491
- guard: "paletteAtBottomDepthTwo",
492
- target: "palette"
493
- },
494
- {
495
- guard: "stackEqualsTwo",
496
- actions: (0, import_xstate2.assign)(({ context }) => ({
497
- stack: context.stack.slice(0, -1)
498
- }))
499
- },
500
- {
501
- guard: "stackEqualsOne",
502
- target: "idle"
503
- }
504
- ],
505
- CLOSE: { target: "idle" },
506
- ESC: [
507
- {
508
- guard: "popupOpen",
509
- actions: { type: "closePopup" }
510
- },
511
- {
512
- guard: "stackDeeperThanTwo",
513
- actions: (0, import_xstate2.assign)(({ context }) => ({
514
- stack: context.stack.slice(0, -1)
515
- }))
516
- },
517
- {
518
- guard: "paletteAtBottomDepthTwo",
519
- target: "palette"
520
- },
521
- {
522
- guard: "stackEqualsTwo",
523
- actions: (0, import_xstate2.assign)(({ context }) => ({
524
- stack: context.stack.slice(0, -1)
525
- }))
526
- },
527
- {
528
- guard: "stackEqualsOne",
529
- target: "idle"
530
- }
531
- ]
532
- }
405
+ reset(stack) {
406
+ store.setState({ stack });
533
407
  }
534
- }
535
- });
408
+ };
409
+ const navStore = store;
410
+ navStore.nav = nav;
411
+ return navStore;
412
+ }
536
413
 
537
- // src/session/store.ts
414
+ // src/browser/session/store.ts
538
415
  var defaultSnapshot = {
539
416
  hover: null,
540
417
  selection: null,
@@ -543,7 +420,8 @@ var defaultSnapshot = {
543
420
  inspectorActive: false,
544
421
  theme: "auto",
545
422
  resolvedTheme: "light",
546
- ingestActive: false
423
+ ingestActive: false,
424
+ user: null
547
425
  };
548
426
  function resolveTheme(preference, detect) {
549
427
  if (preference !== "auto") return preference;
@@ -554,167 +432,145 @@ function resolveTheme(preference, detect) {
554
432
  }
555
433
  return "light";
556
434
  }
557
- function projectFromActor(actor) {
558
- const sn = actor.getSnapshot();
559
- const child = sn.children.highlight;
560
- const childSnapshot = child?.getSnapshot();
561
- const pinned = childSnapshot && childSnapshot.value === "pinned" ? childSnapshot.context.pinnedRef : null;
562
- return {
563
- hover: sn.context.hover,
564
- selection: sn.context.selection,
565
- stack: sn.context.stack,
566
- pinnedHighlight: pinned,
567
- inspectorActive: sn.matches("inspecting"),
568
- theme: sn.context.theme,
569
- resolvedTheme: sn.context.resolvedTheme,
570
- ingestActive: sn.context.ingestActive
571
- };
572
- }
573
435
  function createSession(options = {}) {
574
- const { detectTheme, bindings, ...overrides } = options;
436
+ const {
437
+ detectTheme,
438
+ onMountInspector,
439
+ onDestroyInspector,
440
+ onShowOverlay,
441
+ onHideOverlay,
442
+ onUpdateOverlay,
443
+ ...overrides
444
+ } = options;
575
445
  const initialPref = overrides.theme ?? defaultSnapshot.theme;
576
446
  const initialResolved = overrides.resolvedTheme ?? resolveTheme(initialPref, detectTheme);
577
447
  const initialStack = overrides.stack ?? [];
578
- const input = {
448
+ const nav = createNavigationStore();
449
+ const modeStore = createModeStore({
450
+ nav,
451
+ bindings: {
452
+ mountInspector: onMountInspector,
453
+ destroyInspector: onDestroyInspector
454
+ }
455
+ });
456
+ let highlightMode = "none";
457
+ const hlCtx = {
458
+ ref: null,
459
+ element: null,
460
+ pinnedRef: null,
461
+ color: null
462
+ };
463
+ const highlightActions = {
464
+ hover(ref, element, color) {
465
+ hlCtx.ref = ref;
466
+ hlCtx.element = element ?? null;
467
+ hlCtx.color = color ?? null;
468
+ if (highlightMode === "none") {
469
+ highlightMode = "transient";
470
+ onShowOverlay?.(hlCtx);
471
+ } else if (highlightMode === "transient") {
472
+ onUpdateOverlay?.(hlCtx);
473
+ }
474
+ store.setState({ hover: ref });
475
+ },
476
+ unhover() {
477
+ if (highlightMode === "transient") {
478
+ highlightMode = "none";
479
+ hlCtx.ref = null;
480
+ hlCtx.element = null;
481
+ hlCtx.color = null;
482
+ onHideOverlay?.();
483
+ }
484
+ store.setState({ hover: null });
485
+ },
486
+ pin(ref) {
487
+ const pinRef = ref ?? hlCtx.ref;
488
+ hlCtx.pinnedRef = pinRef;
489
+ if (highlightMode === "none") {
490
+ hlCtx.ref = pinRef;
491
+ }
492
+ if (highlightMode !== "pinned") {
493
+ highlightMode = "pinned";
494
+ onShowOverlay?.(hlCtx);
495
+ }
496
+ store.setState({ pinnedHighlight: hlCtx.pinnedRef });
497
+ },
498
+ unpin() {
499
+ if (highlightMode === "pinned") {
500
+ highlightMode = "none";
501
+ hlCtx.ref = null;
502
+ hlCtx.element = null;
503
+ hlCtx.pinnedRef = null;
504
+ hlCtx.color = null;
505
+ onHideOverlay?.();
506
+ }
507
+ store.setState({ pinnedHighlight: null });
508
+ }
509
+ };
510
+ const store = (0, import_vanilla3.createStore)(() => ({
511
+ ...defaultSnapshot,
579
512
  hover: overrides.hover ?? null,
580
513
  selection: overrides.selection ?? null,
514
+ stack: [],
581
515
  pinnedHighlight: null,
516
+ inspectorActive: false,
582
517
  theme: initialPref,
583
518
  resolvedTheme: initialResolved,
584
- ingestActive: overrides.ingestActive ?? false
585
- };
586
- const providedHighlight = highlightMachine.provide({
587
- actions: {
588
- showOverlay: ({ context }) => bindings?.highlight?.showOverlay?.(context),
589
- hideOverlay: () => bindings?.highlight?.hideOverlay?.(),
590
- updateOverlay: ({ context }) => bindings?.highlight?.updateOverlay?.(context)
519
+ ingestActive: overrides.ingestActive ?? false,
520
+ user: overrides.user ?? null
521
+ }));
522
+ nav.subscribe(() => {
523
+ const { stack } = nav.getState();
524
+ if (store.getState().stack !== stack) {
525
+ store.setState({ stack });
591
526
  }
592
527
  });
593
- const providedSurface = surfaceMachine.provide({
594
- actions: {
595
- mountInspector: () => bindings?.surface?.mountInspector?.(),
596
- destroyInspector: () => bindings?.surface?.destroyInspector?.(),
597
- openActionsPopup: () => bindings?.surface?.openActionsPopup?.(),
598
- closePopup: () => bindings?.surface?.closePopup?.()
599
- },
600
- actors: {
601
- highlight: providedHighlight
528
+ modeStore.subscribe(() => {
529
+ const { inspectorActive } = modeStore.getState();
530
+ if (store.getState().inspectorActive !== inspectorActive) {
531
+ store.setState({ inspectorActive });
602
532
  }
603
533
  });
604
- const actor = (0, import_xstate3.createActor)(providedSurface, { input });
605
- actor.start();
534
+ const session = store;
535
+ session.nav = nav;
536
+ session.mode = modeStore;
537
+ session.highlight = highlightActions;
538
+ session.select = (ref) => {
539
+ if (sameRef(store.getState().selection, ref)) return;
540
+ store.setState({ selection: ref });
541
+ };
542
+ session.setTheme = (theme, resolved) => {
543
+ const state = store.getState();
544
+ const nextResolved = resolved ?? resolveTheme(theme, detectTheme);
545
+ if (state.theme === theme && state.resolvedTheme === nextResolved) return;
546
+ store.setState({ theme, resolvedTheme: nextResolved });
547
+ };
548
+ session.setIngest = (active) => {
549
+ if (store.getState().ingestActive === active) return;
550
+ store.setState({ ingestActive: active });
551
+ };
606
552
  if (initialStack.length > 0) {
607
- actor.send({ type: "OPEN_PALETTE" });
553
+ modeStore.transition.openPalette();
608
554
  for (let i = 1; i < initialStack.length; i++) {
609
- actor.send({ type: "PUSH_VIEW", entry: initialStack[i] });
555
+ modeStore.transition.pushView(initialStack[i]);
610
556
  }
611
557
  } else if (overrides.inspectorActive) {
612
- actor.send({ type: "TOGGLE_INSPECTOR" });
558
+ modeStore.transition.openInspector();
613
559
  }
614
560
  if (overrides.pinnedHighlight) {
615
- actor.send({ type: "PIN", ref: overrides.pinnedHighlight });
616
- }
617
- const store = (0, import_vanilla.createStore)((_set, get) => ({
618
- ...projectFromActor(actor),
619
- actions: {
620
- hover(ref) {
621
- if (sameRef(get().hover, ref)) return;
622
- if (ref === null) {
623
- actor.send({ type: "UNHOVER" });
624
- } else {
625
- actor.send({ type: "HOVER", ref, element: null });
626
- }
627
- },
628
- select(ref) {
629
- if (sameRef(get().selection, ref)) return;
630
- actor.send({ type: "SET_SELECTION", ref });
631
- },
632
- pushStack(entry) {
633
- if (entry.id === "command-palette" && get().stack.length === 0) {
634
- actor.send({ type: "OPEN_PALETTE" });
635
- } else {
636
- actor.send({ type: "PUSH_VIEW", entry });
637
- }
638
- },
639
- popStack() {
640
- const sn = actor.getSnapshot();
641
- if (sn.matches("palette")) {
642
- actor.send({ type: "ESC" });
643
- } else if (sn.matches("viewing")) {
644
- actor.send({ type: "POP_VIEW" });
645
- }
646
- },
647
- clearStack() {
648
- if (get().stack.length === 0) return;
649
- actor.send({ type: "CLOSE" });
650
- },
651
- openView(id, ref) {
652
- const resolvedRef = ref === void 0 ? get().selection : ref;
653
- get().actions.pushStack({ id, ref: resolvedRef });
654
- },
655
- closeView() {
656
- get().actions.clearStack();
657
- },
658
- setInspectorActive(active) {
659
- if (get().inspectorActive === active) return;
660
- actor.send({ type: "TOGGLE_INSPECTOR" });
661
- },
662
- toggleInspector() {
663
- actor.send({ type: "TOGGLE_INSPECTOR" });
664
- },
665
- pinHighlight(ref) {
666
- if (sameRef(get().pinnedHighlight, ref)) return;
667
- actor.send({ type: "PIN", ref });
668
- },
669
- clearPinnedHighlight() {
670
- if (get().pinnedHighlight === null) return;
671
- actor.send({ type: "UNPIN" });
672
- },
673
- setTheme(theme, resolved) {
674
- const state = get();
675
- const nextResolved = resolved ?? resolveTheme(theme, detectTheme);
676
- if (state.theme === theme && state.resolvedTheme === nextResolved)
677
- return;
678
- actor.send({ type: "SET_THEME", theme, resolvedTheme: nextResolved });
679
- },
680
- setIngest(active) {
681
- if (get().ingestActive === active) return;
682
- actor.send({ type: "SET_INGEST", active });
683
- }
684
- }
685
- }));
686
- const projectAndSet = () => {
687
- const next = projectFromActor(actor);
688
- const prev = store.getState();
689
- const updates = {};
690
- if (!sameRef(prev.hover, next.hover)) updates.hover = next.hover;
691
- if (!sameRef(prev.selection, next.selection))
692
- updates.selection = next.selection;
693
- if (prev.stack !== next.stack) updates.stack = next.stack;
694
- if (!sameRef(prev.pinnedHighlight, next.pinnedHighlight))
695
- updates.pinnedHighlight = next.pinnedHighlight;
696
- if (prev.inspectorActive !== next.inspectorActive)
697
- updates.inspectorActive = next.inspectorActive;
698
- if (prev.theme !== next.theme) updates.theme = next.theme;
699
- if (prev.resolvedTheme !== next.resolvedTheme)
700
- updates.resolvedTheme = next.resolvedTheme;
701
- if (prev.ingestActive !== next.ingestActive)
702
- updates.ingestActive = next.ingestActive;
703
- if (Object.keys(updates).length === 0) return;
704
- store.setState(updates);
705
- };
706
- actor.subscribe(projectAndSet);
707
- const session = store;
708
- session.send = (event) => actor.send(event);
561
+ highlightActions.pin(overrides.pinnedHighlight);
562
+ }
709
563
  return session;
710
564
  }
711
565
 
712
- // src/styles/tailwind.built.css
566
+ // src/browser/styles/tailwind.built.css
713
567
  var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
714
568
  @layer properties;
715
569
  @layer theme, base, components, utilities;
716
570
  @layer theme {
717
571
  :root, :host {
572
+ --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
573
+ Roboto, sans-serif;
718
574
  --color-red-400: oklch(70.4% 0.191 22.216);
719
575
  --color-red-500: oklch(63.7% 0.237 25.331);
720
576
  --color-red-700: oklch(50.5% 0.213 27.518);
@@ -780,6 +636,7 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
780
636
  --font-weight-normal: 400;
781
637
  --font-weight-medium: 500;
782
638
  --font-weight-semibold: 600;
639
+ --font-weight-bold: 700;
783
640
  --tracking-tight: -0.025em;
784
641
  --tracking-widest: 0.1em;
785
642
  --leading-relaxed: 1.625;
@@ -954,6 +811,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
954
811
  .\\!visible {
955
812
  visibility: visible !important;
956
813
  }
814
+ .collapse {
815
+ visibility: collapse;
816
+ }
957
817
  .invisible {
958
818
  visibility: hidden;
959
819
  }
@@ -989,9 +849,21 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
989
849
  .end {
990
850
  inset-inline-end: var(--spacing);
991
851
  }
852
+ .-top-1 {
853
+ top: calc(var(--spacing) * -1);
854
+ }
855
+ .-right-1 {
856
+ right: calc(var(--spacing) * -1);
857
+ }
858
+ .right-0 {
859
+ right: calc(var(--spacing) * 0);
860
+ }
992
861
  .right-2 {
993
862
  right: calc(var(--spacing) * 2);
994
863
  }
864
+ .bottom-full {
865
+ bottom: 100%;
866
+ }
995
867
  .bottom-px {
996
868
  bottom: 1px;
997
869
  }
@@ -1040,6 +912,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1040
912
  .mt-1 {
1041
913
  margin-top: calc(var(--spacing) * 1);
1042
914
  }
915
+ .mb-2 {
916
+ margin-bottom: calc(var(--spacing) * 2);
917
+ }
918
+ .mb-6 {
919
+ margin-bottom: calc(var(--spacing) * 6);
920
+ }
1043
921
  .ml-auto {
1044
922
  margin-left: auto;
1045
923
  }
@@ -1058,6 +936,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1058
936
  .inline {
1059
937
  display: inline;
1060
938
  }
939
+ .inline-block {
940
+ display: inline-block;
941
+ }
1061
942
  .inline-flex {
1062
943
  display: inline-flex;
1063
944
  }
@@ -1067,6 +948,14 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1067
948
  .table {
1068
949
  display: table;
1069
950
  }
951
+ .size-2 {
952
+ width: calc(var(--spacing) * 2);
953
+ height: calc(var(--spacing) * 2);
954
+ }
955
+ .size-3 {
956
+ width: calc(var(--spacing) * 3);
957
+ height: calc(var(--spacing) * 3);
958
+ }
1070
959
  .size-3\\.5 {
1071
960
  width: calc(var(--spacing) * 3.5);
1072
961
  height: calc(var(--spacing) * 3.5);
@@ -1157,6 +1046,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1157
1046
  .w-4 {
1158
1047
  width: calc(var(--spacing) * 4);
1159
1048
  }
1049
+ .w-12 {
1050
+ width: calc(var(--spacing) * 12);
1051
+ }
1160
1052
  .w-20 {
1161
1053
  width: calc(var(--spacing) * 20);
1162
1054
  }
@@ -1285,6 +1177,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1285
1177
  .gap-0 {
1286
1178
  gap: calc(var(--spacing) * 0);
1287
1179
  }
1180
+ .gap-0\\.5 {
1181
+ gap: calc(var(--spacing) * 0.5);
1182
+ }
1288
1183
  .gap-1 {
1289
1184
  gap: calc(var(--spacing) * 1);
1290
1185
  }
@@ -1433,9 +1328,15 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1433
1328
  .bg-emerald-50 {
1434
1329
  background-color: var(--color-emerald-50);
1435
1330
  }
1331
+ .bg-emerald-500 {
1332
+ background-color: var(--color-emerald-500);
1333
+ }
1436
1334
  .bg-fuchsia-50 {
1437
1335
  background-color: var(--color-fuchsia-50);
1438
1336
  }
1337
+ .bg-info {
1338
+ background-color: var(--info);
1339
+ }
1439
1340
  .bg-info\\/10 {
1440
1341
  background-color: var(--info);
1441
1342
  @supports (color: color-mix(in lab, red, red)) {
@@ -1568,6 +1469,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1568
1469
  .pe-3 {
1569
1470
  padding-inline-end: calc(var(--spacing) * 3);
1570
1471
  }
1472
+ .pt-2 {
1473
+ padding-top: calc(var(--spacing) * 2);
1474
+ }
1571
1475
  .pr-8 {
1572
1476
  padding-right: calc(var(--spacing) * 8);
1573
1477
  }
@@ -1610,10 +1514,27 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1610
1514
  .text-\\[0\\.625rem\\] {
1611
1515
  font-size: 0.625rem;
1612
1516
  }
1517
+ .text-\\[9px\\] {
1518
+ font-size: 9px;
1519
+ }
1520
+ .text-\\[10px\\] {
1521
+ font-size: 10px;
1522
+ }
1523
+ .text-\\[11px\\] {
1524
+ font-size: 11px;
1525
+ }
1526
+ .leading-none {
1527
+ --tw-leading: 1;
1528
+ line-height: 1;
1529
+ }
1613
1530
  .leading-relaxed {
1614
1531
  --tw-leading: var(--leading-relaxed);
1615
1532
  line-height: var(--leading-relaxed);
1616
1533
  }
1534
+ .font-bold {
1535
+ --tw-font-weight: var(--font-weight-bold);
1536
+ font-weight: var(--font-weight-bold);
1537
+ }
1617
1538
  .font-medium {
1618
1539
  --tw-font-weight: var(--font-weight-medium);
1619
1540
  font-weight: var(--font-weight-medium);
@@ -1661,6 +1582,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1661
1582
  .text-cyan-700 {
1662
1583
  color: var(--color-cyan-700);
1663
1584
  }
1585
+ .text-destructive {
1586
+ color: var(--destructive);
1587
+ }
1664
1588
  .text-destructive-foreground {
1665
1589
  color: var(--destructive-foreground);
1666
1590
  }
@@ -1763,6 +1687,10 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1763
1687
  --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));
1764
1688
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1765
1689
  }
1690
+ .shadow-lg {
1691
+ --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));
1692
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1693
+ }
1766
1694
  .shadow-none {
1767
1695
  --tw-shadow: 0 0 #0000;
1768
1696
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -2005,16 +1933,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2005
1933
  border-color: var(--ring);
2006
1934
  }
2007
1935
  }
2008
- .focus-within\\:bg-accent {
2009
- &:focus-within {
2010
- background-color: var(--accent);
2011
- }
2012
- }
2013
- .focus-within\\:text-accent-foreground {
2014
- &:focus-within {
2015
- color: var(--accent-foreground);
2016
- }
2017
- }
2018
1936
  .focus-within\\:ring-\\[3px\\] {
2019
1937
  &:focus-within {
2020
1938
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
@@ -2120,16 +2038,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2120
2038
  }
2121
2039
  }
2122
2040
  }
2123
- .focus\\:bg-accent {
2124
- &:focus {
2125
- background-color: var(--accent);
2126
- }
2127
- }
2128
- .focus\\:text-accent-foreground {
2129
- &:focus {
2130
- color: var(--accent-foreground);
2131
- }
2132
- }
2133
2041
  .focus\\:outline-none {
2134
2042
  &:focus {
2135
2043
  --tw-outline-style: none;
@@ -2187,6 +2095,11 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2187
2095
  cursor: not-allowed;
2188
2096
  }
2189
2097
  }
2098
+ .disabled\\:opacity-50 {
2099
+ &:disabled {
2100
+ opacity: 50%;
2101
+ }
2102
+ }
2190
2103
  .disabled\\:opacity-60 {
2191
2104
  &:disabled {
2192
2105
  opacity: 60%;
@@ -2629,6 +2542,34 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2629
2542
  height: calc(var(--spacing) * 4);
2630
2543
  }
2631
2544
  }
2545
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:bg-accent {
2546
+ [data-kbd-nav] & {
2547
+ &:focus-within {
2548
+ background-color: var(--accent);
2549
+ }
2550
+ }
2551
+ }
2552
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:text-accent-foreground {
2553
+ [data-kbd-nav] & {
2554
+ &:focus-within {
2555
+ color: var(--accent-foreground);
2556
+ }
2557
+ }
2558
+ }
2559
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus\\:bg-accent {
2560
+ [data-kbd-nav] & {
2561
+ &:focus {
2562
+ background-color: var(--accent);
2563
+ }
2564
+ }
2565
+ }
2566
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus\\:text-accent-foreground {
2567
+ [data-kbd-nav] & {
2568
+ &:focus {
2569
+ color: var(--accent-foreground);
2570
+ }
2571
+ }
2572
+ }
2632
2573
  }
2633
2574
  @layer base {
2634
2575
  *, ::after, ::before {
@@ -3160,12 +3101,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3160
3101
  }
3161
3102
  `;
3162
3103
 
3163
- // src/surface/constants.ts
3104
+ // src/browser/surface/constants.ts
3164
3105
  var SURFACE_HOST_CLASS = "uidex-surface-host";
3165
3106
  var SURFACE_CONTAINER_CLASS = "uidex-container";
3166
- var Z_BASE = 2147483644;
3167
- var Z_OVERLAY = 2147483645;
3168
- var Z_CHROME = 2147483646;
3107
+ var Z_BASE = 2147483630;
3108
+ var Z_OVERLAY = 2147483635;
3109
+ var Z_CHROME = 2147483645;
3169
3110
  var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS},.${SURFACE_CONTAINER_CLASS}`;
3170
3111
  var UIDEX_ATTR_TO_KIND = [
3171
3112
  ["data-uidex", "element"],
@@ -3174,7 +3115,7 @@ var UIDEX_ATTR_TO_KIND = [
3174
3115
  ["data-uidex-primitive", "primitive"]
3175
3116
  ];
3176
3117
 
3177
- // src/surface/host.ts
3118
+ // src/browser/surface/host.ts
3178
3119
  function createSurfaceHost(options) {
3179
3120
  const { mount, stylesheets = [], initialTheme = "light" } = options;
3180
3121
  const hostEl = document.createElement("div");
@@ -3188,6 +3129,8 @@ function createSurfaceHost(options) {
3188
3129
  applyStylesheets(shadow, [tailwind_built_default, ...stylesheets]);
3189
3130
  const chromeEl = document.createElement("div");
3190
3131
  chromeEl.classList.add("uidex-chrome");
3132
+ chromeEl.style.position = "relative";
3133
+ chromeEl.style.zIndex = String(Z_CHROME);
3191
3134
  chromeEl.style.pointerEvents = "auto";
3192
3135
  shadow.appendChild(chromeEl);
3193
3136
  mount.appendChild(hostEl);
@@ -3235,7 +3178,7 @@ function supportsConstructableStylesheets(shadow) {
3235
3178
  }
3236
3179
  }
3237
3180
 
3238
- // src/surface/cursor-tooltip.ts
3181
+ // src/browser/surface/cursor-tooltip.ts
3239
3182
  var import_lucide2 = require("lucide");
3240
3183
  var DEFAULT_TOOLTIP_COLOR = "#34d399";
3241
3184
  var TOOLTIP_OFFSET = 16;
@@ -3293,7 +3236,9 @@ function createCursorTooltip(deps) {
3293
3236
  if (!currentContent) return;
3294
3237
  const { entity, node } = currentContent;
3295
3238
  const style = KIND_STYLE[entity.kind];
3239
+ const demoted = entity.kind === "primitive";
3296
3240
  applyColor(style?.color ?? DEFAULT_TOOLTIP_COLOR);
3241
+ el2.style.opacity = demoted ? "0.55" : "1";
3297
3242
  if (style) {
3298
3243
  const svg = (0, import_lucide2.createElement)(style.icon);
3299
3244
  svg.setAttribute("aria-hidden", "true");
@@ -3341,7 +3286,7 @@ function createCursorTooltip(deps) {
3341
3286
  };
3342
3287
  }
3343
3288
 
3344
- // src/surface/inspector.ts
3289
+ // src/browser/surface/inspector.ts
3345
3290
  function entityForRef(ref, registry) {
3346
3291
  if (registry) {
3347
3292
  const found = registry.get(ref.kind, ref.id);
@@ -3361,7 +3306,8 @@ function entityForRef(ref, registry) {
3361
3306
  }
3362
3307
  function defaultResolveAllMatches(target, registry) {
3363
3308
  if (target.closest(SURFACE_IGNORE_SELECTOR)) return [];
3364
- const matches = [];
3309
+ const semantic = [];
3310
+ const primitives = [];
3365
3311
  const seen = /* @__PURE__ */ new Set();
3366
3312
  let node = target;
3367
3313
  while (node) {
@@ -3374,19 +3320,24 @@ function defaultResolveAllMatches(target, registry) {
3374
3320
  seen.add(key);
3375
3321
  const ref = { kind, id };
3376
3322
  const entity = entityForRef(ref, registry);
3377
- matches.push({
3323
+ const match = {
3378
3324
  element: node,
3379
3325
  ref,
3380
3326
  entity,
3381
3327
  label: displayName(entity, node)
3382
- });
3328
+ };
3329
+ if (kind === "primitive") {
3330
+ primitives.push(match);
3331
+ } else {
3332
+ semantic.push(match);
3333
+ }
3383
3334
  }
3384
3335
  }
3385
3336
  }
3386
3337
  }
3387
3338
  node = node.parentElement;
3388
3339
  }
3389
- return matches;
3340
+ return semantic.concat(primitives);
3390
3341
  }
3391
3342
  function createInspector(options) {
3392
3343
  const {
@@ -3415,14 +3366,14 @@ function createInspector(options) {
3415
3366
  currentEl = topEl;
3416
3367
  stack = matches;
3417
3368
  layerIndex = 0;
3418
- session.getState().actions.hover(stack[0].ref);
3369
+ session.highlight.hover(stack[0].ref);
3419
3370
  }
3420
3371
  onHover?.(makeStack(), cursor);
3421
3372
  } else if (currentEl) {
3422
3373
  currentEl = null;
3423
3374
  stack = [];
3424
3375
  layerIndex = 0;
3425
- session.getState().actions.hover(null);
3376
+ session.highlight.unhover();
3426
3377
  onHover?.(null, cursor);
3427
3378
  }
3428
3379
  };
@@ -3433,7 +3384,7 @@ function createInspector(options) {
3433
3384
  e.preventDefault();
3434
3385
  e.stopPropagation();
3435
3386
  const match = stack[layerIndex];
3436
- session.getState().actions.select(match.ref);
3387
+ session.select(match.ref);
3437
3388
  onSelect?.(match, { x: e.clientX, y: e.clientY });
3438
3389
  };
3439
3390
  const onContextMenu = (e) => {
@@ -3442,7 +3393,7 @@ function createInspector(options) {
3442
3393
  e.stopPropagation();
3443
3394
  layerIndex = (layerIndex + 1) % stack.length;
3444
3395
  const match = stack[layerIndex];
3445
- session.getState().actions.hover(match.ref);
3396
+ session.highlight.hover(match.ref);
3446
3397
  onCycle?.(makeStack(), lastCursor);
3447
3398
  };
3448
3399
  return {
@@ -3472,23 +3423,23 @@ function createInspector(options) {
3472
3423
  document.removeEventListener("mousemove", onMouseMove);
3473
3424
  document.removeEventListener("click", onClick, true);
3474
3425
  document.removeEventListener("contextmenu", onContextMenu, true);
3475
- session.getState().actions.hover(null);
3426
+ session.highlight.unhover();
3476
3427
  onHover?.(null, null);
3477
3428
  }
3478
3429
  };
3479
3430
  }
3480
3431
 
3481
- // src/surface/menu-bar.ts
3432
+ // src/browser/surface/menu-bar.ts
3482
3433
  var import_lucide3 = require("lucide");
3483
3434
 
3484
- // src/internal/cn.ts
3435
+ // src/browser/internal/cn.ts
3485
3436
  var import_clsx = require("clsx");
3486
3437
  var import_tailwind_merge = require("tailwind-merge");
3487
3438
  function cn(...inputs) {
3488
3439
  return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
3489
3440
  }
3490
3441
 
3491
- // src/internal/el.ts
3442
+ // src/browser/internal/el.ts
3492
3443
  function el(tag, options = {}, children = []) {
3493
3444
  const node = document.createElement(tag);
3494
3445
  if (options.class) node.className = cn(options.class);
@@ -3523,7 +3474,7 @@ function el(tag, options = {}, children = []) {
3523
3474
  return node;
3524
3475
  }
3525
3476
 
3526
- // src/surface/keys.ts
3477
+ // src/browser/surface/keys.ts
3527
3478
  var INSPECT_SHORTCUT = { key: "i" };
3528
3479
  function formatShortcutLabel(shortcut) {
3529
3480
  const parts = [];
@@ -3533,7 +3484,7 @@ function formatShortcutLabel(shortcut) {
3533
3484
  return parts.join("");
3534
3485
  }
3535
3486
 
3536
- // src/surface/menu-bar.ts
3487
+ // src/browser/surface/menu-bar.ts
3537
3488
  var ROOT_CLASS = "inline-flex items-center gap-1.5 rounded-xl border border-border bg-popover p-1.5 font-sans text-xs text-popover-foreground shadow-lg/5 select-none not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-xl)-1px)] before:bg-muted/72 before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]";
3538
3489
  var BUTTON_CLASS = "relative z-1 inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md border border-transparent text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring";
3539
3490
  var BUTTON_ACTIVE_CLASS = "border-info/40 bg-info/15 text-info-foreground hover:bg-info/20";
@@ -3544,8 +3495,11 @@ function createMenuBar(options) {
3544
3495
  container,
3545
3496
  session,
3546
3497
  initialCorner = "bottom-right",
3547
- appTitle
3498
+ appTitle,
3499
+ channel = null,
3500
+ pinLayer: initialPinLayer = null
3548
3501
  } = options;
3502
+ let activePinLayer = initialPinLayer;
3549
3503
  const root = el("div", {
3550
3504
  class: ROOT_CLASS,
3551
3505
  attrs: {
@@ -3586,7 +3540,7 @@ function createMenuBar(options) {
3586
3540
  highlightBtn.hidden = true;
3587
3541
  highlightBtn.addEventListener("click", (e) => {
3588
3542
  e.stopPropagation();
3589
- session.send({ type: "UNPIN" });
3543
+ session.highlight.unpin();
3590
3544
  });
3591
3545
  root.appendChild(highlightBtn);
3592
3546
  const inspectIcon = (0, import_lucide3.createElement)(import_lucide3.MousePointerClick);
@@ -3606,9 +3560,166 @@ function createMenuBar(options) {
3606
3560
  );
3607
3561
  inspectBtn.addEventListener("click", (e) => {
3608
3562
  e.stopPropagation();
3609
- session.send({ type: "TOGGLE_INSPECTOR" });
3563
+ session.mode.transition.toggleInspector();
3564
+ inspectBtn.blur();
3610
3565
  });
3611
3566
  root.appendChild(inspectBtn);
3567
+ const pinIcon = (0, import_lucide3.createElement)(import_lucide3.MapPin);
3568
+ pinIcon.setAttribute("class", "size-3.5");
3569
+ pinIcon.setAttribute("aria-hidden", "true");
3570
+ const pinBtn = el(
3571
+ "button",
3572
+ {
3573
+ class: BUTTON_CLASS,
3574
+ attrs: {
3575
+ type: "button",
3576
+ "data-uidex-menubar-pins": "",
3577
+ "aria-label": "Report pins"
3578
+ }
3579
+ },
3580
+ pinIcon
3581
+ );
3582
+ const commitCycler = el("div", {
3583
+ class: "relative z-1 inline-flex items-center gap-0.5",
3584
+ attrs: { "data-uidex-menubar-commit-cycler": "" }
3585
+ });
3586
+ commitCycler.hidden = true;
3587
+ const prevIcon = (0, import_lucide3.createElement)(import_lucide3.ChevronLeft);
3588
+ prevIcon.setAttribute("class", "size-3");
3589
+ prevIcon.setAttribute("aria-hidden", "true");
3590
+ const prevBtn = el(
3591
+ "button",
3592
+ {
3593
+ class: BUTTON_CLASS,
3594
+ attrs: { type: "button", "aria-label": "Previous commit" },
3595
+ style: { width: "18px", height: "18px" }
3596
+ },
3597
+ prevIcon
3598
+ );
3599
+ const commitLabel = el("span", {
3600
+ class: "relative z-1 whitespace-nowrap px-1 text-[10px] font-mono text-muted-foreground",
3601
+ attrs: { "data-uidex-menubar-commit-label": "" }
3602
+ });
3603
+ const nextIcon = (0, import_lucide3.createElement)(import_lucide3.ChevronRight);
3604
+ nextIcon.setAttribute("class", "size-3");
3605
+ nextIcon.setAttribute("aria-hidden", "true");
3606
+ const nextBtn = el(
3607
+ "button",
3608
+ {
3609
+ class: BUTTON_CLASS,
3610
+ attrs: { type: "button", "aria-label": "Next commit" },
3611
+ style: { width: "18px", height: "18px" }
3612
+ },
3613
+ nextIcon
3614
+ );
3615
+ commitCycler.appendChild(prevBtn);
3616
+ commitCycler.appendChild(commitLabel);
3617
+ commitCycler.appendChild(nextBtn);
3618
+ const pinWrapper = el("div", {
3619
+ class: "relative z-1 inline-flex items-center gap-0.5",
3620
+ attrs: { "data-uidex-menubar-pin-wrapper": "" }
3621
+ });
3622
+ pinWrapper.hidden = true;
3623
+ pinWrapper.appendChild(pinBtn);
3624
+ pinWrapper.appendChild(commitCycler);
3625
+ root.appendChild(pinWrapper);
3626
+ const updatePinUI = () => {
3627
+ if (!activePinLayer) {
3628
+ pinWrapper.hidden = true;
3629
+ return;
3630
+ }
3631
+ const pinsVisible = activePinLayer.visible;
3632
+ const state = activePinLayer.filterState;
3633
+ const hasCommits = state.commits.length > 0;
3634
+ pinWrapper.hidden = false;
3635
+ commitCycler.hidden = !pinsVisible || !hasCommits;
3636
+ if (state.commitIndex === -1 || !state.commits[state.commitIndex]) {
3637
+ commitLabel.textContent = `all (${state.commits.length})`;
3638
+ } else {
3639
+ const sha = state.commits[state.commitIndex] ?? "";
3640
+ commitLabel.textContent = sha.slice(0, 7);
3641
+ }
3642
+ pinBtn.className = cn(BUTTON_CLASS, pinsVisible && BUTTON_ACTIVE_CLASS);
3643
+ };
3644
+ pinBtn.addEventListener("click", (e) => {
3645
+ e.stopPropagation();
3646
+ if (activePinLayer) {
3647
+ activePinLayer.setVisible(!activePinLayer.visible);
3648
+ }
3649
+ });
3650
+ prevBtn.addEventListener("click", (e) => {
3651
+ e.stopPropagation();
3652
+ activePinLayer?.prevCommit();
3653
+ });
3654
+ nextBtn.addEventListener("click", (e) => {
3655
+ e.stopPropagation();
3656
+ activePinLayer?.nextCommit();
3657
+ });
3658
+ let unsubscribePinFilter = activePinLayer?.onFilterChange(() => updatePinUI());
3659
+ const presenceIcon = (0, import_lucide3.createElement)(import_lucide3.Users);
3660
+ presenceIcon.setAttribute("class", "size-3.5");
3661
+ presenceIcon.setAttribute("aria-hidden", "true");
3662
+ const presenceBtn = el(
3663
+ "button",
3664
+ {
3665
+ class: BUTTON_CLASS,
3666
+ attrs: {
3667
+ type: "button",
3668
+ "data-uidex-menubar-presence": "",
3669
+ "aria-label": "Online users"
3670
+ }
3671
+ },
3672
+ presenceIcon
3673
+ );
3674
+ presenceBtn.hidden = true;
3675
+ const presenceBadge = el("span", {
3676
+ 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"
3677
+ });
3678
+ presenceBtn.appendChild(presenceBadge);
3679
+ const presencePopover = el("div", {
3680
+ 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"
3681
+ });
3682
+ presencePopover.hidden = true;
3683
+ let presencePopoverVisible = false;
3684
+ presenceBtn.addEventListener("click", (e) => {
3685
+ e.stopPropagation();
3686
+ presencePopoverVisible = !presencePopoverVisible;
3687
+ presencePopover.hidden = !presencePopoverVisible;
3688
+ });
3689
+ const presenceWrapper = el("div", { class: "relative z-1 inline-flex" }, [
3690
+ presenceBtn,
3691
+ presencePopover
3692
+ ]);
3693
+ presenceWrapper.hidden = true;
3694
+ root.appendChild(presenceWrapper);
3695
+ let presenceUsers = [];
3696
+ const updatePresenceUI = () => {
3697
+ const localUserId = session.getState().user?.id ?? null;
3698
+ const peers = localUserId ? presenceUsers.filter((u) => u.userId !== localUserId) : presenceUsers;
3699
+ const count = peers.length;
3700
+ presenceWrapper.hidden = count === 0;
3701
+ presenceBadge.textContent = String(count);
3702
+ presenceBtn.setAttribute(
3703
+ "aria-label",
3704
+ count === 1 ? "1 other user online" : `${count} other users online`
3705
+ );
3706
+ presencePopover.innerHTML = "";
3707
+ for (const user of peers) {
3708
+ const row = el("div", {
3709
+ class: "flex items-center gap-2 rounded px-1.5 py-1"
3710
+ });
3711
+ const dot = el("span", {
3712
+ class: "inline-block size-2 shrink-0 rounded-full bg-emerald-500"
3713
+ });
3714
+ const name = el("span", {
3715
+ class: "truncate",
3716
+ text: user.name || "Anonymous"
3717
+ });
3718
+ row.appendChild(dot);
3719
+ row.appendChild(name);
3720
+ presencePopover.appendChild(row);
3721
+ }
3722
+ };
3612
3723
  const paletteIcon = (0, import_lucide3.createElement)(import_lucide3.Command);
3613
3724
  paletteIcon.setAttribute("class", "size-3.5");
3614
3725
  paletteIcon.setAttribute("aria-hidden", "true");
@@ -3627,10 +3738,11 @@ function createMenuBar(options) {
3627
3738
  paletteBtn.addEventListener("click", (e) => {
3628
3739
  e.stopPropagation();
3629
3740
  if (session.getState().stack.length > 0) {
3630
- session.send({ type: "CLOSE" });
3741
+ session.mode.transition.dismiss();
3631
3742
  } else {
3632
- session.send({ type: "OPEN_PALETTE" });
3743
+ session.mode.transition.openPalette();
3633
3744
  }
3745
+ paletteBtn.blur();
3634
3746
  });
3635
3747
  root.appendChild(paletteBtn);
3636
3748
  container.appendChild(root);
@@ -3722,14 +3834,28 @@ function createMenuBar(options) {
3722
3834
  const vertical = y / (window.innerHeight || 1) < 0.5 ? "top" : "bottom";
3723
3835
  snapTo(`${vertical}-${horizontal}`);
3724
3836
  }
3837
+ const unsubscribePresence = channel?.onPresence(
3838
+ (users) => {
3839
+ presenceUsers = users;
3840
+ updatePresenceUI();
3841
+ }
3842
+ );
3725
3843
  return {
3726
3844
  destroy() {
3845
+ unsubscribePinFilter?.();
3846
+ unsubscribePresence?.();
3727
3847
  unsubscribeSession();
3728
3848
  root.removeEventListener("mousedown", onMouseDown);
3729
3849
  document.removeEventListener("mousemove", onMouseMove);
3730
3850
  document.removeEventListener("mouseup", onMouseUp);
3731
3851
  root.remove();
3732
3852
  },
3853
+ setPinLayer(layer) {
3854
+ unsubscribePinFilter?.();
3855
+ activePinLayer = layer;
3856
+ unsubscribePinFilter = layer.onFilterChange(() => updatePinUI());
3857
+ updatePinUI();
3858
+ },
3733
3859
  snapTo,
3734
3860
  snapToNearest,
3735
3861
  get corner() {
@@ -3741,7 +3867,7 @@ function createMenuBar(options) {
3741
3867
  };
3742
3868
  }
3743
3869
 
3744
- // src/surface/overlay.ts
3870
+ // src/browser/surface/overlay.ts
3745
3871
  var DEFAULT_COLOR = "#34d399";
3746
3872
  var DEFAULT_BORDER_WIDTH = 2;
3747
3873
  var DEFAULT_FILL_OPACITY = 0.08;
@@ -3754,7 +3880,9 @@ function createOverlay(deps) {
3754
3880
  backdrop.style.inset = "0";
3755
3881
  backdrop.style.zIndex = String(Z_OVERLAY);
3756
3882
  backdrop.style.backgroundColor = `rgba(0, 0, 0, ${BACKDROP_OPACITY})`;
3757
- backdrop.style.display = "none";
3883
+ backdrop.style.opacity = "0";
3884
+ backdrop.style.visibility = "hidden";
3885
+ backdrop.style.transition = "opacity 150ms ease-out";
3758
3886
  backdrop.style.pointerEvents = "auto";
3759
3887
  backdrop.style.cursor = "default";
3760
3888
  backdrop.addEventListener("click", (e) => {
@@ -3768,7 +3896,9 @@ function createOverlay(deps) {
3768
3896
  box.style.pointerEvents = "none";
3769
3897
  box.style.zIndex = String(Z_OVERLAY + 1);
3770
3898
  box.style.boxSizing = "border-box";
3771
- box.style.display = "none";
3899
+ box.style.opacity = "0";
3900
+ box.style.transition = "opacity 150ms ease-out";
3901
+ box.style.visibility = "hidden";
3772
3902
  const label = document.createElement("div");
3773
3903
  label.setAttribute("data-uidex-overlay-label", "");
3774
3904
  label.style.position = "absolute";
@@ -3781,6 +3911,17 @@ function createOverlay(deps) {
3781
3911
  label.style.whiteSpace = "nowrap";
3782
3912
  label.style.color = "#0a0a0a";
3783
3913
  label.style.display = "none";
3914
+ box.addEventListener("transitionend", () => {
3915
+ if (box.style.opacity === "0") {
3916
+ box.style.visibility = "hidden";
3917
+ }
3918
+ });
3919
+ backdrop.addEventListener("transitionend", () => {
3920
+ if (backdrop.style.opacity === "0") {
3921
+ backdrop.style.visibility = "hidden";
3922
+ backdrop.style.clipPath = "";
3923
+ }
3924
+ });
3784
3925
  box.appendChild(label);
3785
3926
  container.appendChild(box);
3786
3927
  let target = null;
@@ -3863,14 +4004,19 @@ function createOverlay(deps) {
3863
4004
  label.textContent = "";
3864
4005
  label.style.display = "none";
3865
4006
  }
3866
- backdrop.style.display = opts.backdrop ? "" : "none";
3867
- if (!opts.backdrop) {
4007
+ if (opts.backdrop) {
4008
+ backdrop.style.visibility = "visible";
4009
+ backdrop.style.opacity = "1";
4010
+ } else {
4011
+ backdrop.style.opacity = "0";
4012
+ backdrop.style.visibility = "hidden";
3868
4013
  backdrop.style.clipPath = "";
3869
4014
  }
3870
4015
  }
3871
4016
  const overlay = {
3872
4017
  onDismiss: null,
3873
4018
  show(nextTarget, showOpts) {
4019
+ const wasVisible = target !== null;
3874
4020
  target = nextTarget;
3875
4021
  opts = {
3876
4022
  label: showOpts?.label ?? "",
@@ -3883,14 +4029,18 @@ function createOverlay(deps) {
3883
4029
  };
3884
4030
  applyStyles();
3885
4031
  updatePosition();
3886
- box.style.display = "";
4032
+ if (!wasVisible) {
4033
+ box.style.opacity = "0";
4034
+ box.style.visibility = "visible";
4035
+ box.offsetHeight;
4036
+ }
4037
+ box.style.opacity = "1";
3887
4038
  attach();
3888
4039
  },
3889
4040
  hide() {
3890
4041
  target = null;
3891
- box.style.display = "none";
3892
- backdrop.style.display = "none";
3893
- backdrop.style.clipPath = "";
4042
+ box.style.opacity = "0";
4043
+ backdrop.style.opacity = "0";
3894
4044
  detach();
3895
4045
  },
3896
4046
  destroy() {
@@ -3925,7 +4075,7 @@ function rgbaFromColor(color, alpha) {
3925
4075
  return color;
3926
4076
  }
3927
4077
 
3928
- // src/surface/theme.ts
4078
+ // src/browser/surface/theme.ts
3929
4079
  function createThemeDetector(deps) {
3930
4080
  const { session, onResolve } = deps;
3931
4081
  let resolved = session.getState().resolvedTheme;
@@ -3943,7 +4093,7 @@ function createThemeDetector(deps) {
3943
4093
  const next = detect(preference);
3944
4094
  if (next === resolved) return;
3945
4095
  resolved = next;
3946
- session.getState().actions.setTheme(preference, next);
4096
+ session.setTheme(preference, next);
3947
4097
  onResolve?.(next);
3948
4098
  };
3949
4099
  push();
@@ -3994,7 +4144,7 @@ function readHtmlClassToken() {
3994
4144
  return null;
3995
4145
  }
3996
4146
 
3997
- // src/surface/shell.ts
4147
+ // src/browser/surface/shell.ts
3998
4148
  function createSurfaceShell(options) {
3999
4149
  const cleanup = createCleanupStack();
4000
4150
  const host = createSurfaceHost({
@@ -4008,7 +4158,7 @@ function createSurfaceShell(options) {
4008
4158
  onResolve: (theme) => host.applyTheme(theme)
4009
4159
  });
4010
4160
  cleanup.add(themeDetector);
4011
- const overlay = createOverlay({ container: host.chromeEl });
4161
+ const overlay = createOverlay({ container: host.shadowRoot });
4012
4162
  cleanup.add(overlay);
4013
4163
  const tooltip = createCursorTooltip({
4014
4164
  container: host.chromeEl,
@@ -4023,8 +4173,10 @@ function createSurfaceShell(options) {
4023
4173
  return;
4024
4174
  }
4025
4175
  const { current } = stack;
4176
+ const demoted = current.ref.kind === "primitive";
4026
4177
  overlay.show(current.element, {
4027
- color: KIND_STYLE[current.ref.kind].color
4178
+ color: KIND_STYLE[current.ref.kind].color,
4179
+ ...demoted && { borderStyle: "dashed", fillOpacity: 0.04 }
4028
4180
  });
4029
4181
  tooltip.update(
4030
4182
  {
@@ -4052,7 +4204,9 @@ function createSurfaceShell(options) {
4052
4204
  container: host.chromeEl,
4053
4205
  session: options.session,
4054
4206
  initialCorner: options.initialCorner,
4055
- appTitle: options.appTitle
4207
+ appTitle: options.appTitle,
4208
+ channel: options.channel ?? null,
4209
+ pinLayer: options.pinLayer ?? null
4056
4210
  });
4057
4211
  cleanup.add(menuBar);
4058
4212
  return {