@dfosco/storyboard-core 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/storyboard-ui.css +1 -0
  2. package/dist/storyboard-ui.js +26304 -0
  3. package/dist/storyboard-ui.js.map +1 -0
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +24 -18
  6. package/scaffold/manifest.json +35 -0
  7. package/scaffold/scripts/link.sh +26 -0
  8. package/scaffold/scripts/unlink.sh +10 -0
  9. package/scaffold/skills/create/SKILL.md +501 -0
  10. package/scaffold/skills/storyboard/SKILL.md +360 -0
  11. package/scaffold/skills/update-storyboard/SKILL.md +16 -0
  12. package/scaffold/skills/update-storyboard/update-storyboard-packages.sh +26 -0
  13. package/scaffold/skills/vitest/GENERATION.md +5 -0
  14. package/scaffold/skills/vitest/SKILL.md +52 -0
  15. package/scaffold/skills/vitest/references/advanced-environments.md +264 -0
  16. package/scaffold/skills/vitest/references/advanced-projects.md +300 -0
  17. package/scaffold/skills/vitest/references/advanced-type-testing.md +237 -0
  18. package/scaffold/skills/vitest/references/advanced-vi.md +249 -0
  19. package/scaffold/skills/vitest/references/core-cli.md +166 -0
  20. package/scaffold/skills/vitest/references/core-config.md +174 -0
  21. package/scaffold/skills/vitest/references/core-describe.md +193 -0
  22. package/scaffold/skills/vitest/references/core-expect.md +219 -0
  23. package/scaffold/skills/vitest/references/core-hooks.md +244 -0
  24. package/scaffold/skills/vitest/references/core-test-api.md +233 -0
  25. package/scaffold/skills/vitest/references/features-concurrency.md +250 -0
  26. package/scaffold/skills/vitest/references/features-context.md +238 -0
  27. package/scaffold/skills/vitest/references/features-coverage.md +207 -0
  28. package/scaffold/skills/vitest/references/features-filtering.md +211 -0
  29. package/scaffold/skills/vitest/references/features-mocking.md +265 -0
  30. package/scaffold/skills/vitest/references/features-snapshots.md +207 -0
  31. package/scaffold/skills/worktree/SKILL.md +51 -0
  32. package/scaffold/storyboard.config.json +26 -0
  33. package/scaffold/svelte.config.js +1 -0
  34. package/scaffold/toolbar.config.json +4 -0
  35. package/src/ActionMenuButton.svelte +1 -1
  36. package/src/CanvasCreateMenu.svelte +1 -1
  37. package/src/CoreUIBar.svelte +23 -12
  38. package/src/CreateMenuButton.svelte +1 -1
  39. package/src/InspectorPanel.svelte +144 -49
  40. package/src/SidePanel.svelte +10 -10
  41. package/src/commandActions.js +1 -1
  42. package/src/comments/index.js +0 -3
  43. package/src/devtools-consumer.js +28 -0
  44. package/src/devtools.js +4 -1
  45. package/src/index.js +8 -3
  46. package/src/inspector/highlighter.js +28 -17
  47. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +1 -1
  48. package/src/lib/components/ui/trigger-button/trigger-button.svelte +8 -4
  49. package/src/mountStoryboardCore.js +223 -0
  50. package/src/scaffold.js +100 -0
  51. package/src/stores/themeStore.ts +29 -8
  52. package/src/styles/tailwind.css +16 -0
  53. package/src/svelte-plugin-ui/components/Viewfinder.svelte +18 -0
  54. package/src/ui-entry.js +30 -0
  55. package/src/vite/server-plugin.js +8 -24
  56. package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +24 -6
  57. package/src/workshop/features/createFlow/CreateFlowForm.svelte +1 -1
  58. package/src/workshop/features/createFlow/index.js +0 -1
  59. package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +1 -1
  60. package/src/workshop/features/createPrototype/index.js +0 -1
  61. /package/{core-ui.config.json → toolbar.config.json} +0 -0
@@ -28,6 +28,59 @@
28
28
  /** @type {Element | null} — the currently selected DOM element */
