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
@@ -1,4 +1,4 @@
1
- // src/entities/types.ts
1
+ // src/shared/entities/types.ts
2
2
  var ENTITY_KINDS = [
3
3
  "route",
4
4
  "page",
@@ -36,7 +36,7 @@ function assertEntityKind(kind) {
36
36
  if (!KIND_SET.has(kind)) throw new UnknownEntityKindError(kind);
37
37
  }
38
38
 
39
- // src/entities/registry.ts
39
+ // src/shared/entities/registry.ts
40
40
  function emptyStore() {
41
41
  return {
42
42
  route: /* @__PURE__ */ new Map(),
@@ -118,10 +118,33 @@ function createRegistry() {
118
118
  return ids.has(entity.id);
119
119
  });
120
120
  };
121
- return { add, get, list, query, byScope, touchedBy };
121
+ const reports = /* @__PURE__ */ new Map();
122
+ const reportsCbs = /* @__PURE__ */ new Set();
123
+ const setReports = (kind, id, records) => {
124
+ reports.set(`${kind}:${id}`, records);
125
+ for (const cb of reportsCbs) cb();
126
+ };
127
+ const getReports = (kind, id) => reports.get(`${kind}:${id}`) ?? [];
128
+ const listReportKeys = () => Array.from(reports.keys());
129
+ const onReportsChange = (cb) => {
130
+ reportsCbs.add(cb);
131
+ return () => reportsCbs.delete(cb);
132
+ };
133
+ return {
134
+ add,
135
+ get,
136
+ list,
137
+ query,
138
+ byScope,
139
+ touchedBy,
140
+ setReports,
141
+ getReports,
142
+ listReportKeys,
143
+ onReportsChange
144
+ };
122
145
  }
123
146
 
124
- // src/entities/style.ts
147
+ // src/shared/entities/style.ts
125
148
  import {
126
149
  Circle,
127
150
  FileText,
@@ -191,7 +214,7 @@ var KIND_STYLE = {
191
214
  }
192
215
  };
193
216
 
