uidex 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/cli/cli.cjs +996 -73
  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 +620 -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 +624 -469
  14. package/dist/headless/index.js.map +1 -1
  15. package/dist/index.cjs +4255 -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 +4277 -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 +4298 -2908
  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 +4334 -2924
  34. package/dist/react/index.js.map +1 -1
  35. package/dist/scan/index.cjs +91 -40
  36. package/dist/scan/index.cjs.map +1 -1
  37. package/dist/scan/index.d.cts +26 -0
  38. package/dist/scan/index.d.ts +26 -0
  39. package/dist/scan/index.js +90 -39
  40. package/dist/scan/index.js.map +1 -1
  41. package/package.json +23 -22
  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);
@@ -1268,6 +1157,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1268
1157
  .gap-0 {
1269
1158
  gap: calc(var(--spacing) * 0);
1270
1159
  }
1160
+ .gap-0\\.5 {
1161
+ gap: calc(var(--spacing) * 0.5);
1162
+ }
1271
1163
  .gap-1 {
1272
1164
  gap: calc(var(--spacing) * 1);
1273
1165
  }
@@ -1416,9 +1308,15 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1416
1308
  .bg-emerald-50 {
1417
1309
  background-color: var(--color-emerald-50);
1418
1310
  }
1311
+ .bg-emerald-500 {
1312
+ background-color: var(--color-emerald-500);
1313
+ }
1419
1314
  .bg-fuchsia-50 {
1420
1315
  background-color: var(--color-fuchsia-50);
1421
1316
  }
1317
+ .bg-info {
1318
+ background-color: var(--info);
1319
+ }
1422
1320
  .bg-info\\/10 {
1423
1321
  background-color: var(--info);
1424
1322
  @supports (color: color-mix(in lab, red, red)) {
@@ -1551,6 +1449,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1551
1449
  .pe-3 {
1552
1450
  padding-inline-end: calc(var(--spacing) * 3);
1553
1451
  }
1452
+ .pt-2 {
1453
+ padding-top: calc(var(--spacing) * 2);
1454
+ }
1554
1455
  .pr-8 {
1555
1456
  padding-right: calc(var(--spacing) * 8);
1556
1457
  }
@@ -1593,10 +1494,27 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1593
1494
  .text-\\[0\\.625rem\\] {
1594
1495
  font-size: 0.625rem;
1595
1496
  }
1497
+ .text-\\[9px\\] {
1498
+ font-size: 9px;
1499
+ }
1500
+ .text-\\[10px\\] {
1501
+ font-size: 10px;
1502
+ }
1503
+ .text-\\[11px\\] {
1504
+ font-size: 11px;
1505
+ }
1506
+ .leading-none {
1507
+ --tw-leading: 1;
1508
+ line-height: 1;
1509
+ }
1596
1510
  .leading-relaxed {
1597
1511
  --tw-leading: var(--leading-relaxed);
1598
1512
  line-height: var(--leading-relaxed);
1599
1513
  }
1514
+ .font-bold {
1515
+ --tw-font-weight: var(--font-weight-bold);
1516
+ font-weight: var(--font-weight-bold);
1517
+ }
1600
1518
  .font-medium {
1601
1519
  --tw-font-weight: var(--font-weight-medium);
1602
1520
  font-weight: var(--font-weight-medium);
@@ -1644,6 +1562,9 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1644
1562
  .text-cyan-700 {
1645
1563
  color: var(--color-cyan-700);
1646
1564
  }
1565
+ .text-destructive {
1566
+ color: var(--destructive);
1567
+ }
1647
1568
  .text-destructive-foreground {
1648
1569
  color: var(--destructive-foreground);
1649
1570
  }
@@ -1746,6 +1667,10 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1746
1667
  --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
1668
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1748
1669
  }
1670
+ .shadow-lg {
1671
+ --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));
1672
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1673
+ }
1749
1674
  .shadow-none {
1750
1675
  --tw-shadow: 0 0 #0000;
1751
1676
  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 +1913,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
1988
1913
  border-color: var(--ring);
1989
1914
  }
