@dfosco/storyboard-core 4.2.0-beta.3 → 4.2.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.
- package/commandpalette.config.json +109 -24
- package/dist/storyboard-ui.css +1 -1
- package/dist/storyboard-ui.js +16711 -27239
- package/dist/storyboard-ui.js.map +1 -1
- package/dist/tailwind.css +1 -1
- package/package.json +5 -2
- package/scaffold/agents/prompt-agent.agent.md +181 -0
- package/scaffold/agents/terminal-agent.agent.md +351 -0
- package/scaffold/codex/config.toml +246 -0
- package/scaffold/manifest.json +5 -10
- package/scaffold/skills/canvas/SKILL.md +5 -4
- package/scaffold/skills/ship/SKILL.md +1 -1
- package/scaffold/storyboard.config.json +11 -1
- package/src/ActionMenuButton.jsx +103 -0
- package/src/AutosyncMenuButton.css +67 -0
- package/src/AutosyncMenuButton.jsx +242 -0
- package/src/BranchSelect.jsx +29 -0
- package/src/BranchSelect.module.css +30 -0
- package/src/CanvasAgentsMenu.jsx +89 -0
- package/src/CanvasCreateMenu.jsx +611 -0
- package/src/CanvasSnap.css +27 -0
- package/src/CanvasSnap.jsx +51 -0
- package/src/CanvasUndoRedo.css +36 -0
- package/src/CanvasUndoRedo.jsx +62 -0
- package/src/CanvasZoomControl.css +53 -0
- package/src/CanvasZoomControl.jsx +49 -0
- package/src/CanvasZoomToFit.css +18 -0
- package/src/CanvasZoomToFit.jsx +26 -0
- package/src/CommandMenu.css +8 -0
- package/src/CommandMenu.jsx +287 -0
- package/src/CommandPalette.jsx +35 -0
- package/src/CommandPaletteTrigger.jsx +25 -0
- package/src/CommentsMenuButton.jsx +40 -0
- package/src/CoreUIBar.css +47 -0
- package/src/CoreUIBar.jsx +858 -0
- package/src/CreateMenuButton.jsx +117 -0
- package/src/HideChromeTrigger.jsx +40 -0
- package/src/InspectorPanel.css +109 -0
- package/src/InspectorPanel.jsx +632 -0
- package/src/PwaInstallBanner.css +42 -0
- package/src/PwaInstallBanner.jsx +124 -0
- package/src/SidePanel.jsx +261 -0
- package/src/ThemeMenuButton.jsx +139 -0
- package/src/autosync/server.js +202 -5
- package/src/autosync/server.test.js +111 -0
- package/src/canvas/__tests__/agent-integration.test.js +596 -0
- package/src/canvas/__tests__/helpers/browser.js +95 -0
- package/src/canvas/__tests__/helpers/canvas-api.js +129 -0
- package/src/canvas/__tests__/helpers/perf.js +118 -0
- package/src/canvas/__tests__/helpers/setup.js +176 -0
- package/src/canvas/__tests__/helpers/tmux.js +130 -0
- package/src/canvas/__tests__/helpers/transcript.js +132 -0
- package/src/canvas/__tests__/terminal-integration.test.js +177 -0
- package/src/canvas/hot-pool.js +756 -0
- package/src/canvas/materializer.js +31 -0
- package/src/canvas/materializer.test.js +56 -0
- package/src/canvas/selectedWidgets.js +65 -7
- package/src/canvas/server.js +1802 -22
- package/src/canvas/server.test.js +239 -0
- package/src/canvas/terminal-config.js +330 -0
- package/src/canvas/terminal-registry.js +41 -3
- package/src/canvas/terminal-server.js +1098 -37
- package/src/canvas/writeGuard.js +51 -3
- package/src/canvasConfig.js +67 -1
- package/src/canvasConfig.test.js +79 -1
- package/src/cli/agent.js +85 -0
- package/src/cli/branch.js +232 -0
- package/src/cli/canvasAdd.js +61 -14
- package/src/cli/canvasBatch.js +98 -0
- package/src/cli/canvasBounds.js +1 -1
- package/src/cli/canvasRead.js +1 -1
- package/src/cli/canvasUpdate.js +179 -0
- package/src/cli/compact.js +0 -2
- package/src/cli/create.js +40 -16
- package/src/cli/dev.js +158 -84
- package/src/cli/exit.js +23 -24
- package/src/cli/index.js +55 -2
- package/src/cli/proxy.js +96 -37
- package/src/cli/schemas.js +22 -4
- package/src/cli/server.js +149 -26
- package/src/cli/serverUrl.js +8 -3
- package/src/cli/sessions.js +133 -7
- package/src/cli/setup.js +138 -12
- package/src/cli/terminal-commands.js +20 -9
- package/src/cli/terminal-messaging.js +231 -0
- package/src/cli/terminal-welcome.js +449 -34
- package/src/cli/updateVersion.js +1 -1
- package/src/commandActions.js +1 -0
- package/src/commandPaletteConfig.js +9 -0
- package/src/comments/auth.js +2 -1
- package/src/comments/ui/AuthModal.jsx +114 -0
- package/src/comments/ui/CommentWindow.jsx +329 -0
- package/src/comments/ui/CommentsDrawer.jsx +102 -0
- package/src/comments/ui/Composer.jsx +64 -0
- package/src/comments/ui/authModal.test.js +1 -1
- package/src/comments/ui/commentWindow.js +16 -17
- package/src/comments/ui/commentsDrawer.js +25 -26
- package/src/comments/ui/composer.js +23 -24
- package/src/comments/ui/index.js +2 -3
- package/src/configSchema.js +59 -1
- package/src/configStore.js +161 -0
- package/src/core-ui-colors.css +12 -0
- package/src/devtools.js +17 -19
- package/src/devtools.test.js +18 -9
- package/src/featureFlags.js +12 -5
- package/src/fuzzySearch.test.js +10 -0
- package/src/index.js +15 -3
- package/src/lib/components/ui/alert/alert-action.jsx +11 -0
- package/src/lib/components/ui/alert/alert-description.jsx +11 -0
- package/src/lib/components/ui/alert/alert-title.jsx +11 -0
- package/src/lib/components/ui/alert/alert.jsx +25 -0
- package/src/lib/components/ui/alert/index.js +15 -15
- package/src/lib/components/ui/avatar/avatar-badge.jsx +22 -0
- package/src/lib/components/ui/avatar/avatar-fallback.jsx +18 -0
- package/src/lib/components/ui/avatar/avatar-group-count.jsx +19 -0
- package/src/lib/components/ui/avatar/avatar-group.jsx +19 -0
- package/src/lib/components/ui/avatar/avatar-image.jsx +15 -0
- package/src/lib/components/ui/avatar/avatar.jsx +19 -0
- package/src/lib/components/ui/avatar/index.js +20 -20
- package/src/lib/components/ui/badge/badge.jsx +31 -0
- package/src/lib/components/ui/badge/index.js +2 -2
- package/src/lib/components/ui/button/button.jsx +100 -0
- package/src/lib/components/ui/button/index.js +9 -9
- package/src/lib/components/ui/card/card-action.jsx +11 -0
- package/src/lib/components/ui/card/card-content.jsx +11 -0
- package/src/lib/components/ui/card/card-description.jsx +11 -0
- package/src/lib/components/ui/card/card-footer.jsx +11 -0
- package/src/lib/components/ui/card/card-header.jsx +19 -0
- package/src/lib/components/ui/card/card-title.jsx +11 -0
- package/src/lib/components/ui/card/card.jsx +17 -0
- package/src/lib/components/ui/card/index.js +23 -23
- package/src/lib/components/ui/checkbox/checkbox.jsx +29 -0
- package/src/lib/components/ui/checkbox/index.js +5 -5
- package/src/lib/components/ui/collapsible/collapsible-content.jsx +7 -0
- package/src/lib/components/ui/collapsible/collapsible-trigger.jsx +7 -0
- package/src/lib/components/ui/collapsible/collapsible.jsx +7 -0
- package/src/lib/components/ui/collapsible/index.js +11 -11
- package/src/lib/components/ui/dialog/dialog-close.jsx +7 -0
- package/src/lib/components/ui/dialog/dialog-content.jsx +34 -0
- package/src/lib/components/ui/dialog/dialog-description.jsx +15 -0
- package/src/lib/components/ui/dialog/dialog-footer.jsx +23 -0
- package/src/lib/components/ui/dialog/dialog-header.jsx +11 -0
- package/src/lib/components/ui/dialog/dialog-overlay.jsx +15 -0
- package/src/lib/components/ui/dialog/dialog-portal.jsx +4 -0
- package/src/lib/components/ui/dialog/dialog-title.jsx +15 -0
- package/src/lib/components/ui/dialog/dialog-trigger.jsx +7 -0
- package/src/lib/components/ui/dialog/dialog.jsx +4 -0
- package/src/lib/components/ui/dialog/index.js +32 -32
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +8 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +30 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +22 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +16 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +20 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.jsx +17 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +4 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +29 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +15 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.jsx +16 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +15 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +23 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +4 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +7 -0
- package/src/lib/components/ui/dropdown-menu/dropdown-menu.jsx +4 -0
- package/src/lib/components/ui/dropdown-menu/index.js +52 -52
- package/src/lib/components/ui/input/index.js +5 -5
- package/src/lib/components/ui/input/input.jsx +19 -0
- package/src/lib/components/ui/label/index.js +5 -5
- package/src/lib/components/ui/label/label.jsx +19 -0
- package/src/lib/components/ui/panel/index.js +21 -21
- package/src/lib/components/ui/panel/panel-body.jsx +11 -0
- package/src/lib/components/ui/panel/panel-close.jsx +16 -0
- package/src/lib/components/ui/panel/panel-content.jsx +29 -0
- package/src/lib/components/ui/panel/panel-footer.jsx +11 -0
- package/src/lib/components/ui/panel/panel-header.jsx +11 -0
- package/src/lib/components/ui/panel/panel-title.jsx +12 -0
- package/src/lib/components/ui/panel/panel.jsx +4 -0
- package/src/lib/components/ui/popover/index.js +26 -26
- package/src/lib/components/ui/popover/popover-close.jsx +7 -0
- package/src/lib/components/ui/popover/popover-content.jsx +22 -0
- package/src/lib/components/ui/popover/popover-description.jsx +11 -0
- package/src/lib/components/ui/popover/popover-header.jsx +11 -0
- package/src/lib/components/ui/popover/popover-portal.jsx +4 -0
- package/src/lib/components/ui/popover/popover-title.jsx +11 -0
- package/src/lib/components/ui/popover/popover-trigger.jsx +8 -0
- package/src/lib/components/ui/popover/popover.jsx +4 -0
- package/src/lib/components/ui/searchable-list.jsx +160 -0
- package/src/lib/components/ui/select/index.js +35 -35
- package/src/lib/components/ui/select/select-content.jsx +30 -0
- package/src/lib/components/ui/select/select-group-heading.jsx +17 -0
- package/src/lib/components/ui/select/select-group.jsx +15 -0
- package/src/lib/components/ui/select/select-item.jsx +26 -0
- package/src/lib/components/ui/select/select-label.jsx +11 -0
- package/src/lib/components/ui/select/select-portal.jsx +4 -0
- package/src/lib/components/ui/select/select-scroll-down-button.jsx +18 -0
- package/src/lib/components/ui/select/select-scroll-up-button.jsx +18 -0
- package/src/lib/components/ui/select/select-separator.jsx +15 -0
- package/src/lib/components/ui/select/select-trigger.jsx +25 -0
- package/src/lib/components/ui/select/select.jsx +4 -0
- package/src/lib/components/ui/separator/index.js +5 -5
- package/src/lib/components/ui/separator/separator.jsx +22 -0
- package/src/lib/components/ui/sheet/index.js +32 -32
- package/src/lib/components/ui/sheet/sheet-close.jsx +7 -0
- package/src/lib/components/ui/sheet/sheet-content.jsx +35 -0
- package/src/lib/components/ui/sheet/sheet-description.jsx +15 -0
- package/src/lib/components/ui/sheet/sheet-footer.jsx +11 -0
- package/src/lib/components/ui/sheet/sheet-header.jsx +11 -0
- package/src/lib/components/ui/sheet/sheet-overlay.jsx +15 -0
- package/src/lib/components/ui/sheet/sheet-portal.jsx +4 -0
- package/src/lib/components/ui/sheet/sheet-title.jsx +15 -0
- package/src/lib/components/ui/sheet/sheet-trigger.jsx +7 -0
- package/src/lib/components/ui/sheet/sheet.jsx +4 -0
- package/src/lib/components/ui/textarea/index.js +5 -5
- package/src/lib/components/ui/textarea/textarea.jsx +18 -0
- package/src/lib/components/ui/toggle/index.js +6 -9
- package/src/lib/components/ui/toggle/toggle.jsx +36 -0
- package/src/lib/components/ui/toggle-group/index.js +8 -8
- package/src/lib/components/ui/toggle-group/toggle-group-item.jsx +29 -0
- package/src/lib/components/ui/toggle-group/toggle-group.jsx +43 -0
- package/src/lib/components/ui/tooltip/index.js +3 -3
- package/src/lib/components/ui/tooltip/tooltip-content.jsx +21 -0
- package/src/lib/components/ui/tooltip/tooltip-trigger.jsx +23 -0
- package/src/lib/components/ui/tooltip/tooltip.jsx +11 -0
- package/src/lib/components/ui/trigger-button/index.js +3 -3
- package/src/lib/components/ui/trigger-button/trigger-button.css +38 -0
- package/src/lib/components/ui/trigger-button/trigger-button.jsx +63 -0
- package/src/logger/devLogger.js +238 -0
- package/src/logger/devLogger.test.js +193 -0
- package/src/modes.test.js +4 -4
- package/src/mountStoryboardCore.js +133 -35
- package/src/paletteProviders.js +3 -0
- package/src/paletteProviders.test.js +2 -2
- package/src/server/index.js +104 -40
- package/src/sidepanel.css +214 -0
- package/src/smoothCorners.js +1 -1
- package/src/styles/tailwind.css +1 -1
- package/src/svelte-plugin-ui/__tests__/ModeSwitch.test.ts +8 -8
- package/src/svelte-plugin-ui/__tests__/ToolbarShell.test.ts +11 -10
- package/src/svelte-plugin-ui/components/Icon.css +11 -0
- package/src/svelte-plugin-ui/components/Icon.jsx +281 -0
- package/src/svelte-plugin-ui/components/ModeSwitch.css +90 -0
- package/src/svelte-plugin-ui/components/ModeSwitch.jsx +47 -0
- package/src/svelte-plugin-ui/components/ToolbarShell.css +80 -0
- package/src/svelte-plugin-ui/components/ToolbarShell.jsx +84 -0
- package/src/svelte-plugin-ui/components/Viewfinder.css +412 -0
- package/src/svelte-plugin-ui/components/Viewfinder.jsx +513 -0
- package/src/svelte-plugin-ui/mount.ts +12 -16
- package/src/toolRegistry.js +4 -4
- package/src/toolbarConfigStore.js +30 -0
- package/src/tools/handlers/autosync.js +1 -1
- package/src/tools/handlers/canvasAddWidget.js +1 -1
- package/src/tools/handlers/canvasAgents.js +20 -0
- package/src/tools/handlers/canvasToolbar.js +8 -8
- package/src/tools/handlers/commandPalette.js +9 -0
- package/src/tools/handlers/comments.js +2 -2
- package/src/tools/handlers/create.js +1 -1
- package/src/tools/handlers/devtools.js +19 -3
- package/src/tools/handlers/devtools.test.js +38 -0
- package/src/tools/handlers/featureFlags.js +1 -1
- package/src/tools/handlers/flows.js +3 -3
- package/src/tools/handlers/hideChrome.js +9 -0
- package/src/tools/handlers/paletteTheme.js +35 -0
- package/src/tools/handlers/theme.js +1 -1
- package/src/tools/registry.js +4 -1
- package/src/tools/surfaces/commandList.js +3 -3
- package/src/tools/surfaces/mainToolbar.js +3 -3
- package/src/tools/surfaces/registry.js +4 -4
- package/src/ui/design-modes.ts +2 -2
- package/src/ui/viewfinder.ts +1 -1
- package/src/vite/server-plugin.js +243 -61
- package/src/workshop/features/createCanvas/CreateCanvasForm.jsx +260 -0
- package/src/workshop/features/createCanvas/index.js +1 -1
- package/src/workshop/features/createFlow/CreateFlowForm.jsx +334 -0
- package/src/workshop/features/createFlow/index.js +1 -1
- package/src/workshop/features/createPage/CreatePageForm.jsx +304 -0
- package/src/workshop/features/createPage/index.js +1 -1
- package/src/workshop/features/createPrototype/CreatePrototypeForm.jsx +289 -0
- package/src/workshop/features/createPrototype/index.js +1 -1
- package/src/workshop/features/createPrototype/server.js +98 -0
- package/src/workshop/features/createStory/CreateStoryForm.jsx +208 -0
- package/src/workshop/features/createStory/index.js +1 -1
- package/src/workshop/ui/WorkshopPanel.jsx +98 -0
- package/src/workshop/ui/mount.ts +1 -1
- package/src/worktree/port.js +48 -0
- package/src/worktree/serverRegistry.js +120 -0
- package/toolbar.config.json +93 -42
- package/widgets.config.json +580 -12
- package/scaffold/commandpalette.config.json +0 -4
- package/scaffold/toolbar.config.json +0 -4
- package/src/ActionMenuButton.svelte +0 -119
- package/src/AutosyncMenuButton.svelte +0 -397
- package/src/CanvasCreateMenu.svelte +0 -295
- package/src/CanvasSnap.svelte +0 -87
- package/src/CanvasUndoRedo.svelte +0 -108
- package/src/CanvasZoomControl.svelte +0 -111
- package/src/CanvasZoomToFit.svelte +0 -52
- package/src/CommandMenu.svelte +0 -249
- package/src/CommandPalette.svelte +0 -33
- package/src/CommentsMenuButton.svelte +0 -53
- package/src/CoreUIBar.svelte +0 -847
- package/src/CreateMenuButton.svelte +0 -133
- package/src/DocPanel.svelte +0 -299
- package/src/InspectorPanel.svelte +0 -745
- package/src/PwaInstallBanner.svelte +0 -124
- package/src/SidePanel.svelte +0 -480
- package/src/ThemeMenuButton.svelte +0 -132
- package/src/comments/ui/AuthModal.svelte +0 -108
- package/src/comments/ui/CommentWindow.svelte +0 -333
- package/src/comments/ui/CommentsDrawer.svelte +0 -96
- package/src/comments/ui/Composer.svelte +0 -65
- package/src/lib/components/ui/alert/alert-action.svelte +0 -19
- package/src/lib/components/ui/alert/alert-description.svelte +0 -22
- package/src/lib/components/ui/alert/alert-title.svelte +0 -22
- package/src/lib/components/ui/alert/alert.svelte +0 -38
- package/src/lib/components/ui/avatar/avatar-badge.svelte +0 -25
- package/src/lib/components/ui/avatar/avatar-fallback.svelte +0 -20
- package/src/lib/components/ui/avatar/avatar-group-count.svelte +0 -22
- package/src/lib/components/ui/avatar/avatar-group.svelte +0 -22
- package/src/lib/components/ui/avatar/avatar-image.svelte +0 -17
- package/src/lib/components/ui/avatar/avatar.svelte +0 -24
- package/src/lib/components/ui/badge/badge.svelte +0 -44
- package/src/lib/components/ui/button/button.svelte +0 -108
- package/src/lib/components/ui/card/card-action.svelte +0 -21
- package/src/lib/components/ui/card/card-content.svelte +0 -19
- package/src/lib/components/ui/card/card-description.svelte +0 -19
- package/src/lib/components/ui/card/card-footer.svelte +0 -18
- package/src/lib/components/ui/card/card-header.svelte +0 -21
- package/src/lib/components/ui/card/card-title.svelte +0 -14
- package/src/lib/components/ui/card/card.svelte +0 -21
- package/src/lib/components/ui/checkbox/checkbox.svelte +0 -39
- package/src/lib/components/ui/collapsible/collapsible-content.svelte +0 -7
- package/src/lib/components/ui/collapsible/collapsible-trigger.svelte +0 -7
- package/src/lib/components/ui/collapsible/collapsible.svelte +0 -11
- package/src/lib/components/ui/dialog/dialog-close.svelte +0 -11
- package/src/lib/components/ui/dialog/dialog-content.svelte +0 -42
- package/src/lib/components/ui/dialog/dialog-description.svelte +0 -17
- package/src/lib/components/ui/dialog/dialog-footer.svelte +0 -29
- package/src/lib/components/ui/dialog/dialog-header.svelte +0 -19
- package/src/lib/components/ui/dialog/dialog-overlay.svelte +0 -17
- package/src/lib/components/ui/dialog/dialog-portal.svelte +0 -7
- package/src/lib/components/ui/dialog/dialog-title.svelte +0 -17
- package/src/lib/components/ui/dialog/dialog-trigger.svelte +0 -11
- package/src/lib/components/ui/dialog/dialog.svelte +0 -7
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +0 -16
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -40
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +0 -27
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +0 -18
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +0 -7
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +0 -24
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +0 -20
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +0 -7
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +0 -16
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +0 -34
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +0 -17
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +0 -19
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +0 -17
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +0 -27
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +0 -7
- package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +0 -7
- package/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +0 -7
- package/src/lib/components/ui/input/input.svelte +0 -40
- package/src/lib/components/ui/label/label.svelte +0 -20
- package/src/lib/components/ui/panel/panel-body.svelte +0 -13
- package/src/lib/components/ui/panel/panel-close.svelte +0 -16
- package/src/lib/components/ui/panel/panel-content.svelte +0 -33
- package/src/lib/components/ui/panel/panel-footer.svelte +0 -13
- package/src/lib/components/ui/panel/panel-header.svelte +0 -16
- package/src/lib/components/ui/panel/panel-title.svelte +0 -14
- package/src/lib/components/ui/panel/panel.svelte +0 -15
- package/src/lib/components/ui/popover/popover-close.svelte +0 -7
- package/src/lib/components/ui/popover/popover-content.svelte +0 -27
- package/src/lib/components/ui/popover/popover-description.svelte +0 -19
- package/src/lib/components/ui/popover/popover-header.svelte +0 -19
- package/src/lib/components/ui/popover/popover-portal.svelte +0 -7
- package/src/lib/components/ui/popover/popover-title.svelte +0 -19
- package/src/lib/components/ui/popover/popover-trigger.svelte +0 -17
- package/src/lib/components/ui/popover/popover.svelte +0 -7
- package/src/lib/components/ui/select/select-content.svelte +0 -40
- package/src/lib/components/ui/select/select-group-heading.svelte +0 -19
- package/src/lib/components/ui/select/select-group.svelte +0 -17
- package/src/lib/components/ui/select/select-item.svelte +0 -38
- package/src/lib/components/ui/select/select-label.svelte +0 -18
- package/src/lib/components/ui/select/select-portal.svelte +0 -7
- package/src/lib/components/ui/select/select-scroll-down-button.svelte +0 -20
- package/src/lib/components/ui/select/select-scroll-up-button.svelte +0 -20
- package/src/lib/components/ui/select/select-separator.svelte +0 -17
- package/src/lib/components/ui/select/select-trigger.svelte +0 -27
- package/src/lib/components/ui/select/select.svelte +0 -11
- package/src/lib/components/ui/separator/separator.svelte +0 -23
- package/src/lib/components/ui/sheet/sheet-close.svelte +0 -7
- package/src/lib/components/ui/sheet/sheet-content.svelte +0 -43
- package/src/lib/components/ui/sheet/sheet-description.svelte +0 -17
- package/src/lib/components/ui/sheet/sheet-footer.svelte +0 -18
- package/src/lib/components/ui/sheet/sheet-header.svelte +0 -19
- package/src/lib/components/ui/sheet/sheet-overlay.svelte +0 -17
- package/src/lib/components/ui/sheet/sheet-portal.svelte +0 -7
- package/src/lib/components/ui/sheet/sheet-title.svelte +0 -17
- package/src/lib/components/ui/sheet/sheet-trigger.svelte +0 -7
- package/src/lib/components/ui/sheet/sheet.svelte +0 -7
- package/src/lib/components/ui/textarea/textarea.svelte +0 -21
- package/src/lib/components/ui/toggle/toggle.svelte +0 -45
- package/src/lib/components/ui/toggle-group/toggle-group-item.svelte +0 -35
- package/src/lib/components/ui/toggle-group/toggle-group.svelte +0 -63
- package/src/lib/components/ui/tooltip/tooltip-content.svelte +0 -24
- package/src/lib/components/ui/tooltip/tooltip-trigger.svelte +0 -27
- package/src/lib/components/ui/tooltip/tooltip.svelte +0 -9
- package/src/lib/components/ui/trigger-button/trigger-button.svelte +0 -106
- package/src/svelte-plugin-ui/components/Icon.svelte +0 -181
- package/src/svelte-plugin-ui/components/ModeSwitch.svelte +0 -121
- package/src/svelte-plugin-ui/components/ToolbarShell.svelte +0 -150
- package/src/svelte-plugin-ui/components/Viewfinder.svelte +0 -1001
- package/src/tools/handlers/docs.js +0 -11
- package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +0 -139
- package/src/workshop/features/createFlow/CreateFlowForm.svelte +0 -314
- package/src/workshop/features/createPage/CreatePageForm.svelte +0 -249
- package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +0 -287
- package/src/workshop/features/createStory/CreateStoryForm.svelte +0 -161
- package/src/workshop/ui/WorkshopPanel.svelte +0 -97
package/src/modes.test.js
CHANGED
|
@@ -359,7 +359,7 @@ describe('locked mode', () => {
|
|
|
359
359
|
describe('tool registry', () => {
|
|
360
360
|
const SAMPLE_TOOLS = {
|
|
361
361
|
'*': [
|
|
362
|
-
{ id: '
|
|
362
|
+
{ id: 'workspace', label: 'Workspace', group: 'dev' },
|
|
363
363
|
{ id: 'reset-params', label: 'Reset all params', group: 'dev' },
|
|
364
364
|
],
|
|
365
365
|
'present': [
|
|
@@ -372,14 +372,14 @@ describe('tool registry', () => {
|
|
|
372
372
|
initTools(SAMPLE_TOOLS)
|
|
373
373
|
const tools = getToolsForMode('present')
|
|
374
374
|
const ids = tools.map(t => t.id)
|
|
375
|
-
expect(ids).toContain('
|
|
375
|
+
expect(ids).toContain('workspace')
|
|
376
376
|
expect(ids).toContain('comments-toggle')
|
|
377
377
|
})
|
|
378
378
|
|
|
379
379
|
it('wildcard tools appear in all modes', () => {
|
|
380
380
|
initTools(SAMPLE_TOOLS)
|
|
381
381
|
const protoTools = getToolsForMode('prototype')
|
|
382
|
-
expect(protoTools.map(t => t.id)).toContain('
|
|
382
|
+
expect(protoTools.map(t => t.id)).toContain('workspace')
|
|
383
383
|
})
|
|
384
384
|
|
|
385
385
|
it('mode-specific tools only appear in their mode', () => {
|
|
@@ -390,7 +390,7 @@ describe('tool registry', () => {
|
|
|
390
390
|
|
|
391
391
|
it('tools start with default state', () => {
|
|
392
392
|
initTools(SAMPLE_TOOLS)
|
|
393
|
-
const state = getToolState('
|
|
393
|
+
const state = getToolState('workspace')
|
|
394
394
|
expect(state).toEqual({
|
|
395
395
|
enabled: true,
|
|
396
396
|
active: false,
|
|
@@ -13,17 +13,65 @@
|
|
|
13
13
|
import { installHideParamListener } from './interceptHideParams.js'
|
|
14
14
|
import { installHistorySync } from './hideMode.js'
|
|
15
15
|
import { installBodyClassSync } from './bodyClasses.js'
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
import {
|
|
17
|
+
initCommentsConfig, isCommentsEnabled,
|
|
18
|
+
initFeatureFlags,
|
|
19
|
+
initPlugins,
|
|
20
|
+
initUIConfig,
|
|
21
|
+
initCanvasConfig,
|
|
22
|
+
initCommandPaletteConfig,
|
|
23
|
+
initToolbarConfig, consumeClientToolbarOverrides,
|
|
24
|
+
initCustomerModeConfig,
|
|
25
|
+
getConfig,
|
|
26
|
+
} from '@dfosco/storyboard-core'
|
|
24
27
|
|
|
25
28
|
let _mounted = false
|
|
26
29
|
|
|
30
|
+
const CHROME_HIDDEN_KEY = 'sb-chrome-hidden'
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Migrate localStorage keys renamed in 4.3.0.
|
|
34
|
+
* Runs once at startup, idempotent: only copies if new key doesn't exist yet.
|
|
35
|
+
*/
|
|
36
|
+
function migrateLocalStorageKeys() {
|
|
37
|
+
if (typeof localStorage === 'undefined') return
|
|
38
|
+
const renames = [
|
|
39
|
+
['sb-viewfinder-starred', 'sb-workspace-starred'],
|
|
40
|
+
['sb-viewfinder-recent', 'sb-workspace-recent'],
|
|
41
|
+
['sb-viewfinder-group-folders', 'sb-workspace-group-folders'],
|
|
42
|
+
]
|
|
43
|
+
for (const [oldKey, newKey] of renames) {
|
|
44
|
+
if (localStorage.getItem(newKey) === null && localStorage.getItem(oldKey) !== null) {
|
|
45
|
+
localStorage.setItem(newKey, localStorage.getItem(oldKey))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Restore the saved chrome-hidden state immediately, before React mounts.
|
|
52
|
+
* Prevents a flash of toolbars appearing then disappearing.
|
|
53
|
+
*/
|
|
54
|
+
function applyEarlyChromeState() {
|
|
55
|
+
if (typeof document === 'undefined' || typeof localStorage === 'undefined') return
|
|
56
|
+
const hidden = localStorage.getItem(CHROME_HIDDEN_KEY) === '1'
|
|
57
|
+
if (hidden) {
|
|
58
|
+
document.documentElement.classList.add('storyboard-chrome-hidden')
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Watch for changes to the storyboard-chrome-hidden class and persist to
|
|
64
|
+
* localStorage. Works regardless of which code path toggles the class.
|
|
65
|
+
*/
|
|
66
|
+
function installChromeStatePersistence() {
|
|
67
|
+
if (typeof document === 'undefined' || typeof localStorage === 'undefined') return
|
|
68
|
+
const observer = new MutationObserver(() => {
|
|
69
|
+
const hidden = document.documentElement.classList.contains('storyboard-chrome-hidden')
|
|
70
|
+
localStorage.setItem(CHROME_HIDDEN_KEY, hidden ? '1' : '0')
|
|
71
|
+
})
|
|
72
|
+
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
|
|
73
|
+
}
|
|
74
|
+
|
|
27
75
|
/**
|
|
28
76
|
* Apply the saved theme to Primer CSS attributes immediately, before
|
|
29
77
|
* React or Svelte mount. This prevents a flash of wrong-theme content.
|
|
@@ -138,6 +186,12 @@ export async function mountStoryboardCore(config = {}, options = {}) {
|
|
|
138
186
|
const basePath = options.basePath || '/'
|
|
139
187
|
const customHandlers = options.handlers || {}
|
|
140
188
|
|
|
189
|
+
// Migrate renamed localStorage keys (4.3.0: viewfinder → workspace)
|
|
190
|
+
migrateLocalStorageKeys()
|
|
191
|
+
|
|
192
|
+
// Apply saved chrome-hidden state immediately — before React mount
|
|
193
|
+
applyEarlyChromeState()
|
|
194
|
+
|
|
141
195
|
// Apply saved theme to DOM immediately — before Svelte/React mount
|
|
142
196
|
applyEarlyTheme()
|
|
143
197
|
|
|
@@ -145,60 +199,104 @@ export async function mountStoryboardCore(config = {}, options = {}) {
|
|
|
145
199
|
installHideParamListener()
|
|
146
200
|
installHistorySync()
|
|
147
201
|
installBodyClassSync()
|
|
202
|
+
installChromeStatePersistence()
|
|
148
203
|
|
|
149
|
-
// Initialize config-driven systems
|
|
150
|
-
|
|
204
|
+
// Initialize config-driven systems.
|
|
205
|
+
// The unified config store is already seeded by the virtual module's initConfig().
|
|
206
|
+
// Individual stores are initialized here for backward compatibility — consumers
|
|
207
|
+
// that import directly from these stores still work.
|
|
208
|
+
const uc = getConfig()
|
|
209
|
+
|
|
210
|
+
if (uc.featureFlags && Object.keys(uc.featureFlags).length > 0) {
|
|
211
|
+
initFeatureFlags(uc.featureFlags)
|
|
212
|
+
} else if (config.featureFlags) {
|
|
151
213
|
initFeatureFlags(config.featureFlags)
|
|
152
214
|
}
|
|
153
215
|
|
|
154
|
-
if (
|
|
216
|
+
if (uc.plugins && Object.keys(uc.plugins).length > 0) {
|
|
217
|
+
initPlugins(uc.plugins)
|
|
218
|
+
} else if (config.plugins) {
|
|
155
219
|
initPlugins(config.plugins)
|
|
156
220
|
}
|
|
157
221
|
|
|
158
|
-
if (
|
|
222
|
+
if (uc.ui && Object.keys(uc.ui).length > 0) {
|
|
223
|
+
initUIConfig(uc.ui)
|
|
224
|
+
} else if (config.ui) {
|
|
159
225
|
initUIConfig(config.ui)
|
|
160
226
|
}
|
|
161
227
|
|
|
162
|
-
if (
|
|
228
|
+
if (uc.canvas && Object.keys(uc.canvas).length > 0) {
|
|
229
|
+
initCanvasConfig(uc.canvas)
|
|
230
|
+
} else if (config.canvas) {
|
|
163
231
|
initCanvasConfig(config.canvas)
|
|
164
232
|
}
|
|
165
233
|
|
|
166
234
|
// Load and merge command palette config.
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
initCommandPaletteConfig(deepMergeCp(defaultCmdPaletteConfig, config.commandPalette))
|
|
235
|
+
// If the unified store has commandPalette data, use it directly.
|
|
236
|
+
// Otherwise fall back to legacy merging with bundled defaults.
|
|
237
|
+
const ucCmdPalette = uc.commandPalette
|
|
238
|
+
if (ucCmdPalette && Object.keys(ucCmdPalette).length > 0) {
|
|
239
|
+
initCommandPaletteConfig(ucCmdPalette)
|
|
173
240
|
} else {
|
|
174
|
-
|
|
241
|
+
const defaultCmdPaletteConfig = (await import('../commandpalette.config.json')).default
|
|
242
|
+
if (config.commandPalette) {
|
|
243
|
+
const merged = { ...defaultCmdPaletteConfig, ...config.commandPalette }
|
|
244
|
+
if (config.commandPalette.sections && defaultCmdPaletteConfig.sections) {
|
|
245
|
+
const clientIds = new Set(config.commandPalette.sections.map(s => s.id))
|
|
246
|
+
const preserved = defaultCmdPaletteConfig.sections.filter(s => !clientIds.has(s.id))
|
|
247
|
+
merged.sections = [...config.commandPalette.sections, ...preserved]
|
|
248
|
+
}
|
|
249
|
+
initCommandPaletteConfig(merged)
|
|
250
|
+
} else {
|
|
251
|
+
initCommandPaletteConfig({ ...defaultCmdPaletteConfig })
|
|
252
|
+
}
|
|
175
253
|
}
|
|
176
254
|
|
|
177
255
|
// Initialize customer mode config
|
|
178
|
-
if (
|
|
256
|
+
if (uc.customerMode && Object.keys(uc.customerMode).length > 0) {
|
|
257
|
+
initCustomerModeConfig(uc.customerMode)
|
|
258
|
+
} else if (config.customerMode) {
|
|
179
259
|
initCustomerModeConfig(config.customerMode)
|
|
180
260
|
}
|
|
181
261
|
|
|
182
262
|
// Initialize comments config (framework-agnostic)
|
|
183
|
-
|
|
184
|
-
|
|
263
|
+
const commentsConfig = uc.comments && Object.keys(uc.comments).length > 0 ? uc.comments : config.comments
|
|
264
|
+
if (commentsConfig) {
|
|
265
|
+
initCommentsConfig({ ...config, comments: commentsConfig }, { basePath })
|
|
185
266
|
}
|
|
186
267
|
|
|
187
268
|
// Inject compiled UI styles (await to prevent late restyle / FOUC)
|
|
188
269
|
await injectUIStyles()
|
|
189
270
|
|
|
190
|
-
// Load
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
const { deepMerge } = await import('
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
271
|
+
// Load toolbar config from the unified store.
|
|
272
|
+
// The unified store already has core defaults merged with client overrides.
|
|
273
|
+
// Fall back to legacy merging if unified store wasn't seeded.
|
|
274
|
+
const { deepMerge } = await import('@dfosco/storyboard-core')
|
|
275
|
+
let toolbarConfig = uc.toolbar && Object.keys(uc.toolbar).length > 0
|
|
276
|
+
? { ...uc.toolbar }
|
|
277
|
+
: null
|
|
278
|
+
|
|
279
|
+
if (!toolbarConfig) {
|
|
280
|
+
// Legacy path: unified store not seeded, merge manually
|
|
281
|
+
const defaultConfig = (await import('../toolbar.config.json')).default
|
|
282
|
+
const clientOverrides = consumeClientToolbarOverrides()
|
|
283
|
+
const explicitToolbar = config.toolbar
|
|
284
|
+
|
|
285
|
+
if (explicitToolbar && clientOverrides) {
|
|
286
|
+
toolbarConfig = deepMerge(deepMerge(defaultConfig, clientOverrides), explicitToolbar)
|
|
287
|
+
} else if (explicitToolbar) {
|
|
288
|
+
toolbarConfig = deepMerge(defaultConfig, explicitToolbar)
|
|
289
|
+
} else if (clientOverrides) {
|
|
290
|
+
toolbarConfig = deepMerge(defaultConfig, clientOverrides)
|
|
291
|
+
} else {
|
|
292
|
+
toolbarConfig = { ...defaultConfig }
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Inject repository URL into the toolbar config
|
|
297
|
+
const repo = uc.repository || config.repository
|
|
298
|
+
if (repo?.owner && repo?.name) {
|
|
299
|
+
const repoUrl = `https://github.com/${repo.owner}/${repo.name}`
|
|
202
300
|
|
|
203
301
|
// New tools schema
|
|
204
302
|
if (toolbarConfig.tools?.repository) {
|
package/src/paletteProviders.js
CHANGED
|
@@ -55,6 +55,9 @@ export function buildCommandItems(mode, basePath) {
|
|
|
55
55
|
if (state === 'disabled' || state === 'hidden') continue
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// Skip items hidden from search
|
|
59
|
+
if (action.hideFromCommandPaletteSearch) continue
|
|
60
|
+
|
|
58
61
|
if (action.type === 'submenu') {
|
|
59
62
|
// Flatten submenu children into individual items
|
|
60
63
|
const children = getActionChildren(action.id)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest'
|
|
2
2
|
|
|
3
|
-
// Mock loader and
|
|
3
|
+
// Mock loader and workspace before importing providers
|
|
4
4
|
vi.mock('./loader.js', () => ({
|
|
5
5
|
listStories: vi.fn(() => ['button', 'card']),
|
|
6
6
|
getStoryData: vi.fn((name) => ({
|
|
@@ -34,7 +34,7 @@ vi.mock('./loader.js', () => ({
|
|
|
34
34
|
|
|
35
35
|
vi.mock('./commandActions.js', () => ({
|
|
36
36
|
getActionsForMode: vi.fn(() => [
|
|
37
|
-
{ id: 'core/
|
|
37
|
+
{ id: 'core/workspace', label: 'Go to Workspace', type: 'link', url: '/', toolKey: 'workspace' },
|
|
38
38
|
{ id: 'core/docs', label: 'Documentation', type: 'default', toolKey: 'docs' },
|
|
39
39
|
{ id: 'core/devtools', label: 'DevTools', type: 'submenu', toolKey: 'devtools' },
|
|
40
40
|
{ type: 'header', label: 'Command Menu' },
|
package/src/server/index.js
CHANGED
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
|
|
15
15
|
import http from 'node:http'
|
|
16
16
|
import { spawn } from 'node:child_process'
|
|
17
|
-
import { existsSync
|
|
18
|
-
import { resolve, join
|
|
19
|
-
import { getPort,
|
|
17
|
+
import { existsSync } from 'node:fs'
|
|
18
|
+
import { resolve, join } from 'node:path'
|
|
19
|
+
import { getPort, releasePort, repoRoot } from '../worktree/port.js'
|
|
20
20
|
import { generateCaddyfile, generateRouteConfig, upsertCaddyRoute, isCaddyRunning, reloadCaddy, readDevDomain } from '../cli/proxy.js'
|
|
21
21
|
import { compactAll } from '../canvas/compact.js'
|
|
22
|
+
import { register, unregister, generateId, list, findByWorktree } from '../worktree/serverRegistry.js'
|
|
23
|
+
import { createDevLogger } from '../logger/devLogger.js'
|
|
22
24
|
|
|
23
25
|
const SERVER_PORT_BASE = 4100
|
|
24
26
|
|
|
@@ -46,6 +48,12 @@ const processes = new Map()
|
|
|
46
48
|
/** Route handlers: prefix → handler function */
|
|
47
49
|
const routeHandlers = new Map()
|
|
48
50
|
|
|
51
|
+
// ─── Dev Logger ───
|
|
52
|
+
|
|
53
|
+
let _currentBranch = null
|
|
54
|
+
try { _currentBranch = (await import('node:child_process')).execSync('git branch --show-current', { encoding: 'utf8', cwd: repoRoot() }).trim() } catch { /* empty */ }
|
|
55
|
+
const devLogger = createDevLogger({ root: repoRoot(), devDomain: DEV_DOMAIN.replace('.localhost', ''), branch: _currentBranch })
|
|
56
|
+
|
|
49
57
|
// ─── JSON helpers ───
|
|
50
58
|
|
|
51
59
|
function sendJson(res, status, data) {
|
|
@@ -54,6 +62,21 @@ function sendJson(res, status, data) {
|
|
|
54
62
|
res.end(body)
|
|
55
63
|
}
|
|
56
64
|
|
|
65
|
+
function sendJsonLogged(res, status, data) {
|
|
66
|
+
sendJson(res, status, data)
|
|
67
|
+
if (status >= 400) {
|
|
68
|
+
const ctx = res.__sbLogCtx || {}
|
|
69
|
+
devLogger.logResponse({
|
|
70
|
+
status,
|
|
71
|
+
method: ctx.method || 'UNKNOWN',
|
|
72
|
+
url: ctx.url || '',
|
|
73
|
+
route: ctx.route || null,
|
|
74
|
+
subRoute: ctx.subRoute || null,
|
|
75
|
+
error: data?.error || null,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
57
80
|
async function parseBody(req) {
|
|
58
81
|
const chunks = []
|
|
59
82
|
for await (const chunk of req) chunks.push(chunk)
|
|
@@ -117,15 +140,23 @@ function spawnVite(branch) {
|
|
|
117
140
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
118
141
|
})
|
|
119
142
|
|
|
120
|
-
const entry = { child, port, status: 'starting', cwd, branch }
|
|
143
|
+
const entry = { child, port, status: 'starting', cwd, branch, serverId: generateId() }
|
|
121
144
|
processes.set(branch, entry)
|
|
122
145
|
|
|
146
|
+
// Register in persistent server registry
|
|
147
|
+
register({ id: entry.serverId, worktree: branch, pid: child.pid, port, background: true })
|
|
148
|
+
|
|
123
149
|
// Detect ready state from stdout
|
|
124
150
|
child.stdout.on('data', (data) => {
|
|
125
151
|
const text = data.toString()
|
|
126
152
|
const portMatch = text.match(/localhost:(\d+)/)
|
|
127
153
|
if (portMatch) {
|
|
128
|
-
|
|
154
|
+
const actualPort = Number(portMatch[1])
|
|
155
|
+
if (actualPort !== entry.port) {
|
|
156
|
+
entry.port = actualPort
|
|
157
|
+
// Re-register with the actual port Vite bound to
|
|
158
|
+
register({ id: entry.serverId, worktree: branch, pid: child.pid, port: actualPort, background: true })
|
|
159
|
+
}
|
|
129
160
|
}
|
|
130
161
|
if (text.includes('ready in')) {
|
|
131
162
|
entry.status = 'ready'
|
|
@@ -135,9 +166,12 @@ function spawnVite(branch) {
|
|
|
135
166
|
|
|
136
167
|
child.stderr.on('data', () => { /* suppress */ })
|
|
137
168
|
|
|
138
|
-
child.on('exit', (
|
|
169
|
+
child.on('exit', (_code) => {
|
|
170
|
+
void _code
|
|
139
171
|
entry.status = 'stopped'
|
|
140
172
|
processes.delete(branch)
|
|
173
|
+
unregister(entry.serverId)
|
|
174
|
+
releasePort(branch)
|
|
141
175
|
})
|
|
142
176
|
|
|
143
177
|
return entry
|
|
@@ -167,13 +201,13 @@ function registerCaddyRoute(branch, port) {
|
|
|
167
201
|
// POST /_storyboard/switch-branch
|
|
168
202
|
routeHandlers.set('switch-branch', async (req, res, ctx) => {
|
|
169
203
|
if (ctx.method !== 'POST') {
|
|
170
|
-
|
|
204
|
+
sendJsonLogged(res, 405, { error: 'POST required' })
|
|
171
205
|
return
|
|
172
206
|
}
|
|
173
207
|
|
|
174
208
|
const { branch } = ctx.body
|
|
175
209
|
if (!branch) {
|
|
176
|
-
|
|
210
|
+
sendJsonLogged(res, 400, { error: 'branch is required' })
|
|
177
211
|
return
|
|
178
212
|
}
|
|
179
213
|
|
|
@@ -188,59 +222,77 @@ routeHandlers.set('switch-branch', async (req, res, ctx) => {
|
|
|
188
222
|
if (existing && existing.status === 'ready') {
|
|
189
223
|
const alive = await isPortReady(existing.port)
|
|
190
224
|
if (alive) {
|
|
191
|
-
|
|
225
|
+
sendJsonLogged(res, 200, { url: targetUrl, status: 'already_running' })
|
|
192
226
|
return
|
|
193
227
|
}
|
|
194
228
|
// Stale entry — remove it
|
|
195
229
|
processes.delete(branch)
|
|
196
230
|
}
|
|
197
231
|
|
|
198
|
-
// Check if Vite is already running
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
232
|
+
// Check if Vite is already running in this server's process map
|
|
233
|
+
const _port = getPort(branch)
|
|
234
|
+
void _port
|
|
235
|
+
const existingInRegistry = findByWorktree(branch)
|
|
236
|
+
if (existingInRegistry.length > 0) {
|
|
237
|
+
const latest = existingInRegistry.reduce((a, b) =>
|
|
238
|
+
(a.startedAt || '') >= (b.startedAt || '') ? a : b
|
|
239
|
+
)
|
|
240
|
+
if (await isPortReady(latest.port)) {
|
|
241
|
+
registerCaddyRoute(branch, latest.port)
|
|
242
|
+
sendJsonLogged(res, 200, { url: targetUrl, status: 'already_running' })
|
|
243
|
+
return
|
|
244
|
+
}
|
|
204
245
|
}
|
|
205
246
|
|
|
206
247
|
// Check worktree exists
|
|
207
248
|
const cwd = resolveWorktreeCwd(branch)
|
|
208
249
|
if (!cwd) {
|
|
209
|
-
|
|
250
|
+
sendJsonLogged(res, 404, { error: `Worktree not found: ${branch}. Create it with: npx storyboard dev ${branch}` })
|
|
210
251
|
return
|
|
211
252
|
}
|
|
212
253
|
|
|
213
254
|
// Spawn Vite
|
|
214
255
|
const entry = spawnVite(branch)
|
|
215
256
|
|
|
216
|
-
// Wait for ready
|
|
217
|
-
|
|
257
|
+
// Wait for Vite to report ready via stdout (not TCP polling, which
|
|
258
|
+
// can false-positive on occupied ports from other processes)
|
|
259
|
+
const ready = await (async () => {
|
|
260
|
+
const start = Date.now()
|
|
261
|
+
while (Date.now() - start < HEALTH_TIMEOUT) {
|
|
262
|
+
if (entry.status === 'ready') return true
|
|
263
|
+
await new Promise(r => setTimeout(r, 300))
|
|
264
|
+
}
|
|
265
|
+
return false
|
|
266
|
+
})()
|
|
218
267
|
if (!ready) {
|
|
219
268
|
stopVite(branch)
|
|
220
|
-
|
|
269
|
+
sendJsonLogged(res, 504, { error: `Vite server for ${branch} did not become ready in time` })
|
|
221
270
|
return
|
|
222
271
|
}
|
|
223
272
|
|
|
224
|
-
|
|
273
|
+
sendJsonLogged(res, 200, { url: targetUrl, status: 'started' })
|
|
225
274
|
} catch (err) {
|
|
226
|
-
|
|
275
|
+
sendJsonLogged(res, 500, { error: err.message })
|
|
227
276
|
}
|
|
228
277
|
})
|
|
229
278
|
|
|
230
279
|
// GET /_storyboard/worktrees
|
|
231
280
|
routeHandlers.set('worktrees', async (req, res) => {
|
|
232
281
|
try {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
running: processes.has(name) ? processes.get(name).status : null,
|
|
282
|
+
// Use the server registry (live processes) instead of stale ports.json
|
|
283
|
+
const servers = list()
|
|
284
|
+
const branches = servers.map(srv => ({
|
|
285
|
+
branch: srv.worktree,
|
|
286
|
+
folder: srv.worktree === 'main' ? '' : `branch--${srv.worktree}/`,
|
|
287
|
+
port: srv.port,
|
|
288
|
+
running: processes.has(srv.worktree) ? processes.get(srv.worktree).status : 'background',
|
|
241
289
|
}))
|
|
242
|
-
|
|
243
|
-
|
|
290
|
+
// Always include main even if no server is registered for it
|
|
291
|
+
if (!branches.some(b => b.branch === 'main')) {
|
|
292
|
+
branches.unshift({ branch: 'main', folder: '', port: 1234, running: null })
|
|
293
|
+
}
|
|
294
|
+
sendJsonLogged(res, 200, branches)
|
|
295
|
+
} catch { sendJsonLogged(res, 200, []) }
|
|
244
296
|
})
|
|
245
297
|
|
|
246
298
|
// GET /_storyboard/server/status
|
|
@@ -250,10 +302,10 @@ routeHandlers.set('server', async (req, res, ctx) => {
|
|
|
250
302
|
for (const [branch, entry] of processes) {
|
|
251
303
|
active.push({ branch, port: entry.port, status: entry.status })
|
|
252
304
|
}
|
|
253
|
-
|
|
305
|
+
sendJsonLogged(res, 200, { active, serverPort: SERVER_PORT })
|
|
254
306
|
return
|
|
255
307
|
}
|
|
256
|
-
|
|
308
|
+
sendJsonLogged(res, 404, { error: 'Unknown server route' })
|
|
257
309
|
})
|
|
258
310
|
|
|
259
311
|
// ─── HTTP Server ───
|
|
@@ -280,6 +332,9 @@ const server = http.createServer(async (req, res) => {
|
|
|
280
332
|
const prefix = slashIdx === -1 ? after : after.slice(0, slashIdx)
|
|
281
333
|
const restPath = slashIdx === -1 ? '/' : after.slice(slashIdx)
|
|
282
334
|
|
|
335
|
+
// Attach route context for the logging sendJson wrapper
|
|
336
|
+
res.__sbLogCtx = { method: req.method, url: req.url, route: prefix, subRoute: restPath }
|
|
337
|
+
|
|
283
338
|
const handler = routeHandlers.get(prefix)
|
|
284
339
|
if (handler) {
|
|
285
340
|
let body = {}
|
|
@@ -289,8 +344,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
289
344
|
try {
|
|
290
345
|
await handler(req, res, { body, path: restPath, method: req.method })
|
|
291
346
|
} catch (err) {
|
|
292
|
-
|
|
293
|
-
sendJson(res, 500, { error: err.message })
|
|
347
|
+
sendJsonLogged(res, 500, { error: err.message })
|
|
294
348
|
}
|
|
295
349
|
return
|
|
296
350
|
}
|
|
@@ -298,18 +352,28 @@ const server = http.createServer(async (req, res) => {
|
|
|
298
352
|
|
|
299
353
|
// Health check
|
|
300
354
|
if (pathname === '/health') {
|
|
301
|
-
|
|
355
|
+
sendJsonLogged(res, 200, { ok: true, devDomain: DEV_DOMAIN })
|
|
302
356
|
return
|
|
303
357
|
}
|
|
304
358
|
|
|
305
|
-
|
|
359
|
+
// Set context for catch-all 404
|
|
360
|
+
res.__sbLogCtx = { method: req.method, url: req.url, route: null, subRoute: null }
|
|
361
|
+
sendJsonLogged(res, 404, { error: 'Not found' })
|
|
306
362
|
})
|
|
307
363
|
|
|
308
364
|
export function startServer(port = SERVER_PORT) {
|
|
309
|
-
|
|
310
|
-
|
|
365
|
+
return new Promise((resolve, reject) => {
|
|
366
|
+
server.on('error', (err) => {
|
|
367
|
+
if (err.code === 'EADDRINUSE') {
|
|
368
|
+
reject(Object.assign(new Error(`Port ${port} already in use`), { code: 'EADDRINUSE' }))
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
reject(err)
|
|
372
|
+
})
|
|
373
|
+
server.listen(port, () => {
|
|
374
|
+
resolve(server)
|
|
375
|
+
})
|
|
311
376
|
})
|
|
312
|
-
return server
|
|
313
377
|
}
|
|
314
378
|
|
|
315
379
|
/** Public API for spawning Vite from CLI (with stdout piping) */
|