@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.
- package/dist/storyboard-ui.css +1 -1
- package/dist/storyboard-ui.js +7973 -8418
- package/dist/storyboard-ui.js.map +1 -1
- package/package.json +2 -12
- package/scaffold/AGENTS.md +432 -0
- package/scaffold/gitignore +64 -0
- package/scaffold/manifest.json +13 -8
- package/src/ActionMenuButton.jsx +1 -1
- package/src/AutosyncMenuButton.jsx +1 -1
- package/src/CanvasAgentsMenu.jsx +1 -1
- package/src/CanvasCreateMenu.jsx +1 -1
- package/src/CanvasSnap.jsx +1 -1
- package/src/CanvasZoomToFit.jsx +1 -1
- package/src/CommandMenu.jsx +2 -2
- package/src/CommandPalette.jsx +1 -1
- package/src/CommandPaletteTrigger.jsx +1 -1
- package/src/CommentsMenuButton.jsx +1 -1
- package/src/CoreUIBar.jsx +18 -2
- package/src/CreateMenuButton.jsx +1 -1
- package/src/HideChromeTrigger.jsx +1 -1
- package/src/{svelte-plugin-ui/components/Icon.jsx → Icon.jsx} +8 -10
- package/src/ThemeMenuButton.jsx +1 -1
- package/src/comments/ui/authModal.js +1 -1
- package/src/configSchema.js +2 -0
- package/src/configStore.js +1 -1
- package/src/devtools-consumer.js +2 -2
- package/src/index.js +3 -3
- package/src/mountStoryboardCore.js +3 -3
- package/src/sidepanel.css +1 -1
- package/src/toolbarConfigStore.js +1 -1
- package/src/ui/design-modes.ts +4 -51
- package/src/ui/viewfinder.ts +4 -55
- package/src/ui-entry.js +5 -5
- package/src/vite/server-plugin.js +9 -0
- package/src/workshop/features/createFlow/index.js +1 -1
- package/src/workshop/features/createPrototype/index.js +1 -1
- package/src/workshop/features/registry-server.js +1 -1
- package/src/workshop/ui/mount.ts +3 -65
- package/scaffold/svelte.config.js +0 -1
- package/src/svelte-plugin-ui/__tests__/ModeSwitch.test.ts +0 -75
- package/src/svelte-plugin-ui/__tests__/ToolbarShell.test.ts +0 -126
- package/src/svelte-plugin-ui/__tests__/designModes.test.ts +0 -58
- package/src/svelte-plugin-ui/__tests__/modeStore.test.ts +0 -53
- package/src/svelte-plugin-ui/__tests__/mount.test.ts +0 -29
- package/src/svelte-plugin-ui/components/Icon.css +0 -11
- package/src/svelte-plugin-ui/components/ModeSwitch.css +0 -90
- package/src/svelte-plugin-ui/components/ModeSwitch.jsx +0 -47
- package/src/svelte-plugin-ui/components/ToolbarShell.css +0 -80
- package/src/svelte-plugin-ui/components/ToolbarShell.jsx +0 -84
- package/src/svelte-plugin-ui/components/Viewfinder.css +0 -412
- package/src/svelte-plugin-ui/components/Viewfinder.jsx +0 -513
- package/src/svelte-plugin-ui/index.ts +0 -20
- package/src/svelte-plugin-ui/mount.ts +0 -120
- package/src/svelte-plugin-ui/stores/modeStore.ts +0 -91
- package/src/svelte-plugin-ui/stores/toolStore.ts +0 -71
- package/src/svelte-plugin-ui/stores/types.ts +0 -55
- package/src/svelte-plugin-ui/styles/base.css +0 -69
package/src/CommandMenu.jsx
CHANGED
|
@@ -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 './
|
|
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 './
|
|
17
|
+
import { getCurrentMode, subscribeToMode } from './modes.js'
|
|
18
18
|
|
|
19
19
|
const localDotStyle = {
|
|
20
20
|
display: 'inline-block',
|
package/src/CommandPalette.jsx
CHANGED
|
@@ -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 './
|
|
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 './
|
|
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 './
|
|
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 './
|
|
20
|
-
import {
|
|
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 }))
|
package/src/CreateMenuButton.jsx
CHANGED
|
@@ -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 './
|
|
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 './
|
|
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 }
|
package/src/ThemeMenuButton.jsx
CHANGED
|
@@ -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 './
|
|
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
|
|
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
|
|
package/src/configSchema.js
CHANGED
|
@@ -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' } },
|
package/src/configStore.js
CHANGED
|
@@ -104,7 +104,7 @@ export function clearAllOverrides() {
|
|
|
104
104
|
// ---------------------------------------------------------------------------
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
* Subscribe to config changes. Compatible with
|
|
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
|
package/src/devtools-consumer.js
CHANGED
|
@@ -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
|
|
6
|
-
* devtools.js imports
|
|
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,
|
|
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
|
-
|
|
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 './
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
92
|
+
* Subscribe to toolbar config changes. Compatible with external stores.
|
|
93
93
|
*
|
|
94
94
|
* @param {Function} callback
|
|
95
95
|
* @returns {Function} Unsubscribe
|
package/src/ui/design-modes.ts
CHANGED
|
@@ -1,54 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Design
|
|
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
|
-
|
|
15
|
-
|
|
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() {}
|
package/src/ui/viewfinder.ts
CHANGED
|
@@ -1,58 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Viewfinder
|
|
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
|
-
|
|
18
|
-
|
|
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
|
|
5
|
-
* It re-exports
|
|
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
|
|
23
|
+
// Comments UI
|
|
24
24
|
export { mountComments } from './comments/ui/mount.js'
|
|
25
25
|
|
|
26
|
-
// Viewfinder dashboard
|
|
26
|
+
// Viewfinder dashboard
|
|
27
27
|
export { mountViewfinder, unmountViewfinder } from './ui/viewfinder.ts'
|
|
28
28
|
|
|
29
|
-
// Design modes
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
package/src/workshop/ui/mount.ts
CHANGED
|
@@ -1,68 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workshop
|
|
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 mount — stub (Svelte UI removed).
|
|
8
3
|
*/
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
})
|