1990
1915
  }
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
1916
  .focus-within\\:ring-\\[3px\\] {
2002
1917
  &:focus-within {
2003
1918
  --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
@@ -2103,16 +2018,6 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2103
2018
  }
2104
2019
  }
2105
2020
  }
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
2021
  .focus\\:outline-none {
2117
2022
  &:focus {
2118
2023
  --tw-outline-style: none;
@@ -2170,6 +2075,11 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2170
2075
  cursor: not-allowed;
2171
2076
  }
2172
2077
  }
2078
+ .disabled\\:opacity-50 {
2079
+ &:disabled {
2080
+ opacity: 50%;
2081
+ }
2082
+ }
2173
2083
  .disabled\\:opacity-60 {
2174
2084
  &:disabled {
2175
2085
  opacity: 60%;
@@ -2612,6 +2522,34 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
2612
2522
  height: calc(var(--spacing) * 4);
2613
2523
  }
2614
2524
  }
2525
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:bg-accent {
2526
+ [data-kbd-nav] & {
2527
+ &:focus-within {
2528
+ background-color: var(--accent);
2529
+ }
2530
+ }
2531
+ }
2532
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus-within\\:text-accent-foreground {
2533
+ [data-kbd-nav] & {
2534
+ &:focus-within {
2535
+ color: var(--accent-foreground);
2536
+ }
2537
+ }
2538
+ }
2539
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus\\:bg-accent {
2540
+ [data-kbd-nav] & {
2541
+ &:focus {
2542
+ background-color: var(--accent);
2543
+ }
2544
+ }
2545
+ }
2546
+ .\\[\\[data-kbd-nav\\]_\\&\\]\\:focus\\:text-accent-foreground {
2547
+ [data-kbd-nav] & {
2548
+ &:focus {
2549
+ color: var(--accent-foreground);
2550
+ }
2551
+ }
2552
+ }
2615
2553
  }
