@dfosco/storyboard-core 4.2.2 → 4.2.4

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 (57) hide show
  1. package/dist/storyboard-ui.css +1 -1
  2. package/dist/storyboard-ui.js +7973 -8418
  3. package/dist/storyboard-ui.js.map +1 -1
  4. package/package.json +2 -12
  5. package/scaffold/AGENTS.md +432 -0
  6. package/scaffold/gitignore +64 -0
  7. package/scaffold/manifest.json +13 -8
  8. package/src/ActionMenuButton.jsx +1 -1
  9. package/src/AutosyncMenuButton.jsx +1 -1
  10. package/src/CanvasAgentsMenu.jsx +1 -1
  11. package/src/CanvasCreateMenu.jsx +1 -1
  12. package/src/CanvasSnap.jsx +1 -1
  13. package/src/CanvasZoomToFit.jsx +1 -1
  14. package/src/CommandMenu.jsx +2 -2
  15. package/src/CommandPalette.jsx +1 -1
  16. package/src/CommandPaletteTrigger.jsx +1 -1
  17. package/src/CommentsMenuButton.jsx +1 -1
  18. package/src/CoreUIBar.jsx +18 -2
  19. package/src/CreateMenuButton.jsx +1 -1
  20. package/src/HideChromeTrigger.jsx +1 -1
  21. package/src/{svelte-plugin-ui/components/Icon.jsx → Icon.jsx} +8 -10
  22. package/src/ThemeMenuButton.jsx +1 -1
  23. package/src/comments/ui/authModal.js +1 -1
  24. package/src/configSchema.js +2 -0
  25. package/src/configStore.js +1 -1
  26. package/src/devtools-consumer.js +2 -2
  27. package/src/index.js +3 -3
  28. package/src/mountStoryboardCore.js +3 -3
  29. package/src/sidepanel.css +1 -1
  30. package/src/toolbarConfigStore.js +1 -1
  31. package/src/ui/design-modes.ts +4 -51
  32. package/src/ui/viewfinder.ts +4 -55
  33. package/src/ui-entry.js +5 -5
  34. package/src/vite/server-plugin.js +9 -0
  35. package/src/workshop/features/createFlow/index.js +1 -1
  36. package/src/workshop/features/createPrototype/index.js +1 -1
  37. package/src/workshop/features/registry-server.js +1 -1
  38. package/src/workshop/ui/mount.ts +3 -65
  39. package/scaffold/svelte.config.js +0 -1
  40. package/src/svelte-plugin-ui/__tests__/ModeSwitch.test.ts +0 -75
  41. package/src/svelte-plugin-ui/__tests__/ToolbarShell.test.ts +0 -126
  42. package/src/svelte-plugin-ui/__tests__/designModes.test.ts +0 -58
  43. package/src/svelte-plugin-ui/__tests__/modeStore.test.ts +0 -53
  44. package/src/svelte-plugin-ui/__tests__/mount.test.ts +0 -29
  45. package/src/svelte-plugin-ui/components/Icon.css +0 -11
  46. package/src/svelte-plugin-ui/components/ModeSwitch.css +0 -90
  47. package/src/svelte-plugin-ui/components/ModeSwitch.jsx +0 -47
  48. package/src/svelte-plugin-ui/components/ToolbarShell.css +0 -80
  49. package/src/svelte-plugin-ui/components/ToolbarShell.jsx +0 -84
  50. package/src/svelte-plugin-ui/components/Viewfinder.css +0 -412
  51. package/src/svelte-plugin-ui/components/Viewfinder.jsx +0 -513
  52. package/src/svelte-plugin-ui/index.ts +0 -20
  53. package/src/svelte-plugin-ui/mount.ts +0 -120
  54. package/src/svelte-plugin-ui/stores/modeStore.ts +0 -91
  55. package/src/svelte-plugin-ui/stores/toolStore.ts +0 -71
  56. package/src/svelte-plugin-ui/stores/types.ts +0 -55
  57. package/src/svelte-plugin-ui/styles/base.css +0 -69
@@ -11,10 +11,10 @@ import { useState, useEffect, useMemo, useCallback } from 'react'
11
11
  import * as DropdownMenu from './lib/components/ui/dropdown-menu/index.js'
12
12
  import * as Panel from './lib/components/ui/panel/index.js'