29
29
  let selectedElement = $state(null)
30
30
 
31
+ // ── URL state helpers ─────────────────────────────────────────────
32
+
33
+ /**
34
+ * Build a CSS selector that can re-find `el` later.
35
+ * Prefers id, then data-testid, then an nth-child path from <body>.
36
+ */
37
+ function generateSelector(el) {
38
+ if (!(el instanceof Element)) return null
39
+ if (el.id) return `#${CSS.escape(el.id)}`
40
+
41
+ const testId = el.getAttribute('data-testid')
42
+ if (testId) return `[data-testid="${CSS.escape(testId)}"]`
43
+
44
+ const parts = []
45
+ let cur = el
46
+ while (cur && cur !== document.body && cur !== document.documentElement) {
47
+ let seg = cur.tagName.toLowerCase()
48
+ if (cur.id) {
49
+ parts.unshift(`#${CSS.escape(cur.id)}`)
50
+ break
51
+ }
52
+ const parent = cur.parentElement
53
+ if (parent) {
54
+ const siblings = Array.from(parent.children)
55
+ const idx = siblings.indexOf(cur) + 1
56
+ seg += `:nth-child(${idx})`
57
+ }
58
+ parts.unshift(seg)
59
+ cur = parent
60
+ }
61
+ return parts.length ? parts.join(' > ') : null
62
+ }
63
+
64
+ /** Read the `inspect` search param from the current URL. */
65
+ function getInspectParam() {
66
+ try {
67
+ return new URL(window.location.href).searchParams.get('inspect')
68
+ } catch { return null }
69
+ }
70
+
71
+ /** Set or clear the `inspect` search param without triggering navigation. */
72
+ function setInspectParam(selector) {
73
+ try {
74
+ const url = new URL(window.location.href)
75
+ if (selector) {
76
+ url.searchParams.set('inspect', selector)
77
+ } else {
78
+ url.searchParams.delete('inspect')
79
+ }
80
+ history.replaceState(history.state, '', url.toString())
81
+ } catch {}
82
+ }
83
+
31
84
  /** @type {string[]} */
32
85
  let knownFiles = []
33
86
 
