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