194
- // src/entities/display-name.ts
217
+ // src/shared/entities/display-name.ts
195
218
  function prettify(id) {
196
219
  const parts = id.split(/[-_]+/).filter(Boolean);
197
220
  const words = parts.flatMap((part) => part.split(/(?=[A-Z])/).filter(Boolean));
@@ -209,7 +232,7 @@ function displayName(entity, node) {
209
232
  return prettify(entity.id);
210
233
  }
211
234
 
212
- // src/internal/cleanup.ts
235
+ // src/browser/internal/cleanup.ts
213
236
  function createCleanupStack() {
214
237
  const stack = [];
215
238
  return {
@@ -231,293 +254,147 @@ function createCleanupStack() {
231
254
  };
232
255
  }
233
256
 
234
- // src/session/store.ts
235
- import { createActor } from "xstate";
236
- import { createStore } from "zustand/vanilla";
257
+ // src/browser/session/store.ts
258
+ import { createStore as createStore3 } from "zustand/vanilla";
237
259
 
238
- // src/flows/highlight.ts
239
- import { assign, setup } from "xstate";
240
- var initialContext = {
241
- ref: null,
242
- element: null,
243
- pinnedRef: null,
244
- color: null
260
+ // src/browser/session/mode.ts
261
+ import { createStore } from "zustand/vanilla";
262
+ var COMMAND_PALETTE_ENTRY = {
263
+ id: "command-palette",
264
+ ref: null
245
265
  };
246
- var highlightMachine = setup({
247
- types: {},
248
- actions: {
249
- showOverlay: () => {
266
+ function createModeStore(options) {
267
+ const { nav, bindings } = options;
268
+ const store = createStore(() => ({
269
+ mode: "idle",
270
+ inspectorActive: false
271
+ }));
272
+ const transition = {
273
+ openPalette() {
274
+ const prev = store.getState();
275
+ if (prev.mode === "inspecting") {
276
+ bindings?.destroyInspector?.();
277
+ }
278
+ nav.nav.reset([COMMAND_PALETTE_ENTRY]);
279
+ store.setState({ mode: "palette", inspectorActive: false });
250
280
  },
251
- hideOverlay: () => {
281
+ openInspector() {
282
+ bindings?.mountInspector?.();
283
+ nav.nav.clear();
284
+ store.setState({ mode: "inspecting", inspectorActive: true });
252
285
  },
253
- updateOverlay: () => {
254
- }
255
- }
256
- }).createMachine({
257
- id: "highlight",
258
- initial: "none",
259
- context: initialContext,
260
- states: {
261
- none: {
262
- entry: [
263
- assign({
264
- ref: null,
265
- element: null,
266
- pinnedRef: null,
267
- color: null
268
- }),
269
- { type: "hideOverlay" }
270
- ],
271
- on: {
272
- HOVER: {
273
- target: "transient",
274
- actions: assign(({ event }) => ({
275
- ref: event.ref,
276
- element: event.element,
277
- color: event.color ?? null
278
- }))
279
- },
280
- PIN: {
281
- target: "pinned",
282
- actions: assign(({ context, event }) => ({
283
- ref: event.ref ?? context.ref,
284
- pinnedRef: event.ref ?? context.ref
285
- }))
286
- }
287
- }
286
+ closeInspector() {
287
+ bindings?.destroyInspector?.();
288
+ nav.nav.clear();
289
+ store.setState({ mode: "idle", inspectorActive: false });
288
290
  },
289
- transient: {
290
- entry: { type: "showOverlay" },
291
- on: {
292
- HOVER: {
293
- actions: [
294
- assign(({ event }) => ({
295
- ref: event.ref,
296
- element: event.element,
297
- color: event.color ?? null
298
- })),
299
- { type: "updateOverlay" }
300
- ]
301
- },
302
- UNHOVER: { target: "none" },
303
- PIN: {
304
- target: "pinned",
305
- actions: assign(({ context, event }) => ({
306
- pinnedRef: event.ref ?? context.ref
307
- }))
308
- }
291
+ toggleInspector() {
292
+ if (store.getState().mode === "inspecting") {
293
+ transition.closeInspector();
294
+ } else {
295
+ transition.openInspector();
309
296
  }
310
297
  },
311
- pinned: {
312
- entry: { type: "showOverlay" },
313
- on: {
314
- UNPIN: { target: "none" }
298
+ enterViewing(initialStack) {
299
+ const prev = store.getState();
300
+ if (prev.mode === "inspecting") {
301
+ bindings?.destroyInspector?.();
315
302
  }
316
- }
317
- }
318
- });
319
-
320
- // src/flows/surface.ts
321
- import { assign as assign2, forwardTo, sendTo, setup as setup2 } from "xstate";
322
- var initialContext2 = {
323
- stack: [],
324
- hover: null,
325
- selection: null,
326
- pinnedHighlight: null,
327
- theme: "auto",
328
- resolvedTheme: "light",
329
- ingestActive: false
330
- };
331
- var surfaceMachine = setup2({
332
- types: {},
333
- actors: {
334
- highlight: highlightMachine
335
- },
336
- actions: {
337
- mountInspector: () => {
303
+ nav.nav.reset(initialStack);
304
+ store.setState({ mode: "viewing", inspectorActive: false });
338
305
  },
339
- destroyInspector: () => {
306
+ dismiss() {
307
+ const prev = store.getState();
308
+ if (prev.mode === "inspecting") {
309
+ bindings?.destroyInspector?.();
310
+ }
311
+ nav.nav.clear();
312
+ store.setState({ mode: "idle", inspectorActive: false });
340
313
  },
341
- openActionsPopup: () => {
314
+ popOrTransition() {
315
+ const { stack } = nav.getState();
316
+ if (stack.length > 2) {
317
+ nav.nav.pop();
318
+ } else if (stack.length === 2 && stack[0]?.id === "command-palette") {
319
+ nav.nav.reset([COMMAND_PALETTE_ENTRY]);
320
+ store.setState({ mode: "palette", inspectorActive: false });
321
+ } else if (stack.length === 2) {
322
+ nav.nav.pop();
323
+ } else {
324
+ nav.nav.clear();
325
+ store.setState({ mode: "idle", inspectorActive: false });
326
+ }
342
327
  },
343
- closePopup: () => {
328
+ pushView(entry) {
329
+ const { mode } = store.getState();
330
+ const { stack } = nav.getState();
331
+ if (entry.id === "command-palette" && stack.length === 0) {
332
+ transition.openPalette();
333
+ return;
334
+ }
335
+ switch (mode) {
336
+ case "idle":
337
+ transition.enterViewing([entry]);
338
+ break;
339
+ case "inspecting":
340
+ bindings?.destroyInspector?.();
341
+ nav.nav.reset([entry]);
342
+ store.setState({ mode: "viewing", inspectorActive: false });
343
+ break;
344
+ case "palette":
345
+ case "viewing":
346
+ nav.nav.push(entry);
347
+ if (mode === "palette") {
348
+ store.setState({ mode: "viewing" });
349
+ }
350
+ break;
351
+ }
344
352
  }
345
- },
346
- guards: {
347
- hasPinnedHighlight: ({ context }) => context.pinnedHighlight !== null,
348
- hasActions: () => false,
349
- popupOpen: () => false,
350
- stackDeeperThanTwo: ({ context }) => context.stack.length > 2,
351
- stackEqualsTwo: ({ context }) => context.stack.length === 2,
352
- stackEqualsOne: ({ context }) => context.stack.length === 1,
353
- paletteAtBottomDepthTwo: ({ context }) => context.stack.length === 2 && context.stack[0]?.id === "command-palette"
354
- }
355
- }).createMachine({
356
- id: "surface",
357
- initial: "idle",
358
- context: ({ input }) => ({
359
- ...initialContext2,
360
- ...input ?? {}
361
- }),
362
- invoke: {
363
- id: "highlight",
364
- src: "highlight"
365
- },
366
- on: {
367
- HOVER: {
368
- actions: [
369
- assign2(({ event }) => ({ hover: event.ref })),
370
- forwardTo("highlight")
371
- ]
372
- },
373
- UNHOVER: {
374
- actions: [assign2({ hover: null }), forwardTo("highlight")]
375
- },
376
- PIN: {
377
- actions: [
378
- assign2(({ context, event }) => ({
379
- pinnedHighlight: event.ref ?? context.hover
380
- })),
381
- forwardTo("highlight")
382
- ]
383
- },
384
- UNPIN: {
385
- actions: [assign2({ pinnedHighlight: null }), forwardTo("highlight")]
386
- },
387
- SET_SELECTION: {
388
- actions: assign2(({ event }) => ({ selection: event.ref }))
389
- },
390
- SET_THEME: {
391
- actions: assign2(({ event }) => ({
392
- theme: event.theme,
393
- resolvedTheme: event.resolvedTheme
394
- }))
353
+ };
354
+ const modeStore = store;
355
+ modeStore.transition = transition;
356
+ return modeStore;
357
+ }
358
+
359
+ // src/browser/session/navigation.ts
360
+ import { createStore as createStore2 } from "zustand/vanilla";
361
+ function createNavigationStore() {
362
+ const store = createStore2(() => ({
363
+ stack: []
364
+ }));
365
+ const nav = {
366
+ push(entry) {
367
+ store.setState({ stack: [...store.getState().stack, entry] });
395
368
  },
396
- SET_INGEST: {
397
- actions: assign2(({ event }) => ({ ingestActive: event.active }))
398
- }
399
- },
400
- states: {
401
- idle: {
402
- entry: assign2({ stack: [] }),
403
- on: {
404
- TOGGLE_INSPECTOR: { target: "inspecting" },
405
- OPEN_PALETTE: { target: "palette" },
406
- PUSH_VIEW: {
407
- target: "viewing",
408
- actions: assign2(({ event }) => ({ stack: [event.entry] }))
409
- },
410
- ESC: {
411
- guard: "hasPinnedHighlight",
412
- actions: [
413
- assign2({ pinnedHighlight: null }),
414
- sendTo("highlight", { type: "UNPIN" })
415
- ]
416
- }
369
+ pop() {
370
+ const s = store.getState().stack;
371
+ if (s.length <= 1) {
372
+ store.setState({ stack: [] });
373
+ } else {
374
+ store.setState({ stack: s.slice(0, -1) });
417
375
  }
418
376
  },
419
- inspecting: {
420
- entry: { type: "mountInspector" },
421
- exit: { type: "destroyInspector" },
422
- on: {
423
- SELECT: {
424
- target: "viewing",
425
- actions: assign2(({ context, event }) => ({
426
- selection: event.ref,
427
- stack: [...context.stack, event.entry]
428
- }))
429
- },
430
- PUSH_VIEW: {
431
- target: "viewing",
432
- actions: assign2(({ event }) => ({ stack: [event.entry] }))
433
- },
434
- TOGGLE_INSPECTOR: { target: "idle" },
435
- OPEN_PALETTE: { target: "palette" },
436
- ESC: { target: "idle" }
377
+ replace(entry) {
378
+ const s = store.getState().stack;
379
+ if (s.length === 0) {
380
+ store.setState({ stack: [entry] });
381
+ } else {
382
+ store.setState({ stack: [...s.slice(0, -1), entry] });
437
383
  }
438
384
  },
439
- palette: {
440
- entry: assign2({ stack: [{ id: "command-palette", ref: null }] }),
441
- on: {
442
- PUSH_VIEW: {
443
- target: "viewing",
444
- actions: assign2(({ context, event }) => ({
445
- stack: [...context.stack, event.entry]
446
- }))
447
- },
448
- CLOSE: { target: "idle" },
449
- ESC: { target: "idle" },
450
- CMD_K: [
451
- {
452
- guard: "hasActions",
453
- actions: { type: "openActionsPopup" }
454
- },
455
- { target: "idle" }
456
- ]
457
- }
385
+ clear() {
386
+ store.setState({ stack: [] });
458
387
  },
459
- viewing: {
460
- on: {
461
- PUSH_VIEW: {
462
- actions: assign2(({ context, event }) => ({
463
- stack: [...context.stack, event.entry]
464
- }))
465
- },
466
- POP_VIEW: [
467
- {
468
- guard: "stackDeeperThanTwo",
469
- actions: assign2(({ context }) => ({
470
- stack: context.stack.slice(0, -1)
471
- }))
472
- },
473
- {
474
- guard: "paletteAtBottomDepthTwo",
475
- target: "palette"
476
- },
477
- {
478
- guard: "stackEqualsTwo",
479
- actions: assign2(({ context }) => ({
480
- stack: context.stack.slice(0, -1)
481
- }))
482
- },
483
- {
484
- guard: "stackEqualsOne",
485
- target: "idle"
486
- }
487
- ],
488
- CLOSE: { target: "idle" },
489
- ESC: [
490
- {
491
- guard: "popupOpen",
492
- actions: { type: "closePopup" }
493
- },
494
- {
495
- guard: "stackDeeperThanTwo",
496
- actions: assign2(({ context }) => ({
497
- stack: context.stack.slice(0, -1)
498
- }))
499
- },
500
- {
501
- guard: "paletteAtBottomDepthTwo",
502
- target: "palette"
503
- },
504
- {
505
- guard: "stackEqualsTwo",
506
- actions: assign2(({ context }) => ({
507
- stack: context.stack.slice(0, -1)
508
- }))
509
- },
510
- {
511
- guard: "stackEqualsOne",
512
- target: "idle"
513
- }
514
- ]
515
- }
388
+ reset(stack) {
389
+ store.setState({ stack });
516
390
  }
517
- }
518
- });
391
+ };
392
+ const navStore = store;
393
+ navStore.nav = nav;
394
+ return navStore;
395
+ }
519
396
 
520
- // src/session/store.ts
397
+ // src/browser/session/store.ts
521
398
  var defaultSnapshot = {
522
399
  hover: null,
523
400
  selection: null,
@@ -526,7 +403,8 @@ var defaultSnapshot = {
526
403
  inspectorActive: false,
527
404
  theme: "auto",
528
405
  resolvedTheme: "light",
529
- ingestActive: false
406
+ ingestActive: false,
407
+ user: null
530
408
  };
531
409
  function resolveTheme(preference, detect) {
532
410
  if (preference !== "auto") return preference;
@@ -537,167 +415,145 @@ function resolveTheme(preference, detect) {
537
415
  }
538
416
  return "light";
539
417
  }
540
- function projectFromActor(actor) {
541
- const sn = actor.getSnapshot();
542
- const child = sn.children.highlight;
543
- const childSnapshot = child?.getSnapshot();
544
- const pinned = childSnapshot && childSnapshot.value === "pinned" ? childSnapshot.context.pinnedRef : null;
545
- return {
546
- hover: sn.context.hover,
547
- selection: sn.context.selection,
548
- stack: sn.context.stack,
549
- pinnedHighlight: pinned,
550
- inspectorActive: sn.matches("inspecting"),
551
- theme: sn.context.theme,
552
- resolvedTheme: sn.context.resolvedTheme,
553
- ingestActive: sn.context.ingestActive
554
- };
555
- }
556
418
  function createSession(options = {}) {
557
- const { detectTheme, bindings, ...overrides } = options;
419
+ const {
420
+ detectTheme,
421
+ onMountInspector,
422
+ onDestroyInspector,
423
+ onShowOverlay,
424
+ onHideOverlay,
425
+ onUpdateOverlay,
426
+ ...overrides
427
+ } = options;
558
428
  const initialPref = overrides.theme ?? defaultSnapshot.theme;
559
429
  const initialResolved = overrides.resolvedTheme ?? resolveTheme(initialPref, detectTheme);
560
430
  const initialStack = overrides.stack ?? [];
561
- const input = {
431
+ const nav = createNavigationStore();
432
+ const modeStore = createModeStore({
433
+ nav,
434
+ bindings: {
435
+ mountInspector: onMountInspector,
436
+ destroyInspector: onDestroyInspector
437
+ }
438
+ });
439
+ let highlightMode = "none";
440
+ const hlCtx = {
441
+ ref: null,
442
+ element: null,
443
+ pinnedRef: null,
444
+ color: null
445
+ };
446
+ const highlightActions = {
447
+ hover(ref, element, color) {
448
+ hlCtx.ref = ref;
449
+ hlCtx.element = element ?? null;
450
+ hlCtx.color = color ?? null;
451
+ if (highlightMode === "none") {
452
+ highlightMode = "transient";
453
+ onShowOverlay?.(hlCtx);
454
+ } else if (highlightMode === "transient") {
455
+ onUpdateOverlay?.(hlCtx);
456
+ }
457
+ store.setState({ hover: ref });
458
+ },
459
+ unhover() {
460
+ if (highlightMode === "transient") {
461
+ highlightMode = "none";
462
+ hlCtx.ref = null;
463
+ hlCtx.element = null;
464
+ hlCtx.color = null;
465
+ onHideOverlay?.();
466
+ }
467
+ store.setState({ hover: null });
468
+ },
469
+ pin(ref) {
470
+ const pinRef = ref ?? hlCtx.ref;
471
+ hlCtx.pinnedRef = pinRef;
472
+ if (highlightMode === "none") {
473
+ hlCtx.ref = pinRef;
474
+ }
475
+ if (highlightMode !== "pinned") {
476
+ highlightMode = "pinned";
477
+ onShowOverlay?.(hlCtx);
478
+ }
479
+ store.setState({ pinnedHighlight: hlCtx.pinnedRef });
480
+ },
481
+ unpin() {
482
+ if (highlightMode === "pinned") {
483
+ highlightMode = "none";
484
+ hlCtx.ref = null;
485
+ hlCtx.element = null;
486
+ hlCtx.pinnedRef = null;
487
+ hlCtx.color = null;
488
+ onHideOverlay?.();
489
+ }
490
+ store.setState({ pinnedHighlight: null });
491
+ }
492
+ };
493
+ const store = createStore3(() => ({
494
+ ...defaultSnapshot,
562
495
  hover: overrides.hover ?? null,
563
496
  selection: overrides.selection ?? null,
497
+ stack: [],
564
498
  pinnedHighlight: null,
499
+ inspectorActive: false,
565
500
  theme: initialPref,
566
501
  resolvedTheme: initialResolved,
567
- ingestActive: overrides.ingestActive ?? false
568
- };
569
- const providedHighlight = highlightMachine.provide({
570
- actions: {
571
- showOverlay: ({ context }) => bindings?.highlight?.showOverlay?.(context),
572
- hideOverlay: () => bindings?.highlight?.hideOverlay?.(),
573
- updateOverlay: ({ context }) => bindings?.highlight?.updateOverlay?.(context)
502
+ ingestActive: overrides.ingestActive ?? false,
503
+ user: overrides.user ?? null
504
+ }));
505
+ nav.subscribe(() => {
506
+ const { stack } = nav.getState();
507
+ if (store.getState().stack !== stack) {
508
+ store.setState({ stack });
574
509
  }
575
510
  });
576
- const providedSurface = surfaceMachine.provide({
577
- actions: {
578
- mountInspector: () => bindings?.surface?.mountInspector?.(),
579
- destroyInspector: () => bindings?.surface?.destroyInspector?.(),
580
- openActionsPopup: () => bindings?.surface?.openActionsPopup?.(),
581
- closePopup: () => bindings?.surface?.closePopup?.()
582
- },
583
- actors: {
584
- highlight: providedHighlight
511
+ modeStore.subscribe(() => {
512
+ const { inspectorActive } = modeStore.getState();
513
+ if (store.getState().inspectorActive !== inspectorActive) {
514
+ store.setState({ inspectorActive });
585
515
  }
586
516
  });
587
- const actor = createActor(providedSurface, { input });
588
- actor.start();
517
+ const session = store;
518
+ session.nav = nav;
519
+ session.mode = modeStore;
520
+ session.highlight = highlightActions;
521
+ session.select = (ref) => {
522
+ if (sameRef(store.getState().selection, ref)) return;
523
+ store.setState({ selection: ref });
524
+ };
525
+ session.setTheme = (theme, resolved) => {
526
+ const state = store.getState();
527
+ const nextResolved = resolved ?? resolveTheme(theme, detectTheme);
528
+ if (state.theme === theme && state.resolvedTheme === nextResolved) return;
529
+ store.setState({ theme, resolvedTheme: nextResolved });
530
+ };
531
+ session.setIngest = (active) => {
532
+ if (store.getState().ingestActive === active) return;
533
+ store.setState({ ingestActive: active });
534
+ };
589
535
  if (initialStack.length > 0) {
590
- actor.send({ type: "OPEN_PALETTE" });
536
+ modeStore.transition.openPalette();
591
537
  for (let i = 1; i < initialStack.length; i++) {
592
- actor.send({ type: "PUSH_VIEW", entry: initialStack[i] });
538
+ modeStore.transition.pushView(initialStack[i]);
593
539
  }
594
540
  } else if (overrides.inspectorActive) {
595
- actor.send({ type: "TOGGLE_INSPECTOR" });
541
+ modeStore.transition.openInspector();
596
542
  }
597
543
  if (overrides.pinnedHighlight) {
598
- actor.send({ type: "PIN", ref: overrides.pinnedHighlight });
599
- }
600
- const store = createStore((_set, get) => ({
601
- ...projectFromActor(actor),
602
- actions: {
603
- hover(ref) {
604
- if (sameRef(get().hover, ref)) return;
605
- if (ref === null) {
606
- actor.send({ type: "UNHOVER" });
607
- } else {
608
- actor.send({ type: "HOVER", ref, element: null });
609
- }
610
- },
611
- select(ref) {
612
- if (sameRef(get().selection, ref)) return;
613
- actor.send({ type: "SET_SELECTION", ref });
614
- },
615
- pushStack(entry) {
616
- if (entry.id === "command-palette" && get().stack.length === 0) {
617
- actor.send({ type: "OPEN_PALETTE" });
618
- } else {
619
- actor.send({ type: "PUSH_VIEW", entry });
620
- }
621
- },
622
- popStack() {
623
- const sn = actor.getSnapshot();
624
- if (sn.matches("palette")) {
625
- actor.send({ type: "ESC" });
626
- } else if (sn.matches("viewing")) {
627
- actor.send({ type: "POP_VIEW" });
628
- }
629
- },
630
- clearStack() {
631
- if (get().stack.length === 0) return;
632
- actor.send({ type: "CLOSE" });
633
- },
634
- openView(id, ref) {
635
- const resolvedRef = ref === void 0 ? get().selection : ref;
636
- get().actions.pushStack({ id, ref: resolvedRef });
637
- },
638
- closeView() {
639
- get().actions.clearStack();
640
- },
641
- setInspectorActive(active) {
642
- if (get().inspectorActive === active) return;
643
- actor.send({ type: "TOGGLE_INSPECTOR" });
644
- },
645
- toggleInspector() {
646
- actor.send({ type: "TOGGLE_INSPECTOR" });
647
- },
648
- pinHighlight(ref) {
649
- if (sameRef(get().pinnedHighlight, ref)) return;
650
- actor.send({ type: "PIN", ref });
651
- },
652
- clearPinnedHighlight() {
653
- if (get().pinnedHighlight === null) return;
654
- actor.send({ type: "UNPIN" });
655
- },
656
- setTheme(theme, resolved) {
657
- const state = get();
658
- const nextResolved = resolved ?? resolveTheme(theme, detectTheme);
659
- if (state.theme === theme && state.resolvedTheme === nextResolved)
660
- return;
661
- actor.send({ type: "SET_THEME", theme, resolvedTheme: nextResolved });
662
- },
663
- setIngest(active) {
664
- if (get().ingestActive === active) return;
665
- actor.send({ type: "SET_INGEST", active });
666
- }
667
- }
668
- }));
669
- const projectAndSet = () => {
670
- const next = projectFromActor(actor);
671
- const prev = store.getState();
672
- const updates = {};
673
- if (!sameRef(prev.hover, next.hover)) updates.hover = next.hover;
674
- if (!sameRef(prev.selection, next.selection))
675
- updates.selection = next.selection;
676
- if (prev.stack !== next.stack) updates.stack = next.stack;
677
- if (!sameRef(prev.pinnedHighlight, next.pinnedHighlight))
678
- updates.pinnedHighlight = next.pinnedHighlight;
679
- if (prev.inspectorActive !== next.inspectorActive)
680
- updates.inspectorActive = next.inspectorActive;
681
- if (prev.theme !== next.theme) updates.theme = next.theme;
682
- if (prev.resolvedTheme !== next.resolvedTheme)
683
- updates.resolvedTheme = next.resolvedTheme;
684
- if (prev.ingestActive !== next.ingestActive)
685
- updates.ingestActive = next.ingestActive;
686
- if (Object.keys(updates).length === 0) return;
687
- store.setState(updates);
688
- };
689
- actor.subscribe(projectAndSet);
690
- const session = store;
691
- session.send = (event) => actor.send(event);
544
+ highlightActions.pin(overrides.pinnedHighlight);
545
+ }
692
546
  return session;
693
547
  }
694
548
 
695
- // src/styles/tailwind.built.css
549
+ // src/browser/styles/tailwind.built.css
696
550
  var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
697
551
  @layer properties;
698
552
  @layer theme, base, components, utilities;
699
553
  @layer theme {
700
554
  :root, :host {
555
+ --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
556
+ Roboto, sans-serif;
701
557
  --color-red-400: oklch(70.4% 0.191 22.216);
702
558
  --color-red-500: oklch(63.7% 0.237 25.331);
703
559
  --color-red-700: oklch(50.5% 0.213 27.518);
@@ -763,6 +619,7 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
763
619
  --font-weight-normal: 400;
764
620
  --font-weight-medium: 500;
765
621
  --font-weight-semibold: 600;
622
+ --font-weight-bold: 700;
766
623
  --tracking-tight: -0.025em;
767
624
  --tracking-widest: 0.1em;
768
625
  --leading-relaxed: 1.625;
@@ -937,6 +794,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
937
794
  .\\!visible {
938
795
  visibility: visible !important;
939
796
  }
797
+ .collapse {
798
+ visibility: collapse;
799
+ }
940
800
  .invisible {
941
801
  visibility: hidden;
942
802
  }
@@ -972,9 +832,21 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
972
832
  .end {
973
833
  inset-inline-end: var(--spacing);
974
834
  }
835
+ .-top-1 {
836
+ top: calc(var(--spacing) * -1);
837
+ }
838
+ .-right-1 {
839
+ right: calc(var(--spacing) * -1);
840
+ }
841
+ .right-0 {
842
+ right: calc(var(--spacing) * 0);
843
+ }
975
844
  .right-2 {
976
845
  right: calc(var(--spacing) * 2);
977
846
  }
847
+ .bottom-full {
848
+ bottom: 100%;
849
+ }
978
850
  .bottom-px {
979
851
  bottom: 1px;
980
852
  }
@@ -1023,6 +895,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1023
895
  .mt-1 {
1024
896
  margin-top: calc(var(--spacing) * 1);
1025
897
  }
898
+ .mb-2 {
899
+ margin-bottom: calc(var(--spacing) * 2);
900
+ }
901
+ .mb-6 {
902
+ margin-bottom: calc(var(--spacing) * 6);
903
+ }
1026
904
  .ml-auto {
1027
905
  margin-left: auto;
1028
906
  }
@@ -1041,6 +919,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1041
919
  .inline {
1042
920
  display: inline;
1043
921
  }
922
+ .inline-block {
923
+ display: inline-block;
924
+ }
1044
925
  .inline-flex {
1045
926
  display: inline-flex;
1046
927
  }
@@ -1050,6 +931,14 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1050
931
  .table {
1051
932
  display: table;
1052
933
  }
934
+ .size-2 {
935
+ width: calc(var(--spacing) * 2);
936
+ height: calc(var(--spacing) * 2);
937
+ }
938
+ .size-3 {
939
+ width: calc(var(--spacing) * 3);
940
+ height: calc(var(--spacing) * 3);
941
+ }
1053
942
  .size-3\\.5 {
1054
943
  width: calc(var(--spacing) * 3.5);
1055
944
  height: calc(var(--spacing) * 3.5);
@@ -1140,6 +1029,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1140
1029
  .w-4 {
1141
1030
  width: calc(var(--spacing) * 4);
1142
1031
  }
1032
+ .w-12 {
1033
+ width: calc(var(--spacing) * 12);
1034
+ }
1143
1035
  .w-20 {
1144
1036
  width: calc(var(--spacing) * 20);
1145
1037
  }
@@ -1268,6 +1160,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1268
1160
  .gap-0 {
1269
1161
  gap: calc(var(--spacing) * 0);
1270
1162
  }
1163
+ .gap-0\\.5 {
1164
+ gap: calc(var(--spacing) * 0.5);
1165
+ }
1271
1166
  .gap-1 {
1272
1167
  gap: calc(var(--spacing) * 1);
1273
1168
  }
@@ -1416,9 +1311,15 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1416
1311
  .bg-emerald-50 {
1417
1312
  background-color: var(--color-emerald-50);
1418
1313
  }
1314
+ .bg-emerald-500 {
1315
+ background-color: var(--color-emerald-500);
1316
+ }
1419
1317
  .bg-fuchsia-50 {
1420
1318
  background-color: var(--color-fuchsia-50);
1421
1319
  }
1320
+ .bg-info {
1321
+ background-color: var(--info);
1322
+ }
1422
1323
  .bg-info\\/10 {
1423
1324
  background-color: var(--info);
1424
1325
  @supports (color: color-mix(in lab, red, red)) {
@@ -1551,6 +1452,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1551
1452
  .pe-3 {
1552
1453
  padding-inline-end: calc(var(--spacing) * 3);
1553
1454
  }
1455
+ .pt-2 {
1456
+ padding-top: calc(var(--spacing) * 2);
1457
+ }
1554
1458
  .pr-8 {
1555
1459
  padding-right: calc(var(--spacing) * 8);
1556
1460
  }
@@ -1593,10 +1497,27 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1593
1497
  .text-\\[0\\.625rem\\] {
1594
1498
  font-size: 0.625rem;
1595
1499
  }
1500
+ .text-\\[9px\\] {
1501
+ font-size: 9px;
1502
+ }
1503
+ .text-\\[10px\\] {
1504
+ font-size: 10px;
1505
+ }
1506
+ .text-\\[11px\\] {
1507
+ font-size: 11px;
1508
+ }
1509
+ .leading-none {
1510
+ --tw-leading: 1;
1511
+ line-height: 1;
1512
+ }
1596
1513
  .leading-relaxed {
1597
1514
  --tw-leading: var(--leading-relaxed);
1598
1515
  line-height: var(--leading-relaxed);
1599
1516
  }
1517
+ .font-bold {
1518
+ --tw-font-weight: var(--font-weight-bold);
1519
+ font-weight: var(--font-weight-bold);
1520
+ }
1600
1521
  .font-medium {
1601
1522
  --tw-font-weight: var(--font-weight-medium);
1602
1523
  font-weight: var(--font-weight-medium);
@@ -1644,6 +1565,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1644
1565
  .text-cyan-700 {
1645
1566
  color: var(--color-cyan-700);
1646
1567
  }
1568
+ .text-destructive {
1569
+ color: var(--destructive);
1570
+ }
1647
1571
  .text-destructive-foreground {
1648
1572
  color: var(--destructive-foreground);
1649
1573
  }
@@ -1746,6 +1670,10 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1746
1670
  --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));
1747
1671
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1748
1672
  }
1673
+ .shadow-lg {
1674
+ --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));
1675
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1676
+ }
1749
1677
  .shadow-none {
1750
1678
  --tw-shadow: 0 0 #0000;
1751
1679
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1988,16 +1916,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1988
1916
  border-color: var(--ring);
1989
1917
  }
1990
1918
  }
1991
- .focus-within\\:bg-accent {
1992
- &:focus-within {
1993
- background-color: var(--accent);
1994
- }
1995
- }
1996
- .focus-within\\:text-accent-foreground {
1997
- &:focus-within {
1998
- color: var(--accent-foreground);
1999
- }
2000
- }
2001
1919
  .focus-within\\:ring-\\[3px\\] {
2002
1920
  &:focus-within {
2003
1921
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
@@ -2103,16 +2021,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2103
2021
  }
2104
2022
  }
2105
2023
  }
2106
- .focus\\:bg-accent {
2107
- &:focus {
2108
- background-color: var(--accent);
2109
- }
2110
- }
2111
- .focus\\:text-accent-foreground {
2112
- &:focus {
2113
- color: var(--accent-foreground);
2114
- }
2115
- }
2116
2024
  .focus\\:outline-none {
2117
2025
  &:focus {
2118
2026
  --tw-outline-style: none;
@@ -2170,6 +2078,11 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2170
2078
  cursor: not-allowed;
2171
2079
  }
2172
2080
  }
2081
+ .disabled\\:opacity-50 {
2082
+ &:disabled {
2083
+ opacity: 50%;
2084
+ }
2085
+ }
2173
2086
  .disabled\\:opacity-60 {
2174
2087
  &:disabled {
2175
2088
  opacity: 60%;
@@ -2612,6 +2525,34 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2612
2525
  height: calc(var(--spacing) * 4);
2613
2526
  }
2614
2527
  }
2528
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:bg-accent {
2529
+ [data-kbd-nav] & {
2530
+ &:focus-within {
2531
+ background-color: var(--accent);
2532
+ }
2533
+ }
2534
+ }
2535
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:text-accent-foreground {
2536
+ [data-kbd-nav] & {
2537
+ &:focus-within {
2538
+ color: var(--accent-foreground);
2539
+ }
2540
+ }
2541
+ }
2542
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus\\:bg-accent {
2543
+ [data-kbd-nav] & {
2544
+ &:focus {
2545
+ background-color: var(--accent);
2546
+ }
2547
+ }
2548
+ }
2549
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus\\:text-accent-foreground {
2550
+ [data-kbd-nav] & {
2551
+ &:focus {
2552
+ color: var(--accent-foreground);
2553
+ }
2554
+ }
2555
+ }
2615
2556
  }
2616
2557
  @layer base {
2617
2558
  *, ::after, ::before {
@@ -3143,12 +3084,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3143
3084
  }
3144
3085
  `;
3145
3086
 
3146
- // src/surface/constants.ts
3087
+ // src/browser/surface/constants.ts
3147
3088
  var SURFACE_HOST_CLASS = "uidex-surface-host";
3148
3089
  var SURFACE_CONTAINER_CLASS = "uidex-container";
3149
- var Z_BASE = 2147483644;
3150
- var Z_OVERLAY = 2147483645;
3151
- var Z_CHROME = 2147483646;
3090
+ var Z_BASE = 2147483630;
3091
+ var Z_OVERLAY = 2147483635;
3092
+ var Z_CHROME = 2147483645;
3152
3093
  var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS},.${SURFACE_CONTAINER_CLASS}`;
3153
3094
  var UIDEX_ATTR_TO_KIND = [
3154
3095
  ["data-uidex", "element"],
@@ -3157,7 +3098,7 @@ var UIDEX_ATTR_TO_KIND = [
3157
3098
  ["data-uidex-primitive", "primitive"]
3158
3099
  ];
3159
3100
 
3160
- // src/surface/host.ts
3101
+ // src/browser/surface/host.ts
3161
3102
  function createSurfaceHost(options) {
3162
3103
  const { mount, stylesheets = [], initialTheme = "light" } = options;
3163
3104
  const hostEl = document.createElement("div");
@@ -3171,6 +3112,8 @@ function createSurfaceHost(options) {
3171
3112
  applyStylesheets(shadow, [tailwind_built_default, ...stylesheets]);
3172
3113
  const chromeEl = document.createElement("div");
3173
3114
  chromeEl.classList.add("uidex-chrome");
3115
+ chromeEl.style.position = "relative";
3116
+ chromeEl.style.zIndex = String(Z_CHROME);
3174
3117
  chromeEl.style.pointerEvents = "auto";
3175
3118
  shadow.appendChild(chromeEl);
3176
3119
  mount.appendChild(hostEl);
@@ -3218,7 +3161,7 @@ function supportsConstructableStylesheets(shadow) {
3218
3161
  }
3219
3162
  }
3220
3163
 
3221
- // src/surface/cursor-tooltip.ts
3164
+ // src/browser/surface/cursor-tooltip.ts
3222
3165
  import { createElement as createLucideElement } from "lucide";
3223
3166
  var DEFAULT_TOOLTIP_COLOR = "#34d399";
3224
3167
  var TOOLTIP_OFFSET = 16;
@@ -3276,7 +3219,9 @@ function createCursorTooltip(deps) {
3276
3219
  if (!currentContent) return;
3277
3220
  const { entity, node } = currentContent;
3278
3221
  const style = KIND_STYLE[entity.kind];
3222
+ const demoted = entity.kind === "primitive";
3279
3223
  applyColor(style?.color ?? DEFAULT_TOOLTIP_COLOR);
3224
+ el2.style.opacity = demoted ? "0.55" : "1";
3280
3225
  if (style) {
3281
3226
  const svg = createLucideElement(style.icon);
3282
3227
  svg.setAttribute("aria-hidden", "true");
@@ -3324,7 +3269,7 @@ function createCursorTooltip(deps) {
3324
3269
  };
3325
3270
  }
3326
3271
 
3327
- // src/surface/inspector.ts
3272
+ // src/browser/surface/inspector.ts
3328
3273
  function entityForRef(ref, registry) {
3329
3274
  if (registry) {
3330
3275
  const found = registry.get(ref.kind, ref.id);
@@ -3344,7 +3289,8 @@ function entityForRef(ref, registry) {
3344
3289
  }
3345
3290
  function defaultResolveAllMatches(target, registry) {
3346
3291
  if (target.closest(SURFACE_IGNORE_SELECTOR)) return [];
3347
- const matches = [];
3292
+ const semantic = [];
3293
+ const primitives = [];
3348
3294
  const seen = /* @__PURE__ */ new Set();
3349
3295
  let node = target;
3350
3296
  while (node) {
@@ -3357,19 +3303,24 @@ function defaultResolveAllMatches(target, registry) {
3357
3303
  seen.add(key);
3358
3304
  const ref = { kind, id };
3359
3305
  const entity = entityForRef(ref, registry);
3360
- matches.push({
3306
+ const match = {
3361
3307
  element: node,
3362
3308
  ref,
3363
3309
  entity,
3364
3310
  label: displayName(entity, node)
3365
- });
3311
+ };
3312
+ if (kind === "primitive") {
3313
+ primitives.push(match);
3314
+ } else {
3315
+ semantic.push(match);
3316
+ }
3366
3317
  }
3367
3318
  }
3368
3319
  }
3369
3320
  }
3370
3321
  node = node.parentElement;
3371
3322
  }
3372
- return matches;
3323
+ return semantic.concat(primitives);
3373
3324
  }
3374
3325
  function createInspector(options) {
3375
3326
  const {
@@ -3398,14 +3349,14 @@ function createInspector(options) {
3398
3349
  currentEl = topEl;
3399
3350
  stack = matches;
3400
3351
  layerIndex = 0;
3401
- session.getState().actions.hover(stack[0].ref);
3352
+ session.highlight.hover(stack[0].ref);
3402
3353
  }
3403
3354
  onHover?.(makeStack(), cursor);
3404
3355
  } else if (currentEl) {
3405
3356
  currentEl = null;
3406
3357
  stack = [];
3407
3358
  layerIndex = 0;
3408
- session.getState().actions.hover(null);
3359
+ session.highlight.unhover();
3409
3360
  onHover?.(null, cursor);
3410
3361
  }
3411
3362
  };
@@ -3416,7 +3367,7 @@ function createInspector(options) {
3416
3367
  e.preventDefault();
3417
3368
  e.stopPropagation();
3418
3369
  const match = stack[layerIndex];
3419
- session.getState().actions.select(match.ref);
3370
+ session.select(match.ref);
3420
3371
  onSelect?.(match, { x: e.clientX, y: e.clientY });
3421
3372
  };
3422
3373
  const onContextMenu = (e) => {
@@ -3425,7 +3376,7 @@ function createInspector(options) {
3425
3376
  e.stopPropagation();
3426
3377
  layerIndex = (layerIndex + 1) % stack.length;
3427
3378
  const match = stack[layerIndex];
3428
- session.getState().actions.hover(match.ref);
3379
+ session.highlight.hover(match.ref);
3429
3380
  onCycle?.(makeStack(), lastCursor);
3430
3381
  };
3431
3382
  return {
@@ -3455,28 +3406,32 @@ function createInspector(options) {
3455
3406
  document.removeEventListener("mousemove", onMouseMove);
3456
3407
  document.removeEventListener("click", onClick, true);
3457
3408
  document.removeEventListener("contextmenu", onContextMenu, true);
3458
- session.getState().actions.hover(null);
3409
+ session.highlight.unhover();
3459
3410
  onHover?.(null, null);
3460
3411
  }
3461
3412
  };
3462
3413
  }
3463
3414
 
3464
- // src/surface/menu-bar.ts
3415
+ // src/browser/surface/menu-bar.ts
3465
3416
  import {
3417
+ ChevronLeft,
3418
+ ChevronRight,
3466
3419
  Command,
3467
3420
  Highlighter,
3421
+ MapPin,
3468
3422
  MousePointerClick as MousePointerClick2,
3423
+ Users,
3469
3424
  createElement as createLucideElement2
3470
3425
  } from "lucide";
3471
3426
 
3472
- // src/internal/cn.ts
3427
+ // src/browser/internal/cn.ts
3473
3428
  import { clsx } from "clsx";
3474
3429
  import { twMerge } from "tailwind-merge";
3475
3430
  function cn(...inputs) {
3476
3431
  return twMerge(clsx(inputs));
3477
3432
  }
3478
3433
 
3479
- // src/internal/el.ts
3434
+ // src/browser/internal/el.ts
3480
3435
  function el(tag, options = {}, children = []) {
3481
3436
  const node = document.createElement(tag);
3482
3437
  if (options.class) node.className = cn(options.class);
@@ -3511,7 +3466,7 @@ function el(tag, options = {}, children = []) {
3511
3466
  return node;
3512
3467
  }
3513
3468
 
3514
- // src/surface/keys.ts
3469
+ // src/browser/surface/keys.ts
3515
3470
  var INSPECT_SHORTCUT = { key: "i" };
3516
3471
  function formatShortcutLabel(shortcut) {
3517
3472
  const parts = [];
@@ -3521,7 +3476,7 @@ function formatShortcutLabel(shortcut) {
3521
3476
  return parts.join("");
3522
3477
  }
3523
3478
 
3524
- // src/surface/menu-bar.ts
3479
+ // src/browser/surface/menu-bar.ts
3525
3480
  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%)]";
3526
3481
  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";
3527
3482
  var BUTTON_ACTIVE_CLASS = "border-info/40 bg-info/15 text-info-foreground hover:bg-info/20";
@@ -3532,8 +3487,11 @@ function createMenuBar(options) {
3532
3487
  container,
3533
3488
  session,
3534
3489
  initialCorner = "bottom-right",
3535
- appTitle
3490
+ appTitle,
3491
+ channel = null,
3492
+ pinLayer: initialPinLayer = null
3536
3493
  } = options;
3494
+ let activePinLayer = initialPinLayer;
3537
3495
  const root = el("div", {
3538
3496
  class: ROOT_CLASS,
3539
3497
  attrs: {
@@ -3574,7 +3532,7 @@ function createMenuBar(options) {
3574
3532
  highlightBtn.hidden = true;
3575
3533
  highlightBtn.addEventListener("click", (e) => {
3576
3534
  e.stopPropagation();
3577
- session.send({ type: "UNPIN" });
3535
+ session.highlight.unpin();
3578
3536
  });
3579
3537
  root.appendChild(highlightBtn);
3580
3538
  const inspectIcon = createLucideElement2(MousePointerClick2);
@@ -3594,9 +3552,166 @@ function createMenuBar(options) {
3594
3552
  );
3595
3553
  inspectBtn.addEventListener("click", (e) => {
3596
3554
  e.stopPropagation();
3597
- session.send({ type: "TOGGLE_INSPECTOR" });
3555
+ session.mode.transition.toggleInspector();
3556
+ inspectBtn.blur();
3598
3557
  });
3599
3558
  root.appendChild(inspectBtn);
3559
+ const pinIcon = createLucideElement2(MapPin);
3560
+ pinIcon.setAttribute("class", "size-3.5");
3561
+ pinIcon.setAttribute("aria-hidden", "true");
3562
+ const pinBtn = el(
3563
+ "button",
3564
+ {
3565
+ class: BUTTON_CLASS,
3566
+ attrs: {
3567
+ type: "button",
3568
+ "data-uidex-menubar-pins": "",
3569
+ "aria-label": "Report pins"
3570
+ }
3571
+ },
3572
+ pinIcon
3573
+ );
3574
+ const commitCycler = el("div", {
3575
+ class: "relative z-1 inline-flex items-center gap-0.5",
3576
+ attrs: { "data-uidex-menubar-commit-cycler": "" }
3577
+ });
3578
+ commitCycler.hidden = true;
3579
+ const prevIcon = createLucideElement2(ChevronLeft);
3580
+ prevIcon.setAttribute("class", "size-3");
3581
+ prevIcon.setAttribute("aria-hidden", "true");
3582
+ const prevBtn = el(
3583
+ "button",
3584
+ {
3585
+ class: BUTTON_CLASS,
3586
+ attrs: { type: "button", "aria-label": "Previous commit" },
3587
+ style: { width: "18px", height: "18px" }
3588
+ },
3589
+ prevIcon
3590
+ );
3591
+ const commitLabel = el("span", {
3592
+ class: "relative z-1 whitespace-nowrap px-1 text-[10px] font-mono text-muted-foreground",
3593
+ attrs: { "data-uidex-menubar-commit-label": "" }
3594
+ });
3595
+ const nextIcon = createLucideElement2(ChevronRight);
3596
+ nextIcon.setAttribute("class", "size-3");
3597
+ nextIcon.setAttribute("aria-hidden", "true");
3598
+ const nextBtn = el(
3599
+ "button",
3600
+ {
3601
+ class: BUTTON_CLASS,
3602
+ attrs: { type: "button", "aria-label": "Next commit" },
3603
+ style: { width: "18px", height: "18px" }
3604
+ },
3605
+ nextIcon
3606
+ );
3607
+ commitCycler.appendChild(prevBtn);
3608
+ commitCycler.appendChild(commitLabel);
3609
+ commitCycler.appendChild(nextBtn);
3610
+ const pinWrapper = el("div", {
3611
+ class: "relative z-1 inline-flex items-center gap-0.5",
3612
+ attrs: { "data-uidex-menubar-pin-wrapper": "" }
3613
+ });
3614
+ pinWrapper.hidden = true;
3615
+ pinWrapper.appendChild(pinBtn);
3616
+ pinWrapper.appendChild(commitCycler);
3617
+ root.appendChild(pinWrapper);
3618
+ const updatePinUI = () => {
3619
+ if (!activePinLayer) {
3620
+ pinWrapper.hidden = true;
3621
+ return;
3622
+ }
3623
+ const pinsVisible = activePinLayer.visible;
3624
+ const state = activePinLayer.filterState;
3625
+ const hasCommits = state.commits.length > 0;
3626
+ pinWrapper.hidden = false;
3627
+ commitCycler.hidden = !pinsVisible || !hasCommits;
3628
+ if (state.commitIndex === -1 || !state.commits[state.commitIndex]) {
3629
+ commitLabel.textContent = `all (${state.commits.length})`;
3630
+ } else {
3631
+ const sha = state.commits[state.commitIndex] ?? "";
3632
+ commitLabel.textContent = sha.slice(0, 7);
3633
+ }
3634
+ pinBtn.className = cn(BUTTON_CLASS, pinsVisible && BUTTON_ACTIVE_CLASS);
3635
+ };
3636
+ pinBtn.addEventListener("click", (e) => {
3637
+ e.stopPropagation();
3638
+ if (activePinLayer) {
3639
+ activePinLayer.setVisible(!activePinLayer.visible);
3640
+ }
3641
+ });
3642
+ prevBtn.addEventListener("click", (e) => {
3643
+ e.stopPropagation();
3644
+ activePinLayer?.prevCommit();
3645
+ });
3646
+ nextBtn.addEventListener("click", (e) => {
3647
+ e.stopPropagation();
3648
+ activePinLayer?.nextCommit();
3649
+ });
3650
+ let unsubscribePinFilter = activePinLayer?.onFilterChange(() => updatePinUI());
3651
+ const presenceIcon = createLucideElement2(Users);
3652
+ presenceIcon.setAttribute("class", "size-3.5");
3653
+ presenceIcon.setAttribute("aria-hidden", "true");
3654
+ const presenceBtn = el(
3655
+ "button",
3656
+ {
3657
+ class: BUTTON_CLASS,
3658
+ attrs: {
3659
+ type: "button",
3660
+ "data-uidex-menubar-presence": "",
3661
+ "aria-label": "Online users"
3662
+ }
3663
+ },
3664
+ presenceIcon
3665
+ );
3666
+ presenceBtn.hidden = true;
3667
+ const presenceBadge = el("span", {
3668
+ 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"
3669
+ });
3670
+ presenceBtn.appendChild(presenceBadge);
3671
+ const presencePopover = el("div", {
3672
+ 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"
3673
+ });
3674
+ presencePopover.hidden = true;
3675
+ let presencePopoverVisible = false;
3676
+ presenceBtn.addEventListener("click", (e) => {
3677
+ e.stopPropagation();
3678
+ presencePopoverVisible = !presencePopoverVisible;
3679
+ presencePopover.hidden = !presencePopoverVisible;
3680
+ });
3681
+ const presenceWrapper = el("div", { class: "relative z-1 inline-flex" }, [
3682
+ presenceBtn,
3683
+ presencePopover
3684
+ ]);
3685
+ presenceWrapper.hidden = true;
3686
+ root.appendChild(presenceWrapper);
3687
+ let presenceUsers = [];
3688
+ const updatePresenceUI = () => {
3689
+ const localUserId = session.getState().user?.id ?? null;
3690
+ const peers = localUserId ? presenceUsers.filter((u) => u.userId !== localUserId) : presenceUsers;
3691
+ const count = peers.length;
3692
+ presenceWrapper.hidden = count === 0;
3693
+ presenceBadge.textContent = String(count);
3694
+ presenceBtn.setAttribute(
3695
+ "aria-label",
3696
+ count === 1 ? "1 other user online" : `${count} other users online`
3697
+ );
3698
+ presencePopover.innerHTML = "";
3699
+ for (const user of peers) {
3700
+ const row = el("div", {
3701
+ class: "flex items-center gap-2 rounded px-1.5 py-1"
3702
+ });
3703
+ const dot = el("span", {
3704
+ class: "inline-block size-2 shrink-0 rounded-full bg-emerald-500"
3705
+ });
3706
+ const name = el("span", {
3707
+ class: "truncate",
3708
+ text: user.name || "Anonymous"
3709
+ });
3710
+ row.appendChild(dot);
3711
+ row.appendChild(name);
3712
+ presencePopover.appendChild(row);
3713
+ }
3714
+ };
3600
3715
  const paletteIcon = createLucideElement2(Command);
3601
3716
  paletteIcon.setAttribute("class", "size-3.5");
3602
3717
  paletteIcon.setAttribute("aria-hidden", "true");
@@ -3615,10 +3730,11 @@ function createMenuBar(options) {
3615
3730
  paletteBtn.addEventListener("click", (e) => {
3616
3731
  e.stopPropagation();
3617
3732
  if (session.getState().stack.length > 0) {
3618
- session.send({ type: "CLOSE" });
3733
+ session.mode.transition.dismiss();
3619
3734
  } else {
3620
- session.send({ type: "OPEN_PALETTE" });
3735
+ session.mode.transition.openPalette();
3621
3736
  }
3737
+ paletteBtn.blur();
3622
3738
  });
3623
3739
  root.appendChild(paletteBtn);
3624
3740
  container.appendChild(root);
@@ -3710,14 +3826,28 @@ function createMenuBar(options) {
3710
3826
  const vertical = y / (window.innerHeight || 1) < 0.5 ? "top" : "bottom";
3711
3827
  snapTo(`${vertical}-${horizontal}`);
3712
3828
  }
3829
+ const unsubscribePresence = channel?.onPresence(
3830
+ (users) => {
3831
+ presenceUsers = users;
3832
+ updatePresenceUI();
3833
+ }
3834
+ );
3713
3835
  return {
3714
3836
  destroy() {
3837
+ unsubscribePinFilter?.();
3838
+ unsubscribePresence?.();
3715
3839
  unsubscribeSession();
3716
3840
  root.removeEventListener("mousedown", onMouseDown);
3717
3841
  document.removeEventListener("mousemove", onMouseMove);
3718
3842
  document.removeEventListener("mouseup", onMouseUp);
3719
3843
  root.remove();
3720
3844
  },
3845
+ setPinLayer(layer) {
3846
+ unsubscribePinFilter?.();
3847
+ activePinLayer = layer;
3848
+ unsubscribePinFilter = layer.onFilterChange(() => updatePinUI());
3849
+ updatePinUI();
3850
+ },
3721
3851
  snapTo,
3722
3852
  snapToNearest,
3723
3853
  get corner() {
@@ -3729,7 +3859,7 @@ function createMenuBar(options) {
3729
3859
  };
3730
3860
  }
3731
3861
 
3732
- // src/surface/overlay.ts
3862
+ // src/browser/surface/overlay.ts
3733
3863
  var DEFAULT_COLOR = "#34d399";
3734
3864
  var DEFAULT_BORDER_WIDTH = 2;
3735
3865
  var DEFAULT_FILL_OPACITY = 0.08;
@@ -3742,7 +3872,9 @@ function createOverlay(deps) {
3742
3872
  backdrop.style.inset = "0";
3743
3873
  backdrop.style.zIndex = String(Z_OVERLAY);
3744
3874
  backdrop.style.backgroundColor = `rgba(0, 0, 0, ${BACKDROP_OPACITY})`;
3745
- backdrop.style.display = "none";
3875
+ backdrop.style.opacity = "0";
3876
+ backdrop.style.visibility = "hidden";
3877
+ backdrop.style.transition = "opacity 150ms ease-out";
3746
3878
  backdrop.style.pointerEvents = "auto";
3747
3879
  backdrop.style.cursor = "default";
3748
3880
  backdrop.addEventListener("click", (e) => {
@@ -3756,7 +3888,9 @@ function createOverlay(deps) {
3756
3888
  box.style.pointerEvents = "none";
3757
3889
  box.style.zIndex = String(Z_OVERLAY + 1);
3758
3890
  box.style.boxSizing = "border-box";
3759
- box.style.display = "none";
3891
+ box.style.opacity = "0";
3892
+ box.style.transition = "opacity 150ms ease-out";
3893
+ box.style.visibility = "hidden";
3760
3894
  const label = document.createElement("div");
3761
3895
  label.setAttribute("data-uidex-overlay-label", "");
3762
3896
  label.style.position = "absolute";
@@ -3769,6 +3903,17 @@ function createOverlay(deps) {
3769
3903
  label.style.whiteSpace = "nowrap";
3770
3904
  label.style.color = "#0a0a0a";
3771
3905
  label.style.display = "none";
3906
+ box.addEventListener("transitionend", () => {
3907
+ if (box.style.opacity === "0") {
3908
+ box.style.visibility = "hidden";
3909
+ }
3910
+ });
3911
+ backdrop.addEventListener("transitionend", () => {
3912
+ if (backdrop.style.opacity === "0") {
3913
+ backdrop.style.visibility = "hidden";
3914
+ backdrop.style.clipPath = "";
3915
+ }
3916
+ });
3772
3917
  box.appendChild(label);
3773
3918
  container.appendChild(box);
3774
3919
  let target = null;
@@ -3851,14 +3996,19 @@ function createOverlay(deps) {
3851
3996
  label.textContent = "";
3852
3997
  label.style.display = "none";
3853
3998
  }
3854
- backdrop.style.display = opts.backdrop ? "" : "none";
3855
- if (!opts.backdrop) {
3999
+ if (opts.backdrop) {
4000
+ backdrop.style.visibility = "visible";
4001
+ backdrop.style.opacity = "1";
4002
+ } else {
4003
+ backdrop.style.opacity = "0";
4004
+ backdrop.style.visibility = "hidden";
3856
4005
  backdrop.style.clipPath = "";
3857
4006
  }
3858
4007
  }
3859
4008
  const overlay = {
3860
4009
  onDismiss: null,
3861
4010
  show(nextTarget, showOpts) {
4011
+ const wasVisible = target !== null;
3862
4012
  target = nextTarget;
3863
4013
  opts = {
3864
4014
  label: showOpts?.label ?? "",
@@ -3871,14 +4021,18 @@ function createOverlay(deps) {
3871
4021
  };
3872
4022
  applyStyles();
3873
4023
  updatePosition();
3874
- box.style.display = "";
4024
+ if (!wasVisible) {
4025
+ box.style.opacity = "0";
4026
+ box.style.visibility = "visible";
4027
+ box.offsetHeight;
4028
+ }
4029
+ box.style.opacity = "1";
3875
4030
  attach();
3876
4031
  },
3877
4032
  hide() {
3878
4033
  target = null;
3879
- box.style.display = "none";
3880
- backdrop.style.display = "none";
3881
- backdrop.style.clipPath = "";
4034
+ box.style.opacity = "0";
4035
+ backdrop.style.opacity = "0";
3882
4036
  detach();
3883
4037
  },
3884
4038
  destroy() {
@@ -3913,7 +4067,7 @@ function rgbaFromColor(color, alpha) {
3913
4067
  return color;
3914
4068
  }
3915
4069
 
3916
- // src/surface/theme.ts
4070
+ // src/browser/surface/theme.ts
3917
4071
  function createThemeDetector(deps) {
3918
4072
  const { session, onResolve } = deps;
3919
4073
  let resolved = session.getState().resolvedTheme;
@@ -3931,7 +4085,7 @@ function createThemeDetector(deps) {
3931
4085
  const next = detect(preference);
3932
4086
  if (next === resolved) return;
3933
4087
  resolved = next;
3934
- session.getState().actions.setTheme(preference, next);
4088
+ session.setTheme(preference, next);
3935
4089
  onResolve?.(next);
3936
4090
  };
3937
4091
  push();
@@ -3982,7 +4136,7 @@ function readHtmlClassToken() {
3982
4136
  return null;
3983
4137
  }
3984
4138
 
3985
- // src/surface/shell.ts
4139
+ // src/browser/surface/shell.ts
3986
4140
  function createSurfaceShell(options) {
3987
4141
  const cleanup = createCleanupStack();
3988
4142
  const host = createSurfaceHost({
@@ -3996,7 +4150,7 @@ function createSurfaceShell(options) {
3996
4150
  onResolve: (theme) => host.applyTheme(theme)
3997
4151
  });
3998
4152
  cleanup.add(themeDetector);
3999
- const overlay = createOverlay({ container: host.chromeEl });
4153
+ const overlay = createOverlay({ container: host.shadowRoot });
4000
4154
  cleanup.add(overlay);
4001
4155
  const tooltip = createCursorTooltip({
4002
4156
  container: host.chromeEl,
@@ -4011,8 +4165,10 @@ function createSurfaceShell(options) {
4011
4165
  return;
4012
4166
  }
4013
4167
  const { current } = stack;
4168
+ const demoted = current.ref.kind === "primitive";
4014
4169
  overlay.show(current.element, {
4015
- color: KIND_STYLE[current.ref.kind].color
4170
+ color: KIND_STYLE[current.ref.kind].color,
4171
+ ...demoted && { borderStyle: "dashed", fillOpacity: 0.04 }
4016
4172
  });
4017
4173
  tooltip.update(
4018
4174
  {
@@ -4040,7 +4196,9 @@ function createSurfaceShell(options) {
4040
4196
  container: host.chromeEl,
4041
4197
  session: options.session,
4042
4198
  initialCorner: options.initialCorner,
4043
- appTitle: options.appTitle
4199
+ appTitle: options.appTitle,
4200
+ channel: options.channel ?? null,
4201
+ pinLayer: options.pinLayer ?? null
4044
4202
  });
4045
4203
  cleanup.add(menuBar);
4046
4204
  return {