13
13
  import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
14
- import Icon from './svelte-plugin-ui/components/Icon.jsx'
14
+ import Icon from './Icon.jsx'
15
15
  import { getActionsForMode, executeAction, getActionChildren, subscribeToCommandActions } from './commandActions.js'
16
16
  import { getToolbarToolState, isToolbarToolLocalOnly, subscribeToToolbarToolStates } from './toolStateStore.js'
17
- import { getCurrentMode, subscribeToMode } from './svelte-plugin-ui/stores/types.js'
17
+ import { getCurrentMode, subscribeToMode } from './modes.js'
18
18
 
19
19
  const localDotStyle = {
20
20
  display: 'inline-block',
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { useCallback } from 'react'
8
8
  import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
9
- import Icon from './svelte-plugin-ui/components/Icon.jsx'
9
+ import Icon from './Icon.jsx'
10
10
 
11
11
  export default function CommandPalette({
12
12
  tabindex,
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { useCallback } from 'react'
7
7
  import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
8
- import Icon from './svelte-plugin-ui/components/Icon.jsx'
8
+ import Icon from './Icon.jsx'
9
9
 
10
10
  export default function CommandPaletteTrigger({ config = {}, tabindex }) {
11
11
  const openPalette = useCallback(() => {
@@ -1,6 +1,6 @@
1
1
  import { useState, useEffect } from 'react'
2
2
  import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
3
- import Icon from './svelte-plugin-ui/components/Icon.jsx'
3
+ import Icon from './Icon.jsx'
4
4
  import { isAuthenticated } from './comments/auth.js'
5
5
  import { isCommentModeActive, toggleCommentMode, subscribeToCommentMode } from './comments/commentMode.js'
6
6
  import { openAuthModal } from './comments/ui/authModal.js'
package/src/CoreUIBar.jsx CHANGED
@@ -16,8 +16,8 @@ import * as Panel from './lib/components/ui/panel/index.js'
16
16
  import PwaInstallBanner from './PwaInstallBanner.jsx'
17
17
  import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
18
18
  import * as Tooltip from './lib/components/ui/tooltip/index.js'
19
- import Icon from './svelte-plugin-ui/components/Icon.jsx'
20
- import { modeState } from './svelte-plugin-ui/stores/modeStore.js'
19
+ import Icon from './Icon.jsx'
20
+ import { getCurrentMode, getRegisteredModes, subscribeToMode, isModeSwitcherVisible } from './modes.js'
21
21
  import { sidePanelState, togglePanel } from './stores/sidePanelStore.js'
22
22
  import {
23
23
  initCommandActions, registerCommandAction, getActionChildren,
@@ -114,6 +114,22 @@ function menuVisibleInMode(menu, mode) {
114
114
  return menu.modes.includes('*') || menu.modes.includes(mode)
115
115
  }
116
116
 
117
+ // Subscribable mode state for useExternalStore (replaces Svelte modeState)
118
+ const modeState = {
119
+ get mode() { return getCurrentMode() },
120
+ get modes() { return getRegisteredModes() },
121
+ get switcherVisible() { return isModeSwitcherVisible() },
122
+ subscribe(fn) {
123
+ const snapshot = () => ({
124
+ mode: getCurrentMode(),
125
+ modes: getRegisteredModes(),
126
+ switcherVisible: isModeSwitcherVisible(),
127
+ })
128
+ fn(snapshot())
129
+ return subscribeToMode(() => fn(snapshot()))
130
+ },
131
+ }
132
+
117
133
  // Hook to subscribe to an external store (modeState, sidePanelState)
118
134
  function useExternalStore(store) {
119
135
  const [value, setValue] = useState(() => ({ ...store }))
@@ -2,7 +2,7 @@ import { useState, useMemo } from 'react'
2
2
  import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
3
3
  import * as DropdownMenu from './lib/components/ui/dropdown-menu/index.js'
4
4
  import * as Panel from './lib/components/ui/panel/index.js'
5
- import Icon from './svelte-plugin-ui/components/Icon.jsx'
5
+ import Icon from './Icon.jsx'
6
6
  import { isExcludedByRoute } from '@dfosco/storyboard-core'
7
7
 
8
8
  export default function CreateMenuButton({ features: featuresProp = [], data, config = { label: 'Create' }, localOnly: _localOnly, tabindex }) {
@@ -6,7 +6,7 @@
6
6
 
7
7
  import { useState, useEffect, useCallback } from 'react'
8
8
  import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
9
- import Icon from './svelte-plugin-ui/components/Icon.jsx'
9
+ import Icon from './Icon.jsx'
10
10
 
11
11
  export default function HideChromeTrigger({ config = {}, tabindex }) {
12
12
  const [hidden, setHidden] = useState(
@@ -50,14 +50,6 @@ const customIcons = {
50
50
  'M17.6103 6.5C18.2731 6.5 18.8104 7.03731 18.8104 7.70012C18.8104 8.36293 18.2731 8.90025 17.6103 8.90025H17.5986C16.9358 8.90025 16.3984 8.36293 16.3984 7.70012C16.3984 7.03731 16.9358 6.5 17.5986 6.5H17.6103Z',
51
51
  ],
52
52
  },
53
- 'flow': {
54
- viewBox: '0 0 24 24',
55
- strokeWidth: '2.5',
56
- strokePaths: [
57
- 'M13 19L22 12L13 5L13 19Z',
58
- 'M2 19L11 12L2 5L2 19Z',
59
- ],
60
- },
61
53
  'sticky-note': {
62
54
  viewBox: '0 0 14 14',
63
55
  fillRule: 'evenodd',
@@ -76,6 +68,14 @@ const customIcons = {
76
68
  viewBox: '0 0 24 24',
77
69
  path: 'm4.7144 15.9555 4.7174-2.6471.079-.2307-.079-.1275h-.2307l-.7893-.0486-2.6956-.0729-2.3375-.0971-2.2646-.1214-.5707-.1215-.5343-.7042.0546-.3522.4797-.3218.686.0608 1.5179.1032 2.2767.1578 1.6514.0972 2.4468.255h.3886l.0546-.1579-.1336-.0971-.1032-.0972L6.973 9.8356l-2.55-1.6879-1.3356-.9714-.7225-.4918-.3643-.4614-.1578-1.0078.6557-.7225.8803.0607.2246.0607.8925.686 1.9064 1.4754 2.4893 1.8336.3643.3035.1457-.1032.0182-.0728-.164-.2733-1.3539-2.4467-1.445-2.4893-.6435-1.032-.17-.6194c-.0607-.255-.1032-.4674-.1032-.7285L6.287.1335 6.6997 0l.9957.1336.419.3642.6192 1.4147 1.0018 2.2282 1.5543 3.0296.4553.8985.2429.8318.091.255h.1579v-.1457l.1275-1.706.2368-2.0947.2307-2.6957.0789-.7589.3764-.9107.7468-.4918.5828.2793.4797.686-.0668.4433-.2853 1.8517-.5586 2.9021-.3643 1.9429h.2125l.2429-.2429.9835-1.3053 1.6514-2.0643.7286-.8196.85-.9046.5464-.4311h1.0321l.759 1.1293-.34 1.1657-1.0625 1.3478-.8804 1.1414-1.2628 1.7-.7893 1.36.0729.1093.1882-.0183 2.8535-.607 1.5421-.2794 1.8396-.3157.8318.3886.091.3946-.3278.8075-1.967.4857-2.3072.4614-3.4364.8136-.0425.0304.0486.0607 1.5482.1457.6618.0364h1.621l3.0175.2247.7892.522.4736.6376-.079.4857-1.2142.6193-1.6393-.3886-3.825-.9107-1.3113-.3279h-.1822v.1093l1.0929 1.0686 2.0035 1.8092 2.5075 2.3314.1275.5768-.3218.4554-.34-.0486-2.2039-1.6575-.85-.7468-1.9246-1.621h-.1275v.17l.4432.6496 2.3436 3.5214.1214 1.0807-.17.3521-.6071.2125-.6679-.1214-1.3721-1.9246L14.38 17.959l-1.1414-1.9428-.1397.079-.674 7.2552-.3156.3703-.7286.2793-.6071-.4614-.3218-.7468.3218-1.4753.3886-1.9246.3157-1.53.2853-1.9004.17-.6314-.0121-.0425-.1397.0182-1.4328 1.9672-2.1796 2.9446-1.7243 1.8456-.4128.164-.7164-.3704.0667-.6618.4008-.5889 2.386-3.0357 1.4389-1.882.929-1.0868-.0062-.1579h-.0546l-6.3385 4.1164-1.1293.1457-.4857-.4554.0608-.7467.2307-.2429 1.9064-1.3114Z',
78
70
  },
71
+ 'flow': {
72
+ viewBox: '0 0 24 24',
73
+ strokeWidth: '2.5',
74
+ strokePaths: [
75
+ 'M13 19L22 12L13 5L13 19Z',
76
+ 'M2 19L11 12L2 5L2 19Z',
77
+ ],
78
+ },
79
79
  }
80
80
 
81
81
  /* ─── Iconoir icons (stroke-based unless fill: true) ─── */
@@ -277,5 +277,3 @@ export default function Icon({
277
277
 
278
278
  return <span className={className} style={wrapperStyle}>{svgContent}</span>
279
279
  }
280
-
281
- export { Icon }
@@ -1,7 +1,7 @@
1
1
  import { useState, useEffect } from 'react'
2
2
  import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
3
3
  import * as DropdownMenu from './lib/components/ui/dropdown-menu/index.js'
4
- import Icon from './svelte-plugin-ui/components/Icon.jsx'
4
+ import Icon from './Icon.jsx'
5
5
  import { themeState, setTheme, getTheme, THEMES, themeSyncState, getThemeSyncTargets, setThemeSyncTarget } from '@dfosco/storyboard-core'
6
6
 
7
7
  export default function ThemeMenuButton({ config = {}, data: _data, localOnly: _localOnly, tabindex = -1 }) {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Auth modal — delegates to the React PATDialog via a custom event.
3
3
  *
4
- * The Svelte AuthModal is no longer mounted directly. Instead, we dispatch
4
+ * The AuthModal is no longer mounted directly. Instead, we dispatch
5
5
  * a 'storyboard:open-auth-modal' event that the React ViewfinderNew listens for.
6
6
  */
7
7
 
@@ -117,6 +117,7 @@
117
117
  * @typedef {object} StoryboardConfig
118
118
  * @property {string} [customDomain]
119
119
  * @property {string} [devDomain]
120
+ * @property {string} [devDomainColor] — CSS color for the BranchBar in local dev (default: blue)
120
121
  * @property {{ owner: string, name: string }} [repository]
121
122
  * @property {{ enabled: boolean }} [modes]
122
123
  * @property {{ discussions: { category: string } }} [comments]
@@ -145,6 +146,7 @@ export const builtinPasteRules = [
145
146
  export const configDefaults = {
146
147
  customDomain: '',
147
148
  devDomain: '',
149
+ devDomainColor: '',
148
150
  repository: { owner: '', name: '' },
149
151
  modes: { enabled: false },
150
152
  comments: { discussions: { category: 'Comments' } },
@@ -104,7 +104,7 @@ export function clearAllOverrides() {
104
104
  // ---------------------------------------------------------------------------
105
105
 
106
106
  /**
107
- * Subscribe to config changes. Compatible with Svelte stores.
107
+ * Subscribe to config changes. Compatible with external stores.
108
108
  *
109
109
  * @param {Function} callback - Called with the full merged config
110
110
  * @returns {Function} Unsubscribe
@@ -2,8 +2,8 @@
2
2
  * Consumer-safe proxy for mountDevTools.
3
3
  *
4
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
5
+ * so consumers don't need internal dependencies. The real mountDevTools in
6
+ * devtools.js imports internal deps and is only usable in the source repo
7
7
  * or via the compiled UI bundle.
8
8
  */
9
9
 
package/src/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * storyboard-core — framework-agnostic data layer.
3
3
  *
4
4
  * This barrel exports all core utilities that have zero framework dependencies.
5
- * Any frontend (React, Vue, Svelte, Alpine, vanilla JS) can use these directly.
5
+ * Any frontend (React, Vue, Alpine, vanilla JS) can use these directly.
6
6
  */
7
7
 
8
8
  // Data index initialization
@@ -52,7 +52,7 @@ export { initTools, setToolAction, setToolState, getToolState, getToolsForMode,
52
52
 
53
53
  // Dev tools (vanilla JS, framework-agnostic)
54
54
  // mountDevTools delegates to the compiled UI bundle so consumers
55
- // don't need svelte installed — svelte is bundled into ui-runtime.
55
+
56
56
  export { mountDevTools } from './devtools-consumer.js'
57
57
  export { mountFlowDebug } from './sceneDebug.js'
58
58
  // Deprecated alias
@@ -118,7 +118,7 @@ export { trackRecent, getRecent, clearRecent } from './recentArtifacts.js'
118
118
  export { scoreMatch } from './fuzzySearch.js'
119
119
 
120
120
  // Icon component for UI rendering
121
- export { Icon, default as IconDefault } from './svelte-plugin-ui/components/Icon.jsx'
121
+ export { default as Icon, default as IconDefault } from './Icon.jsx'
122
122
 
123
123
  // Shared UI components
124
124
  export { default as BranchSelect } from './BranchSelect.jsx'
@@ -2,7 +2,7 @@
2
2
  * mountStoryboardCore — single entry point for consumer apps.
3
3
  *
4
4
  * Initializes all storyboard systems (URL state, history, comments, devtools)
5
- * and mounts the compiled Svelte UI. Consumers call this once at app startup.
5
+ * Consumers call this once at app startup.
6
6
  *
7
7
  * Usage:
8
8
  * import { mountStoryboardCore } from '@dfosco/storyboard-core'
@@ -74,7 +74,7 @@ function installChromeStatePersistence() {
74
74
 
75
75
  /**
76
76
  * Apply the saved theme to Primer CSS attributes immediately, before
77
- * React or Svelte mount. This prevents a flash of wrong-theme content.
77
+ * React mount. This prevents a flash of wrong-theme content.
78
78
  * Reads the same `sb-color-scheme` localStorage key used by themeStore.
79
79
  */
80
80
  function applyEarlyTheme() {
@@ -192,7 +192,7 @@ export async function mountStoryboardCore(config = {}, options = {}) {
192
192
  // Apply saved chrome-hidden state immediately — before React mount
193
193
  applyEarlyChromeState()
194
194
 
195
- // Apply saved theme to DOM immediately — before Svelte/React mount
195
+ // Apply saved theme to DOM immediately — before React mount
196
196
  applyEarlyTheme()
197
197
 
198
198
  // Initialize framework-agnostic systems
package/src/sidepanel.css CHANGED
@@ -87,7 +87,7 @@ html.sb-sidepanel-open.sb-sidepanel-bottom .sb-mode-switch {
87
87
  }
88
88
 
89
89
  /* -----------------------------------------------------------------------
90
- Panel element styles (migrated from Svelte scoped <style>)
90
+ Panel element styles (migrated from scoped styles)
91
91
  ----------------------------------------------------------------------- */
92
92
 
93
93
  .sb-sidepanel {
@@ -89,7 +89,7 @@ export function getToolbarConfig() {
89
89
  // ---------------------------------------------------------------------------
90
90
 
91
91
  /**
92
- * Subscribe to toolbar config changes. Compatible with Svelte stores.
92
+ * Subscribe to toolbar config changes. Compatible with external stores.
93
93
  *
94
94
  * @param {Function} callback
95
95
  * @returns {Function} Unsubscribe
@@ -1,54 +1,7 @@
1
1
  /**
2
- * Design-modes UI mount entry point.
3
- *
4
- * Call mountDesignModesUI() once at app startup to render the ModeSwitch
5
- * and ToolbarShell components. Framework-agnostic — works from any JS
6
- * context (React StoryboardProvider, vanilla JS, etc.).
7
- *
8
- * Usage:
9
- * import { mountDesignModesUI } from '@dfosco/storyboard-core/ui/design-modes'
10
- * mountDesignModesUI() // mounts to document.body
11
- * mountDesignModesUI(document.getElementById('my-container'))
2
+ * Design modes UI mount stub (Svelte UI removed).
3
+ * These functions are no-ops. Mode switching is handled by React components.
12
4
  */
13
5
 
14
- import { mountSveltePlugin, type PluginHandle } from '../svelte-plugin-ui/mount.js'
15
- import ModeSwitch from '../svelte-plugin-ui/components/ModeSwitch.jsx'
16
- // TODO: Re-enable after migrating devtools features into tool registry
17
- // import ToolbarShell from '../svelte-plugin-ui/components/ToolbarShell.jsx'
18
-
19
- let handles: PluginHandle[] = []
20
-
21
- /**
22
- * Mount the design-modes UI (ModeSwitch + ToolbarShell).
23
- *
24
- * Idempotent — calling multiple times is a no-op.
25
- * Returns a teardown function that removes both components.
26
- *
27
- * @param container - DOM element to mount into (defaults to document.body)
28
- */
29
- export function mountDesignModesUI(
30
- container?: HTMLElement,
31
- ): () => void {
32
- // Prevent double-mount
33
- if (handles.length > 0) return unmountDesignModesUI
34
-
35
- const target = container ?? document.body
36
-
37
- handles.push(
38
- mountSveltePlugin(target, ModeSwitch),
39
- // TODO: Re-enable after migrating devtools features into tool registry
40
- // mountSveltePlugin(target, ToolbarShell),
41
- )
42
-
43
- return unmountDesignModesUI
44
- }
45
-
46
- /**
47
- * Remove the design-modes UI from the DOM.
48
- */
49
- export function unmountDesignModesUI(): void {
50
- for (const h of handles) {
51
- h.destroy()
52
- }
53
- handles = []
54
- }
6
+ export function mountDesignModesUI() {}
7
+ export function unmountDesignModesUI() {}
@@ -1,58 +1,7 @@
1
1
  /**
2
- * Viewfinder UI mount entry point.
3
- *
4
- * Call mountViewfinder() to render the prototype index dashboard.
5
- * Framework-agnostic — works from any JS context (React wrapper,
6
- * vanilla JS, etc.).
7
- *
8
- * Usage:
9
- * import { mountViewfinder } from '@dfosco/storyboard-core/ui/viewfinder'
10
- * const handle = mountViewfinder(document.getElementById('root'), {
11
- * title: 'My Storyboard',
12
- * knownRoutes: ['Dashboard', 'Settings'],
13
- * })
14
- * // later: handle.destroy()
2
+ * Viewfinder mount stub (Svelte UI removed).
3
+ * These functions are no-ops. The viewfinder is rendered by React.
15
4
  */
16
5
 
17
- import { mountSveltePlugin, type PluginHandle } from '../svelte-plugin-ui/mount.js'
18
- import Viewfinder from '../svelte-plugin-ui/components/Viewfinder.jsx'
19
-
20
- export interface ViewfinderProps {
21
- title?: string
22
- subtitle?: string
23
- basePath?: string
24
- knownRoutes?: string[]
25
- showThumbnails?: boolean
26
- hideDefaultFlow?: boolean
27
- }
28
-
29
- let handle: PluginHandle | null = null
30
-
31
- /**
32
- * Mount the Viewfinder dashboard into a DOM container.
33
- *
34
- * Idempotent — calling multiple times is a no-op.
35
- * Returns a handle with destroy() method.
36
- *
37
- * @param container - DOM element to mount into
38
- * @param props - Viewfinder configuration
39
- */
40
- export function mountViewfinder(
41
- container: HTMLElement,
42
- props?: ViewfinderProps,
43
- ): PluginHandle {
44
- if (handle) return handle
45
-
46
- handle = mountSveltePlugin(container, Viewfinder as any, props as any)
47
- return handle
48
- }
49
-
50
- /**
51
- * Remove the Viewfinder from the DOM.
52
- */
53
- export function unmountViewfinder(): void {
54
- if (handle) {
55
- handle.destroy()
56
- handle = null
57
- }
58
- }
6
+ export function mountViewfinder() {}
7
+ export function unmountViewfinder() {}
package/src/ui-entry.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * UI entry point — compiled into dist/storyboard-ui.js via Vite library build.
3
3
  *
4
- * This file is the entry for the pre-compiled Svelte UI bundle.
5
- * It re-exports all UI mount functions that depend on Svelte.
4
+ * This file is the entry for the pre-compiled UI bundle.
5
+ * It re-exports UI mount functions.
6
6
  * Consumers never import this directly — they use mountStoryboardCore()
7
7
  * or the package self-reference '@dfosco/storyboard-core/ui-runtime'.
8
8
  */
@@ -20,11 +20,11 @@ import './modes.css'
20
20
  // CoreUIBar (floating toolbar)
21
21
  export { mountDevTools, unmountDevTools } from './devtools.js'
22
22
 
23
- // Comments UI (Svelte-based comment pins, windows, drawers)
23
+ // Comments UI
24
24
  export { mountComments } from './comments/ui/mount.js'
25
25
 
26
- // Viewfinder dashboard (Svelte component)
26
+ // Viewfinder dashboard
27
27
  export { mountViewfinder, unmountViewfinder } from './ui/viewfinder.ts'
28
28
 
29
- // Design modes (Svelte component)
29
+ // Design modes
30
30
  export { mountDesignModesUI as mountDesignModes } from './ui/design-modes.ts'
@@ -567,6 +567,15 @@ export default function storyboardServer() {
567
567
  injectTo: 'head',
568
568
  })
569
569
 
570
+ // Inject per-domain branch bar color (configurable via devDomainColor)
571
+ if (config.devDomainColor) {
572
+ tags.push({
573
+ tag: 'script',
574
+ children: `window.__SB_DEV_DOMAIN_COLOR__=${JSON.stringify(config.devDomainColor)}`,
575
+ injectTo: 'head',
576
+ })
577
+ }
578
+
570
579
  // Browser error bridge — forwards console.error/warn and uncaught
571
580
  // exceptions to the dev server via HMR for structured o11y logging
572
581
  tags.push({
@@ -6,7 +6,7 @@
6
6
  * - label: Display name for the menu item
7
7
  * - icon: Emoji or HTML for the menu icon
8
8
  * - overlayId: Unique ID for the overlay
9
- * - overlay: Svelte component rendered in the overlay
9
+ * - overlay: Component rendered in the overlay
10
10
  * - serverSetup: Called by the server plugin to register API routes
11
11
  */
12
12
 
@@ -6,7 +6,7 @@
6
6
  * - label: Display name for the menu item
7
7
  * - icon: Emoji or HTML for the menu icon
8
8
  * - overlayId: Unique ID for the overlay
9
- * - overlay: Svelte component rendered in the overlay
9
+ * - overlay: Component rendered in the overlay
10
10
  * - serverSetup: Called by the server plugin to register API routes
11
11
  */
12
12
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Workshop feature server registry.
3
3
  *
4
- * Server-safe registry that only imports server handlers (no Svelte/UI code).
4
+ * Server-safe registry that only imports server handlers (no UI code).
5
5
  * Used by the Vite server plugin to wire API routes.
6
6
  *
7
7
  * To add a new feature:
@@ -1,68 +1,6 @@
1
1
  /**
2
- * Workshop Dev Panel — Svelte-based mount entry point.
3
- *
4
- * Replaces the Alpine.js-based mount.js. Uses mountSveltePlugin() to render
5
- * the WorkshopPanel component with enabled features from the registry.
6
- *
7
- * Injected into the page by the storyboard server plugin via transformIndexHtml.
2
+ * Workshop panel mountstub (Svelte UI removed).
8
3
  */
9
4
 
10
- import { mountSveltePlugin, type PluginHandle } from '../../svelte-plugin-ui/mount.js'
11
- import WorkshopPanel from './WorkshopPanel.jsx'
12
- import { features as allFeatures } from '../features/registry.js'
13
- import '../../../dist/tailwind.css'
14
-
15
- let handle: PluginHandle | null = null
16
-
17
- /**
18
- * Resolve which features are enabled from a data attribute on the script tag,
19
- * injected by the server plugin. Falls back to all features enabled.
20
- */
21
- function getEnabledFeatures(): Record<string, boolean> {
22
- const script = document.querySelector('script[data-workshop-features]')
23
- if (script) {
24
- try {
25
- return JSON.parse((script as HTMLElement).dataset.workshopFeatures!)
26
- } catch { /* ignore */ }
27
- }
28
- return Object.fromEntries(Object.keys(allFeatures).map((k) => [k, true]))
29
- }
30
-
31
- /**
32
- * Mount the Workshop panel into the page.
33
- *
34
- * Idempotent — calling multiple times is a no-op.
35
- */
36
- export function mountWorkshop(): void {
37
- if (handle) return
38
-
39
- const enabledConfig = getEnabledFeatures()
40
-
41
- const features = Object.entries(allFeatures)
42
- .filter(([name]) => enabledConfig[name] !== false)
43
- .filter(([, f]) => f.label && f.overlayId && f.overlay)
44
- .map(([, f]) => ({
45
- name: f.name,
46
- label: f.label,
47
- icon: f.icon,
48
- overlayId: f.overlayId,
49
- overlay: f.overlay,
50
- }))
51
-
52
- handle = mountSveltePlugin(document.body, WorkshopPanel as any, { features } as any)
53
- }
54
-
55
- /**
56
- * Remove the Workshop panel from the DOM.
57
- */
58
- export function unmountWorkshop(): void {
59
- if (handle) {
60
- handle.destroy()
61
- handle = null
62
- }
63
- }
64
-
65
- // Auto-mount removed — CoreUIBar now owns the workshop UI.
66
- // This module is still loaded by the server plugin for the
67
- // data-workshop-features attribute, but does not self-mount.
68
- // mountWorkshop()
5
+ export function mountWorkshopPanel() {}
6
+ export function unmountWorkshopPanel() {}
@@ -1 +0,0 @@
1
- export default {}
@@ -1,75 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest'
2
- import { render, screen } from '@testing-library/react'
3
- import { userEvent } from '@testing-library/user-event'
4
- import React from 'react'
5
- import {
6
- registerMode,
7
- activateMode,
8
- initModesConfig,
9
- } from '@dfosco/storyboard-core'
10
- import { _resetModes } from '@test/modes'
11
- import ModeSwitch from '../components/ModeSwitch.jsx'
12
-
13
- afterEach(() => {
14
- _resetModes()
15
- const url = new URL(window.location.href)
16
- url.searchParams.delete('mode')
17
- window.history.replaceState(null, '', url.toString())
18
- })
19
-
20
- describe('ModeSwitch', () => {
21
- it('renders nothing when fewer than 2 modes registered', () => {
22
- registerMode('prototype', { label: 'Navigate' })
23
- const { container } = render(React.createElement(ModeSwitch))
24
- expect(container.querySelector('[role="tablist"]')).toBeNull()
25
- })
26
-
27
- it('renders a tab for each registered mode', () => {
28
- initModesConfig({ enabled: true })
29
- registerMode('prototype', { label: 'Navigate' })
30
- registerMode('inspect', { label: 'Develop' })
31
- registerMode('present', { label: 'Collaborate' })
32
-
33
- render(React.createElement(ModeSwitch))
34
-
35
- expect(screen.getByRole('tab', { name: 'Navigate' })).toBeInTheDocument()
36
- expect(screen.getByRole('tab', { name: 'Develop' })).toBeInTheDocument()
37
- expect(screen.getByRole('tab', { name: 'Collaborate' })).toBeInTheDocument()
38
- })
39
-
40
- it('marks the active mode tab as selected', () => {
41
- initModesConfig({ enabled: true })
42
- registerMode('prototype', { label: 'Navigate' })
43
- registerMode('inspect', { label: 'Develop' })
44
-
45
- render(React.createElement(ModeSwitch))
46
-
47
- const navigateTab = screen.getByRole('tab', { name: 'Navigate' })
48
- const developTab = screen.getByRole('tab', { name: 'Develop' })
49
-
50
- expect(navigateTab).toHaveAttribute('aria-selected', 'true')
51
- expect(developTab).toHaveAttribute('aria-selected', 'false')
52
- })
53
-
54
- it('switches mode on button click', async () => {
55
- initModesConfig({ enabled: true })
56
- registerMode('prototype', { label: 'Navigate' })
57
- registerMode('inspect', { label: 'Develop' })
58
-
59
- render(React.createElement(ModeSwitch))
60
- const user = userEvent.setup()
61
- await user.click(screen.getByRole('tab', { name: 'Develop' }))
62
-
63
- expect(screen.getByRole('tab', { name: 'Develop' })).toHaveAttribute('aria-selected', 'true')
64
- expect(screen.getByRole('tab', { name: 'Navigate' })).toHaveAttribute('aria-selected', 'false')
65
- })
66
-
67
- it('renders nothing when modes are locked', () => {
68
- registerMode('prototype', { label: 'Navigate' })
69
- registerMode('inspect', { label: 'Develop' })
70
- initModesConfig({ enabled: true, locked: 'prototype' })
71
-
72
- const { container } = render(React.createElement(ModeSwitch))
73
- expect(container.querySelector('[role="tablist"]')).toBeNull()
74
- })
75
- })