@dfosco/storyboard-core 4.2.0-beta.2 → 4.2.0-beta.21
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 +17379 -28568
- 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 -0
- package/scaffold/skills/canvas/SKILL.md +5 -4
- package/scaffold/skills/ship/SKILL.md +1 -1
- package/scaffold/storyboard.config.json +14 -1
- package/scaffold/toolbar.config.json +1 -1
- package/src/ActionMenuButton.jsx +100 -0
- package/src/AutosyncMenuButton.css +67 -0
- package/src/AutosyncMenuButton.jsx +241 -0
- package/src/BranchSelect.jsx +29 -0
- package/src/BranchSelect.module.css +30 -0
- package/src/CanvasAgentsMenu.jsx +87 -0
- package/src/CanvasCreateMenu.jsx +609 -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 +286 -0
- package/src/CommandPalette.jsx +35 -0
- package/src/CommandPaletteTrigger.jsx +25 -0
- package/src/CommentsMenuButton.jsx +38 -0
- package/src/CoreUIBar.css +47 -0
- package/src/CoreUIBar.jsx +855 -0
- package/src/CreateMenuButton.jsx +116 -0
- package/src/HideChromeTrigger.jsx +40 -0
- package/src/InspectorPanel.css +109 -0
- package/src/InspectorPanel.jsx +629 -0
- package/src/PwaInstallBanner.css +42 -0
- package/src/PwaInstallBanner.jsx +124 -0
- package/src/SidePanel.jsx +260 -0
- package/src/ThemeMenuButton.jsx +136 -0
- package/src/autosync/server.js +202 -5
- package/src/autosync/server.test.js +112 -0
- package/src/canvas/__tests__/agent-integration.test.js +593 -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 +129 -0
- package/src/canvas/__tests__/terminal-integration.test.js +175 -0
- package/src/canvas/hot-pool.js +757 -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 +1801 -22
- package/src/canvas/server.test.js +239 -0
- package/src/canvas/terminal-config.js +331 -0
- package/src/canvas/terminal-registry.js +38 -0
- package/src/canvas/terminal-server.js +1037 -29
- 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 +59 -12
- 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/create.js +38 -14
- package/src/cli/dev.js +157 -83
- 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 +148 -25
- package/src/cli/serverUrl.js +8 -3
- package/src/cli/sessions.js +131 -5
- package/src/cli/setup.js +109 -11
- package/src/cli/terminal-commands.js +16 -8
- package/src/cli/terminal-messaging.js +231 -0
- package/src/cli/terminal-welcome.js +365 -33
- 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 +14 -2
- 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 +159 -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 +123 -27
- package/src/paletteProviders.js +3 -0
- package/src/paletteProviders.test.js +2 -2
- package/src/server/index.js +98 -36
- package/src/sidepanel.css +214 -0
- 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 +512 -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 +19 -0
- package/src/tools/handlers/canvasToolbar.js +8 -8
- package/src/tools/handlers/commandPalette.js +9 -0
- package/src/tools/handlers/comments.js +1 -1
- package/src/tools/handlers/create.js +1 -1
- package/src/tools/handlers/devtools.js +16 -0
- package/src/tools/handlers/devtools.test.js +38 -0
- package/src/tools/handlers/flows.js +1 -1
- 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 +242 -60
- 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/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/cli/proxy.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* storyboard proxy —
|
|
2
|
+
* storyboard proxy — Manage the Caddy reverse proxy.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* storyboard proxy start Start or reload Caddy proxy
|
|
6
|
+
* storyboard proxy close Stop Caddy proxy
|
|
3
7
|
*
|
|
4
8
|
* Caddy listens on port 80 for storyboard.localhost, routing:
|
|
5
9
|
* /storyboard/* → main (port 1234)
|
|
6
10
|
* /<branchname>/storyboard/* → worktree (assigned port)
|
|
7
11
|
*
|
|
8
12
|
* Requires Caddy to be installed (brew install caddy).
|
|
9
|
-
* Port 80 requires `sudo caddy start` — handled by `storyboard setup`.
|
|
10
13
|
*/
|
|
11
14
|
|
|
12
15
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'
|
|
13
16
|
import { execSync } from 'child_process'
|
|
14
17
|
import { dirname, resolve } from 'path'
|
|
15
18
|
import { portsFilePath } from '../worktree/port.js'
|
|
19
|
+
import { list as listRunningServers } from '../worktree/serverRegistry.js'
|
|
16
20
|
|
|
17
21
|
export function readDevDomain(cwd) {
|
|
18
22
|
try {
|
|
@@ -26,25 +30,48 @@ export function readDevDomain(cwd) {
|
|
|
26
30
|
|
|
27
31
|
const DOMAIN = readDevDomain()
|
|
28
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Build branch→port map from running servers (servers.json) + explicit overrides.
|
|
35
|
+
* Only includes branches with live processes — prevents stale routes from
|
|
36
|
+
* creating redirect loops when ports get reused by different Vite instances.
|
|
37
|
+
* When multiple servers run for the same worktree, latest startedAt wins.
|
|
38
|
+
*/
|
|
39
|
+
function livePortMap(portOverrides = {}) {
|
|
40
|
+
const map = {}
|
|
41
|
+
try {
|
|
42
|
+
for (const srv of listRunningServers()) {
|
|
43
|
+
if (srv.worktree === 'main') continue
|
|
44
|
+
const existing = map[srv.worktree]
|
|
45
|
+
if (!existing || srv.startedAt > existing.startedAt) {
|
|
46
|
+
map[srv.worktree] = { port: srv.port, startedAt: srv.startedAt }
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} catch { /* registry unavailable — rely on overrides only */ }
|
|
50
|
+
|
|
51
|
+
// Build final branch→port, overrides always win
|
|
52
|
+
const result = {}
|
|
53
|
+
for (const [name, info] of Object.entries(map)) result[name] = info.port
|
|
54
|
+
Object.assign(result, portOverrides)
|
|
55
|
+
// Remove 'main' if accidentally passed — handled separately
|
|
56
|
+
delete result.main
|
|
57
|
+
return result
|
|
58
|
+
}
|
|
59
|
+
|
|
29
60
|
export function generateCaddyfile(portOverrides = {}) {
|
|
30
61
|
const portsFile = portsFilePath()
|
|
31
62
|
const dir = dirname(portsFile)
|
|
32
63
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
33
64
|
|
|
34
|
-
|
|
65
|
+
// Main port from ports.json (always 1234 unless overridden)
|
|
66
|
+
let mainPort = 1234
|
|
35
67
|
if (existsSync(portsFile)) {
|
|
36
68
|
try {
|
|
37
|
-
ports = JSON.parse(readFileSync(portsFile, 'utf8'))
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
69
|
+
const ports = JSON.parse(readFileSync(portsFile, 'utf8'))
|
|
70
|
+
mainPort = ports.main || 1234
|
|
71
|
+
} catch { /* use default */ }
|
|
41
72
|
}
|
|
42
73
|
|
|
43
|
-
|
|
44
|
-
Object.assign(ports, portOverrides)
|
|
45
|
-
|
|
46
|
-
const mainPort = ports.main || 1234
|
|
47
|
-
const branches = Object.entries(ports).filter(([name]) => name !== 'main')
|
|
74
|
+
const branches = Object.entries(livePortMap(portOverrides))
|
|
48
75
|
|
|
49
76
|
// Build Caddy route blocks — branches first (more specific), main last (fallback)
|
|
50
77
|
const branchBlocks = branches
|
|
@@ -104,15 +131,17 @@ const CADDY_ADMIN = 'http://localhost:2019'
|
|
|
104
131
|
* Tagged with @id so it can be upserted independently of other repos.
|
|
105
132
|
*/
|
|
106
133
|
export function generateRouteConfig(portOverrides = {}) {
|
|
134
|
+
// Main port from ports.json
|
|
107
135
|
const portsFile = portsFilePath()
|
|
108
|
-
let
|
|
136
|
+
let mainPort = 1234
|
|
109
137
|
if (existsSync(portsFile)) {
|
|
110
|
-
try {
|
|
138
|
+
try {
|
|
139
|
+
const ports = JSON.parse(readFileSync(portsFile, 'utf8'))
|
|
140
|
+
mainPort = ports.main || 1234
|
|
141
|
+
} catch { /* use default */ }
|
|
111
142
|
}
|
|
112
|
-
Object.assign(ports, portOverrides)
|
|
113
143
|
|
|
114
|
-
const
|
|
115
|
-
const branches = Object.entries(ports).filter(([name]) => name !== 'main')
|
|
144
|
+
const branches = Object.entries(livePortMap(portOverrides))
|
|
116
145
|
|
|
117
146
|
// Branch subroutes first (more specific), then main fallback
|
|
118
147
|
const subroutes = branches.map(([name, port]) => ({
|
|
@@ -215,7 +244,16 @@ export function upsertCaddyRoute(routeConfig) {
|
|
|
215
244
|
|
|
216
245
|
export function startCaddy(caddyfilePath) {
|
|
217
246
|
try {
|
|
218
|
-
execSync(`
|
|
247
|
+
execSync(`caddy start --config "${caddyfilePath}" >/dev/null 2>&1`, { stdio: ['inherit', 'pipe', 'pipe'] })
|
|
248
|
+
return true
|
|
249
|
+
} catch {
|
|
250
|
+
return false
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function stopCaddy() {
|
|
255
|
+
try {
|
|
256
|
+
execSync('curl -sf -X POST http://localhost:2019/stop', { timeout: 5000, stdio: 'ignore' })
|
|
219
257
|
return true
|
|
220
258
|
} catch {
|
|
221
259
|
return false
|
|
@@ -226,35 +264,56 @@ export function startCaddy(caddyfilePath) {
|
|
|
226
264
|
const isDirectRun = process.argv[2] === 'proxy'
|
|
227
265
|
if (isDirectRun) {
|
|
228
266
|
const { intro, outro, log, spinner } = await import('@clack/prompts')
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
const caddyfilePath = generateCaddyfile()
|
|
267
|
+
const subcommand = process.argv[3]
|
|
232
268
|
|
|
233
269
|
if (!isCaddyInstalled()) {
|
|
270
|
+
intro('storyboard proxy')
|
|
234
271
|
log.error('Caddy is not installed. Run: brew install caddy')
|
|
235
272
|
process.exit(1)
|
|
236
273
|
}
|
|
237
274
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
275
|
+
if (subcommand === 'close' || subcommand === 'stop') {
|
|
276
|
+
intro('storyboard proxy close')
|
|
277
|
+
if (!isCaddyRunning()) {
|
|
278
|
+
log.info('Proxy is not running.')
|
|
279
|
+
outro('')
|
|
280
|
+
} else if (stopCaddy()) {
|
|
281
|
+
log.success('Proxy stopped.')
|
|
282
|
+
outro('')
|
|
246
283
|
} else {
|
|
247
|
-
|
|
284
|
+
log.error('Failed to stop proxy.')
|
|
248
285
|
process.exit(1)
|
|
249
286
|
}
|
|
250
|
-
} else {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
287
|
+
} else if (subcommand === 'start' || !subcommand) {
|
|
288
|
+
intro('storyboard proxy start')
|
|
289
|
+
const caddyfilePath = generateCaddyfile()
|
|
290
|
+
|
|
291
|
+
const s = spinner()
|
|
292
|
+
if (isCaddyRunning()) {
|
|
293
|
+
s.start('Updating proxy routes...')
|
|
294
|
+
const routeConfig = generateRouteConfig()
|
|
295
|
+
if (upsertCaddyRoute(routeConfig)) {
|
|
296
|
+
s.stop('Proxy routes updated (admin API)')
|
|
297
|
+
} else if (reloadCaddy(caddyfilePath)) {
|
|
298
|
+
s.stop('Proxy reloaded (Caddyfile fallback)')
|
|
299
|
+
} else {
|
|
300
|
+
s.stop('Failed to update proxy')
|
|
301
|
+
process.exit(1)
|
|
302
|
+
}
|
|
254
303
|
} else {
|
|
255
|
-
s.
|
|
256
|
-
|
|
304
|
+
s.start('Starting proxy...')
|
|
305
|
+
if (startCaddy(caddyfilePath)) {
|
|
306
|
+
s.stop('Proxy started')
|
|
307
|
+
} else {
|
|
308
|
+
s.stop('Failed to start proxy')
|
|
309
|
+
process.exit(1)
|
|
310
|
+
}
|
|
257
311
|
}
|
|
312
|
+
outro(`Proxy ready at http://${DOMAIN}/`)
|
|
313
|
+
} else {
|
|
314
|
+
intro('storyboard proxy')
|
|
315
|
+
log.error(`Unknown subcommand: "${subcommand}"`)
|
|
316
|
+
log.info('Available: start, close')
|
|
317
|
+
process.exit(1)
|
|
258
318
|
}
|
|
259
|
-
outro(`Proxy ready at http://${DOMAIN}/`)
|
|
260
319
|
}
|
package/src/cli/schemas.js
CHANGED
|
@@ -170,13 +170,26 @@ export const widgetSchema = {
|
|
|
170
170
|
},
|
|
171
171
|
x: {
|
|
172
172
|
type: 'number',
|
|
173
|
-
|
|
174
|
-
description: 'X position',
|
|
173
|
+
description: 'X position (omit for auto-positioning)',
|
|
175
174
|
},
|
|
176
175
|
y: {
|
|
177
176
|
type: 'number',
|
|
178
|
-
|
|
179
|
-
|
|
177
|
+
description: 'Y position (omit for auto-positioning)',
|
|
178
|
+
},
|
|
179
|
+
near: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
description: 'Place near this widget ID (default: auto-selects last widget). Use --near false to disable',
|
|
182
|
+
},
|
|
183
|
+
direction: {
|
|
184
|
+
type: 'string',
|
|
185
|
+
default: 'right',
|
|
186
|
+
description: 'Direction from reference widget: right, left, above, below',
|
|
187
|
+
aliases: ['dir'],
|
|
188
|
+
},
|
|
189
|
+
resolve: {
|
|
190
|
+
type: 'boolean',
|
|
191
|
+
default: false,
|
|
192
|
+
description: 'Run server-side collision detection on the target position',
|
|
180
193
|
},
|
|
181
194
|
props: {
|
|
182
195
|
type: 'string',
|
|
@@ -187,6 +200,11 @@ export const widgetSchema = {
|
|
|
187
200
|
description: 'Path to a JSON file containing widget props (avoids shell escaping)',
|
|
188
201
|
aliases: ['pf'],
|
|
189
202
|
},
|
|
203
|
+
json: {
|
|
204
|
+
type: 'boolean',
|
|
205
|
+
default: false,
|
|
206
|
+
description: 'Output result as JSON (includes widget id)',
|
|
207
|
+
},
|
|
190
208
|
}
|
|
191
209
|
|
|
192
210
|
/** @type {FlagSchema} */
|
package/src/cli/server.js
CHANGED
|
@@ -1,35 +1,75 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* storyboard server
|
|
3
|
-
*
|
|
4
|
-
* Manages Vite child processes and serves the /_storyboard/ API.
|
|
5
|
-
* If a branch is given, also starts Vite for that branch immediately.
|
|
2
|
+
* storyboard server — Dev server lifecycle management.
|
|
6
3
|
*
|
|
7
4
|
* Usage:
|
|
8
|
-
* storyboard server
|
|
9
|
-
* storyboard server
|
|
10
|
-
* storyboard server
|
|
5
|
+
* storyboard server List running dev servers
|
|
6
|
+
* storyboard server list List running dev servers
|
|
7
|
+
* storyboard server start [wt] Start the persistent server + Vite for a worktree
|
|
8
|
+
* storyboard server stop <id> Stop a dev server by worktree name or ID
|
|
11
9
|
*/
|
|
12
10
|
|
|
13
11
|
import * as p from '@clack/prompts'
|
|
14
12
|
import { startServer, SERVER_PORT, spawnViteForBranch } from '../server/index.js'
|
|
15
13
|
import { parseFlags } from './flags.js'
|
|
16
14
|
import { readDevDomain, generateCaddyfile, generateRouteConfig, upsertCaddyRoute, isCaddyRunning } from './proxy.js'
|
|
17
|
-
import { detectWorktreeName, getPort } from '../worktree/port.js'
|
|
15
|
+
import { detectWorktreeName, getPort, releasePort, repoRoot } from '../worktree/port.js'
|
|
16
|
+
import {
|
|
17
|
+
list,
|
|
18
|
+
findByWorktree,
|
|
19
|
+
findById,
|
|
20
|
+
unregister,
|
|
21
|
+
prune,
|
|
22
|
+
} from '../worktree/serverRegistry.js'
|
|
18
23
|
|
|
19
24
|
const flagSchema = {
|
|
20
25
|
port: { type: 'number', description: 'Server port (default: 4100)' },
|
|
26
|
+
background: { type: 'boolean', default: false, description: 'Run in background (--bg alias)' },
|
|
27
|
+
bg: { type: 'boolean', default: false, description: 'Run in background' },
|
|
28
|
+
multiple: { type: 'boolean', default: false, description: 'Allow multiple servers per worktree' },
|
|
21
29
|
}
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
const { flags, positional } = parseFlags(process.argv.slice(3), flagSchema)
|
|
25
|
-
const port = flags.port || SERVER_PORT
|
|
26
|
-
const branchArg = positional[0] || undefined
|
|
31
|
+
// ─── Commands ────────────────────────────────────────────
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
function serverList() {
|
|
34
|
+
const servers = list()
|
|
35
|
+
if (servers.length === 0) {
|
|
36
|
+
p.log.info('No dev servers running.')
|
|
37
|
+
return
|
|
38
|
+
}
|
|
29
39
|
|
|
30
40
|
const devDomain = readDevDomain()
|
|
31
41
|
|
|
32
|
-
|
|
42
|
+
p.log.info('Running dev servers:\n')
|
|
43
|
+
for (const s of servers) {
|
|
44
|
+
const isMain = s.worktree === 'main'
|
|
45
|
+
const basePath = isMain ? '/' : `/branch--${s.worktree}/`
|
|
46
|
+
const proxyUrl = `http://${devDomain}${basePath}`
|
|
47
|
+
const bg = s.background ? ' (bg)' : ''
|
|
48
|
+
console.log(` ${s.id} ${s.worktree.padEnd(32)} :${String(s.port).padEnd(5)} PID ${String(s.pid).padEnd(7)} ${proxyUrl}${bg}`)
|
|
49
|
+
}
|
|
50
|
+
console.log()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function serverStart(branchArg, flags) {
|
|
54
|
+
const { background, bg, multiple } = flags
|
|
55
|
+
const isBackground = background || bg
|
|
56
|
+
const worktreeName = branchArg || detectWorktreeName()
|
|
57
|
+
|
|
58
|
+
// Check for duplicate worktree servers
|
|
59
|
+
if (!multiple) {
|
|
60
|
+
const existing = findByWorktree(worktreeName)
|
|
61
|
+
if (existing.length > 0) {
|
|
62
|
+
p.log.error(`A dev server is already running for "${worktreeName}" (ID: ${existing[0].id}, PID: ${existing[0].pid}).`)
|
|
63
|
+
p.log.info(`To stop it: npx storyboard server stop ${existing[0].id}`)
|
|
64
|
+
p.log.info(`To allow multiple: npx storyboard server start ${worktreeName} --multiple`)
|
|
65
|
+
process.exit(1)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const devDomain = readDevDomain()
|
|
70
|
+
const port = flags.port || SERVER_PORT
|
|
71
|
+
|
|
72
|
+
// Register server itself with Caddy
|
|
33
73
|
try {
|
|
34
74
|
const serverRoute = generateRouteConfig({ __server__: port })
|
|
35
75
|
if (isCaddyRunning()) {
|
|
@@ -37,33 +77,116 @@ async function main() {
|
|
|
37
77
|
}
|
|
38
78
|
} catch { /* Caddy not available */ }
|
|
39
79
|
|
|
40
|
-
|
|
80
|
+
try {
|
|
81
|
+
await startServer(port)
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (err.code === 'EADDRINUSE') {
|
|
84
|
+
p.log.error(`Port ${port} is already in use — another server may be running.`)
|
|
85
|
+
p.log.info('Try: npx storyboard server list')
|
|
86
|
+
process.exit(1)
|
|
87
|
+
}
|
|
88
|
+
throw err
|
|
89
|
+
}
|
|
41
90
|
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const isMain = initialBranch === 'main'
|
|
45
|
-
const basePath = isMain ? '/' : `/branch--${initialBranch}/`
|
|
91
|
+
const isMain = worktreeName === 'main'
|
|
92
|
+
const basePath = isMain ? '/' : `/branch--${worktreeName}/`
|
|
46
93
|
const proxyUrl = `http://${devDomain}${basePath}`
|
|
47
94
|
|
|
48
|
-
p.log.step(`Starting dev session for ${
|
|
95
|
+
p.log.step(`Starting dev session for ${worktreeName}...`)
|
|
49
96
|
|
|
50
97
|
try {
|
|
51
|
-
const entry = spawnViteForBranch(
|
|
52
|
-
|
|
53
|
-
// Wait for ready
|
|
98
|
+
const entry = spawnViteForBranch(worktreeName)
|
|
54
99
|
const { waitForPort } = await import('../server/index.js')
|
|
55
100
|
const ready = await waitForPort(entry.port)
|
|
56
101
|
|
|
57
102
|
if (ready) {
|
|
58
|
-
p.log.success(proxyUrl)
|
|
103
|
+
p.log.success(`[${entry.serverId}] ${proxyUrl}`)
|
|
59
104
|
} else {
|
|
60
105
|
p.log.warning(`Vite started but may not be ready yet — check ${proxyUrl}`)
|
|
61
106
|
}
|
|
62
107
|
} catch (err) {
|
|
63
|
-
p.log.error(`Failed to start dev for ${
|
|
108
|
+
p.log.error(`Failed to start dev for ${worktreeName}: ${err.message}`)
|
|
64
109
|
}
|
|
65
110
|
|
|
66
111
|
p.outro('Server running')
|
|
67
112
|
}
|
|
68
113
|
|
|
114
|
+
function serverStop(target) {
|
|
115
|
+
if (!target) {
|
|
116
|
+
p.log.error('Usage: storyboard server stop <worktree|ID>')
|
|
117
|
+
process.exit(1)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Try by ID first
|
|
121
|
+
let entry = findById(target)
|
|
122
|
+
|
|
123
|
+
// Then by worktree name
|
|
124
|
+
if (!entry) {
|
|
125
|
+
const matches = findByWorktree(target)
|
|
126
|
+
if (matches.length === 0) {
|
|
127
|
+
p.log.error(`No server found for "${target}".`)
|
|
128
|
+
p.log.info('Run `npx storyboard server list` to see running servers.')
|
|
129
|
+
process.exit(1)
|
|
130
|
+
}
|
|
131
|
+
if (matches.length > 1) {
|
|
132
|
+
p.log.error(`Multiple servers running for worktree "${target}":`)
|
|
133
|
+
for (const s of matches) {
|
|
134
|
+
console.log(` ${s.id} PID: ${s.pid} Port: ${s.port}`)
|
|
135
|
+
}
|
|
136
|
+
p.log.info('Specify an ID: npx storyboard server stop <ID>')
|
|
137
|
+
process.exit(1)
|
|
138
|
+
}
|
|
139
|
+
entry = matches[0]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
process.kill(entry.pid, 'SIGTERM')
|
|
144
|
+
p.log.success(`Stopped server ${entry.id} (PID: ${entry.pid}, worktree: "${entry.worktree}")`)
|
|
145
|
+
} catch (err) {
|
|
146
|
+
if (err.code === 'ESRCH') {
|
|
147
|
+
p.log.info(`Server ${entry.id} (PID: ${entry.pid}) was already dead.`)
|
|
148
|
+
} else {
|
|
149
|
+
p.log.error(`Failed to kill PID ${entry.pid}: ${err.message}`)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
unregister(entry.id)
|
|
154
|
+
releasePort(entry.worktree)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ─── Dispatch ────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
async function main() {
|
|
160
|
+
const { flags, positional } = parseFlags(process.argv.slice(3), flagSchema)
|
|
161
|
+
const subcommand = positional[0]
|
|
162
|
+
|
|
163
|
+
p.intro('storyboard server')
|
|
164
|
+
|
|
165
|
+
switch (subcommand) {
|
|
166
|
+
case undefined:
|
|
167
|
+
case 'list':
|
|
168
|
+
serverList()
|
|
169
|
+
break
|
|
170
|
+
case 'start':
|
|
171
|
+
await serverStart(positional[1], flags)
|
|
172
|
+
break
|
|
173
|
+
case 'stop':
|
|
174
|
+
serverStop(positional[1])
|
|
175
|
+
break
|
|
176
|
+
default: {
|
|
177
|
+
// Catch unknown subcommands before falling through to legacy branch behavior
|
|
178
|
+
const knownSubcommands = ['list', 'start', 'stop']
|
|
179
|
+
if (subcommand && !subcommand.match(/^[a-z0-9]/) || ['exit', 'help', 'status'].includes(subcommand)) {
|
|
180
|
+
p.log.error(`Unknown subcommand: "${subcommand}"`)
|
|
181
|
+
p.log.info(`Available: ${knownSubcommands.join(', ')}`)
|
|
182
|
+
p.log.info(`To start a branch: npx storyboard server start <branch>`)
|
|
183
|
+
process.exit(1)
|
|
184
|
+
}
|
|
185
|
+
// Legacy behavior: treat argument as branch name (storyboard server <branch>)
|
|
186
|
+
await serverStart(subcommand, flags)
|
|
187
|
+
break
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
69
192
|
main()
|
package/src/cli/serverUrl.js
CHANGED
|
@@ -6,11 +6,16 @@
|
|
|
6
6
|
* dev server. Falls back to ports.json if Caddy isn't reachable.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { detectWorktreeName,
|
|
9
|
+
import { detectWorktreeName, resolveRunningPort } from '../worktree/port.js'
|
|
10
10
|
import { readDevDomain } from './proxy.js'
|
|
11
11
|
import { execSync } from 'child_process'
|
|
12
12
|
|
|
13
13
|
export function getServerUrl() {
|
|
14
|
+
// Prefer explicit env var (set by terminal agent sessions)
|
|
15
|
+
if (process.env.STORYBOARD_SERVER_URL) {
|
|
16
|
+
return process.env.STORYBOARD_SERVER_URL.replace(/\/$/, '')
|
|
17
|
+
}
|
|
18
|
+
|
|
14
19
|
const name = detectWorktreeName()
|
|
15
20
|
|
|
16
21
|
// Try Caddy admin API for the real port
|
|
@@ -50,7 +55,7 @@ export function getServerUrl() {
|
|
|
50
55
|
// Caddy not running or not reachable — fall through
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
// Fallback to ports.json
|
|
54
|
-
const port =
|
|
58
|
+
// Fallback to server registry / ports.json
|
|
59
|
+
const port = resolveRunningPort(name)
|
|
55
60
|
return `http://localhost:${port}`
|
|
56
61
|
}
|
package/src/cli/sessions.js
CHANGED
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
|
|
13
13
|
import * as p from '@clack/prompts'
|
|
14
14
|
import { execSync as execSyncFn } from 'node:child_process'
|
|
15
|
-
import { detectWorktreeName,
|
|
15
|
+
import { detectWorktreeName, resolveRunningPort } from '../worktree/port.js'
|
|
16
16
|
import { readDevDomain } from './proxy.js'
|
|
17
17
|
import { parseFlags } from './flags.js'
|
|
18
18
|
import { dim, cyan, bold, yellow } from './intro.js'
|
|
19
19
|
|
|
20
20
|
const blue = (s) => `\x1b[34m${s}\x1b[0m`
|
|
21
21
|
const orange = (s) => `\x1b[38;5;180m${s}\x1b[0m`
|
|
22
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`
|
|
22
23
|
|
|
23
24
|
const flagSchema = {
|
|
24
25
|
all: { type: 'boolean', description: 'Show sessions from all branches' },
|
|
@@ -74,13 +75,13 @@ function summaryText(entry, showCanvas = true) {
|
|
|
74
75
|
return canvas ? `${canvas} › ${name}` : name
|
|
75
76
|
}
|
|
76
77
|
|
|
77
|
-
/** Detect the current tmux session name (
|
|
78
|
+
/** Detect the current tmux session name (only if it's a storyboard session) */
|
|
78
79
|
function getCurrentTmuxSession() {
|
|
79
80
|
try {
|
|
80
81
|
const result = execSyncFn('tmux display-message -p "#{session_name}" 2>/dev/null', {
|
|
81
82
|
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
82
|
-
})
|
|
83
|
-
return result.
|
|
83
|
+
}).trim()
|
|
84
|
+
return result.startsWith('sb-') ? result : null
|
|
84
85
|
} catch {
|
|
85
86
|
return null
|
|
86
87
|
}
|
|
@@ -126,9 +127,130 @@ function formatRow(idx, entry, isCurrent = false, showCanvas = true) {
|
|
|
126
127
|
return ` ${dim(num)}${statusColored}${dim(modified)}${dim(created)}${summaryColored}${badges}`
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
/** Call the bulk cleanup API */
|
|
131
|
+
async function cleanupSessions(worktreeName, port, statuses) {
|
|
132
|
+
const { proxyBase, directBase } = getBaseUrl(worktreeName, port)
|
|
133
|
+
for (const base of [proxyBase, directBase]) {
|
|
134
|
+
try {
|
|
135
|
+
const res = await fetch(`${base}_storyboard/terminal/sessions/cleanup`, {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
headers: { 'Content-Type': 'application/json' },
|
|
138
|
+
body: JSON.stringify({ statuses }),
|
|
139
|
+
signal: AbortSignal.timeout(5000),
|
|
140
|
+
})
|
|
141
|
+
if (res.ok) return await res.json()
|
|
142
|
+
} catch { continue }
|
|
143
|
+
}
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Wait for a single keypress and return it. Returns null if not a TTY. */
|
|
148
|
+
function waitForKey() {
|
|
149
|
+
const { stdin } = process
|
|
150
|
+
if (!stdin.isTTY) return Promise.resolve(null)
|
|
151
|
+
return new Promise((resolve) => {
|
|
152
|
+
const wasRaw = stdin.isRaw
|
|
153
|
+
stdin.setRawMode(true)
|
|
154
|
+
stdin.resume()
|
|
155
|
+
stdin.once('data', (data) => {
|
|
156
|
+
stdin.setRawMode(wasRaw)
|
|
157
|
+
stdin.pause()
|
|
158
|
+
resolve(data)
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Count sessions by status */
|
|
164
|
+
function countByStatus(sessions) {
|
|
165
|
+
let live = 0, background = 0, archived = 0
|
|
166
|
+
for (const s of sessions) {
|
|
167
|
+
if (s.status === 'live') live++
|
|
168
|
+
else if (s.status === 'background') background++
|
|
169
|
+
else if (s.status === 'archived') archived++
|
|
170
|
+
}
|
|
171
|
+
return { live, background, archived }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Show cleanup pre-prompt. Returns true if user chose cleanup (and it was handled). */
|
|
175
|
+
async function showCleanupPrompt(sessions, worktreeName, port) {
|
|
176
|
+
const { live, background, archived } = countByStatus(sessions)
|
|
177
|
+
const cleanable = background + archived
|
|
178
|
+
if (cleanable === 0) return false
|
|
179
|
+
|
|
180
|
+
const parts = []
|
|
181
|
+
if (live > 0) parts.push(blue(`${live} live`))
|
|
182
|
+
if (background > 0) parts.push(orange(`${background} background`))
|
|
183
|
+
if (archived > 0) parts.push(dim(`${archived} archived`))
|
|
184
|
+
|
|
185
|
+
p.log.info(`${sessions.length} sessions: ${parts.join(dim(' · '))}`)
|
|
186
|
+
process.stdout.write(`\n ${dim('Press')} Tab ${dim('to clean up sessions, or')} Enter ${dim('to browse')}\n\n`)
|
|
187
|
+
|
|
188
|
+
const key = await waitForKey()
|
|
189
|
+
if (!key) return false // non-TTY, skip pre-prompt
|
|
190
|
+
const keyStr = key.toString()
|
|
191
|
+
|
|
192
|
+
// Tab = \t, Enter = \r or \n, Ctrl+C = \x03
|
|
193
|
+
if (keyStr === '\x03') {
|
|
194
|
+
p.outro(dim('Done'))
|
|
195
|
+
process.exit(0)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (keyStr !== '\t') return false // Enter or any other key → continue to session list
|
|
199
|
+
|
|
200
|
+
// Build cleanup options
|
|
201
|
+
const options = []
|
|
202
|
+
if (archived > 0) {
|
|
203
|
+
options.push({
|
|
204
|
+
value: 'archived',
|
|
205
|
+
label: `Remove ${bold(String(archived))} archived session${archived !== 1 ? 's' : ''}`,
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
if (cleanable > 0 && (archived > 0 && background > 0)) {
|
|
209
|
+
options.push({
|
|
210
|
+
value: 'all',
|
|
211
|
+
label: `Remove ${bold(String(cleanable))} archived + background sessions`,
|
|
212
|
+
})
|
|
213
|
+
} else if (background > 0 && archived === 0) {
|
|
214
|
+
options.push({
|
|
215
|
+
value: 'all',
|
|
216
|
+
label: `Remove ${bold(String(background))} background session${background !== 1 ? 's' : ''}`,
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
options.push({ value: 'cancel', label: dim('Cancel') })
|
|
220
|
+
|
|
221
|
+
const action = await p.select({
|
|
222
|
+
message: 'Clean up sessions',
|
|
223
|
+
options,
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
if (p.isCancel(action) || action === 'cancel') return false
|
|
227
|
+
|
|
228
|
+
const statuses = action === 'archived' ? ['archived'] : ['archived', 'background']
|
|
229
|
+
const targetCount = action === 'archived' ? archived : cleanable
|
|
230
|
+
const statusLabel = action === 'archived' ? 'archived' : 'archived + background'
|
|
231
|
+
|
|
232
|
+
const confirm = await p.confirm({
|
|
233
|
+
message: `Remove ${bold(String(targetCount))} ${statusLabel} session${targetCount !== 1 ? 's' : ''}? This cannot be undone.`,
|
|
234
|
+
})
|
|
235
|
+
if (p.isCancel(confirm) || !confirm) return false
|
|
236
|
+
|
|
237
|
+
const result = await cleanupSessions(worktreeName, port, statuses)
|
|
238
|
+
if (result && result.success) {
|
|
239
|
+
p.log.success(green(`Removed ${result.removed} session${result.removed !== 1 ? 's' : ''}`))
|
|
240
|
+
const rem = result.remaining
|
|
241
|
+
if (rem && rem.total > 0) {
|
|
242
|
+
p.log.info(dim(`${rem.total} remaining: ${rem.live} live, ${rem.background} background, ${rem.archived} archived`))
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
p.log.error('Cleanup failed — could not reach dev server')
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return true // handled — re-enter loop to refresh list
|
|
249
|
+
}
|
|
250
|
+
|
|
129
251
|
async function main() {
|
|
130
252
|
const worktreeName = detectWorktreeName()
|
|
131
|
-
const port =
|
|
253
|
+
const port = resolveRunningPort(worktreeName)
|
|
132
254
|
const currentTmuxSession = getCurrentTmuxSession()
|
|
133
255
|
|
|
134
256
|
// Session list loop — user can navigate back here after actions
|
|
@@ -155,6 +277,10 @@ async function main() {
|
|
|
155
277
|
process.exit(0)
|
|
156
278
|
}
|
|
157
279
|
|
|
280
|
+
// Show Tab cleanup pre-prompt when there are cleanable sessions
|
|
281
|
+
const didCleanup = await showCleanupPrompt(sessions, worktreeName, port)
|
|
282
|
+
if (didCleanup) continue // re-fetch and re-render after cleanup
|
|
283
|
+
|
|
158
284
|
// Determine if all sessions are from the same canvas (hide canvas name if so)
|
|
159
285
|
const canvasIds = new Set(sessions.map(s => s.canvasId).filter(c => c && c !== 'unknown'))
|
|
160
286
|
const showCanvas = canvasIds.size > 1
|