@dfosco/storyboard 0.5.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commandpalette.config.json +152 -0
- package/dist/storyboard-ui.css +1 -0
- package/dist/storyboard-ui.js +21328 -0
- package/dist/storyboard-ui.js.map +1 -0
- package/dist/tailwind.css +2 -0
- package/dist/tiny-canvas.css +1 -0
- package/dist/tiny-canvas.js +389 -0
- package/package.json +121 -0
- package/paste.config.json +67 -0
- package/scaffold/AGENTS.md +432 -0
- 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/deploy.yml +103 -0
- package/scaffold/githooks/pre-push +114 -0
- package/scaffold/gitignore +64 -0
- package/scaffold/manifest.json +56 -0
- package/scaffold/preview.yml +181 -0
- package/scaffold/scripts/link.sh +26 -0
- package/scaffold/scripts/unlink.sh +10 -0
- package/scaffold/skills/agent-browser/SKILL.md +260 -0
- package/scaffold/skills/canvas/SKILL.md +364 -0
- package/scaffold/skills/create/SKILL.md +501 -0
- package/scaffold/skills/ship/SKILL.md +237 -0
- package/scaffold/skills/storyboard/SKILL.md +360 -0
- package/scaffold/skills/update-storyboard/SKILL.md +16 -0
- package/scaffold/skills/update-storyboard/update-storyboard-packages.sh +26 -0
- package/scaffold/skills/vitest/GENERATION.md +5 -0
- package/scaffold/skills/vitest/SKILL.md +52 -0
- package/scaffold/skills/vitest/references/advanced-environments.md +264 -0
- package/scaffold/skills/vitest/references/advanced-projects.md +300 -0
- package/scaffold/skills/vitest/references/advanced-type-testing.md +237 -0
- package/scaffold/skills/vitest/references/advanced-vi.md +249 -0
- package/scaffold/skills/vitest/references/core-cli.md +166 -0
- package/scaffold/skills/vitest/references/core-config.md +174 -0
- package/scaffold/skills/vitest/references/core-describe.md +193 -0
- package/scaffold/skills/vitest/references/core-expect.md +219 -0
- package/scaffold/skills/vitest/references/core-hooks.md +244 -0
- package/scaffold/skills/vitest/references/core-test-api.md +233 -0
- package/scaffold/skills/vitest/references/features-concurrency.md +250 -0
- package/scaffold/skills/vitest/references/features-context.md +238 -0
- package/scaffold/skills/vitest/references/features-coverage.md +207 -0
- package/scaffold/skills/vitest/references/features-filtering.md +211 -0
- package/scaffold/skills/vitest/references/features-mocking.md +265 -0
- package/scaffold/skills/vitest/references/features-snapshots.md +207 -0
- package/scaffold/skills/worktree/SKILL.md +93 -0
- package/scaffold/storyboard.config.json +44 -0
- package/src/canvas/Canvas.jsx +78 -0
- package/src/canvas/Draggable.jsx +235 -0
- package/src/canvas/index.d.ts +41 -0
- package/src/canvas/index.js +6 -0
- package/src/canvas/style.css +118 -0
- package/src/canvas/useResetCanvas.js +17 -0
- package/src/canvas/utils.js +136 -0
- package/src/core/assets/fonts/IoskeleyMono-Bold.woff2 +0 -0
- package/src/core/assets/fonts/IoskeleyMono-Italic.woff2 +0 -0
- package/src/core/assets/fonts/IoskeleyMono-Medium.woff2 +0 -0
- package/src/core/assets/fonts/IoskeleyMono-Regular.woff2 +0 -0
- package/src/core/assets/fonts/IoskeleyMono-SemiBold.woff2 +0 -0
- package/src/core/autosync/server.js +714 -0
- package/src/core/autosync/server.test.js +158 -0
- package/src/core/canvas/__tests__/agent-integration.test.js +596 -0
- package/src/core/canvas/__tests__/helpers/browser.js +95 -0
- package/src/core/canvas/__tests__/helpers/canvas-api.js +129 -0
- package/src/core/canvas/__tests__/helpers/perf.js +118 -0
- package/src/core/canvas/__tests__/helpers/setup.js +176 -0
- package/src/core/canvas/__tests__/helpers/tmux.js +130 -0
- package/src/core/canvas/__tests__/helpers/transcript.js +132 -0
- package/src/core/canvas/__tests__/terminal-integration.test.js +177 -0
- package/src/core/canvas/collision.js +292 -0
- package/src/core/canvas/collision.test.js +371 -0
- package/src/core/canvas/compact.js +83 -0
- package/src/core/canvas/deriveCanvasId.test.js +40 -0
- package/src/core/canvas/githubEmbeds.js +527 -0
- package/src/core/canvas/githubEmbeds.test.js +302 -0
- package/src/core/canvas/hot-pool.js +766 -0
- package/src/core/canvas/identity.js +107 -0
- package/src/core/canvas/identity.test.js +100 -0
- package/src/core/canvas/materializer.js +259 -0
- package/src/core/canvas/materializer.test.js +356 -0
- package/src/core/canvas/selectedWidgets.js +270 -0
- package/src/core/canvas/selectedWidgets.test.js +321 -0
- package/src/core/canvas/server.js +3134 -0
- package/src/core/canvas/server.test.js +379 -0
- package/src/core/canvas/terminal-config.js +330 -0
- package/src/core/canvas/terminal-registry.js +465 -0
- package/src/core/canvas/terminal-server.js +1436 -0
- package/src/core/canvas/writeGuard.js +53 -0
- package/src/core/cli/agent.js +85 -0
- package/src/core/cli/branch.js +386 -0
- package/src/core/cli/canvasAdd.js +241 -0
- package/src/core/cli/canvasBatch.js +98 -0
- package/src/core/cli/canvasBounds.js +160 -0
- package/src/core/cli/canvasRead.js +236 -0
- package/src/core/cli/canvasUpdate.js +179 -0
- package/src/core/cli/code.js +67 -0
- package/src/core/cli/compact.js +62 -0
- package/src/core/cli/create.js +674 -0
- package/src/core/cli/dev-helpers.js +53 -0
- package/src/core/cli/dev-helpers.test.js +53 -0
- package/src/core/cli/dev.js +430 -0
- package/src/core/cli/exit.js +38 -0
- package/src/core/cli/flags.js +174 -0
- package/src/core/cli/flags.test.js +155 -0
- package/src/core/cli/index.js +233 -0
- package/src/core/cli/intro.js +37 -0
- package/src/core/cli/proxy.js +319 -0
- package/src/core/cli/proxy.test.js +63 -0
- package/src/core/cli/schemas.js +223 -0
- package/src/core/cli/server.js +192 -0
- package/src/core/cli/serverUrl.js +61 -0
- package/src/core/cli/sessions.js +459 -0
- package/src/core/cli/setup.js +404 -0
- package/src/core/cli/terminal-commands.js +287 -0
- package/src/core/cli/terminal-messaging.js +231 -0
- package/src/core/cli/terminal-welcome.js +515 -0
- package/src/core/cli/updateVersion.js +124 -0
- package/src/core/comments/api.js +284 -0
- package/src/core/comments/api.test.js +282 -0
- package/src/core/comments/auth.js +151 -0
- package/src/core/comments/auth.test.js +167 -0
- package/src/core/comments/commentCache.js +109 -0
- package/src/core/comments/commentCache.test.js +48 -0
- package/src/core/comments/commentDrafts.js +68 -0
- package/src/core/comments/commentMode.js +63 -0
- package/src/core/comments/commentMode.test.js +90 -0
- package/src/core/comments/config.js +47 -0
- package/src/core/comments/config.test.js +77 -0
- package/src/core/comments/graphql.js +65 -0
- package/src/core/comments/graphql.test.js +95 -0
- package/src/core/comments/index.js +42 -0
- package/src/core/comments/metadata.js +52 -0
- package/src/core/comments/metadata.test.js +110 -0
- package/src/core/comments/queries.js +245 -0
- package/src/core/comments/ui/AuthModal.jsx +114 -0
- package/src/core/comments/ui/CommentOverlay.js +52 -0
- package/src/core/comments/ui/CommentWindow.jsx +329 -0
- package/src/core/comments/ui/CommentsDrawer.jsx +102 -0
- package/src/core/comments/ui/Composer.jsx +64 -0
- package/src/core/comments/ui/authModal.js +66 -0
- package/src/core/comments/ui/authModal.test.js +76 -0
- package/src/core/comments/ui/comment-cursor-dark.svg +1 -0
- package/src/core/comments/ui/comment-cursor.svg +1 -0
- package/src/core/comments/ui/comment-layout.css +142 -0
- package/src/core/comments/ui/commentWindow.js +121 -0
- package/src/core/comments/ui/comments.css +242 -0
- package/src/core/comments/ui/commentsDrawer.js +84 -0
- package/src/core/comments/ui/composer.js +136 -0
- package/src/core/comments/ui/index.js +14 -0
- package/src/core/comments/ui/mount.js +687 -0
- package/src/core/comments/ui/mount.test.js +336 -0
- package/src/core/data/dotPath.js +53 -0
- package/src/core/data/dotPath.test.js +114 -0
- package/src/core/data/loader.js +409 -0
- package/src/core/data/loader.test.js +599 -0
- package/src/core/data/viewfinder.js +363 -0
- package/src/core/data/viewfinder.test.js +456 -0
- package/src/core/devtools/devtools-consumer.js +28 -0
- package/src/core/devtools/devtools.js +144 -0
- package/src/core/devtools/devtools.test.js +75 -0
- package/src/core/devtools/sceneDebug.js +112 -0
- package/src/core/devtools/sceneDebug.test.js +141 -0
- package/src/core/index.js +124 -0
- package/src/core/inspector/fiberWalker.js +239 -0
- package/src/core/inspector/highlighter.js +275 -0
- package/src/core/inspector/mouseMode.js +259 -0
- package/src/core/lib/components/ui/alert/alert-action.jsx +11 -0
- package/src/core/lib/components/ui/alert/alert-description.jsx +11 -0
- package/src/core/lib/components/ui/alert/alert-title.jsx +11 -0
- package/src/core/lib/components/ui/alert/alert.jsx +25 -0
- package/src/core/lib/components/ui/alert/index.js +17 -0
- package/src/core/lib/components/ui/avatar/avatar-badge.jsx +22 -0
- package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +18 -0
- package/src/core/lib/components/ui/avatar/avatar-group-count.jsx +19 -0
- package/src/core/lib/components/ui/avatar/avatar-group.jsx +19 -0
- package/src/core/lib/components/ui/avatar/avatar-image.jsx +15 -0
- package/src/core/lib/components/ui/avatar/avatar.jsx +19 -0
- package/src/core/lib/components/ui/avatar/index.js +22 -0
- package/src/core/lib/components/ui/badge/badge.jsx +31 -0
- package/src/core/lib/components/ui/badge/index.js +2 -0
- package/src/core/lib/components/ui/button/button.jsx +100 -0
- package/src/core/lib/components/ui/button/index.js +12 -0
- package/src/core/lib/components/ui/card/card-action.jsx +11 -0
- package/src/core/lib/components/ui/card/card-content.jsx +11 -0
- package/src/core/lib/components/ui/card/card-description.jsx +11 -0
- package/src/core/lib/components/ui/card/card-footer.jsx +11 -0
- package/src/core/lib/components/ui/card/card-header.jsx +19 -0
- package/src/core/lib/components/ui/card/card-title.jsx +11 -0
- package/src/core/lib/components/ui/card/card.jsx +17 -0
- package/src/core/lib/components/ui/card/index.js +25 -0
- package/src/core/lib/components/ui/checkbox/checkbox.jsx +29 -0
- package/src/core/lib/components/ui/checkbox/index.js +6 -0
- package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +7 -0
- package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +7 -0
- package/src/core/lib/components/ui/collapsible/collapsible.jsx +7 -0
- package/src/core/lib/components/ui/collapsible/index.js +13 -0
- package/src/core/lib/components/ui/dialog/dialog-close.jsx +7 -0
- package/src/core/lib/components/ui/dialog/dialog-content.jsx +34 -0
- package/src/core/lib/components/ui/dialog/dialog-description.jsx +15 -0
- package/src/core/lib/components/ui/dialog/dialog-footer.jsx +23 -0
- package/src/core/lib/components/ui/dialog/dialog-header.jsx +11 -0
- package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +15 -0
- package/src/core/lib/components/ui/dialog/dialog-portal.jsx +4 -0
- package/src/core/lib/components/ui/dialog/dialog-title.jsx +15 -0
- package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +7 -0
- package/src/core/lib/components/ui/dialog/dialog.jsx +4 -0
- package/src/core/lib/components/ui/dialog/index.js +34 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +8 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +30 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +22 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +16 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +7 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +20 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-label.jsx +17 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +4 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +7 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +29 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +15 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.jsx +16 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +15 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +23 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +4 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +7 -0
- package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +4 -0
- package/src/core/lib/components/ui/dropdown-menu/index.js +54 -0
- package/src/core/lib/components/ui/input/index.js +7 -0
- package/src/core/lib/components/ui/input/input.jsx +19 -0
- package/src/core/lib/components/ui/label/index.js +7 -0
- package/src/core/lib/components/ui/label/label.jsx +19 -0
- package/src/core/lib/components/ui/panel/index.js +24 -0
- package/src/core/lib/components/ui/panel/panel-body.jsx +11 -0
- package/src/core/lib/components/ui/panel/panel-close.jsx +16 -0
- package/src/core/lib/components/ui/panel/panel-content.jsx +29 -0
- package/src/core/lib/components/ui/panel/panel-footer.jsx +11 -0
- package/src/core/lib/components/ui/panel/panel-header.jsx +11 -0
- package/src/core/lib/components/ui/panel/panel-title.jsx +12 -0
- package/src/core/lib/components/ui/panel/panel.jsx +4 -0
- package/src/core/lib/components/ui/popover/index.js +28 -0
- package/src/core/lib/components/ui/popover/popover-close.jsx +7 -0
- package/src/core/lib/components/ui/popover/popover-content.jsx +22 -0
- package/src/core/lib/components/ui/popover/popover-description.jsx +11 -0
- package/src/core/lib/components/ui/popover/popover-header.jsx +11 -0
- package/src/core/lib/components/ui/popover/popover-portal.jsx +4 -0
- package/src/core/lib/components/ui/popover/popover-title.jsx +11 -0
- package/src/core/lib/components/ui/popover/popover-trigger.jsx +8 -0
- package/src/core/lib/components/ui/popover/popover.jsx +4 -0
- package/src/core/lib/components/ui/searchable-list.jsx +160 -0
- package/src/core/lib/components/ui/select/index.js +37 -0
- package/src/core/lib/components/ui/select/select-content.jsx +30 -0
- package/src/core/lib/components/ui/select/select-group-heading.jsx +17 -0
- package/src/core/lib/components/ui/select/select-group.jsx +15 -0
- package/src/core/lib/components/ui/select/select-item.jsx +26 -0
- package/src/core/lib/components/ui/select/select-label.jsx +11 -0
- package/src/core/lib/components/ui/select/select-portal.jsx +4 -0
- package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +18 -0
- package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +18 -0
- package/src/core/lib/components/ui/select/select-separator.jsx +15 -0
- package/src/core/lib/components/ui/select/select-trigger.jsx +25 -0
- package/src/core/lib/components/ui/select/select.jsx +4 -0
- package/src/core/lib/components/ui/separator/index.js +7 -0
- package/src/core/lib/components/ui/separator/separator.jsx +22 -0
- package/src/core/lib/components/ui/sheet/index.js +34 -0
- package/src/core/lib/components/ui/sheet/sheet-close.jsx +7 -0
- package/src/core/lib/components/ui/sheet/sheet-content.jsx +35 -0
- package/src/core/lib/components/ui/sheet/sheet-description.jsx +15 -0
- package/src/core/lib/components/ui/sheet/sheet-footer.jsx +11 -0
- package/src/core/lib/components/ui/sheet/sheet-header.jsx +11 -0
- package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +15 -0
- package/src/core/lib/components/ui/sheet/sheet-portal.jsx +4 -0
- package/src/core/lib/components/ui/sheet/sheet-title.jsx +15 -0
- package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +7 -0
- package/src/core/lib/components/ui/sheet/sheet.jsx +4 -0
- package/src/core/lib/components/ui/textarea/index.js +7 -0
- package/src/core/lib/components/ui/textarea/textarea.jsx +18 -0
- package/src/core/lib/components/ui/toggle/index.js +8 -0
- package/src/core/lib/components/ui/toggle/toggle.jsx +36 -0
- package/src/core/lib/components/ui/toggle-group/index.js +10 -0
- package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +29 -0
- package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +43 -0
- package/src/core/lib/components/ui/tooltip/index.js +3 -0
- package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +21 -0
- package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +23 -0
- package/src/core/lib/components/ui/tooltip/tooltip.jsx +11 -0
- package/src/core/lib/components/ui/trigger-button/index.js +6 -0
- package/src/core/lib/components/ui/trigger-button/trigger-button.css +38 -0
- package/src/core/lib/components/ui/trigger-button/trigger-button.jsx +63 -0
- package/src/core/lib/utils/index.js +6 -0
- package/src/core/logger/devLogger.js +238 -0
- package/src/core/logger/devLogger.test.js +193 -0
- package/src/core/modes/modes.css +98 -0
- package/src/core/modes/modes.js +492 -0
- package/src/core/modes/modes.test.js +562 -0
- package/src/core/mountStoryboardCore.js +478 -0
- package/src/core/rename-watcher/config.json +23 -0
- package/src/core/rename-watcher/watcher.js +531 -0
- package/src/core/scaffold.js +100 -0
- package/src/core/server/index.js +391 -0
- package/src/core/session/bodyClasses.js +128 -0
- package/src/core/session/bodyClasses.test.js +192 -0
- package/src/core/session/hashSubscribe.js +19 -0
- package/src/core/session/hashSubscribe.test.js +62 -0
- package/src/core/session/hideMode.js +424 -0
- package/src/core/session/hideMode.test.js +268 -0
- package/src/core/session/interceptHideParams.js +35 -0
- package/src/core/session/interceptHideParams.test.js +90 -0
- package/src/core/session/localStorage.js +134 -0
- package/src/core/session/localStorage.test.js +148 -0
- package/src/core/session/session.js +76 -0
- package/src/core/session/session.test.js +91 -0
- package/src/core/stores/canvasConfig.js +134 -0
- package/src/core/stores/canvasConfig.test.js +120 -0
- package/src/core/stores/commandActions.js +284 -0
- package/src/core/stores/commandPaletteConfig.js +31 -0
- package/src/core/stores/configSchema.js +232 -0
- package/src/core/stores/configSchema.test.js +72 -0
- package/src/core/stores/configStore.js +161 -0
- package/src/core/stores/customerModeConfig.js +30 -0
- package/src/core/stores/featureFlags.js +127 -0
- package/src/core/stores/paletteProviders.js +360 -0
- package/src/core/stores/paletteProviders.test.js +186 -0
- package/src/core/stores/plugins.js +40 -0
- package/src/core/stores/plugins.test.js +68 -0
- package/src/core/stores/recentArtifacts.js +68 -0
- package/src/core/stores/recentArtifacts.test.js +71 -0
- package/src/core/stores/sidePanelStore.ts +143 -0
- package/src/core/stores/themeStore.ts +291 -0
- package/src/core/stores/toolRegistry.js +227 -0
- package/src/core/stores/toolStateStore.js +183 -0
- package/src/core/stores/toolStateStore.test.js +220 -0
- package/src/core/stores/toolbarConfigStore.js +165 -0
- package/src/core/stores/uiConfig.js +64 -0
- package/src/core/stores/uiConfig.test.js +63 -0
- package/src/core/styles/tailwind.css +204 -0
- package/src/core/tools/handlers/autosync.js +12 -0
- package/src/core/tools/handlers/canvasAddWidget.js +11 -0
- package/src/core/tools/handlers/canvasAgents.js +20 -0
- package/src/core/tools/handlers/canvasToolbar.js +56 -0
- package/src/core/tools/handlers/commandPalette.js +9 -0
- package/src/core/tools/handlers/comments.js +16 -0
- package/src/core/tools/handlers/create.js +39 -0
- package/src/core/tools/handlers/devtools.js +122 -0
- package/src/core/tools/handlers/devtools.test.js +87 -0
- package/src/core/tools/handlers/featureFlags.js +21 -0
- package/src/core/tools/handlers/flows.js +68 -0
- package/src/core/tools/handlers/hideChrome.js +9 -0
- package/src/core/tools/handlers/hideToolbars.js +25 -0
- package/src/core/tools/handlers/inspector.js +19 -0
- package/src/core/tools/handlers/paletteTheme.js +35 -0
- package/src/core/tools/handlers/theme.js +9 -0
- package/src/core/tools/registry.js +26 -0
- package/src/core/tools/surfaces/canvasToolbar.js +10 -0
- package/src/core/tools/surfaces/commandList.js +10 -0
- package/src/core/tools/surfaces/mainToolbar.js +11 -0
- package/src/core/tools/surfaces/registry.js +19 -0
- package/src/core/ui/ActionMenuButton.jsx +114 -0
- package/src/core/ui/AutosyncMenuButton.css +67 -0
- package/src/core/ui/AutosyncMenuButton.jsx +242 -0
- package/src/core/ui/BranchSelect.jsx +29 -0
- package/src/core/ui/BranchSelect.module.css +30 -0
- package/src/core/ui/CanvasAgentsMenu.jsx +89 -0
- package/src/core/ui/CanvasCreateMenu.jsx +611 -0
- package/src/core/ui/CanvasSnap.css +27 -0
- package/src/core/ui/CanvasSnap.jsx +51 -0
- package/src/core/ui/CanvasUndoRedo.css +36 -0
- package/src/core/ui/CanvasUndoRedo.jsx +62 -0
- package/src/core/ui/CanvasZoomControl.css +53 -0
- package/src/core/ui/CanvasZoomControl.jsx +49 -0
- package/src/core/ui/CanvasZoomToFit.css +18 -0
- package/src/core/ui/CanvasZoomToFit.jsx +26 -0
- package/src/core/ui/CommandMenu.css +8 -0
- package/src/core/ui/CommandMenu.jsx +287 -0
- package/src/core/ui/CommandPalette.jsx +35 -0
- package/src/core/ui/CommandPaletteTrigger.jsx +25 -0
- package/src/core/ui/CommentsMenuButton.jsx +40 -0
- package/src/core/ui/CoreUIBar.css +47 -0
- package/src/core/ui/CoreUIBar.jsx +905 -0
- package/src/core/ui/CreateMenuButton.jsx +117 -0
- package/src/core/ui/HideChromeTrigger.jsx +48 -0
- package/src/core/ui/Icon.jsx +279 -0
- package/src/core/ui/InspectorPanel.css +109 -0
- package/src/core/ui/InspectorPanel.jsx +632 -0
- package/src/core/ui/PwaInstallBanner.css +42 -0
- package/src/core/ui/PwaInstallBanner.jsx +124 -0
- package/src/core/ui/SidePanel.jsx +261 -0
- package/src/core/ui/ThemeMenuButton.jsx +139 -0
- package/src/core/ui/core-ui-colors.css +129 -0
- package/src/core/ui/design-modes.ts +7 -0
- package/src/core/ui/sidepanel.css +301 -0
- package/src/core/ui/viewfinder.ts +7 -0
- package/src/core/ui-entry.js +30 -0
- package/src/core/utils/fuzzySearch.js +117 -0
- package/src/core/utils/fuzzySearch.test.js +119 -0
- package/src/core/utils/mobileViewport.js +57 -0
- package/src/core/utils/mobileViewport.test.js +68 -0
- package/src/core/utils/prodMode.js +38 -0
- package/src/core/utils/smoothCorners.js +20 -0
- package/src/core/vite/docs-handler.js +155 -0
- package/src/core/vite/server-plugin.js +797 -0
- package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +260 -0
- package/src/core/workshop/features/createCanvas/index.js +14 -0
- package/src/core/workshop/features/createFlow/CreateFlowForm.jsx +334 -0
- package/src/core/workshop/features/createFlow/index.js +19 -0
- package/src/core/workshop/features/createFlow/server.js +663 -0
- package/src/core/workshop/features/createPage/CreatePageForm.jsx +304 -0
- package/src/core/workshop/features/createPage/index.js +11 -0
- package/src/core/workshop/features/createPrototype/CreatePrototypeForm.jsx +289 -0
- package/src/core/workshop/features/createPrototype/index.js +19 -0
- package/src/core/workshop/features/createPrototype/server.js +433 -0
- package/src/core/workshop/features/createStory/CreateStoryForm.jsx +208 -0
- package/src/core/workshop/features/createStory/index.js +14 -0
- package/src/core/workshop/features/registry-server.js +22 -0
- package/src/core/workshop/features/registry.js +28 -0
- package/src/core/workshop/features/templateIndex.js +155 -0
- package/src/core/workshop/ui/WorkshopPanel.jsx +98 -0
- package/src/core/workshop/ui/mount.ts +6 -0
- package/src/core/worktree/port.js +268 -0
- package/src/core/worktree/port.test.js +222 -0
- package/src/core/worktree/serverRegistry.js +120 -0
- package/src/internals/AuthModal/AuthModal.jsx +132 -0
- package/src/internals/AuthModal/AuthModal.module.css +221 -0
- package/src/internals/BranchBar/BranchBar.jsx +87 -0
- package/src/internals/BranchBar/BranchBar.module.css +247 -0
- package/src/internals/BranchBar/useBranches.js +93 -0
- package/src/internals/BranchBar/useBranches.test.js +68 -0
- package/src/internals/CommandPalette/CommandPalette.jsx +1361 -0
- package/src/internals/CommandPalette/CreateDialog.jsx +219 -0
- package/src/internals/CommandPalette/command-palette.css +180 -0
- package/src/internals/FlowError.module.css +30 -0
- package/src/internals/Icon.jsx +279 -0
- package/src/internals/StoryboardContext.js +3 -0
- package/src/internals/Viewfinder.jsx +1479 -0
- package/src/internals/Viewfinder.module.css +1540 -0
- package/src/internals/Workspace.jsx +7 -0
- package/src/internals/__mocks__/virtual-storyboard-data-index.js +4 -0
- package/src/internals/canvas/CanvasControls.jsx +112 -0
- package/src/internals/canvas/CanvasControls.module.css +135 -0
- package/src/internals/canvas/CanvasPage.bridge.test.jsx +387 -0
- package/src/internals/canvas/CanvasPage.dragdrop.test.jsx +350 -0
- package/src/internals/canvas/CanvasPage.jsx +3092 -0
- package/src/internals/canvas/CanvasPage.module.css +187 -0
- package/src/internals/canvas/CanvasPage.multiselect.test.jsx +358 -0
- package/src/internals/canvas/CanvasToolbar.jsx +73 -0
- package/src/internals/canvas/CanvasToolbar.module.css +92 -0
- package/src/internals/canvas/ComponentErrorBoundary.jsx +50 -0
- package/src/internals/canvas/ConnectorLayer.jsx +208 -0
- package/src/internals/canvas/ConnectorLayer.module.css +129 -0
- package/src/internals/canvas/MarqueeOverlay.jsx +20 -0
- package/src/internals/canvas/PageSelector.jsx +587 -0
- package/src/internals/canvas/PageSelector.module.css +261 -0
- package/src/internals/canvas/PageSelector.test.jsx +113 -0
- package/src/internals/canvas/WebGLContextPool.jsx +292 -0
- package/src/internals/canvas/WebGLContextPool.test.jsx +165 -0
- package/src/internals/canvas/canvasApi.js +164 -0
- package/src/internals/canvas/canvasReloadGuard.js +37 -0
- package/src/internals/canvas/canvasReloadGuard.test.js +27 -0
- package/src/internals/canvas/canvasTheme.js +118 -0
- package/src/internals/canvas/componentIsolate.jsx +165 -0
- package/src/internals/canvas/componentSetIsolate.jsx +257 -0
- package/src/internals/canvas/computeCanvasBounds.test.js +121 -0
- package/src/internals/canvas/connectorGeometry.js +132 -0
- package/src/internals/canvas/hotPoolDevLogs.js +25 -0
- package/src/internals/canvas/textSelection.js +10 -0
- package/src/internals/canvas/textSelection.test.js +26 -0
- package/src/internals/canvas/useCanvas.js +126 -0
- package/src/internals/canvas/useCanvas.test.js +26 -0
- package/src/internals/canvas/useMarqueeSelect.js +213 -0
- package/src/internals/canvas/useMarqueeSelect.test.js +78 -0
- package/src/internals/canvas/useUndoRedo.js +86 -0
- package/src/internals/canvas/useUndoRedo.test.js +231 -0
- package/src/internals/canvas/widgets/CodePenEmbed.jsx +293 -0
- package/src/internals/canvas/widgets/CodePenEmbed.module.css +161 -0
- package/src/internals/canvas/widgets/ComponentSetWidget.jsx +2 -0
- package/src/internals/canvas/widgets/ComponentSetWidget.module.css +89 -0
- package/src/internals/canvas/widgets/ComponentWidget.jsx +14 -0
- package/src/internals/canvas/widgets/ComponentWidget.module.css +0 -0
- package/src/internals/canvas/widgets/CropOverlay.jsx +179 -0
- package/src/internals/canvas/widgets/CropOverlay.module.css +154 -0
- package/src/internals/canvas/widgets/ExpandedPane.jsx +474 -0
- package/src/internals/canvas/widgets/ExpandedPane.module.css +179 -0
- package/src/internals/canvas/widgets/ExpandedPane.test.jsx +240 -0
- package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +111 -0
- package/src/internals/canvas/widgets/ExpandedPaneTopBar.module.css +59 -0
- package/src/internals/canvas/widgets/ExpandedPaneTopBar.test.jsx +45 -0
- package/src/internals/canvas/widgets/FigmaEmbed.jsx +296 -0
- package/src/internals/canvas/widgets/FigmaEmbed.module.css +222 -0
- package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +151 -0
- package/src/internals/canvas/widgets/FrozenTerminalOverlay.module.css +83 -0
- package/src/internals/canvas/widgets/ImageWidget.jsx +287 -0
- package/src/internals/canvas/widgets/ImageWidget.module.css +81 -0
- package/src/internals/canvas/widgets/LinkPreview.jsx +439 -0
- package/src/internals/canvas/widgets/LinkPreview.module.css +585 -0
- package/src/internals/canvas/widgets/LinkPreview.test.jsx +193 -0
- package/src/internals/canvas/widgets/MarkdownBlock.jsx +354 -0
- package/src/internals/canvas/widgets/MarkdownBlock.module.css +377 -0
- package/src/internals/canvas/widgets/MarkdownBlock.test.jsx +92 -0
- package/src/internals/canvas/widgets/PromptWidget.jsx +428 -0
- package/src/internals/canvas/widgets/PromptWidget.module.css +273 -0
- package/src/internals/canvas/widgets/PrototypeEmbed.jsx +463 -0
- package/src/internals/canvas/widgets/PrototypeEmbed.module.css +579 -0
- package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +10 -0
- package/src/internals/canvas/widgets/ResizeHandle.jsx +67 -0
- package/src/internals/canvas/widgets/ResizeHandle.module.css +29 -0
- package/src/internals/canvas/widgets/StickyNote.jsx +92 -0
- package/src/internals/canvas/widgets/StickyNote.module.css +70 -0
- package/src/internals/canvas/widgets/StickyNote.test.jsx +116 -0
- package/src/internals/canvas/widgets/StorySetWidget.jsx +208 -0
- package/src/internals/canvas/widgets/StorySetWidget.module.css +89 -0
- package/src/internals/canvas/widgets/StoryWidget.jsx +334 -0
- package/src/internals/canvas/widgets/StoryWidget.module.css +211 -0
- package/src/internals/canvas/widgets/TerminalReadWidget.jsx +146 -0
- package/src/internals/canvas/widgets/TerminalReadWidget.module.css +94 -0
- package/src/internals/canvas/widgets/TerminalWidget.jsx +704 -0
- package/src/internals/canvas/widgets/TerminalWidget.module.css +444 -0
- package/src/internals/canvas/widgets/TilesWidget.jsx +300 -0
- package/src/internals/canvas/widgets/TilesWidget.module.css +133 -0
- package/src/internals/canvas/widgets/WidgetChrome.jsx +580 -0
- package/src/internals/canvas/widgets/WidgetChrome.module.css +421 -0
- package/src/internals/canvas/widgets/WidgetWrapper.jsx +15 -0
- package/src/internals/canvas/widgets/WidgetWrapper.module.css +25 -0
- package/src/internals/canvas/widgets/codepenUrl.js +75 -0
- package/src/internals/canvas/widgets/codepenUrl.test.js +76 -0
- package/src/internals/canvas/widgets/embedInteraction.test.jsx +173 -0
- package/src/internals/canvas/widgets/embedOverlay.module.css +35 -0
- package/src/internals/canvas/widgets/embedTheme.js +148 -0
- package/src/internals/canvas/widgets/expandUtils.js +559 -0
- package/src/internals/canvas/widgets/expandUtils.test.js +155 -0
- package/src/internals/canvas/widgets/figmaUrl.js +118 -0
- package/src/internals/canvas/widgets/figmaUrl.test.js +139 -0
- package/src/internals/canvas/widgets/githubUrl.js +82 -0
- package/src/internals/canvas/widgets/githubUrl.test.js +74 -0
- package/src/internals/canvas/widgets/iframeDevLogs.js +49 -0
- package/src/internals/canvas/widgets/iframeDevLogs.test.jsx +81 -0
- package/src/internals/canvas/widgets/index.js +42 -0
- package/src/internals/canvas/widgets/pasteRules.js +295 -0
- package/src/internals/canvas/widgets/pasteRules.test.js +474 -0
- package/src/internals/canvas/widgets/snapshotDisplay.test.jsx +211 -0
- package/src/internals/canvas/widgets/tilePool.js +23 -0
- package/src/internals/canvas/widgets/tiles/diagonal-bl.png +0 -0
- package/src/internals/canvas/widgets/tiles/diagonal-br.png +0 -0
- package/src/internals/canvas/widgets/tiles/diagonal-tl.png +0 -0
- package/src/internals/canvas/widgets/tiles/leaf.png +0 -0
- package/src/internals/canvas/widgets/tiles/quarter-tl.png +0 -0
- package/src/internals/canvas/widgets/tiles/quarter-tr.png +0 -0
- package/src/internals/canvas/widgets/tiles/solid-a.png +0 -0
- package/src/internals/canvas/widgets/tiles/solid-b.png +0 -0
- package/src/internals/canvas/widgets/widgetConfig.js +291 -0
- package/src/internals/canvas/widgets/widgetConfig.test.js +68 -0
- package/src/internals/canvas/widgets/widgetIcons.jsx +190 -0
- package/src/internals/canvas/widgets/widgetProps.js +133 -0
- package/src/internals/context/FormContext.js +13 -0
- package/src/internals/context/FormContext.test.js +48 -0
- package/src/internals/context.jsx +481 -0
- package/src/internals/context.test.jsx +296 -0
- package/src/internals/hashPreserver.js +73 -0
- package/src/internals/hashPreserver.test.js +107 -0
- package/src/internals/hooks/useConfig.js +14 -0
- package/src/internals/hooks/useFeatureFlag.js +14 -0
- package/src/internals/hooks/useFlows.js +50 -0
- package/src/internals/hooks/useFlows.test.js +134 -0
- package/src/internals/hooks/useHideMode.js +31 -0
- package/src/internals/hooks/useHideMode.test.js +43 -0
- package/src/internals/hooks/useLocalStorage.js +57 -0
- package/src/internals/hooks/useLocalStorage.test.js +75 -0
- package/src/internals/hooks/useMode.js +43 -0
- package/src/internals/hooks/useObject.js +101 -0
- package/src/internals/hooks/useObject.test.js +74 -0
- package/src/internals/hooks/useOverride.js +84 -0
- package/src/internals/hooks/useOverride.test.js +71 -0
- package/src/internals/hooks/usePrototypeReloadGuard.js +64 -0
- package/src/internals/hooks/useRecord.js +158 -0
- package/src/internals/hooks/useRecord.test.js +221 -0
- package/src/internals/hooks/useScene.js +38 -0
- package/src/internals/hooks/useScene.test.js +66 -0
- package/src/internals/hooks/useSceneData.js +108 -0
- package/src/internals/hooks/useSceneData.test.js +136 -0
- package/src/internals/hooks/useSession.js +4 -0
- package/src/internals/hooks/useSession.test.js +8 -0
- package/src/internals/hooks/useThemeState.js +61 -0
- package/src/internals/hooks/useThemeState.test.js +66 -0
- package/src/internals/hooks/useUndoRedo.js +28 -0
- package/src/internals/hooks/useUndoRedo.test.js +64 -0
- package/src/internals/index.js +58 -0
- package/src/internals/story/ComponentSetPage.jsx +198 -0
- package/src/internals/story/ComponentSetPage.module.css +129 -0
- package/src/internals/story/StoryPage.jsx +147 -0
- package/src/internals/story/StoryPage.module.css +18 -0
- package/src/internals/test-utils.js +45 -0
- package/src/internals/vite/data-plugin.js +1508 -0
- package/src/internals/vite/data-plugin.test.js +1223 -0
- package/src/test-utils.js +44 -0
- package/toolbar.config.json +271 -0
- package/widgets.config.json +1537 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git helpers for storyboard dev CLI.
|
|
3
|
+
* Extracted for testability — no side effects on import.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { execFileSync } from 'child_process'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if the working tree has uncommitted changes (staged or unstaged).
|
|
10
|
+
*/
|
|
11
|
+
export function hasUncommittedChanges(cwd) {
|
|
12
|
+
try {
|
|
13
|
+
const status = execFileSync('git', ['status', '--porcelain'], { cwd, encoding: 'utf8' }).trim()
|
|
14
|
+
return status.length > 0
|
|
15
|
+
} catch {
|
|
16
|
+
return false
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if a local branch exists.
|
|
22
|
+
*/
|
|
23
|
+
export function localBranchExists(name, cwd) {
|
|
24
|
+
try {
|
|
25
|
+
execFileSync('git', ['show-ref', '--verify', `refs/heads/${name}`], { cwd, stdio: 'ignore' })
|
|
26
|
+
return true
|
|
27
|
+
} catch {
|
|
28
|
+
return false
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the default branch for the repo root (main, master, or origin/HEAD target).
|
|
34
|
+
* Returns null if none can be determined.
|
|
35
|
+
*/
|
|
36
|
+
export function resolveDefaultBranch(cwd) {
|
|
37
|
+
// Verify we're actually inside a git repository
|
|
38
|
+
try {
|
|
39
|
+
execFileSync('git', ['rev-parse', '--git-dir'], { cwd, stdio: 'ignore' })
|
|
40
|
+
} catch {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
for (const candidate of ['main', 'master']) {
|
|
44
|
+
if (localBranchExists(candidate, cwd)) return candidate
|
|
45
|
+
}
|
|
46
|
+
// Try origin/HEAD
|
|
47
|
+
try {
|
|
48
|
+
const ref = execFileSync('git', ['symbolic-ref', 'refs/remotes/origin/HEAD'], { cwd, encoding: 'utf8' }).trim()
|
|
49
|
+
const name = ref.replace('refs/remotes/origin/', '')
|
|
50
|
+
if (name && localBranchExists(name, cwd)) return name
|
|
51
|
+
} catch { /* no origin/HEAD */ }
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { hasUncommittedChanges, localBranchExists, resolveDefaultBranch } from './dev-helpers.js'
|
|
3
|
+
import { execSync } from 'child_process'
|
|
4
|
+
|
|
5
|
+
// These tests run against the real git repo — they verify the helpers
|
|
6
|
+
// work correctly with actual git state.
|
|
7
|
+
|
|
8
|
+
const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim()
|
|
9
|
+
|
|
10
|
+
describe('hasUncommittedChanges', () => {
|
|
11
|
+
it('returns a boolean', () => {
|
|
12
|
+
const result = hasUncommittedChanges(repoRoot)
|
|
13
|
+
expect(typeof result).toBe('boolean')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('returns false for non-existent directory', () => {
|
|
17
|
+
expect(hasUncommittedChanges('/tmp/nonexistent-repo-12345')).toBe(false)
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
describe('localBranchExists', () => {
|
|
22
|
+
it('returns true for a branch that exists', () => {
|
|
23
|
+
// The current branch must exist
|
|
24
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: repoRoot, encoding: 'utf8' }).trim()
|
|
25
|
+
expect(localBranchExists(branch, repoRoot)).toBe(true)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('returns false for a branch that does not exist', () => {
|
|
29
|
+
expect(localBranchExists('__nonexistent-branch-xyz-99999__', repoRoot)).toBe(false)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('returns false for invalid cwd', () => {
|
|
33
|
+
expect(localBranchExists('main', '/tmp/nonexistent-repo-12345')).toBe(false)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('resolveDefaultBranch', () => {
|
|
38
|
+
it('returns a string or null', () => {
|
|
39
|
+
const result = resolveDefaultBranch(repoRoot)
|
|
40
|
+
expect(result === null || typeof result === 'string').toBe(true)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('prefers main over master when main exists', () => {
|
|
44
|
+
// If main exists in this repo, it should be the default
|
|
45
|
+
if (localBranchExists('main', repoRoot)) {
|
|
46
|
+
expect(resolveDefaultBranch(repoRoot)).toBe('main')
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('returns null for non-git directory', () => {
|
|
51
|
+
expect(resolveDefaultBranch('/tmp')).toBe(null)
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storyboard dev [branch] — Start Vite with correct base path.
|
|
3
|
+
*
|
|
4
|
+
* If a storyboard server is already running (from another `sb dev`),
|
|
5
|
+
* operates in "client mode": asks the existing server to spawn Vite
|
|
6
|
+
* for this branch, prints the URL, and exits. This allows multiple
|
|
7
|
+
* branches to run simultaneously through the same server + Caddy proxy.
|
|
8
|
+
*
|
|
9
|
+
* Otherwise, starts the server in the foreground as the "owner".
|
|
10
|
+
* Ctrl+C kills Vite, releases the port, and exits cleanly.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* storyboard dev # detect worktree from cwd
|
|
14
|
+
* storyboard dev main # start dev for repo root
|
|
15
|
+
* storyboard dev <worktree> # start dev for existing worktree
|
|
16
|
+
* storyboard dev <branch> # auto-create worktree + start dev
|
|
17
|
+
*
|
|
18
|
+
* Main: http://<devDomain>.localhost/
|
|
19
|
+
* Branch: http://<devDomain>.localhost/branch--<name>/
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import * as p from '@clack/prompts'
|
|
23
|
+
import http from 'node:http'
|
|
24
|
+
import { execFileSync } from 'child_process'
|
|
25
|
+
import { existsSync } from 'fs'
|
|
26
|
+
import { resolve } from 'path'
|
|
27
|
+
import { detectWorktreeName, getPort, releasePort, repoRoot, worktreeDir } from '../worktree/port.js'
|
|
28
|
+
import { generateCaddyfile, generateRouteConfig, upsertCaddyRoute, isCaddyRunning, readDevDomain } from './proxy.js'
|
|
29
|
+
import { startRenameWatcher } from '../rename-watcher/watcher.js'
|
|
30
|
+
import { parseFlags } from './flags.js'
|
|
31
|
+
import { hasUncommittedChanges, localBranchExists, resolveDefaultBranch } from './dev-helpers.js'
|
|
32
|
+
import { compactAll } from '../canvas/compact.js'
|
|
33
|
+
|
|
34
|
+
const flagSchema = {
|
|
35
|
+
port: { type: 'number', description: 'Override dev server port' },
|
|
36
|
+
create: { type: 'boolean', default: true, description: 'Allow creating worktrees/branches (disable with --no-create)' },
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if a remote branch exists on origin.
|
|
41
|
+
* @param {string} name
|
|
42
|
+
* @param {string} cwd
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
function remoteBranchExists(name, cwd) {
|
|
46
|
+
try {
|
|
47
|
+
const result = execFileSync('git', ['ls-remote', '--exit-code', '--heads', 'origin', name], { cwd, encoding: 'utf8' })
|
|
48
|
+
return result.trim().length > 0
|
|
49
|
+
} catch {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a git worktree and install dependencies.
|
|
56
|
+
* @param {string} name — worktree/branch name
|
|
57
|
+
* @param {string} root — repo root path
|
|
58
|
+
* @param {object} opts
|
|
59
|
+
* @param {boolean} opts.newBranch — create a new branch from HEAD
|
|
60
|
+
* @returns {string} path to the new worktree directory
|
|
61
|
+
*/
|
|
62
|
+
function createWorktree(name, root, { newBranch = false } = {}) {
|
|
63
|
+
const targetDir = resolve(root, 'worktrees', name)
|
|
64
|
+
|
|
65
|
+
const gitArgs = newBranch
|
|
66
|
+
? ['worktree', 'add', targetDir, '-b', name]
|
|
67
|
+
: ['worktree', 'add', targetDir, name]
|
|
68
|
+
|
|
69
|
+
p.log.step(`Creating worktree: worktrees/${name}`)
|
|
70
|
+
execFileSync('git', gitArgs, { cwd: root, stdio: 'inherit' })
|
|
71
|
+
|
|
72
|
+
p.log.step('Installing dependencies…')
|
|
73
|
+
const npmBin = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
74
|
+
execFileSync(npmBin, ['install'], { cwd: targetDir, stdio: 'inherit' })
|
|
75
|
+
|
|
76
|
+
getPort(name)
|
|
77
|
+
return targetDir
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Resolve the target worktree for `storyboard dev [branch]`.
|
|
82
|
+
*
|
|
83
|
+
* When no argument is given and the repo root is on a non-main branch,
|
|
84
|
+
* prompts the user to convert it to a proper worktree.
|
|
85
|
+
*
|
|
86
|
+
* @param {string|undefined} branchArg — positional branch argument
|
|
87
|
+
* @param {object} opts
|
|
88
|
+
* @param {boolean} opts.allowCreate — whether creation is allowed
|
|
89
|
+
* @returns {Promise<{ worktreeName: string, targetCwd: string, created: boolean }>}
|
|
90
|
+
*/
|
|
91
|
+
async function resolveDevTarget(branchArg, { allowCreate = true } = {}) {
|
|
92
|
+
// No argument — detect from cwd
|
|
93
|
+
if (!branchArg) {
|
|
94
|
+
const detectedName = detectWorktreeName()
|
|
95
|
+
|
|
96
|
+
// Already in a worktree or on main — use cwd as-is
|
|
97
|
+
const root = repoRoot()
|
|
98
|
+
const realCwd = resolve(process.cwd())
|
|
99
|
+
const isAtRoot = realCwd === resolve(root)
|
|
100
|
+
if (detectedName === 'main' || !isAtRoot) {
|
|
101
|
+
return { worktreeName: detectedName, targetCwd: process.cwd(), created: false }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Root is on a non-main branch — check for existing worktree first
|
|
105
|
+
const branch = detectedName
|
|
106
|
+
const existingDir = worktreeDir(branch)
|
|
107
|
+
if (existsSync(resolve(existingDir, '.git'))) {
|
|
108
|
+
p.log.info(`Root is on branch "${branch}" — using existing worktree`)
|
|
109
|
+
return { worktreeName: branch, targetCwd: existingDir, created: false }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// No worktree exists — prompt the user to convert
|
|
113
|
+
p.log.warning(`Root is on branch "${branch}" instead of main.`)
|
|
114
|
+
const shouldConvert = await p.confirm({
|
|
115
|
+
message: `Convert "${branch}" to a worktree? (moves branch to worktrees/${branch}/)`,
|
|
116
|
+
initialValue: true,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
if (p.isCancel(shouldConvert) || !shouldConvert) {
|
|
120
|
+
// User declined — proceed with root as-is (legacy behavior)
|
|
121
|
+
return { worktreeName: detectedName, targetCwd: process.cwd(), created: false }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// User accepted — validate and convert
|
|
125
|
+
if (!allowCreate) {
|
|
126
|
+
p.log.error('Cannot convert — --no-create flag is set.')
|
|
127
|
+
process.exit(1)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (hasUncommittedChanges(root)) {
|
|
131
|
+
p.log.error('Cannot convert — uncommitted changes in working tree.')
|
|
132
|
+
p.log.info('Commit or stash your changes first, then run `sb dev` again.')
|
|
133
|
+
process.exit(1)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const defaultBranch = resolveDefaultBranch(root)
|
|
137
|
+
if (!defaultBranch) {
|
|
138
|
+
p.log.error('Cannot determine default branch (main/master). Switch root manually.')
|
|
139
|
+
process.exit(1)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
p.log.step(`Switching root to "${defaultBranch}"`)
|
|
143
|
+
execFileSync('git', ['checkout', defaultBranch], { cwd: root, stdio: 'inherit' })
|
|
144
|
+
|
|
145
|
+
const targetDir = createWorktree(branch, root, { newBranch: false })
|
|
146
|
+
|
|
147
|
+
// Offer to open the new worktree in VS Code
|
|
148
|
+
const shouldOpen = await p.confirm({
|
|
149
|
+
message: 'Open this worktree in VS Code?',
|
|
150
|
+
initialValue: true,
|
|
151
|
+
})
|
|
152
|
+
if (shouldOpen && !p.isCancel(shouldOpen)) {
|
|
153
|
+
try {
|
|
154
|
+
execFileSync('code', [targetDir], { stdio: 'inherit' })
|
|
155
|
+
} catch {
|
|
156
|
+
p.log.warning(`Could not open VS Code. Run: code ${targetDir}`)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { worktreeName: branch, targetCwd: targetDir, created: true }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const root = repoRoot()
|
|
164
|
+
|
|
165
|
+
// "main" → repo root
|
|
166
|
+
if (branchArg === 'main') {
|
|
167
|
+
return { worktreeName: 'main', targetCwd: root, created: false }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Existing worktree directory
|
|
171
|
+
const existingDir = worktreeDir(branchArg)
|
|
172
|
+
if (existsSync(resolve(existingDir, '.git'))) {
|
|
173
|
+
return { worktreeName: branchArg, targetCwd: existingDir, created: false }
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// From here on we need to create — check if allowed
|
|
177
|
+
if (!allowCreate) {
|
|
178
|
+
p.log.error(`Worktree "${branchArg}" does not exist. Use without --no-create to auto-create.`)
|
|
179
|
+
process.exit(1)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Branch exists (local or remote) — create worktree from it
|
|
183
|
+
const hasLocal = localBranchExists(branchArg, root)
|
|
184
|
+
const hasRemote = !hasLocal && remoteBranchExists(branchArg, root)
|
|
185
|
+
|
|
186
|
+
if (hasLocal || hasRemote) {
|
|
187
|
+
const targetDir = createWorktree(branchArg, root, { newBranch: false })
|
|
188
|
+
return { worktreeName: branchArg, targetCwd: targetDir, created: true }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Branch doesn't exist — interactive TTY gets a prompt, non-interactive auto-creates
|
|
192
|
+
const isTTY = process.stdin.isTTY
|
|
193
|
+
|
|
194
|
+
if (isTTY) {
|
|
195
|
+
const confirmed = await p.confirm({
|
|
196
|
+
message: `Branch "${branchArg}" doesn't exist. Create it from HEAD?`,
|
|
197
|
+
})
|
|
198
|
+
if (p.isCancel(confirmed) || !confirmed) {
|
|
199
|
+
p.cancel('Cancelled.')
|
|
200
|
+
process.exit(0)
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
p.log.step(`Branch "${branchArg}" not found — creating from HEAD`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const targetDir = createWorktree(branchArg, root, { newBranch: true })
|
|
207
|
+
return { worktreeName: branchArg, targetCwd: targetDir, created: true }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─── Server Detection ───
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if a storyboard server is already running on the expected port.
|
|
214
|
+
* Returns the server's health response if running, or null.
|
|
215
|
+
*/
|
|
216
|
+
function checkExistingServer(port) {
|
|
217
|
+
return new Promise((resolve) => {
|
|
218
|
+
const req = http.get(`http://localhost:${port}/health`, (res) => {
|
|
219
|
+
let data = ''
|
|
220
|
+
res.on('data', (c) => (data += c))
|
|
221
|
+
res.on('end', () => {
|
|
222
|
+
try { resolve(JSON.parse(data)) }
|
|
223
|
+
catch { resolve(null) }
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
req.on('error', () => resolve(null))
|
|
227
|
+
req.setTimeout(2000, () => { req.destroy(); resolve(null) })
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Ask an existing server to spawn Vite for a branch via the switch-branch API.
|
|
233
|
+
* Returns the parsed JSON response.
|
|
234
|
+
*/
|
|
235
|
+
function requestSwitchBranch(port, branch) {
|
|
236
|
+
return new Promise((resolve, reject) => {
|
|
237
|
+
const body = JSON.stringify({ branch })
|
|
238
|
+
const req = http.request(
|
|
239
|
+
{ hostname: 'localhost', port, path: '/_storyboard/switch-branch', method: 'POST',
|
|
240
|
+
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } },
|
|
241
|
+
(res) => {
|
|
242
|
+
let data = ''
|
|
243
|
+
res.on('data', (c) => (data += c))
|
|
244
|
+
res.on('end', () => {
|
|
245
|
+
try {
|
|
246
|
+
const json = JSON.parse(data)
|
|
247
|
+
if (res.statusCode >= 400) reject(new Error(json.error || `HTTP ${res.statusCode}`))
|
|
248
|
+
else resolve(json)
|
|
249
|
+
} catch { reject(new Error(`Bad response: ${data}`)) }
|
|
250
|
+
})
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
req.on('error', reject)
|
|
254
|
+
req.setTimeout(90_000, () => { req.destroy(); reject(new Error('Timeout waiting for Vite to start')) })
|
|
255
|
+
req.end(body)
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ─── Client Mode ───
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Run in client mode: an existing server handles Vite lifecycle.
|
|
263
|
+
* We just ask it to start the branch, print URLs, and exit.
|
|
264
|
+
*/
|
|
265
|
+
async function runClientMode(serverPort, worktreeName, domain) {
|
|
266
|
+
const isMain = worktreeName === 'main'
|
|
267
|
+
const basePath = isMain ? '/' : `/branch--${worktreeName}/`
|
|
268
|
+
const proxyUrl = `http://${domain}${basePath}`
|
|
269
|
+
|
|
270
|
+
p.log.info(`Server already running on :${serverPort} — requesting dev session for "${worktreeName}"...`)
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const result = await requestSwitchBranch(serverPort, worktreeName)
|
|
274
|
+
|
|
275
|
+
if (result.status === 'already_running') {
|
|
276
|
+
p.log.success(`Already running: ${proxyUrl}`)
|
|
277
|
+
} else {
|
|
278
|
+
p.log.success(`Started: ${proxyUrl}`)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
p.log.info(`Stop with: npx storyboard server stop ${worktreeName}`)
|
|
282
|
+
p.outro('Ready')
|
|
283
|
+
} catch (err) {
|
|
284
|
+
p.log.error(`Failed to start dev for "${worktreeName}": ${err.message}`)
|
|
285
|
+
process.exit(1)
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ─── Owner Mode ───
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Run in owner mode: start the server + Vite in the foreground.
|
|
293
|
+
* This is the original behavior — the process owns the server lifecycle.
|
|
294
|
+
*/
|
|
295
|
+
async function runOwnerMode(worktreeName, targetCwd, domain, serverPort) {
|
|
296
|
+
getPort(worktreeName) // allocate port for this worktree
|
|
297
|
+
const isMain = worktreeName === 'main'
|
|
298
|
+
const basePath = isMain ? '/' : `/branch--${worktreeName}/`
|
|
299
|
+
const proxyUrl = `http://${domain}${basePath}`
|
|
300
|
+
|
|
301
|
+
p.log.info('Starting storyboard server...')
|
|
302
|
+
|
|
303
|
+
const { startServer, spawnViteForBranch } = await import('../server/index.js')
|
|
304
|
+
|
|
305
|
+
let serverInstance
|
|
306
|
+
try {
|
|
307
|
+
serverInstance = await startServer()
|
|
308
|
+
} catch (err) {
|
|
309
|
+
if (err.code === 'EADDRINUSE') {
|
|
310
|
+
// Race condition: server started between our health check and bind attempt.
|
|
311
|
+
// Fall back to client mode.
|
|
312
|
+
p.log.info('Server started by another process — switching to client mode...')
|
|
313
|
+
await runClientMode(serverPort, worktreeName, domain)
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
throw err
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Compact bloated canvas JSONL files before starting Vite
|
|
320
|
+
const compacted = compactAll(targetCwd)
|
|
321
|
+
if (compacted.length > 0) {
|
|
322
|
+
for (const r of compacted) {
|
|
323
|
+
p.log.info(`[compact] ${r.name}: ${(r.before / 1024).toFixed(0)}KB → ${(r.after / 1024).toFixed(0)}KB`)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const entry = spawnViteForBranch(worktreeName)
|
|
328
|
+
const renameWatcher = startRenameWatcher(targetCwd)
|
|
329
|
+
|
|
330
|
+
const compactInterval = setInterval(() => {
|
|
331
|
+
try {
|
|
332
|
+
const results = compactAll(targetCwd)
|
|
333
|
+
for (const r of results) {
|
|
334
|
+
p.log.info(`[compact] ${r.name}: ${(r.before / 1024).toFixed(0)}KB → ${(r.after / 1024).toFixed(0)}KB`)
|
|
335
|
+
}
|
|
336
|
+
} catch { /* non-critical */ }
|
|
337
|
+
}, 15 * 60 * 1000)
|
|
338
|
+
|
|
339
|
+
function cleanup() {
|
|
340
|
+
renameWatcher.close()
|
|
341
|
+
clearInterval(compactInterval)
|
|
342
|
+
try { entry.child.kill('SIGTERM') } catch { /* already dead */ }
|
|
343
|
+
try { serverInstance.close() } catch { /* best effort */ }
|
|
344
|
+
releasePort(worktreeName)
|
|
345
|
+
process.exit(0)
|
|
346
|
+
}
|
|
347
|
+
process.on('SIGINT', cleanup)
|
|
348
|
+
process.on('SIGTERM', cleanup)
|
|
349
|
+
|
|
350
|
+
const ready = await (async () => {
|
|
351
|
+
const start = Date.now()
|
|
352
|
+
while (Date.now() - start < 60_000) {
|
|
353
|
+
if (entry.status === 'ready') return true
|
|
354
|
+
await new Promise(r => setTimeout(r, 300))
|
|
355
|
+
}
|
|
356
|
+
return false
|
|
357
|
+
})()
|
|
358
|
+
|
|
359
|
+
if (ready) {
|
|
360
|
+
const actualDirectUrl = `http://localhost:${entry.port}${basePath}`
|
|
361
|
+
|
|
362
|
+
if (isCaddyRunning()) {
|
|
363
|
+
const routeConfig = generateRouteConfig({ [worktreeName]: entry.port })
|
|
364
|
+
if (upsertCaddyRoute(routeConfig)) {
|
|
365
|
+
generateCaddyfile({ [worktreeName]: entry.port })
|
|
366
|
+
}
|
|
367
|
+
p.log.success(`${proxyUrl}`)
|
|
368
|
+
p.log.info(`direct: ${actualDirectUrl}`)
|
|
369
|
+
} else {
|
|
370
|
+
p.log.success(actualDirectUrl)
|
|
371
|
+
p.log.warning('Proxy not running — run `npx storyboard setup` for clean URLs')
|
|
372
|
+
}
|
|
373
|
+
p.outro('Ready')
|
|
374
|
+
|
|
375
|
+
entry.child.stdout.pipe(process.stdout)
|
|
376
|
+
entry.child.stderr.pipe(process.stderr)
|
|
377
|
+
} else {
|
|
378
|
+
p.log.warning(`Vite may still be starting — check ${proxyUrl}`)
|
|
379
|
+
p.outro('Server running')
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
entry.child.on('exit', (code) => {
|
|
383
|
+
renameWatcher.close()
|
|
384
|
+
clearInterval(compactInterval)
|
|
385
|
+
try { serverInstance.close() } catch { /* best effort */ }
|
|
386
|
+
if (code && code !== 0) {
|
|
387
|
+
p.log.error(`Vite exited with code ${code}`)
|
|
388
|
+
}
|
|
389
|
+
process.exit(code ?? 0)
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ─── Main ───
|
|
394
|
+
|
|
395
|
+
async function main() {
|
|
396
|
+
const { flags, positional } = parseFlags(process.argv.slice(3), flagSchema)
|
|
397
|
+
|
|
398
|
+
const branchArg = positional[0] || undefined
|
|
399
|
+
const allowCreate = flags.create
|
|
400
|
+
|
|
401
|
+
p.intro('storyboard dev')
|
|
402
|
+
|
|
403
|
+
const { worktreeName, targetCwd, created } = await resolveDevTarget(branchArg, { allowCreate })
|
|
404
|
+
|
|
405
|
+
if (created) {
|
|
406
|
+
p.log.success(`Worktree ready: worktrees/${worktreeName}`)
|
|
407
|
+
} else if (branchArg) {
|
|
408
|
+
p.log.info(`Using ${worktreeName === 'main' ? 'main repo' : `worktrees/${worktreeName}`}`)
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const domain = readDevDomain(targetCwd)
|
|
412
|
+
|
|
413
|
+
// Check if a storyboard server is already running for this repo
|
|
414
|
+
const { SERVER_PORT } = await import('../server/index.js')
|
|
415
|
+
const existingServer = await checkExistingServer(SERVER_PORT)
|
|
416
|
+
|
|
417
|
+
if (existingServer?.ok) {
|
|
418
|
+
// Validate the running server belongs to the same repo (devDomain match)
|
|
419
|
+
if (existingServer.devDomain && existingServer.devDomain !== domain) {
|
|
420
|
+
p.log.error(`Port ${SERVER_PORT} is in use by a different project (domain: ${existingServer.devDomain}).`)
|
|
421
|
+
p.log.info('Stop it with `npx storyboard exit`, or use a different devDomain in storyboard.config.json.')
|
|
422
|
+
process.exit(1)
|
|
423
|
+
}
|
|
424
|
+
await runClientMode(SERVER_PORT, worktreeName, domain)
|
|
425
|
+
} else {
|
|
426
|
+
await runOwnerMode(worktreeName, targetCwd, domain, SERVER_PORT)
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storyboard exit — Stop all running dev servers and the Caddy proxy.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as p from '@clack/prompts'
|
|
6
|
+
import { list, unregister } from '../worktree/serverRegistry.js'
|
|
7
|
+
import { isCaddyRunning, stopCaddy } from './proxy.js'
|
|
8
|
+
|
|
9
|
+
p.intro('storyboard exit')
|
|
10
|
+
|
|
11
|
+
// 1. Stop all registered dev servers
|
|
12
|
+
const servers = list()
|
|
13
|
+
if (servers.length > 0) {
|
|
14
|
+
let stopped = 0
|
|
15
|
+
for (const s of servers) {
|
|
16
|
+
try {
|
|
17
|
+
process.kill(s.pid, 'SIGTERM')
|
|
18
|
+
stopped++
|
|
19
|
+
} catch { /* already dead */ }
|
|
20
|
+
unregister(s.id)
|
|
21
|
+
}
|
|
22
|
+
p.log.success(`Stopped ${stopped} dev server${stopped !== 1 ? 's' : ''}`)
|
|
23
|
+
} else {
|
|
24
|
+
p.log.info('No dev servers running')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 2. Stop Caddy proxy via admin API (no sudo needed)
|
|
28
|
+
if (isCaddyRunning()) {
|
|
29
|
+
if (stopCaddy()) {
|
|
30
|
+
p.log.success('Proxy stopped')
|
|
31
|
+
} else {
|
|
32
|
+
p.log.warning('Failed to stop proxy via admin API')
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
p.log.info('Proxy was not running')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
p.outro('All stopped')
|