@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/canvas/writeGuard.js
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Canvas write guard — coordinates the canvas server with Vite's file watcher
|
|
3
|
+
* to prevent duplicate HMR events.
|
|
4
|
+
*
|
|
5
|
+
* When the canvas server writes to a .canvas.jsonl file, it immediately pushes
|
|
6
|
+
* an HMR event via pushCanvasUpdate(). Vite's file watcher also detects the
|
|
7
|
+
* change ~100-500ms later and the data plugin sends a second HMR event. This
|
|
8
|
+
* duplicate event can cause visible rollbacks when the client has made new edits
|
|
9
|
+
* in the intervening window.
|
|
10
|
+
*
|
|
11
|
+
* The guard tracks files currently being written by the canvas server. The data
|
|
12
|
+
* plugin checks this before sending watcher-triggered HMR events and skips them
|
|
13
|
+
* if the server has already pushed.
|
|
14
|
+
*
|
|
15
|
+
* Uses globalThis with a Symbol key to guarantee the same Map instance is shared
|
|
16
|
+
* across all import paths (e.g., relative imports from server.js vs package
|
|
17
|
+
* imports from data-plugin.js).
|
|
4
18
|
*/
|
|
5
|
-
|
|
19
|
+
|
|
20
|
+
const KEY = Symbol.for('sb:canvasWriteGuard')
|
|
21
|
+
if (!globalThis[KEY]) globalThis[KEY] = new Map()
|
|
22
|
+
|
|
23
|
+
/** @type {Map<string, number>} filePath → active write count */
|
|
24
|
+
const guard = globalThis[KEY]
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Mark a file as being written by the canvas server.
|
|
28
|
+
* Call before appendEvent / writeFileSync.
|
|
29
|
+
*/
|
|
30
|
+
export function markCanvasWrite(filePath) {
|
|
31
|
+
guard.set(filePath, (guard.get(filePath) || 0) + 1)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Unmark a file after the watcher has had time to fire.
|
|
36
|
+
* Call via setTimeout after the write + push completes.
|
|
37
|
+
*/
|
|
38
|
+
export function unmarkCanvasWrite(filePath) {
|
|
39
|
+
const count = (guard.get(filePath) || 1) - 1
|
|
40
|
+
if (count <= 0) guard.delete(filePath)
|
|
41
|
+
else guard.set(filePath, count)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if a file is currently being written by the canvas server.
|
|
46
|
+
* The data plugin uses this to skip duplicate watcher-triggered HMR events.
|
|
47
|
+
*/
|
|
48
|
+
export function isCanvasWriteInFlight(filePath) {
|
|
49
|
+
return guard.has(filePath)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Legacy export for backward compatibility
|
|
53
|
+
export const canvasWritesInFlight = guard
|
package/src/canvasConfig.js
CHANGED
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
|
|
22
22
|
let _pasteRules = []
|
|
23
23
|
let _terminal = {}
|
|
24
|
+
let _agents = {}
|
|
25
|
+
let _zoom = { min: 10, max: 250, step: 10 }
|
|
24
26
|
|
|
25
27
|
// ---------------------------------------------------------------------------
|
|
26
28
|
// Configuration
|
|
@@ -30,11 +32,15 @@ let _terminal = {}
|
|
|
30
32
|
* Initialize canvas config from storyboard.config.json's "canvas" key.
|
|
31
33
|
* Called by mountStoryboardCore.
|
|
32
34
|
*
|
|
33
|
-
* @param {{ pasteRules?: object[], terminal?: object }} [config]
|
|
35
|
+
* @param {{ pasteRules?: object[], terminal?: object, agents?: object, zoom?: object }} [config]
|
|
34
36
|
*/
|
|
35
37
|
export function initCanvasConfig(config = {}) {
|
|
36
38
|
_pasteRules = Array.isArray(config.pasteRules) ? config.pasteRules : []
|
|
37
39
|
_terminal = config.terminal && typeof config.terminal === 'object' ? config.terminal : {}
|
|
40
|
+
_agents = config.agents && typeof config.agents === 'object' ? config.agents : {}
|
|
41
|
+
_zoom = config.zoom && typeof config.zoom === 'object'
|
|
42
|
+
? { min: config.zoom.min ?? 10, max: config.zoom.max ?? 250, step: config.zoom.step ?? 10 }
|
|
43
|
+
: { min: 10, max: 250, step: 10 }
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
/**
|
|
@@ -55,6 +61,64 @@ export function getTerminalConfig() {
|
|
|
55
61
|
return _terminal
|
|
56
62
|
}
|
|
57
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Get agent configurations.
|
|
66
|
+
*
|
|
67
|
+
* @returns {object}
|
|
68
|
+
*/
|
|
69
|
+
export function getAgentsConfig() {
|
|
70
|
+
return _agents
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get canvas zoom configuration (min, max, step).
|
|
75
|
+
*
|
|
76
|
+
* @returns {{ min: number, max: number, step: number }}
|
|
77
|
+
*/
|
|
78
|
+
export function getCanvasZoom() {
|
|
79
|
+
return _zoom
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Check if a terminal/agent widget should be resizable based on config.
|
|
84
|
+
* Agent-level `resizable` overrides the base `terminal.resizable`.
|
|
85
|
+
*
|
|
86
|
+
* @param {string|null} [agentId] — agent ID to check for overrides
|
|
87
|
+
* @returns {boolean}
|
|
88
|
+
*/
|
|
89
|
+
export function isTerminalResizable(agentId = null) {
|
|
90
|
+
if (agentId) {
|
|
91
|
+
const agentCfg = _agents[agentId]
|
|
92
|
+
if (agentCfg && agentCfg.resizable !== undefined) return agentCfg.resizable
|
|
93
|
+
}
|
|
94
|
+
return _terminal.resizable ?? false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get effective default dimensions for a terminal/agent widget.
|
|
99
|
+
* Cascade: agent config > terminal config > provided fallbacks.
|
|
100
|
+
*
|
|
101
|
+
* @param {string|null} [agentId] — agent ID to check for overrides
|
|
102
|
+
* @param {{ width: number, height: number }} [fallback] — schema-level fallbacks
|
|
103
|
+
* @returns {{ width: number, height: number }}
|
|
104
|
+
*/
|
|
105
|
+
export function getTerminalDimensions(agentId = null, fallback = { width: 800, height: 450 }) {
|
|
106
|
+
const base = {
|
|
107
|
+
width: _terminal.defaultWidth ?? fallback.width,
|
|
108
|
+
height: _terminal.defaultHeight ?? fallback.height,
|
|
109
|
+
}
|
|
110
|
+
if (agentId) {
|
|
111
|
+
const agentCfg = _agents[agentId]
|
|
112
|
+
if (agentCfg) {
|
|
113
|
+
return {
|
|
114
|
+
width: agentCfg.defaultWidth ?? base.width,
|
|
115
|
+
height: agentCfg.defaultHeight ?? base.height,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return base
|
|
120
|
+
}
|
|
121
|
+
|
|
58
122
|
// ---------------------------------------------------------------------------
|
|
59
123
|
// Test helpers
|
|
60
124
|
// ---------------------------------------------------------------------------
|
|
@@ -65,4 +129,6 @@ export function getTerminalConfig() {
|
|
|
65
129
|
export function _resetCanvasConfig() {
|
|
66
130
|
_pasteRules = []
|
|
67
131
|
_terminal = {}
|
|
132
|
+
_agents = {}
|
|
133
|
+
_zoom = { min: 10, max: 250, step: 10 }
|
|
68
134
|
}
|
package/src/canvasConfig.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest'
|
|
2
|
-
import { initCanvasConfig, getPasteRules, _resetCanvasConfig } from './canvasConfig.js'
|
|
2
|
+
import { initCanvasConfig, getPasteRules, isTerminalResizable, getTerminalDimensions, _resetCanvasConfig } from './canvasConfig.js'
|
|
3
3
|
|
|
4
4
|
describe('canvasConfig', () => {
|
|
5
5
|
beforeEach(() => {
|
|
@@ -40,3 +40,81 @@ describe('canvasConfig', () => {
|
|
|
40
40
|
expect(getPasteRules()).toEqual([])
|
|
41
41
|
})
|
|
42
42
|
})
|
|
43
|
+
|
|
44
|
+
describe('isTerminalResizable', () => {
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
_resetCanvasConfig()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('returns false by default', () => {
|
|
50
|
+
expect(isTerminalResizable()).toBe(false)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('returns terminal.resizable when set', () => {
|
|
54
|
+
initCanvasConfig({ terminal: { resizable: true } })
|
|
55
|
+
expect(isTerminalResizable()).toBe(true)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('agent overrides terminal resizable', () => {
|
|
59
|
+
initCanvasConfig({
|
|
60
|
+
terminal: { resizable: false },
|
|
61
|
+
agents: { myAgent: { resizable: true } },
|
|
62
|
+
})
|
|
63
|
+
expect(isTerminalResizable('myAgent')).toBe(true)
|
|
64
|
+
expect(isTerminalResizable()).toBe(false)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('agent can disable resizable even when terminal enables it', () => {
|
|
68
|
+
initCanvasConfig({
|
|
69
|
+
terminal: { resizable: true },
|
|
70
|
+
agents: { fixedAgent: { resizable: false } },
|
|
71
|
+
})
|
|
72
|
+
expect(isTerminalResizable('fixedAgent')).toBe(false)
|
|
73
|
+
expect(isTerminalResizable()).toBe(true)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('falls back to terminal config for unknown agent', () => {
|
|
77
|
+
initCanvasConfig({ terminal: { resizable: true } })
|
|
78
|
+
expect(isTerminalResizable('nonexistent')).toBe(true)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('getTerminalDimensions', () => {
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
_resetCanvasConfig()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('returns fallback defaults when no config', () => {
|
|
88
|
+
expect(getTerminalDimensions()).toEqual({ width: 800, height: 450 })
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('returns terminal config dimensions', () => {
|
|
92
|
+
initCanvasConfig({ terminal: { defaultWidth: 1000, defaultHeight: 600 } })
|
|
93
|
+
expect(getTerminalDimensions()).toEqual({ width: 1000, height: 600 })
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('agent overrides terminal dimensions', () => {
|
|
97
|
+
initCanvasConfig({
|
|
98
|
+
terminal: { defaultWidth: 1000, defaultHeight: 600 },
|
|
99
|
+
agents: { bigAgent: { defaultWidth: 1400, defaultHeight: 800 } },
|
|
100
|
+
})
|
|
101
|
+
expect(getTerminalDimensions('bigAgent')).toEqual({ width: 1400, height: 800 })
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('agent partial override inherits from terminal for unset dimensions', () => {
|
|
105
|
+
initCanvasConfig({
|
|
106
|
+
terminal: { defaultWidth: 1000, defaultHeight: 600 },
|
|
107
|
+
agents: { wideAgent: { defaultWidth: 1400 } },
|
|
108
|
+
})
|
|
109
|
+
expect(getTerminalDimensions('wideAgent')).toEqual({ width: 1400, height: 600 })
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('falls back to terminal config for unknown agent', () => {
|
|
113
|
+
initCanvasConfig({ terminal: { defaultWidth: 900, defaultHeight: 500 } })
|
|
114
|
+
expect(getTerminalDimensions('nonexistent')).toEqual({ width: 900, height: 500 })
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('uses custom fallback when provided', () => {
|
|
118
|
+
expect(getTerminalDimensions(null, { width: 1200, height: 450 })).toEqual({ width: 1200, height: 450 })
|
|
119
|
+
})
|
|
120
|
+
})
|
package/src/cli/agent.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* storyboard agent signal — signal agent status to the canvas server.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx storyboard agent signal --widget <id> --canvas <canvasId> --status done|error|running [--message "..."]
|
|
7
|
+
*
|
|
8
|
+
* Environment variables (auto-set by terminal server):
|
|
9
|
+
* STORYBOARD_WIDGET_ID — widget ID (fallback if --widget not provided)
|
|
10
|
+
* STORYBOARD_CANVAS_ID — canvas ID (fallback if --canvas not provided)
|
|
11
|
+
* STORYBOARD_SERVER_URL — server base URL
|
|
12
|
+
* STORYBOARD_BRANCH — current branch
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { parseFlags } from './flags.js'
|
|
16
|
+
import { dim, bold, cyan, yellow } from './intro.js'
|
|
17
|
+
|
|
18
|
+
const subcommand = process.argv[3]
|
|
19
|
+
|
|
20
|
+
if (subcommand === 'signal') {
|
|
21
|
+
const flagSchema = {
|
|
22
|
+
widget: { type: 'string', description: 'Widget ID' },
|
|
23
|
+
canvas: { type: 'string', description: 'Canvas ID' },
|
|
24
|
+
status: { type: 'string', required: true, description: 'Status: done, error, or running' },
|
|
25
|
+
message: { type: 'string', description: 'Optional status message' },
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { flags } = parseFlags(process.argv.slice(4), flagSchema)
|
|
29
|
+
|
|
30
|
+
const widgetId = flags.widget || process.env.STORYBOARD_WIDGET_ID
|
|
31
|
+
const canvasId = flags.canvas || process.env.STORYBOARD_CANVAS_ID
|
|
32
|
+
const status = flags.status
|
|
33
|
+
const message = flags.message || null
|
|
34
|
+
const serverUrl = process.env.STORYBOARD_SERVER_URL || 'http://localhost:1234'
|
|
35
|
+
const branch = process.env.STORYBOARD_BRANCH || 'unknown'
|
|
36
|
+
|
|
37
|
+
if (!widgetId || !canvasId || !status) {
|
|
38
|
+
console.error(`${bold('Usage:')} npx storyboard agent signal --status done|error|running`)
|
|
39
|
+
console.error(`${dim('Widget and canvas IDs are read from environment if not provided.')}`)
|
|
40
|
+
process.exit(1)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const validStatuses = ['done', 'error', 'running']
|
|
44
|
+
if (!validStatuses.includes(status)) {
|
|
45
|
+
console.error(`${bold('Error:')} Status must be one of: ${validStatuses.join(', ')}`)
|
|
46
|
+
process.exit(1)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const url = `${serverUrl}/_storyboard/canvas/agent/signal`
|
|
51
|
+
const res = await fetch(url, {
|
|
52
|
+
method: 'POST',
|
|
53
|
+
headers: { 'Content-Type': 'application/json' },
|
|
54
|
+
body: JSON.stringify({ widgetId, canvasId, branch, status, message }),
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
if (res.ok) {
|
|
58
|
+
console.log(`${cyan('✓')} Agent status: ${bold(status)}${message ? ` — ${message}` : ''}`)
|
|
59
|
+
} else {
|
|
60
|
+
const data = await res.json().catch(() => ({}))
|
|
61
|
+
console.error(`${yellow('⚠')} Server returned ${res.status}: ${data.error || 'unknown error'}`)
|
|
62
|
+
// Fallback: write directly to terminal config
|
|
63
|
+
await fallbackWrite({ branch, canvasId, widgetId, status, message })
|
|
64
|
+
}
|
|
65
|
+
} catch {
|
|
66
|
+
// Server not reachable — write directly to terminal config file
|
|
67
|
+
await fallbackWrite({ branch, canvasId, widgetId, status, message })
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
console.error(`${bold('Usage:')} npx storyboard agent <signal>`)
|
|
71
|
+
console.error(`${dim('Subcommands: signal')}`)
|
|
72
|
+
process.exit(1)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function fallbackWrite({ branch, canvasId, widgetId, status, message }) {
|
|
76
|
+
try {
|
|
77
|
+
const { updateAgentStatus, initTerminalConfig } = await import('../canvas/terminal-config.js')
|
|
78
|
+
initTerminalConfig(process.cwd())
|
|
79
|
+
updateAgentStatus({ branch, canvasId, widgetId, status, message })
|
|
80
|
+
console.log(`${cyan('✓')} Agent status written to config file (server offline): ${bold(status)}`)
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.error(`Failed to write agent status: ${err.message}`)
|
|
83
|
+
process.exit(1)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storyboard branch — Interactive guide to switch to a branch worktree.
|
|
3
|
+
*
|
|
4
|
+
* Deterministic flow (no AI):
|
|
5
|
+
* 1. Ask which branch to work on
|
|
6
|
+
* 2. Stash uncommitted work (named stash for safety)
|
|
7
|
+
* 3. Create worktree if needed (git worktree add + npm install)
|
|
8
|
+
* 4. Pull --rebase from origin
|
|
9
|
+
* 5. Apply stash into the new worktree
|
|
10
|
+
* 6. Confirm to user
|
|
11
|
+
*
|
|
12
|
+
* Also available as post-setup prompt in setup.js.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* npx storyboard branch
|
|
16
|
+
* npx storyboard branch <name> # skip the prompt
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import * as p from '@clack/prompts'
|
|
20
|
+
import { execFileSync } from 'child_process'
|
|
21
|
+
import { existsSync } from 'fs'
|
|
22
|
+
import { resolve } from 'path'
|
|
23
|
+
import { repoRoot, worktreeDir, listWorktrees, getPort, detectWorktreeName } from '../worktree/port.js'
|
|
24
|
+
import { hasUncommittedChanges, localBranchExists } from './dev-helpers.js'
|
|
25
|
+
import { dim, green, yellow, bold, cyan } from './intro.js'
|
|
26
|
+
|
|
27
|
+
/** Check if a remote branch exists on origin. */
|
|
28
|
+
function remoteBranchExists(name, cwd) {
|
|
29
|
+
try {
|
|
30
|
+
const result = execFileSync('git', ['ls-remote', '--exit-code', '--heads', 'origin', name], { cwd, encoding: 'utf8' })
|
|
31
|
+
return result.trim().length > 0
|
|
32
|
+
} catch {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Get the current branch name. */
|
|
38
|
+
function currentBranch(cwd) {
|
|
39
|
+
try {
|
|
40
|
+
return execFileSync('git', ['branch', '--show-current'], { cwd, encoding: 'utf8' }).trim()
|
|
41
|
+
} catch {
|
|
42
|
+
return 'unknown'
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Validate a branch name for git. */
|
|
47
|
+
function isValidBranchName(name) {
|
|
48
|
+
if (!name || name.trim().length === 0) return 'Branch name cannot be empty'
|
|
49
|
+
const n = name.trim()
|
|
50
|
+
if (/\s/.test(n)) return 'Branch name cannot contain spaces'
|
|
51
|
+
if (/\.\./.test(n)) return 'Branch name cannot contain ".."'
|
|
52
|
+
if (/[~^:\\]/.test(n)) return 'Branch name cannot contain ~, ^, :, or \\'
|
|
53
|
+
if (n.startsWith('-')) return 'Branch name cannot start with "-"'
|
|
54
|
+
if (n.endsWith('.lock')) return 'Branch name cannot end with ".lock"'
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function runBranchGuide(branchArg) {
|
|
59
|
+
const root = repoRoot()
|
|
60
|
+
const existing = listWorktrees()
|
|
61
|
+
const fromBranch = currentBranch(root)
|
|
62
|
+
const fromWorktree = detectWorktreeName()
|
|
63
|
+
|
|
64
|
+
// 1. Get branch name
|
|
65
|
+
let targetBranch = branchArg?.trim()
|
|
66
|
+
|
|
67
|
+
if (!targetBranch) {
|
|
68
|
+
if (existing.length > 0) {
|
|
69
|
+
// Render as columns that fit the terminal width
|
|
70
|
+
const maxLen = Math.max(...existing.map(w => w.length))
|
|
71
|
+
const colWidth = maxLen + 2
|
|
72
|
+
const termWidth = process.stdout.columns || 80
|
|
73
|
+
const cols = Math.max(1, Math.floor(termWidth / colWidth))
|
|
74
|
+
const rows = Math.ceil(existing.length / cols)
|
|
75
|
+
const lines = []
|
|
76
|
+
for (let r = 0; r < rows; r++) {
|
|
77
|
+
const parts = []
|
|
78
|
+
for (let c = 0; c < cols; c++) {
|
|
79
|
+
const idx = c * rows + r
|
|
80
|
+
if (idx < existing.length) {
|
|
81
|
+
parts.push(cyan(existing[idx].padEnd(colWidth)))
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
lines.push(` ${parts.join('')}`)
|
|
85
|
+
}
|
|
86
|
+
p.log.info(`${dim('Existing worktrees:')}\n${lines.join('\n')}`)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = await p.text({
|
|
90
|
+
message: 'Which branch do you want to work on? Select one from above or type a new one',
|
|
91
|
+
placeholder: 'e.g. 4.3.0--my-feature',
|
|
92
|
+
validate: isValidBranchName,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
if (p.isCancel(result)) {
|
|
96
|
+
p.cancel('Cancelled')
|
|
97
|
+
process.exit(0)
|
|
98
|
+
}
|
|
99
|
+
targetBranch = result.trim()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 2. Check if worktree already exists
|
|
103
|
+
const wtDir = worktreeDir(targetBranch)
|
|
104
|
+
if (existsSync(resolve(wtDir, '.git'))) {
|
|
105
|
+
p.log.success(`Worktree ${bold(targetBranch)} already exists`)
|
|
106
|
+
p.note(
|
|
107
|
+
[
|
|
108
|
+
` ${green('cd')} ${dim(`.worktrees/${targetBranch}`)}`,
|
|
109
|
+
` ${green('npx storyboard dev')} ${dim('to start developing')}`,
|
|
110
|
+
].join('\n'),
|
|
111
|
+
'Ready to go'
|
|
112
|
+
)
|
|
113
|
+
p.outro('')
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 3. Stash uncommitted work
|
|
118
|
+
let didStash = false
|
|
119
|
+
const sourceDir = fromWorktree === 'main' ? root : worktreeDir(fromWorktree)
|
|
120
|
+
|
|
121
|
+
if (hasUncommittedChanges(sourceDir)) {
|
|
122
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
|
|
123
|
+
const stashName = `${fromBranch}→${targetBranch}@${timestamp}`
|
|
124
|
+
|
|
125
|
+
p.log.step('Stashing uncommitted work…')
|
|
126
|
+
try {
|
|
127
|
+
execFileSync('git', ['stash', 'push', '-m', stashName], { cwd: sourceDir, stdio: 'pipe' })
|
|
128
|
+
didStash = true
|
|
129
|
+
p.log.success(`Work stashed: ${dim(stashName)}`)
|
|
130
|
+
} catch {
|
|
131
|
+
p.log.warning('Could not stash changes — proceeding anyway')
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 4. Resolve branch and create worktree
|
|
136
|
+
const hasLocal = localBranchExists(targetBranch, root)
|
|
137
|
+
const hasRemote = !hasLocal && remoteBranchExists(targetBranch, root)
|
|
138
|
+
const isNew = !hasLocal && !hasRemote
|
|
139
|
+
|
|
140
|
+
if (isNew) {
|
|
141
|
+
p.log.step(`Creating new branch ${bold(targetBranch)} from HEAD`)
|
|
142
|
+
} else if (hasRemote) {
|
|
143
|
+
// Fetch the remote branch so git worktree add can use it
|
|
144
|
+
p.log.step(`Fetching ${bold(targetBranch)} from origin…`)
|
|
145
|
+
try {
|
|
146
|
+
execFileSync('git', ['fetch', 'origin', targetBranch], { cwd: root, stdio: 'pipe' })
|
|
147
|
+
// Create a local tracking branch
|
|
148
|
+
try {
|
|
149
|
+
execFileSync('git', ['branch', targetBranch, `origin/${targetBranch}`], { cwd: root, stdio: 'pipe' })
|
|
150
|
+
} catch { /* may already exist after fetch */ }
|
|
151
|
+
} catch {
|
|
152
|
+
p.log.warning('Could not fetch from origin — using local state')
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
p.log.step(`Using existing branch ${bold(targetBranch)}`)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Create the worktree
|
|
159
|
+
const targetDir = resolve(root, '.worktrees', targetBranch)
|
|
160
|
+
const spin = p.spinner()
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const gitArgs = isNew
|
|
164
|
+
? ['worktree', 'add', targetDir, '-b', targetBranch]
|
|
165
|
+
: ['worktree', 'add', targetDir, targetBranch]
|
|
166
|
+
|
|
167
|
+
spin.start(`Creating worktree .worktrees/${targetBranch}`)
|
|
168
|
+
execFileSync('git', gitArgs, { cwd: root, stdio: 'pipe' })
|
|
169
|
+
spin.stop(`Worktree created: .worktrees/${targetBranch}`)
|
|
170
|
+
} catch (err) {
|
|
171
|
+
spin.stop('Failed to create worktree')
|
|
172
|
+
p.log.error(err.message || 'git worktree add failed')
|
|
173
|
+
process.exit(1)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Install dependencies
|
|
177
|
+
try {
|
|
178
|
+
spin.start('Installing dependencies…')
|
|
179
|
+
const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
180
|
+
execFileSync(npmBin, ['install'], { cwd: targetDir, stdio: 'pipe' })
|
|
181
|
+
spin.stop('Dependencies installed')
|
|
182
|
+
} catch {
|
|
183
|
+
spin.stop('npm install failed — you may need to run it manually')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Assign a dev server port
|
|
187
|
+
getPort(targetBranch)
|
|
188
|
+
|
|
189
|
+
// 5. Pull --rebase from origin
|
|
190
|
+
if (!isNew) {
|
|
191
|
+
try {
|
|
192
|
+
spin.start('Pulling latest changes…')
|
|
193
|
+
execFileSync('git', ['pull', '--rebase', 'origin', targetBranch], { cwd: targetDir, stdio: 'pipe' })
|
|
194
|
+
spin.stop('Up to date with origin')
|
|
195
|
+
} catch {
|
|
196
|
+
spin.stop(dim('No remote changes (or origin not available)'))
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 6. Apply stash (if we stashed earlier)
|
|
201
|
+
if (didStash) {
|
|
202
|
+
try {
|
|
203
|
+
// Apply (not pop) — keeps the backup in stash list
|
|
204
|
+
execFileSync('git', ['stash', 'apply'], { cwd: targetDir, stdio: 'pipe' })
|
|
205
|
+
p.log.success('Previous work applied to this branch')
|
|
206
|
+
} catch {
|
|
207
|
+
p.log.warning('Stash apply had conflicts — resolve them manually')
|
|
208
|
+
p.log.info(` Your work is safe in ${dim('git stash list')}`)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 7. Summary
|
|
213
|
+
const lines = [
|
|
214
|
+
` Your branch is set up as a worktree in ${green(`.worktrees/${targetBranch}`)}`,
|
|
215
|
+
]
|
|
216
|
+
if (didStash) {
|
|
217
|
+
lines.push(` Your uncommitted work has been safely moved`)
|
|
218
|
+
}
|
|
219
|
+
lines.push('')
|
|
220
|
+
lines.push(` ${green('cd')} ${dim(`.worktrees/${targetBranch}`)}`)
|
|
221
|
+
lines.push(` ${green('npx storyboard dev')} ${dim('to start developing')}`)
|
|
222
|
+
lines.push('')
|
|
223
|
+
lines.push(` ${dim('Tip: ask your AI agent about worktrees — they\'re great!')}`)
|
|
224
|
+
|
|
225
|
+
p.note(lines.join('\n'), `Branch ${bold(targetBranch)} is ready`)
|
|
226
|
+
p.outro('')
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Direct invocation
|
|
230
|
+
const branchArg = process.argv[3] || undefined
|
|
231
|
+
p.intro('storyboard branch')
|
|
232
|
+
runBranchGuide(branchArg)
|
package/src/cli/canvasAdd.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* storyboard canvas add <widget-type> — Add a widget to an existing canvas.
|
|
3
3
|
*
|
|
4
|
+
* Widgets are auto-positioned by default near (in priority order):
|
|
5
|
+
* 1. The active agent/terminal running the command ($STORYBOARD_WIDGET_ID)
|
|
6
|
+
* 2. The user's currently selected widget
|
|
7
|
+
* 3. The center of the user's viewport
|
|
8
|
+
* 4. The last widget on the canvas
|
|
9
|
+
*
|
|
10
|
+
* Use --x/--y for explicit coordinates, or --near <widget-id> to place
|
|
11
|
+
* relative to a specific widget. Use --near false to opt out entirely.
|
|
12
|
+
*
|
|
4
13
|
* Usage:
|
|
5
14
|
* storyboard canvas add sticky-note --canvas my-canvas
|
|
6
|
-
* storyboard canvas add markdown --canvas my-canvas --
|
|
7
|
-
* storyboard canvas add prototype --canvas my-canvas --
|
|
15
|
+
* storyboard canvas add markdown --canvas my-canvas --near widget-abc
|
|
16
|
+
* storyboard canvas add prototype --canvas my-canvas --x 100 --y 200
|
|
17
|
+
* storyboard canvas add sticky-note --canvas my-canvas --near false --x 0 --y 0
|
|
8
18
|
*
|
|
9
19
|
* Known widget types: sticky-note, markdown, prototype
|
|
10
20
|
* The server accepts any type string — new widget types work automatically.
|
|
@@ -44,14 +54,15 @@ async function canvasAdd() {
|
|
|
44
54
|
|
|
45
55
|
const flagMode = hasFlags(rest) || Boolean(widgetType)
|
|
46
56
|
const { flags, errors } = flagMode ? parseFlags(flagTokens, widgetSchema) : { flags: {}, errors: [] }
|
|
57
|
+
const jsonOutput = flags.json || false
|
|
47
58
|
|
|
48
59
|
if (errors.length) {
|
|
49
60
|
for (const e of errors) p.log.error(e)
|
|
50
61
|
process.exit(1)
|
|
51
62
|
}
|
|
52
63
|
|
|
53
|
-
p.intro('storyboard canvas add')
|
|
54
|
-
await ensureDevServer()
|
|
64
|
+
if (!jsonOutput) p.intro('storyboard canvas add')
|
|
65
|
+
await ensureDevServer({ quiet: jsonOutput })
|
|
55
66
|
|
|
56
67
|
// Widget type
|
|
57
68
|
if (!widgetType) {
|
|
@@ -93,9 +104,24 @@ async function canvasAdd() {
|
|
|
93
104
|
return v
|
|
94
105
|
})()
|
|
95
106
|
|
|
107
|
+
// Position flags: only send explicit position if user provided --x/--y.
|
|
108
|
+
// --near=false explicitly opts out of auto-positioning.
|
|
109
|
+
const hasExplicitX = flags.x !== undefined && flags.x !== null
|
|
110
|
+
const hasExplicitY = flags.y !== undefined && flags.y !== null
|
|
111
|
+
const hasExplicitPosition = hasExplicitX || hasExplicitY
|
|
96
112
|
const x = flags.x ?? 0
|
|
97
113
|
const y = flags.y ?? 0
|
|
98
114
|
|
|
115
|
+
// --near: string widget ID, or literal "false" to opt out
|
|
116
|
+
const nearRaw = flags.near
|
|
117
|
+
const nearOptOut = nearRaw === 'false' || nearRaw === false
|
|
118
|
+
const near = (nearRaw && !nearOptOut) ? nearRaw : null
|
|
119
|
+
const direction = flags.direction || 'right'
|
|
120
|
+
const resolve = flags.resolve || !!near
|
|
121
|
+
|
|
122
|
+
// Pass the calling agent/terminal's widget ID so the server can auto-position near it
|
|
123
|
+
const source = process.env.STORYBOARD_WIDGET_ID || null
|
|
124
|
+
|
|
99
125
|
let props = {}
|
|
100
126
|
if (flags['props-file']) {
|
|
101
127
|
try {
|
|
@@ -167,19 +193,40 @@ async function canvasAdd() {
|
|
|
167
193
|
}
|
|
168
194
|
|
|
169
195
|
// Submit
|
|
196
|
+
if (jsonOutput) {
|
|
197
|
+
// JSON mode: no spinners/clack UI, just raw JSON output for scripting
|
|
198
|
+
try {
|
|
199
|
+
const body = { name: canvasName, type: widgetType, props }
|
|
200
|
+
if (hasExplicitPosition) body.position = { x, y }
|
|
201
|
+
if (nearOptOut) body.near = false
|
|
202
|
+
if (near) { body.near = near; body.direction = direction }
|
|
203
|
+
if (resolve) body.resolve = true
|
|
204
|
+
if (source) body.source = source
|
|
205
|
+
const result = await serverPost('/_storyboard/canvas/widget', body)
|
|
206
|
+
const widget = result.widget || result
|
|
207
|
+
console.log(JSON.stringify(widget))
|
|
208
|
+
} catch (err) {
|
|
209
|
+
console.error(JSON.stringify({ error: err.message }))
|
|
210
|
+
process.exit(1)
|
|
211
|
+
}
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
|
|
170
215
|
const s = p.spinner()
|
|
171
216
|
s.start(`Adding ${widgetType} widget...`)
|
|
172
217
|
|
|
173
218
|
try {
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
219
|
+
const body = { name: canvasName, type: widgetType, props }
|
|
220
|
+
if (hasExplicitPosition) body.position = { x, y }
|
|
221
|
+
if (nearOptOut) body.near = false
|
|
222
|
+
if (near) { body.near = near; body.direction = direction }
|
|
223
|
+
if (resolve) body.resolve = true
|
|
224
|
+
if (source) body.source = source
|
|
225
|
+
const result = await serverPost('/_storyboard/canvas/widget', body)
|
|
180
226
|
s.stop(`Widget added!`)
|
|
181
|
-
|
|
182
|
-
|
|
227
|
+
const widgetId = result.widget?.id || result.id
|
|
228
|
+
if (widgetId) {
|
|
229
|
+
p.log.success(` ${widgetType} → ${canvasName} (id: ${widgetId})`)
|
|
183
230
|
} else {
|
|
184
231
|
p.log.success(` ${widgetType} → ${canvasName}`)
|
|
185
232
|
}
|