2616
2554
  @layer base {
2617
2555
  *, ::after, ::before {
@@ -3143,12 +3081,12 @@ var tailwind_built_default = `/*! tailwindcss v4.2.2 | MIT License | https://tai
3143
3081
  }
3144
3082
  `;
3145
3083
 
3146
- // src/surface/constants.ts
3084
+ // src/browser/surface/constants.ts
3147
3085
  var SURFACE_HOST_CLASS = "uidex-surface-host";
3148
3086
  var SURFACE_CONTAINER_CLASS = "uidex-container";
3149
- var Z_BASE = 2147483644;
3150
- var Z_OVERLAY = 2147483645;
3151
- var Z_CHROME = 2147483646;
3087
+ var Z_BASE = 2147483630;
3088
+ var Z_OVERLAY = 2147483635;
3089
+ var Z_CHROME = 2147483645;
3152
3090
  var SURFACE_IGNORE_SELECTOR = `.${SURFACE_HOST_CLASS},.${SURFACE_CONTAINER_CLASS}`;
3153
3091
  var UIDEX_ATTR_TO_KIND = [
3154
3092
  ["data-uidex", "element"],
@@ -3157,7 +3095,7 @@ var UIDEX_ATTR_TO_KIND = [
3157
3095
  ["data-uidex-primitive", "primitive"]
3158
3096
  ];
3159
3097
 
3160
- // src/surface/host.ts
3098
+ // src/browser/surface/host.ts
3161
3099
  function createSurfaceHost(options) {
3162
3100
  const { mount, stylesheets = [], initialTheme = "light" } = options;
3163
3101
  const hostEl = document.createElement("div");
@@ -3171,6 +3109,8 @@ function createSurfaceHost(options) {
3171
3109
  applyStylesheets(shadow, [tailwind_built_default, ...stylesheets]);
3172
3110
  const chromeEl = document.createElement("div");
3173
3111
  chromeEl.classList.add("uidex-chrome");
3112
+ chromeEl.style.position = "relative";
3113
+ chromeEl.style.zIndex = String(Z_CHROME);
3174
3114
  chromeEl.style.pointerEvents = "auto";
3175
3115
  shadow.appendChild(chromeEl);
3176
3116
  mount.appendChild(hostEl);
@@ -3218,7 +3158,7 @@ function supportsConstructableStylesheets(shadow) {
3218
3158
  }
3219
3159
  }
3220
3160
 
3221
- // src/surface/cursor-tooltip.ts
3161
+ // src/browser/surface/cursor-tooltip.ts
3222
3162
  import { createElement as createLucideElement } from "lucide";
3223
3163
  var DEFAULT_TOOLTIP_COLOR = "#34d399";
3224
3164
  var TOOLTIP_OFFSET = 16;
@@ -3276,7 +3216,9 @@ function createCursorTooltip(deps) {
3276
3216
  if (!currentContent) return;
3277
3217
  const { entity, node } = currentContent;
3278
3218
  const style = KIND_STYLE[entity.kind];
3219
+ const demoted = entity.kind === "primitive";
3279
3220
  applyColor(style?.color ?? DEFAULT_TOOLTIP_COLOR);
3221
+ el2.style.opacity = demoted ? "0.55" : "1";
3280
3222
  if (style) {
3281
3223
  const svg = createLucideElement(style.icon);
3282
3224
  svg.setAttribute("aria-hidden", "true");
@@ -3324,7 +3266,7 @@ function createCursorTooltip(deps) {
3324
3266
  };
3325
3267
  }
3326
3268
 
3327
- // src/surface/inspector.ts
3269
+ // src/browser/surface/inspector.ts
3328
3270
  function entityForRef(ref, registry) {
3329
3271
  if (registry) {
3330
3272
  const found = registry.get(ref.kind, ref.id);
@@ -3344,7 +3286,8 @@ function entityForRef(ref, registry) {
3344
3286
  }
3345
3287
  function defaultResolveAllMatches(target, registry) {
3346
3288
  if (target.closest(SURFACE_IGNORE_SELECTOR)) return [];
3347
- const matches = [];
3289
+ const semantic = [];
3290
+ const primitives = [];
3348
3291
  const seen = /* @__PURE__ */ new Set();
3349
3292
  let node = target;
3350
3293
  while (node) {
@@ -3357,19 +3300,24 @@ function defaultResolveAllMatches(target, registry) {
3357
3300
  seen.add(key);
3358
3301
  const ref = { kind, id };
3359
3302
  const entity = entityForRef(ref, registry);
3360
- matches.push({
3303
+ const match = {
3361
3304
  element: node,
3362
3305
  ref,
3363
3306
  entity,
3364
3307
  label: displayName(entity, node)
3365
- });
3308
+ };
3309
+ if (kind === "primitive") {
3310
+ primitives.push(match);
3311
+ } else {
3312
+ semantic.push(match);
3313
+ }
3366
3314
  }
3367
3315
  }
3368
3316
  }
3369
3317
  }
3370
3318
  node = node.parentElement;
3371
3319
  }
3372
- return matches;
3320
+ return semantic.concat(primitives);
3373
3321
  }
3374
3322
  function createInspector(options) {
3375
3323
  const {
@@ -3398,14 +3346,14 @@ function createInspector(options) {
3398
3346
  currentEl = topEl;
3399
3347
  stack = matches;
3400
3348
  layerIndex = 0;
3401
- session.getState().actions.hover(stack[0].ref);
3349
+ session.highlight.hover(stack[0].ref);
3402
3350
  }
3403
3351
  onHover?.(makeStack(), cursor);
3404
3352
  } else if (currentEl) {
3405
3353
  currentEl = null;
3406
3354
  stack = [];
3407
3355
  layerIndex = 0;
3408
- session.getState().actions.hover(null);
3356
+ session.highlight.unhover();
3409
3357
  onHover?.(null, cursor);
3410
3358
  }
3411
3359
  };
@@ -3416,7 +3364,7 @@ function createInspector(options) {
3416
3364
  e.preventDefault();
3417
3365
  e.stopPropagation();
3418
3366
  const match = stack[layerIndex];
3419
- session.getState().actions.select(match.ref);
3367
+ session.select(match.ref);
3420
3368
  onSelect?.(match, { x: e.clientX, y: e.clientY });
3421
3369
  };
3422
3370
  const onContextMenu = (e) => {
@@ -3425,7 +3373,7 @@ function createInspector(options) {
3425
3373
  e.stopPropagation();
3426
3374
  layerIndex = (layerIndex + 1) % stack.length;
3427
3375
  const match = stack[layerIndex];
3428
- session.getState().actions.hover(match.ref);
3376
+ session.highlight.hover(match.ref);
3429
3377
  onCycle?.(makeStack(), lastCursor);
3430
3378
  };
3431
3379
  return {
@@ -3455,28 +3403,32 @@ function createInspector(options) {
3455
3403
  document.removeEventListener("mousemove", onMouseMove);
3456
3404
  document.removeEventListener("click", onClick, true);
3457
3405
  document.removeEventListener("contextmenu", onContextMenu, true);
3458
- session.getState().actions.hover(null);
3406
+ session.highlight.unhover();
3459
3407
  onHover?.(null, null);
3460
3408
  }
3461
3409
  };
3462
3410
  }
3463
3411
 
3464
- // src/surface/menu-bar.ts
3412
+ // src/browser/surface/menu-bar.ts
3465
3413
  import {
3414
+ ChevronLeft,
3415
+ ChevronRight,
3466
3416
  Command,
3467
3417
  Highlighter,
3418
+ MapPin,
3468
3419
  MousePointerClick as MousePointerClick2,
3420
+ Users,
3469
3421
  createElement as createLucideElement2
3470
3422
  } from "lucide";
3471
3423
 
3472
- // src/internal/cn.ts
3424
+ // src/browser/internal/cn.ts
3473
3425
  import { clsx } from "clsx";
3474
3426
  import { twMerge } from "tailwind-merge";
3475
3427
  function cn(...inputs) {
3476
3428
  return twMerge(clsx(inputs));
3477
3429
  }
3478
3430
 
3479
- // src/internal/el.ts
3431
+ // src/browser/internal/el.ts
3480
3432
  function el(tag, options = {}, children = []) {
3481
3433
  const node = document.createElement(tag);
3482
3434
  if (options.class) node.className = cn(options.class);
@@ -3511,7 +3463,7 @@ function el(tag, options = {}, children = []) {
3511
3463
  return node;
3512
3464
  }
3513
3465
 
3514
- // src/surface/keys.ts
3466
+ // src/browser/surface/keys.ts
3515
3467
  var INSPECT_SHORTCUT = { key: "i" };
3516
3468
  function formatShortcutLabel(shortcut) {
3517
3469
  const parts = [];
@@ -3521,7 +3473,7 @@ function formatShortcutLabel(shortcut) {
3521
3473
  return parts.join("");
3522
3474
  }
3523
3475
 
3524
- // src/surface/menu-bar.ts
3476
+ // src/browser/surface/menu-bar.ts
3525
3477
  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
3478
  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
3479
  var BUTTON_ACTIVE_CLASS = "border-info/40 bg-info/15 text-info-foreground hover:bg-info/20";
@@ -3532,8 +3484,11 @@ function createMenuBar(options) {
3532
3484
  container,
3533
3485
  session,
3534
3486
  initialCorner = "bottom-right",
3535
- appTitle
3487
+ appTitle,
3488
+ channel = null,
3489
+ pinLayer: initialPinLayer = null
3536
3490
  } = options;
3491
+ let activePinLayer = initialPinLayer;
3537
3492
  const root = el("div", {
3538
3493
  class: ROOT_CLASS,
3539
3494
  attrs: {
@@ -3574,7 +3529,7 @@ function createMenuBar(options) {
3574
3529
  highlightBtn.hidden = true;
3575
3530
  highlightBtn.addEventListener("click", (e) => {
3576
3531
  e.stopPropagation();
3577
- session.send({ type: "UNPIN" });
3532
+ session.highlight.unpin();
3578
3533
  });
3579
3534
  root.appendChild(highlightBtn);
3580
3535
  const inspectIcon = createLucideElement2(MousePointerClick2);
@@ -3594,9 +3549,166 @@ function createMenuBar(options) {
3594
3549
  );
3595
3550
  inspectBtn.addEventListener("click", (e) => {
3596
3551
  e.stopPropagation();
3597
- session.send({ type: "TOGGLE_INSPECTOR" });
3552
+ session.mode.transition.toggleInspector();
3553
+ inspectBtn.blur();
3598
3554
  });
3599
3555
  root.appendChild(inspectBtn);
3556
+ const pinIcon = createLucideElement2(MapPin);
3557
+ pinIcon.setAttribute("class", "size-3.5");
3558
+ pinIcon.setAttribute("aria-hidden", "true");
3559
+ const pinBtn = el(
3560
+ "button",
3561
+ {
3562
+ class: BUTTON_CLASS,
3563
+ attrs: {
3564
+ type: "button",
3565
+ "data-uidex-menubar-pins": "",
3566
+ "aria-label": "Report pins"
3567
+ }
3568
+ },
3569
+ pinIcon
3570
+ );
3571
+ const commitCycler = el("div", {
3572
+ class: "relative z-1 inline-flex items-center gap-0.5",
3573
+ attrs: { "data-uidex-menubar-commit-cycler": "" }
3574
+ });
3575
+ commitCycler.hidden = true;
3576
+ const prevIcon = createLucideElement2(ChevronLeft);
3577
+ prevIcon.setAttribute("class", "size-3");
3578
+ prevIcon.setAttribute("aria-hidden", "true");
3579
+ const prevBtn = el(
3580
+ "button",
3581
+ {
3582
+ class: BUTTON_CLASS,
3583
+ attrs: { type: "button", "aria-label": "Previous commit" },
3584
+ style: { width: "18px", height: "18px" }
3585
+ },
3586
+ prevIcon
3587
+ );
3588
+ const commitLabel = el("span", {
3589
+ class: "relative z-1 whitespace-nowrap px-1 text-[10px] font-mono text-muted-foreground",
3590
+ attrs: { "data-uidex-menubar-commit-label": "" }
3591
+ });
3592
+ const nextIcon = createLucideElement2(ChevronRight);
3593
+ nextIcon.setAttribute("class", "size-3");
3594
+ nextIcon.setAttribute("aria-hidden", "true");
3595
+ const nextBtn = el(
3596
+ "button",
3597
+ {
3598
+ class: BUTTON_CLASS,
3599
+ attrs: { type: "button", "aria-label": "Next commit" },
3600
+ style: { width: "18px", height: "18px" }
3601
+ },
3602
+ nextIcon
3603
+ );
3604
+ commitCycler.appendChild(prevBtn);
3605
+ commitCycler.appendChild(commitLabel);
3606
+ commitCycler.appendChild(nextBtn);
3607
+ const pinWrapper = el("div", {
3608
+ class: "relative z-1 inline-flex items-center gap-0.5",
3609
+ attrs: { "data-uidex-menubar-pin-wrapper": "" }
3610
+ });
3611
+ pinWrapper.hidden = true;
3612
+ pinWrapper.appendChild(pinBtn);
3613
+ pinWrapper.appendChild(commitCycler);
3614
+ root.appendChild(pinWrapper);
3615
+ const updatePinUI = () => {
3616
+ if (!activePinLayer) {
3617
+ pinWrapper.hidden = true;
3618
+ return;
3619
+ }
3620
+ const pinsVisible = activePinLayer.visible;
3621
+ const state = activePinLayer.filterState;
3622
+ const hasCommits = state.commits.length > 0;
3623
+ pinWrapper.hidden = false;
3624
+ commitCycler.hidden = !pinsVisible || !hasCommits;
3625
+ if (state.commitIndex === -1 || !state.commits[state.commitIndex]) {
3626
+ commitLabel.textContent = `all (${state.commits.length})`;
3627
+ } else {
3628
+ const sha = state.commits[state.commitIndex] ?? "";
3629
+ commitLabel.textContent = sha.slice(0, 7);
3630
+ }
3631
+ pinBtn.className = cn(BUTTON_CLASS, pinsVisible && BUTTON_ACTIVE_CLASS);
3632
+ };
3633
+ pinBtn.addEventListener("click", (e) => {
3634
+ e.stopPropagation();
3635
+ if (activePinLayer) {
3636
+ activePinLayer.setVisible(!activePinLayer.visible);
3637
+ }
3638
+ });
3639
+ prevBtn.addEventListener("click", (e) => {
3640
+ e.stopPropagation();
3641
+ activePinLayer?.prevCommit();
3642
+ });
3643
+ nextBtn.addEventListener("click", (e) => {
3644
+ e.stopPropagation();
3645
+ activePinLayer?.nextCommit();
3646
+ });
3647
+ let unsubscribePinFilter = activePinLayer?.onFilterChange(() => updatePinUI());
3648
+ const presenceIcon = createLucideElement2(Users);
3649
+ presenceIcon.setAttribute("class", "size-3.5");
3650
+ presenceIcon.setAttribute("aria-hidden", "true");
3651
+ const presenceBtn = el(
3652
+ "button",
3653
+ {
3654
+ class: BUTTON_CLASS,
3655
+ attrs: {
3656
+ type: "button",
3657
+ "data-uidex-menubar-presence": "",
3658
+ "aria-label": "Online users"
3659
+ }
3660
+ },
3661
+ presenceIcon
3662
+ );
3663
+ presenceBtn.hidden = true;
3664
+ const presenceBadge = el("span", {
3665
+ 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"
3666
+ });
3667
+ presenceBtn.appendChild(presenceBadge);
3668
+ const presencePopover = el("div", {
3669
+ 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"
3670
+ });
3671
+ presencePopover.hidden = true;
3672
+ let presencePopoverVisible = false;
3673
+ presenceBtn.addEventListener("click", (e) => {
3674
+ e.stopPropagation();
3675
+ presencePopoverVisible = !presencePopoverVisible;
3676
+ presencePopover.hidden = !presencePopoverVisible;
3677
+ });
3678
+ const presenceWrapper = el("div", { class: "relative z-1 inline-flex" }, [
3679
+ presenceBtn,
3680
+ presencePopover
3681
+ ]);
3682
+ presenceWrapper.hidden = true;
3683
+ root.appendChild(presenceWrapper);
3684
+ let presenceUsers = [];
3685
+ const updatePresenceUI = () => {
3686
+ const localUserId = session.getState().user?.id ?? null;
3687
+ const peers = localUserId ? presenceUsers.filter((u) => u.userId !== localUserId) : presenceUsers;
3688
+ const count = peers.length;
3689
+ presenceWrapper.hidden = count === 0;
3690
+ presenceBadge.textContent = String(count);
3691
+ presenceBtn.setAttribute(
3692
+ "aria-label",
3693
+ count === 1 ? "1 other user online" : `${count} other users online`
3694
+ );
3695
+ presencePopover.innerHTML = "";
3696
+ for (const user of peers) {
3697
+ const row = el("div", {
3698
+ class: "flex items-center gap-2 rounded px-1.5 py-1"
3699
+ });
3700
+ const dot = el("span", {
3701
+ class: "inline-block size-2 shrink-0 rounded-full bg-emerald-500"
3702
+ });
3703
+ const name = el("span", {
3704
+ class: "truncate",
3705
+ text: user.name || "Anonymous"
3706
+ });
3707
+ row.appendChild(dot);
3708
+ row.appendChild(name);
3709
+ presencePopover.appendChild(row);
3710
+ }
3711
+ };
3600
3712
  const paletteIcon = createLucideElement2(Command);
3601
3713
  paletteIcon.setAttribute("class", "size-3.5");
3602
3714
  paletteIcon.setAttribute("aria-hidden", "true");
@@ -3615,10 +3727,11 @@ function createMenuBar(options) {
3615
3727
  paletteBtn.addEventListener("click", (e) => {
3616
3728
  e.stopPropagation();
3617
3729
  if (session.getState().stack.length > 0) {
3618
- session.send({ type: "CLOSE" });
3730
+ session.mode.transition.dismiss();
3619
3731
  } else {
3620
- session.send({ type: "OPEN_PALETTE" });
3732
+ session.mode.transition.openPalette();
3621
3733
  }
3734
+ paletteBtn.blur();
3622
3735
  });
3623
3736
  root.appendChild(paletteBtn);
3624
3737
  container.appendChild(root);
@@ -3710,14 +3823,28 @@ function createMenuBar(options) {
3710
3823
  const vertical = y / (window.innerHeight || 1) < 0.5 ? "top" : "bottom";
3711
3824
  snapTo(`${vertical}-${horizontal}`);
3712
3825
  }
3826
+ const unsubscribePresence = channel?.onPresence(
3827
+ (users) => {
3828
+ presenceUsers = users;
3829
+ updatePresenceUI();
3830
+ }
3831
+ );
3713
3832
  return {
3714
3833
  destroy() {
3834
+ unsubscribePinFilter?.();
3835
+ unsubscribePresence?.();
3715
3836
  unsubscribeSession();
3716
3837
  root.removeEventListener("mousedown", onMouseDown);
3717
3838
  document.removeEventListener("mousemove", onMouseMove);
3718
3839
  document.removeEventListener("mouseup", onMouseUp);
3719
3840
  root.remove();
3720
3841
  },
3842
+ setPinLayer(layer) {
3843
+ unsubscribePinFilter?.();
3844
+ activePinLayer = layer;
3845
+ unsubscribePinFilter = layer.onFilterChange(() => updatePinUI());
3846
+ updatePinUI();
3847
+ },
3721
3848
  snapTo,
3722
3849
  snapToNearest,
3723
3850
  get corner() {
@@ -3729,7 +3856,7 @@ function createMenuBar(options) {
3729
3856
  };
3730
3857
  }
3731
3858
 
3732
- // src/surface/overlay.ts
3859
+ // src/browser/surface/overlay.ts
3733
3860
  var DEFAULT_COLOR = "#34d399";
3734
3861
  var DEFAULT_BORDER_WIDTH = 2;
3735
3862
  var DEFAULT_FILL_OPACITY = 0.08;
@@ -3742,7 +3869,9 @@ function createOverlay(deps) {
3742
3869
  backdrop.style.inset = "0";
3743
3870
  backdrop.style.zIndex = String(Z_OVERLAY);
3744
3871
  backdrop.style.backgroundColor = `rgba(0, 0, 0, ${BACKDROP_OPACITY})`;
3745
- backdrop.style.display = "none";
3872
+ backdrop.style.opacity = "0";
3873
+ backdrop.style.visibility = "hidden";
3874
+ backdrop.style.transition = "opacity 150ms ease-out";
3746
3875
  backdrop.style.pointerEvents = "auto";
3747
3876
  backdrop.style.cursor = "default";
3748
3877
  backdrop.addEventListener("click", (e) => {
@@ -3756,7 +3885,9 @@ function createOverlay(deps) {
3756
3885
  box.style.pointerEvents = "none";
3757
3886
  box.style.zIndex = String(Z_OVERLAY + 1);
3758
3887
  box.style.boxSizing = "border-box";
3759
- box.style.display = "none";
3888
+ box.style.opacity = "0";
3889
+ box.style.transition = "opacity 150ms ease-out";
3890
+ box.style.visibility = "hidden";
3760
3891
  const label = document.createElement("div");
3761
3892
  label.setAttribute("data-uidex-overlay-label", "");
3762
3893
  label.style.position = "absolute";
@@ -3769,6 +3900,17 @@ function createOverlay(deps) {
3769
3900
  label.style.whiteSpace = "nowrap";
3770
3901
  label.style.color = "#0a0a0a";
3771
3902
  label.style.display = "none";
3903
+ box.addEventListener("transitionend", () => {
3904
+ if (box.style.opacity === "0") {
3905
+ box.style.visibility = "hidden";
3906
+ }
3907
+ });
3908
+ backdrop.addEventListener("transitionend", () => {
3909
+ if (backdrop.style.opacity === "0") {
3910
+ backdrop.style.visibility = "hidden";
3911
+ backdrop.style.clipPath = "";
3912
+ }
3913
+ });
3772
3914
  box.appendChild(label);
3773
3915
  container.appendChild(box);
3774
3916
  let target = null;
@@ -3851,14 +3993,19 @@ function createOverlay(deps) {
3851
3993
  label.textContent = "";
3852
3994
  label.style.display = "none";
3853
3995
  }
3854
- backdrop.style.display = opts.backdrop ? "" : "none";
3855
- if (!opts.backdrop) {
3996
+ if (opts.backdrop) {
3997
+ backdrop.style.visibility = "visible";
3998
+ backdrop.style.opacity = "1";
3999
+ } else {
4000
+ backdrop.style.opacity = "0";
4001
+ backdrop.style.visibility = "hidden";
3856
4002
  backdrop.style.clipPath = "";
3857
4003
  }
3858
4004
  }
3859
4005
  const overlay = {
3860
4006
  onDismiss: null,
3861
4007
  show(nextTarget, showOpts) {
4008
+ const wasVisible = target !== null;
3862
4009
  target = nextTarget;
3863
4010
  opts = {
3864
4011
  label: showOpts?.label ?? "",
@@ -3871,14 +4018,18 @@ function createOverlay(deps) {
3871
4018
  };
3872
4019
  applyStyles();
3873
4020
  updatePosition();
3874
- box.style.display = "";
4021
+ if (!wasVisible) {
4022
+ box.style.opacity = "0";
4023
+ box.style.visibility = "visible";
4024
+ box.offsetHeight;
4025
+ }
4026
+ box.style.opacity = "1";
3875
4027
  attach();
3876
4028
  },
3877
4029
  hide() {
3878
4030
  target = null;
3879
- box.style.display = "none";
3880
- backdrop.style.display = "none";
3881
- backdrop.style.clipPath = "";
4031
+ box.style.opacity = "0";
4032
+ backdrop.style.opacity = "0";
3882
4033
  detach();
3883
4034
  },
3884
4035
  destroy() {
@@ -3913,7 +4064,7 @@ function rgbaFromColor(color, alpha) {
3913
4064
  return color;
3914
4065
  }
3915
4066
 
3916
- // src/surface/theme.ts
4067
+ // src/browser/surface/theme.ts
3917
4068
  function createThemeDetector(deps) {
3918
4069
  const { session, onResolve } = deps;
3919
4070
  let resolved = session.getState().resolvedTheme;
@@ -3931,7 +4082,7 @@ function createThemeDetector(deps) {
3931
4082
  const next = detect(preference);
3932
4083
  if (next === resolved) return;
3933
4084
  resolved = next;
3934
- session.getState().actions.setTheme(preference, next);
4085
+ session.setTheme(preference, next);
3935
4086
  onResolve?.(next);
3936
4087
  };
3937
4088
  push();
@@ -3982,7 +4133,7 @@ function readHtmlClassToken() {
3982
4133
  return null;
3983
4134
  }
3984
4135
 
3985
- // src/surface/shell.ts
4136
+ // src/browser/surface/shell.ts
3986
4137
  function createSurfaceShell(options) {
3987
4138
  const cleanup = createCleanupStack();
3988
4139
  const host = createSurfaceHost({
@@ -3996,7 +4147,7 @@ function createSurfaceShell(options) {
3996
4147
  onResolve: (theme) => host.applyTheme(theme)
3997
4148
  });
3998
4149
  cleanup.add(themeDetector);
3999
- const overlay = createOverlay({ container: host.chromeEl });
4150
+ const overlay = createOverlay({ container: host.shadowRoot });
4000
4151
  cleanup.add(overlay);
4001
4152
  const tooltip = createCursorTooltip({
4002
4153
  container: host.chromeEl,
@@ -4011,8 +4162,10 @@ function createSurfaceShell(options) {
4011
4162
  return;
4012
4163
  }
4013
4164
  const { current } = stack;
4165
+ const demoted = current.ref.kind === "primitive";
4014
4166
  overlay.show(current.element, {
4015
- color: KIND_STYLE[current.ref.kind].color
4167
+ color: KIND_STYLE[current.ref.kind].color,
4168
+ ...demoted && { borderStyle: "dashed", fillOpacity: 0.04 }
4016
4169
  });
4017
4170
  tooltip.update(
4018
4171
  {
@@ -4040,7 +4193,9 @@ function createSurfaceShell(options) {
4040
4193
  container: host.chromeEl,
4041
4194
  session: options.session,
4042
4195
  initialCorner: options.initialCorner,
4043
- appTitle: options.appTitle
4196
+ appTitle: options.appTitle,
4197
+ channel: options.channel ?? null,
4198
+ pinLayer: options.pinLayer ?? null
4044
4199
  });
4045
4200
  cleanup.add(menuBar);
4046
4201
  return {