@@ -44,7 +97,10 @@
44
97
  async function loadStaticData() {
45
98
  if (staticInspectorData) return staticInspectorData
46
99
  try {
47
- const res = await fetch(`${import.meta.env.BASE_URL}_storyboard/inspector.json`)
100
+ // Use window.location to derive base path at runtime, since
101
+ // import.meta.env.BASE_URL is baked at compile time in the UI bundle
102
+ const basePath = window.__STORYBOARD_BASE_PATH__ || '/'
103
+ const res = await fetch(`${basePath}_storyboard/inspector.json`)
48
104
  if (res.ok) {
49
105
  staticInspectorData = await res.json()
50
106
  return staticInspectorData
@@ -55,12 +111,19 @@
55
111
 
56
112
  /**
57
113
  * Fetch source file content — uses dev middleware in dev, static JSON in prod.
114
+ * Uses runtime detection (not import.meta.env.DEV) so this works in the
115
+ * pre-compiled UI bundle where compile-time env vars are baked in.
58
116
  */
59
117
  async function fetchSourceContent(filePath) {
60
- if (import.meta.env.DEV) {
118
+ // Try the dev middleware first — works when Vite server plugin is active
119
+ try {
61
120
  const res = await fetch(`/_storyboard/docs/source?path=${encodeURIComponent(filePath)}`)
62
- return res.ok ? (await res.json())?.content || '' : ''
63
- }
121
+ if (res.ok) {
122
+ const json = await res.json()
123
+ return json?.content || ''
124
+ }
125
+ } catch {}
126
+ // Fall back to static inspector JSON for deployed/production builds
64
127
  const data = await loadStaticData()
65
128
  return data?.sources?.[filePath] || ''
66
129
  }
@@ -322,6 +385,8 @@
322
385
  inspecting = false
323
386
  // Show persistent highlight on the selected element
324
387
  mouseMode?.showHighlight(el)
388
+ // Persist selection to URL
389
+ setInspectParam(generateSelector(el))
325
390
  }
326
391
 
327
392
  function handleDeactivate() {
@@ -333,6 +398,8 @@
333
398
  mouseMode?.hideHighlight()
334
399
  mouseMode?.activate()
335
400
  inspecting = true
401
+ // Clear URL param while re-selecting
402
+ setInspectParam(null)
336
403
  }
337
404
 
338
405
  function stopInspecting() {
@@ -360,38 +427,62 @@
360
427
  onDeactivate: handleDeactivate,
361
428
  })
362
429
 
363
- // Auto-start inspector mode
364
- startInspecting()
365
-
366
- // Pre-fetch file list and repo info
367
- if (import.meta.env.DEV) {
368
- // Dev: use live middleware endpoints
369
- try {
370
- const [filesRes, repoRes] = await Promise.all([
371
- fetch('/_storyboard/docs/files'),
372
- fetch('/_storyboard/docs/repo'),
373
- ])
374
- if (filesRes.ok) {
375
- const data = await filesRes.json()
376
- knownFiles = data.files || []
377
- }
378
- if (repoRes.ok) {
379
- repoInfo = await repoRes.json()
380
- }
381
- } catch {}
382
- } else {
383
- // Production: load from static build-time JSON
430
+ // Pre-fetch file list and repo info FIRST (needed for source resolution)
431
+ // Try dev middleware first, fall back to static JSON
432
+ let filesLoaded = false
433
+ try {
434
+ const [filesRes, repoRes] = await Promise.all([
435
+ fetch('/_storyboard/docs/files'),
436
+ fetch('/_storyboard/docs/repo'),
437
+ ])
438
+ if (filesRes.ok) {
439
+ const data = await filesRes.json()
440
+ knownFiles = data.files || []
441
+ filesLoaded = true
442
+ }
443
+ if (repoRes.ok) {
444
+ repoInfo = await repoRes.json()
445
+ }
446
+ } catch {}
447
+
448
+ if (!filesLoaded) {
449
+ // Fall back to static build-time JSON
384
450
  const data = await loadStaticData()
385
451
  if (data) {
386
452
  knownFiles = data.files || []
387
453
  repoInfo = data.repo || null
388
454
  }
389
455
  }
456
+
457
+ // Restore inspector selection from URL param (after files are loaded)
458
+ const savedSelector = getInspectParam()
459
+ let restored = false
460
+ if (savedSelector) {
461
+ // Retry with delay — the React page may still be rendering
462
+ for (let attempt = 0; attempt < 5 && !restored; attempt++) {
463
+ if (attempt > 0) await new Promise(r => setTimeout(r, 300))
464
+ try {
465
+ const el = document.querySelector(savedSelector)
466
+ if (el) {
467
+ handleSelect(el)
468
+ restored = true
469
+ }
470
+ } catch {
471
+ break // invalid selector, don't retry
472
+ }
473
+ }
474
+ if (!restored) setInspectParam(null)
475
+ }
476
+
477
+ if (!restored) {
478
+ startInspecting()
479
+ }
390
480
  })
391
481
 
392
482
  onDestroy(() => {
393
483
  mouseMode?.deactivate()
394
484
  mouseMode?.hideHighlight()
485
+ setInspectParam(null)
395
486
  })
396
487
  </script>
397
488
 
@@ -433,7 +524,7 @@
433
524
  class="mt-2 px-4 py-1.5 text-xs font-medium rounded-md border cursor-pointer transition-colors"
434
525
  style:background="transparent"
435
526
  style:color="var(--fgColor-muted)"
436
- style:border-color="var(--borderColor-default)"
527
+ style:border-color="var(--borderColor-default, var(--color-border, #d1d9e0))"
437
528
  onclick={stopInspecting}
438
529
  >
439
530
  Cancel
@@ -442,29 +533,19 @@
442
533
 
443
534
  <!-- Selected state -->
444
535
  {:else}
445
- <div class="flex flex-col flex-1 min-h-0 p-3 gap-3">
536
+ <div class="flex flex-col flex-1 min-h-0 p-3 pt-0 gap-3">
446
537
  <!-- Component name -->
447
538
  <div>
448
539
  <h3 class="text-base font-bold m-0 inspector-mono" style:color="var(--color-purple, #7655a4)">
449
540
  {componentInfo.name}
450
541
  </h3>
451
-
452
- <!-- Source file -->
453
- {#if sourcePath}
454
- <p class="inline-flex items-center gap-1 mt-1 text-xs m-0 inspector-mono" style:color="var(--fgColor-muted)">
455
- <Icon name="primer/file-code" size={12} />
456
- {sourcePath}
457
- </p>
458
- {/if}
459
542
  </div>
460
543
 
461
544
  <!-- Source code -->
462
545
  {#if sourcePath}
463
- <div class="border rounded-md overflow-hidden flex-1 min-h-0 flex flex-col" style:border-color="var(--borderColor-default)">
546
+ <div class="border rounded-md overflow-hidden flex-1 min-h-0 flex flex-col inspector-code-block" style:border-color="var(--borderColor-default, var(--color-border, #d1d9e0))">
464
547
  <div
465
- class="flex items-center justify-between w-full px-3 py-1.5 text-xs font-semibold shrink-0"
466
- style:background="var(--bgColor-default)"
467
- style:color="var(--fgColor-muted)"
548
+ class="flex items-center justify-between w-full px-3 py-1.5 text-xs font-semibold shrink-0 inspector-code-header"
468
549
  >
469
550
  <span class="flex items-center gap-1.5 min-w-0">
470
551
  <Icon name="primer/file-code" size={12} />
@@ -475,8 +556,7 @@
475
556
  href={githubUrl}
476
557
  target="_blank"
477
558
  rel="noopener noreferrer"
478
- class="flex items-center gap-1 shrink-0 text-xs no-underline hover:underline inspector-mono"
479
- style:color="var(--fgColor-muted)"
559
+ class="flex items-center gap-1 shrink-0 text-xs no-underline hover:underline inspector-mono inspector-code-link"
480
560
  >
481
561
  <Icon name="primer/mark-github" size={14} />
482
562
  <span>GitHub</span>
@@ -484,9 +564,9 @@
484
564
  {/if}
485
565
  </div>
486
566
 
487
- <div class="border-t flex-1 min-h-0 flex flex-col" style:border-color="var(--borderColor-default)">
567
+ <div class="border-t flex-1 min-h-0 flex flex-col" style:border-color="#30363d">
488
568
  {#if sourceLoading}
489
- <div class="px-3 py-4 text-xs text-center" style:color="var(--fgColor-muted)">
569
+ <div class="px-3 py-4 text-xs text-center" style:color="#8b949e">
490
570
  Loading source…
491
571
  </div>
492
572
  {:else if sourceCode}
@@ -494,11 +574,11 @@
494
574
  {#if highlightedHtml}
495
575
  <div class="shiki-wrapper">{@html highlightedHtml}</div>
496
576
  {:else}
497
- <pre class="m-0 text-xs leading-relaxed inspector-mono source-pre" style:color="var(--fgColor-default)">{sourceCode}</pre>
577
+ <pre class="m-0 text-xs leading-relaxed inspector-mono source-pre" style:color="#c9d1d9">{sourceCode}</pre>
498
578
  {/if}
499
579
  </div>
500
580
  {:else}
501
- <div class="px-3 py-4 text-xs text-center" style:color="var(--fgColor-muted)">
581
+ <div class="px-3 py-4 text-xs text-center" style:color="#8b949e">
502
582
  Unable to load source
503
583
  </div>
504
584
  {/if}
@@ -526,10 +606,6 @@
526
606
  font-family: "Ioskeley Mono", ui-monospace, monospace;
527
607
  }
528
608
 
529
- .inspector-toggle-active {
530
- box-shadow: 0 0 0 2px color-mix(in srgb, var(--color-purple, #7655a4) 30%, transparent);
531
- }
532
-
533
609
  .inspector-pulse-dot {
534
610
  width: 8px;
535
611
  height: 8px;
@@ -584,4 +660,23 @@
584
660
  background: color-mix(in srgb, var(--color-purple, #7655a4) 20%, transparent);
585
661
  border-left: 2px solid var(--color-purple, #7655a4);
586
662
  }
663
+
664
+ /* Force dark chrome on the code block — independent of page theme */
665
+ .inspector-code-block {
666
+ background: #0d1117;
667
+ border-color: #30363d !important;
668
+ }
669
+
670
+ .inspector-code-header {
671
+ background: #161b22;
672
+ color: #8b949e;
673
+ border-bottom: 1px solid #30363d;
674
+ }
675
+
676
+ .inspector-code-link {
677
+ color: #8b949e;
678
+ }
679
+ .inspector-code-link:hover {
680
+ color: #c9d1d9;
681
+ }
587
682
  </style>
@@ -276,11 +276,11 @@
276
276
  z-index: 9998;
277
277
  display: flex;
278
278
  flex-direction: column;
279
- background-color: var(--bgColor-default, #0d1117);
280
- border-left: 1px solid var(--borderColor-default, #30363d);
279
+ background-color: var(--bgColor-default, var(--color-background, #ffffff));
280
+ border-left: 1px solid var(--borderColor-default, var(--color-border, #d0d7de));
281
281
  box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);
282
282
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
283
- color: var(--fgColor-default, #e6edf3);
283
+ color: var(--fgColor-default, var(--color-foreground, #1f2328));
284
284
  animation: sb-sidepanel-slide-in 0.25s ease;
285
285
  }
286
286
 
@@ -293,7 +293,7 @@
293
293
  width: 100% !important;
294
294
  height: var(--sidepanel-height, 300px);
295
295
  border-left: none;
296
- border-top: 1px solid var(--borderColor-default, #30363d);
296
+ border-top: 1px solid var(--borderColor-default, var(--color-border, #d0d7de));
297
297
  box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.15);
298
298
  animation: sb-sidepanel-slide-up 0.25s ease;
299
299
  }
@@ -320,7 +320,7 @@
320
320
  left: 0;
321
321
  right: 0;
322
322
  height: 3px;
323
- background: var(--mode-color, var(--borderColor-default, #30363d));
323
+ background: var(--mode-color, var(--borderColor-default, var(--color-border, #d0d7de)));
324
324
  }
325
325
 
326
326
  /* Drag handle — side mode (left edge, vertical) */
@@ -378,7 +378,7 @@
378
378
  font-weight: 600;
379
379
  text-transform: uppercase;
380
380
  letter-spacing: 0.05em;
381
- color: var(--fgColor-muted, #848d97);
381
+ color: var(--fgColor-muted, var(--color-muted-foreground, #656d76));
382
382
  padding-left: 4px;
383
383
  }
384
384
 
@@ -392,7 +392,7 @@
392
392
  appearance: none;
393
393
  border: none;
394
394
  background: transparent;
395
- color: var(--fgColor-muted, #848d97);
395
+ color: var(--fgColor-muted, var(--color-muted-foreground, #656d76));
396
396
  cursor: pointer;
397
397
  padding: 6px;
398
398
  border-radius: 6px;
@@ -404,7 +404,7 @@
404
404
 
405
405
  .sb-sidepanel-action-btn:hover {
406
406
  background: var(--bgColor-neutral-muted, rgba(110, 118, 129, 0.1));
407
- color: var(--fgColor-default, #e6edf3);
407
+ color: var(--fgColor-default, var(--color-foreground, #1f2328));
408
408
  }
409
409
 
410
410
  /* Body */
@@ -425,8 +425,8 @@
425
425
  .sb-sidepanel-spinner {
426
426
  width: 20px;
427
427
  height: 20px;
428
- border: 2px solid var(--borderColor-default, #30363d);
429
- border-top-color: var(--fgColor-muted, #848d97);
428
+ border: 2px solid var(--borderColor-default, var(--color-border, #d0d7de));
429
+ border-top-color: var(--fgColor-muted, var(--color-muted-foreground, #656d76));
430
430
  border-radius: 50%;
431
431
  animation: sb-spin 0.6s linear infinite;
432
432
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Command Actions — config-driven registry for command menu entries.
3
3
  *
4
- * The command section of core-ui.config.json declares action metadata:
4
+ * The command section of toolbar.config.json declares action metadata:
5
5
  * id, label, type, hideFrom, separatorBefore
6
6
  *
7
7
  * Handler shapes by type:
@@ -40,6 +40,3 @@ export { saveDraft, getDraft, clearDraft, composerDraftKey, replyDraftKey } from
40
40
 
41
41
  // GraphQL client (for advanced use)
42
42
  export { graphql } from './graphql.js'
43
-
44
- // Mount (Svelte UI)
45
- export { mountComments } from './ui/mount.js'
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Consumer-safe proxy for mountDevTools.
3
+ *
4
+ * Delegates to the compiled UI bundle (@dfosco/storyboard-core/ui-runtime)
5
+ * so consumers don't need svelte installed. The real mountDevTools in
6
+ * devtools.js imports svelte directly and is only usable in the source repo
7
+ * or via the compiled UI bundle.
8
+ */
9
+
10
+ export async function mountDevTools(options = {}) {
11
+ const ui = await import('@dfosco/storyboard-core/ui-runtime')
12
+ return ui.mountDevTools(options)
13
+ }
14
+
15
+ export async function unmountDevTools() {
16
+ const ui = await import('@dfosco/storyboard-core/ui-runtime')
17
+ return ui.unmountDevTools()
18
+ }
19
+
20
+ /** @deprecated Use mountDevTools instead. */
21
+ export function mountFlowDebug(options = {}) {
22
+ return mountDevTools(options)
23
+ }
24
+
25
+ /** @deprecated Use mountDevTools instead. */
26
+ export function mountSceneDebug(options = {}) {
27
+ return mountDevTools(options)
28
+ }
package/src/devtools.js CHANGED
@@ -30,6 +30,9 @@ export async function mountDevTools(options = {}) {
30
30
  // Prevent double-mount
31
31
  if (wrapper) return
32
32
 
33
+ // Skip mounting entirely when loaded inside a prototype embed iframe
34
+ if (typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('_sb_embed')) return
35
+
33
36
  const { mount } = await import('svelte')
34
37
  const { default: CoreUIBar } = await import('./CoreUIBar.svelte')
35
38
 
@@ -105,7 +108,7 @@ export async function mountDevTools(options = {}) {
105
108
 
106
109
  instance = mount(CoreUIBar, {
107
110
  target: wrapper,
108
- props: { basePath },
111
+ props: { basePath, toolbarConfig: options.toolbarConfig },
109
112
  })
110
113
  }
111
114
 
package/src/index.js CHANGED
@@ -28,7 +28,7 @@ export { getByPath, setByPath, deepClone } from './dotPath.js'
28
28
  export { getParam, setParam, getAllParams, removeParam } from './session.js'
29
29
 
30
30
  // localStorage persistence
31
- export { getLocal, setLocal, removeLocal, getAllLocal, subscribeToStorage, getStorageSnapshot } from './localStorage.js'
31
+ export { getLocal, setLocal, removeLocal, getAllLocal, subscribeToStorage, getStorageSnapshot, notifyChange } from './localStorage.js'
32
32
 
33
33
  // Hide mode (clean URLs)
34
34
  export { isHideMode, activateHideMode, deactivateHideMode, getShadow, setShadow, removeShadow, getAllShadows, pushSnapshot, getOverrideHistory, getCurrentSnapshot, getCurrentRoute, getCurrentIndex, getNextIndex, canUndo, canRedo, undo, redo, syncHashToHistory, installHistorySync } from './hideMode.js'
@@ -49,11 +49,16 @@ export { registerMode, unregisterMode, getRegisteredModes, getCurrentMode, activ
49
49
  export { initTools, setToolAction, setToolState, getToolState, getToolsForMode, subscribeToTools, getToolsSnapshot } from './modes.js'
50
50
 
51
51
  // Dev tools (vanilla JS, framework-agnostic)
52
- export { mountDevTools } from './devtools.js'
52
+ // mountDevTools delegates to the compiled UI bundle so consumers
53
+ // don't need svelte installed — svelte is bundled into ui-runtime.
54
+ export { mountDevTools } from './devtools-consumer.js'
53
55
  export { mountFlowDebug } from './sceneDebug.js'
54
56
  // Deprecated alias
55
57
  export { mountSceneDebug } from './sceneDebug.js'
56
58
 
59
+ // Single entry point for consumer apps
60
+ export { mountStoryboardCore } from './mountStoryboardCore.js'
61
+
57
62
  // Viewfinder utilities
58
63
  export { hash, resolveFlowRoute, getFlowMeta, buildPrototypeIndex } from './viewfinder.js'
59
64
  // Deprecated aliases
@@ -63,7 +68,7 @@ export { resolveSceneRoute, getSceneMeta } from './viewfinder.js'
63
68
  export { initFeatureFlags, getFlag, setFlag, toggleFlag, getAllFlags, resetFlags, getFlagKeys, syncFlagBodyClasses } from './featureFlags.js'
64
69
 
65
70
  // Command actions (config-driven command menu entries)
66
- export { initCommandActions, registerCommandAction, unregisterCommandAction, setDynamicActions, clearDynamicActions, getActionsForMode, executeAction, getActionChildren, subscribeToCommandActions, getCommandActionsSnapshot, setRoutingBasePath } from './commandActions.js'
71
+ export { initCommandActions, registerCommandAction, unregisterCommandAction, setDynamicActions, clearDynamicActions, getActionsForMode, executeAction, getActionChildren, subscribeToCommandActions, getCommandActionsSnapshot, setRoutingBasePath, isExcludedByRoute } from './commandActions.js'
67
72
 
68
73
  // Plugin configuration
69
74
  export { initPlugins, isPluginEnabled, getPluginsConfig } from './plugins.js'
@@ -4,24 +4,35 @@
4
4
  * Uses shiki/core with only the four languages the inspector needs,
5
5
  * avoiding the full shiki bundle that registers 200+ lazy-loaded
6
6
  * language chunks (which break in deployed/static environments).
7
+ *
8
+ * Each import() has .catch() so consumer bundlers (esbuild dep optimizer)
9
+ * treat them as runtime-fallible and don't fail at build time when shiki
10
+ * isn't installed. Returns null when shiki is unavailable.
7
11
  */
8
- import { createHighlighterCore } from 'shiki/core'
9
- import { createOnigurumaEngine } from 'shiki/engine/oniguruma'
10
-
11
12
  export async function createInspectorHighlighter() {
12
- const [tsx, jsx, javascript, typescript, githubDark, wasm] =
13
- await Promise.all([
14
- import('shiki/dist/langs/tsx.mjs'),
15
- import('shiki/dist/langs/jsx.mjs'),
16
- import('shiki/dist/langs/javascript.mjs'),
17
- import('shiki/dist/langs/typescript.mjs'),
18
- import('shiki/dist/themes/github-dark.mjs'),
19
- import('shiki/wasm'),
20
- ])
13
+ try {
14
+ const [shikiCore, oniguruma, tsx, jsx, javascript, typescript, githubDark, wasm] =
15
+ await Promise.all([
16
+ import('shiki/core').catch(() => null),
17
+ import('shiki/engine/oniguruma').catch(() => null),
18
+ import('shiki/dist/langs/tsx.mjs').catch(() => null),
19
+ import('shiki/dist/langs/jsx.mjs').catch(() => null),
20
+ import('shiki/dist/langs/javascript.mjs').catch(() => null),
21
+ import('shiki/dist/langs/typescript.mjs').catch(() => null),
22
+ import('shiki/dist/themes/github-dark.mjs').catch(() => null),
23
+ import('shiki/wasm').catch(() => null),
24
+ ])
25
+
26
+ if (!shikiCore || !oniguruma || !tsx || !jsx || !javascript || !typescript || !githubDark || !wasm) {
27
+ return null
28
+ }
21
29
 
22
- return createHighlighterCore({
23
- themes: [githubDark.default],
24
- langs: [tsx.default, jsx.default, javascript.default, typescript.default],
25
- engine: createOnigurumaEngine(wasm),
26
- })
30
+ return shikiCore.createHighlighterCore({
31
+ themes: [githubDark.default],
32
+ langs: [tsx.default, jsx.default, javascript.default, typescript.default],
33
+ engine: oniguruma.createOnigurumaEngine(wasm),
34
+ })
35
+ } catch {
36
+ return null
37
+ }
27
38
  }
@@ -19,7 +19,7 @@
19
19
  {sideOffset}
20
20
  {align}
21
21
  class={cn(
22
- "font-sans data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 w-(--bits-dropdown-menu-anchor-width) data-closed:overflow-hidden z-[10000] bg-popover border-3 border-slate-400 text-slate-800 fill-slate-600 min-w-40 rounded-xl p-2 shadow-xl duration-100 overflow-x-hidden overflow-y-auto",
22
+ "font-sans data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 w-(--bits-dropdown-menu-anchor-width) data-closed:overflow-hidden z-[10000] bg-popover border-3 border-slate-400 text-slate-800 fill-slate-600 min-w-40 rounded-xl p-2 shadow-xl duration-100 overflow-x-hidden overflow-y-auto",
23
23
  className
24
24
  )}
25
25
  {...restProps}
@@ -12,11 +12,15 @@
12
12
  <script module>
13
13
  import { cn } from "../../../utils/index.js";
14
14
 
15
- // Register CSS Houdini paint worklet for superellipse masks
15
+ // Register CSS Houdini paint worklet for superellipse masks.
16
+ // Inlined from smooth-corners (MIT) to avoid Vite-specific ?url import
17
+ // that breaks when this source is consumed from node_modules.
16
18
  if (typeof CSS !== 'undefined' && 'paintWorklet' in CSS) {
17
- import('smooth-corners/paint.js?url').then(mod => {
18
- CSS.paintWorklet.addModule(mod.default)
19
- }).catch(() => {})
19
+ try {
20
+ const worklet = `class P{static get inputProperties(){return["--smooth-corners"]}superellipse(a,b,nX=4,nY){if(Number.isNaN(nX))nX=4;if(typeof nY==="undefined"||Number.isNaN(nY))nY=nX;if(nX>100)nX=100;if(nY>100)nY=100;if(nX<1e-11)nX=1e-11;if(nY<1e-11)nY=1e-11;const nX2=2/nX,nY2=nY?2/nY:nX2,steps=360,step=(2*Math.PI)/steps;return Array.from({length:steps},(_,i)=>{const t=i*step,cosT=Math.cos(t),sinT=Math.sin(t);return{x:Math.abs(cosT)**nX2*a*Math.sign(cosT),y:Math.abs(sinT)**nY2*b*Math.sign(sinT)}})}paint(ctx,geom,props){const[nX,nY]=props.get("--smooth-corners").toString().replace(/ /g,"").split(",");const w=geom.width/2,h=geom.height/2,s=this.superellipse(w,h,parseFloat(nX),parseFloat(nY));ctx.fillStyle="#000";ctx.setTransform(1,0,0,1,w,h);ctx.beginPath();for(let i=0;i<s.length;i++){const{x,y}=s[i];i===0?ctx.moveTo(x,y):ctx.lineTo(x,y)}ctx.closePath();ctx.fill()}}registerPaint("smooth-corners",P);`;
21
+ const blob = new Blob([worklet], { type: 'application/javascript' });
22
+ CSS.paintWorklet.addModule(URL.createObjectURL(blob));
23
+ } catch {}
20
24
  }
21
25
  </script>
22
26