@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,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storyboard setup — One-time setup for the Storyboard dev environment.
|
|
3
|
+
*
|
|
4
|
+
* Idempotent: safe to run multiple times, only does what's needed.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx storyboard setup # interactive
|
|
8
|
+
* npx storyboard setup --skip-branch # non-interactive, skip branch prompt
|
|
9
|
+
* npx storyboard setup --branch=<name> # non-interactive, switch to branch after setup
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as p from '@clack/prompts'
|
|
13
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync, readdirSync, symlinkSync } from 'fs'
|
|
14
|
+
import path from 'path'
|
|
15
|
+
import { execSync } from 'child_process'
|
|
16
|
+
import { generateCaddyfile, isCaddyInstalled, isCaddyRunning, startCaddy, reloadCaddy } from './proxy.js'
|
|
17
|
+
import { gettingStartedLines, dim, magenta, bold, yellow, green } from './intro.js'
|
|
18
|
+
import { parseFlags } from './flags.js'
|
|
19
|
+
|
|
20
|
+
const flagSchema = {
|
|
21
|
+
'skip-branch': { type: 'boolean', default: false, description: 'Skip the branch prompt at the end' },
|
|
22
|
+
branch: { type: 'string', description: 'Switch to a branch after setup (non-interactive)' },
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { flags } = parseFlags(process.argv.slice(3), flagSchema)
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Run a potentially slow task with a spinner that only appears after 500ms.
|
|
29
|
+
* If the task completes quickly, shows the done message immediately.
|
|
30
|
+
*/
|
|
31
|
+
async function withSpin(label, doneMsg, fn) {
|
|
32
|
+
const spin = p.spinner()
|
|
33
|
+
const timer = setTimeout(() => spin.start(label), 500)
|
|
34
|
+
try {
|
|
35
|
+
await fn()
|
|
36
|
+
clearTimeout(timer)
|
|
37
|
+
spin.stop(doneMsg)
|
|
38
|
+
} catch (err) {
|
|
39
|
+
clearTimeout(timer)
|
|
40
|
+
spin.stop(`Failed: ${label}`)
|
|
41
|
+
throw err
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function mascot() {
|
|
46
|
+
const d = dim('·')
|
|
47
|
+
const f = magenta
|
|
48
|
+
const b = dim
|
|
49
|
+
const msg = ` ${bold('Happy prototyping!')} 🎨`
|
|
50
|
+
return [
|
|
51
|
+
` ${b('╭─────────────────╮')}`,
|
|
52
|
+
` ${b('│')} ${d} ${f('◠')} ${f('◡')} ${f('◠')} ${d} ${b('│')}${msg}`,
|
|
53
|
+
` ${b('│')} ${d} ${d} ${d} ${d} ${d} ${b('│')}`,
|
|
54
|
+
` ${b('╰─────────────────╯')}`,
|
|
55
|
+
].join('\n')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function run(cmd, opts = {}) {
|
|
59
|
+
execSync(cmd, { stdio: 'pipe', ...opts })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function isInstalled(cmd) {
|
|
63
|
+
try {
|
|
64
|
+
execSync(`which ${cmd}`, { stdio: 'ignore' })
|
|
65
|
+
return true
|
|
66
|
+
} catch {
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
p.intro('storyboard setup')
|
|
72
|
+
|
|
73
|
+
// 1. Check for node_modules (quick sanity check — full install runs at the end)
|
|
74
|
+
if (!existsSync('node_modules')) {
|
|
75
|
+
p.log.info('node_modules not found — will install at end of setup')
|
|
76
|
+
} else {
|
|
77
|
+
p.log.success('Dependencies present')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 2. Homebrew
|
|
81
|
+
let hasBrew = isInstalled('brew')
|
|
82
|
+
if (!hasBrew) {
|
|
83
|
+
const brewSpin = p.spinner()
|
|
84
|
+
brewSpin.start('Installing Homebrew...')
|
|
85
|
+
try {
|
|
86
|
+
run('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"')
|
|
87
|
+
const brewPaths = ['/opt/homebrew/bin/brew', '/usr/local/bin/brew', '/home/linuxbrew/.linuxbrew/bin/brew']
|
|
88
|
+
for (const bp of brewPaths) {
|
|
89
|
+
if (existsSync(bp)) {
|
|
90
|
+
process.env.PATH = `${bp.replace(/\/brew$/, '')}:${process.env.PATH}`
|
|
91
|
+
break
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
hasBrew = true
|
|
95
|
+
brewSpin.stop('Homebrew installed')
|
|
96
|
+
} catch {
|
|
97
|
+
brewSpin.stop('Failed to install Homebrew')
|
|
98
|
+
p.log.warning('Install manually: https://brew.sh')
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 3. Caddy
|
|
103
|
+
if (hasBrew) {
|
|
104
|
+
if (isCaddyInstalled()) {
|
|
105
|
+
p.log.success('Caddy proxy installed')
|
|
106
|
+
} else {
|
|
107
|
+
const caddySpin = p.spinner()
|
|
108
|
+
caddySpin.start('Installing Caddy...')
|
|
109
|
+
try {
|
|
110
|
+
run('brew install caddy')
|
|
111
|
+
caddySpin.stop('Caddy proxy installed')
|
|
112
|
+
} catch {
|
|
113
|
+
caddySpin.stop('Failed to install Caddy')
|
|
114
|
+
p.log.warning('Install manually: brew install caddy')
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 4. GitHub CLI
|
|
119
|
+
if (isInstalled('gh')) {
|
|
120
|
+
p.log.success('GitHub CLI installed')
|
|
121
|
+
} else {
|
|
122
|
+
const ghSpin = p.spinner()
|
|
123
|
+
ghSpin.start('Installing GitHub CLI...')
|
|
124
|
+
try {
|
|
125
|
+
run('brew install gh')
|
|
126
|
+
ghSpin.stop('GitHub CLI installed')
|
|
127
|
+
} catch {
|
|
128
|
+
ghSpin.stop('Failed to install GitHub CLI')
|
|
129
|
+
p.log.warning('Install manually: brew install gh')
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 5. VS Code CLI
|
|
135
|
+
if (isInstalled('code')) {
|
|
136
|
+
p.log.success('VS Code CLI installed')
|
|
137
|
+
} else {
|
|
138
|
+
// Try to install the `code` CLI from VS Code's known locations
|
|
139
|
+
const codePaths = [
|
|
140
|
+
'/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code',
|
|
141
|
+
'/usr/local/bin/code',
|
|
142
|
+
]
|
|
143
|
+
let installed = false
|
|
144
|
+
for (const codePath of codePaths) {
|
|
145
|
+
if (existsSync(codePath)) {
|
|
146
|
+
p.log.success('VS Code CLI available (symlink exists)')
|
|
147
|
+
installed = true
|
|
148
|
+
break
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (!installed) {
|
|
152
|
+
// Try the VS Code shell command installer
|
|
153
|
+
const vsCodeApp = '/Applications/Visual Studio Code.app'
|
|
154
|
+
if (existsSync(vsCodeApp)) {
|
|
155
|
+
const shellScript = `${vsCodeApp}/Contents/Resources/app/bin/code`
|
|
156
|
+
if (existsSync(shellScript)) {
|
|
157
|
+
try {
|
|
158
|
+
// Create symlink in /usr/local/bin
|
|
159
|
+
run(`ln -sf "${shellScript}" /usr/local/bin/code`)
|
|
160
|
+
p.log.success('VS Code CLI installed (symlinked to /usr/local/bin/code)')
|
|
161
|
+
installed = true
|
|
162
|
+
} catch {
|
|
163
|
+
// Fall through to manual instructions
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!installed) {
|
|
168
|
+
p.log.warning('VS Code CLI not found. Open VS Code and run:')
|
|
169
|
+
p.log.info(' Cmd+Shift+P → "Shell Command: Install \'code\' command in PATH"')
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 6. Git hooks
|
|
175
|
+
{
|
|
176
|
+
// Create .githooks/ if it doesn't exist — copy from scaffold template
|
|
177
|
+
if (!existsSync('.githooks')) {
|
|
178
|
+
const scaffoldHooks = path.resolve(import.meta.dirname, '..', '..', 'scaffold', 'githooks')
|
|
179
|
+
if (existsSync(scaffoldHooks)) {
|
|
180
|
+
try {
|
|
181
|
+
run(`cp -r "${scaffoldHooks}" .githooks`)
|
|
182
|
+
run('chmod +x .githooks/*')
|
|
183
|
+
} catch { /* fall through */ }
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (existsSync('.githooks')) {
|
|
188
|
+
try {
|
|
189
|
+
run('git config core.hooksPath .githooks')
|
|
190
|
+
if (existsSync('.githooks/pre-push')) {
|
|
191
|
+
run('chmod +x .githooks/pre-push')
|
|
192
|
+
}
|
|
193
|
+
p.log.success('Git hooks activated (.githooks/)')
|
|
194
|
+
} catch {
|
|
195
|
+
p.log.warning('Failed to set git hooks path')
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
p.log.info(dim('No .githooks/ directory — run storyboard-scaffold first'))
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 7. Asset directories
|
|
203
|
+
{
|
|
204
|
+
const dirs = ['assets/canvas/images', 'assets/.storyboard-public/terminal-snapshots', '.storyboard', '.storyboard/terminals', '.storyboard/terminal-buffers', '.storyboard/logs']
|
|
205
|
+
for (const dir of dirs) {
|
|
206
|
+
if (!existsSync(dir)) {
|
|
207
|
+
try { mkdirSync(dir, { recursive: true }) } catch { /* ignore */ }
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Scaffold initial .selectedwidgets.json if missing
|
|
212
|
+
const selectedWidgetsPath = '.storyboard/.selectedwidgets.json'
|
|
213
|
+
if (!existsSync(selectedWidgetsPath)) {
|
|
214
|
+
try {
|
|
215
|
+
writeFileSync(selectedWidgetsPath, JSON.stringify({
|
|
216
|
+
canvasId: null,
|
|
217
|
+
canvasFile: null,
|
|
218
|
+
selectedWidgetIds: [],
|
|
219
|
+
widgets: [],
|
|
220
|
+
}, null, 2) + '\n', 'utf-8')
|
|
221
|
+
} catch { /* ignore */ }
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
p.log.success('Canvas asset directories ready')
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 7a. Scaffold .gitignore entries for private canvas images
|
|
228
|
+
{
|
|
229
|
+
const gitignorePath = '.gitignore'
|
|
230
|
+
const privatePatterns = [
|
|
231
|
+
'src/canvas/images/~*',
|
|
232
|
+
'assets/canvas/images/~*',
|
|
233
|
+
'assets/canvas/snapshots/~*',
|
|
234
|
+
'assets/.storyboard-public/terminal-snapshots/~*',
|
|
235
|
+
]
|
|
236
|
+
if (existsSync(gitignorePath)) {
|
|
237
|
+
try {
|
|
238
|
+
let content = readFileSync(gitignorePath, 'utf-8')
|
|
239
|
+
const missing = privatePatterns.filter(p => !content.includes(p))
|
|
240
|
+
if (missing.length > 0) {
|
|
241
|
+
const block = '\n# Private canvas images (tilde prefix = not committed)\n' + missing.join('\n') + '\n'
|
|
242
|
+
content = content.trimEnd() + '\n' + block
|
|
243
|
+
writeFileSync(gitignorePath, content, 'utf-8')
|
|
244
|
+
p.log.success('Added private image patterns to .gitignore')
|
|
245
|
+
}
|
|
246
|
+
} catch { /* ignore */ }
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// 7b. Copilot agents
|
|
251
|
+
{
|
|
252
|
+
const agentsDir = '.agents'
|
|
253
|
+
if (!existsSync(agentsDir)) {
|
|
254
|
+
try { mkdirSync(agentsDir, { recursive: true }) } catch { /* ignore */ }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const scaffoldAgents = path.resolve(import.meta.dirname, '..', '..', 'scaffold', 'agents')
|
|
258
|
+
if (existsSync(scaffoldAgents)) {
|
|
259
|
+
try {
|
|
260
|
+
const agentFiles = execSync(`ls "${scaffoldAgents}"`, { encoding: 'utf8' }).trim().split('\n').filter(Boolean)
|
|
261
|
+
for (const file of agentFiles) {
|
|
262
|
+
const dest = path.join(agentsDir, file)
|
|
263
|
+
if (!existsSync(dest)) {
|
|
264
|
+
run(`cp "${path.join(scaffoldAgents, file)}" "${dest}"`)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (agentFiles.length > 0) {
|
|
268
|
+
p.log.success('Copilot agents scaffolded (.agents/)')
|
|
269
|
+
}
|
|
270
|
+
} catch { /* ignore */ }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Create symlinks in each CLI's expected agent directory.
|
|
274
|
+
// Source of truth: .agents/*.agent.md
|
|
275
|
+
// Copilot CLI reads .github/agents/*.md
|
|
276
|
+
// Claude Code reads .claude/agents/*.md
|
|
277
|
+
// Codex CLI uses .codex/config.toml (no agent files needed)
|
|
278
|
+
try {
|
|
279
|
+
const agentFiles = readdirSync(agentsDir).filter(f => f.endsWith('.agent.md'))
|
|
280
|
+
const targets = [
|
|
281
|
+
{ dir: path.join('.github', 'agents'), label: 'Copilot CLI' },
|
|
282
|
+
{ dir: path.join('.claude', 'agents'), label: 'Claude Code' },
|
|
283
|
+
]
|
|
284
|
+
for (const { dir, label } of targets) {
|
|
285
|
+
try { mkdirSync(dir, { recursive: true }) } catch { /* ignore */ }
|
|
286
|
+
let linked = 0
|
|
287
|
+
for (const file of agentFiles) {
|
|
288
|
+
const linkName = file.replace('.agent.md', '.md')
|
|
289
|
+
const linkPath = path.join(dir, linkName)
|
|
290
|
+
const target = path.relative(dir, path.join(agentsDir, file))
|
|
291
|
+
if (!existsSync(linkPath)) {
|
|
292
|
+
try { symlinkSync(target, linkPath); linked++ } catch { /* ignore */ }
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (linked > 0) {
|
|
296
|
+
p.log.success(`${label} agent symlinks created (${dir}/)`)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} catch { /* ignore */ }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// 8. Proxy
|
|
303
|
+
if (isCaddyInstalled()) {
|
|
304
|
+
const proxySpin = p.spinner()
|
|
305
|
+
const caddyfilePath = generateCaddyfile()
|
|
306
|
+
if (isCaddyRunning()) {
|
|
307
|
+
proxySpin.start('Reloading proxy...')
|
|
308
|
+
reloadCaddy(caddyfilePath)
|
|
309
|
+
proxySpin.stop('Proxy reloaded')
|
|
310
|
+
} else {
|
|
311
|
+
proxySpin.start('Starting proxy...')
|
|
312
|
+
startCaddy(caddyfilePath)
|
|
313
|
+
proxySpin.stop('Proxy started')
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 9. Install / sync dependencies
|
|
318
|
+
// Skip npm install if a dev server is running — it would trigger Vite's
|
|
319
|
+
// file watcher and restart the server on a different port.
|
|
320
|
+
{
|
|
321
|
+
const { SERVER_PORT } = await import('../server/index.js')
|
|
322
|
+
const { readDevDomain: readDomain } = await import('./proxy.js')
|
|
323
|
+
const http = await import('node:http')
|
|
324
|
+
|
|
325
|
+
const devRunning = await new Promise((resolve) => {
|
|
326
|
+
const req = http.get(`http://localhost:${SERVER_PORT}/health`, (res) => {
|
|
327
|
+
if (res.statusCode !== 200) { resolve(false); return }
|
|
328
|
+
let data = ''
|
|
329
|
+
res.on('data', (chunk) => { data += chunk })
|
|
330
|
+
res.on('end', () => {
|
|
331
|
+
try {
|
|
332
|
+
const json = JSON.parse(data)
|
|
333
|
+
const ourDomain = readDomain()
|
|
334
|
+
resolve(json.ok === true && json.devDomain === ourDomain)
|
|
335
|
+
} catch { resolve(false) }
|
|
336
|
+
})
|
|
337
|
+
})
|
|
338
|
+
req.on('error', () => resolve(false))
|
|
339
|
+
req.setTimeout(1000, () => { req.destroy(); resolve(false) })
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
if (devRunning) {
|
|
343
|
+
p.log.info(dim('Skipping npm install — dev server is running (would cause restart)'))
|
|
344
|
+
p.log.info(dim('Run `npm install` manually after stopping the dev server if needed'))
|
|
345
|
+
} else {
|
|
346
|
+
try {
|
|
347
|
+
await withSpin(
|
|
348
|
+
'Installing dependencies...',
|
|
349
|
+
'Dependencies installed',
|
|
350
|
+
() => { run('npm install', { stdio: 'ignore' }) }
|
|
351
|
+
)
|
|
352
|
+
} catch {
|
|
353
|
+
p.log.warning('npm install failed — run it manually to see details')
|
|
354
|
+
p.log.info(` ${dim('npm install')}`)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
p.note(
|
|
360
|
+
[
|
|
361
|
+
...gettingStartedLines(),
|
|
362
|
+
'',
|
|
363
|
+
` ${dim('Run')} ${yellow('npx storyboard')} ${dim('to see all commands')}`,
|
|
364
|
+
].join('\n'),
|
|
365
|
+
'Getting started'
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
// Offer branch guide
|
|
369
|
+
{
|
|
370
|
+
// Non-interactive: --branch=<name> or --skip-branch
|
|
371
|
+
if (flags.branch) {
|
|
372
|
+
const { runBranchGuide } = await import('./branch.js')
|
|
373
|
+
await runBranchGuide(flags.branch)
|
|
374
|
+
} else if (flags['skip-branch']) {
|
|
375
|
+
console.log()
|
|
376
|
+
console.log(mascot())
|
|
377
|
+
p.outro('')
|
|
378
|
+
} else {
|
|
379
|
+
// Interactive: ask the user
|
|
380
|
+
let currentBranchName = 'main'
|
|
381
|
+
try {
|
|
382
|
+
currentBranchName = execSync('git branch --show-current', { encoding: 'utf8' }).trim() || 'main'
|
|
383
|
+
} catch { /* empty */ }
|
|
384
|
+
|
|
385
|
+
const wantBranch = await p.select({
|
|
386
|
+
message: 'Want to work from a different branch?',
|
|
387
|
+
options: [
|
|
388
|
+
{ value: true, label: 'Yes (help me create it)' },
|
|
389
|
+
{ value: false, label: `No (stay on current branch ${bold(currentBranchName)})` },
|
|
390
|
+
],
|
|
391
|
+
initialValue: false,
|
|
392
|
+
})
|
|
393
|
+
|
|
394
|
+
if (!p.isCancel(wantBranch) && wantBranch) {
|
|
395
|
+
const { runBranchGuide } = await import('./branch.js')
|
|
396
|
+
await runBranchGuide()
|
|
397
|
+
} else {
|
|
398
|
+
console.log()
|
|
399
|
+
console.log(mascot())
|
|
400
|
+
p.log.info(`${dim('Non-interactive:')} ${green('npx sb setup --skip-branch')}`)
|
|
401
|
+
p.outro('')
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* storyboard terminal {close|open|remove} --id <name-or-id>
|
|
4
|
+
*
|
|
5
|
+
* Subcommands for managing terminal sessions by friendly name, tmux name, or widget ID.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as p from '@clack/prompts'
|
|
9
|
+
import { execSync } from 'node:child_process'
|
|
10
|
+
import { detectWorktreeName, resolveRunningPort } from '../worktree/port.js'
|
|
11
|
+
import { readDevDomain } from './proxy.js'
|
|
12
|
+
import { parseFlags } from './flags.js'
|
|
13
|
+
import { dim, cyan, bold, yellow } from './intro.js'
|
|
14
|
+
|
|
15
|
+
const flagSchema = {
|
|
16
|
+
id: { type: 'string', required: true, description: 'Session name, tmux name, or widget ID' },
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const subcommand = process.argv[3]
|
|
20
|
+
const { flags, missing, errors } = parseFlags(process.argv.slice(4), flagSchema)
|
|
21
|
+
|
|
22
|
+
/** Resolve the dev server base URL (proxy or direct) */
|
|
23
|
+
function getBaseUrl(worktreeName, port) {
|
|
24
|
+
const domain = readDevDomain()
|
|
25
|
+
const isMain = worktreeName === 'main'
|
|
26
|
+
const proxyBase = isMain ? `http://${domain}/` : `http://${domain}/branch--${worktreeName}/`
|
|
27
|
+
const directBase = isMain ? `http://localhost:${port}/` : `http://localhost:${port}/branch--${worktreeName}/`
|
|
28
|
+
return { proxyBase, directBase }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Fetch all sessions from the dev server */
|
|
32
|
+
async function fetchSessions(worktreeName, port) {
|
|
33
|
+
const { proxyBase, directBase } = getBaseUrl(worktreeName, port)
|
|
34
|
+
for (const base of [proxyBase, directBase]) {
|
|
35
|
+
try {
|
|
36
|
+
const res = await fetch(`${base}_storyboard/terminal/sessions`, { signal: AbortSignal.timeout(3000) })
|
|
37
|
+
if (!res.ok) continue
|
|
38
|
+
const data = await res.json()
|
|
39
|
+
return { sessions: data.sessions || [], base }
|
|
40
|
+
} catch { continue }
|
|
41
|
+
}
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** POST/DELETE helper that tries proxy then direct */
|
|
46
|
+
async function apiRequest(worktreeName, port, path, method = 'POST', body = null) {
|
|
47
|
+
const { proxyBase, directBase } = getBaseUrl(worktreeName, port)
|
|
48
|
+
for (const base of [proxyBase, directBase]) {
|
|
49
|
+
try {
|
|
50
|
+
const opts = { method, signal: AbortSignal.timeout(5000) }
|
|
51
|
+
if (body) {
|
|
52
|
+
opts.headers = { 'Content-Type': 'application/json' }
|
|
53
|
+
opts.body = JSON.stringify(body)
|
|
54
|
+
}
|
|
55
|
+
const res = await fetch(`${base}${path}`, opts)
|
|
56
|
+
if (res.ok) return { ok: true, data: await res.json().catch(() => ({})) }
|
|
57
|
+
} catch { continue }
|
|
58
|
+
}
|
|
59
|
+
return { ok: false }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Resolve --id to a session entry. Tries:
|
|
64
|
+
* 1. Friendly name match (e.g. "red-robin")
|
|
65
|
+
* 2. Exact tmux name match (e.g. "sb-abc123def456")
|
|
66
|
+
* 3. Widget ID match (e.g. "terminal-abc123")
|
|
67
|
+
*/
|
|
68
|
+
function resolveSession(sessions, id) {
|
|
69
|
+
// Friendly name
|
|
70
|
+
const byName = sessions.find(s => s.name === id)
|
|
71
|
+
if (byName) return byName
|
|
72
|
+
// Tmux name
|
|
73
|
+
const byTmux = sessions.find(s => s.tmuxName === id)
|
|
74
|
+
if (byTmux) return byTmux
|
|
75
|
+
// Widget ID
|
|
76
|
+
const byWidget = sessions.find(s => s.widgetId === id)
|
|
77
|
+
if (byWidget) return byWidget
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Detect the current tmux session name (only if it's a storyboard session) */
|
|
82
|
+
function getCurrentTmuxSession() {
|
|
83
|
+
try {
|
|
84
|
+
const name = execSync('tmux display-message -p "#{session_name}" 2>/dev/null', {
|
|
85
|
+
encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
|
|
86
|
+
}).trim()
|
|
87
|
+
// Only return if this is a storyboard terminal session
|
|
88
|
+
return name.startsWith('sb-') ? name : null
|
|
89
|
+
} catch {
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Check if we're inside ANY tmux (storyboard or user's own) */
|
|
95
|
+
function _isInsideTmux() {
|
|
96
|
+
return Boolean(process.env.TMUX)
|
|
97
|
+
}
|
|
98
|
+
void _isInsideTmux
|
|
99
|
+
|
|
100
|
+
// ── Close (archive) ──
|
|
101
|
+
|
|
102
|
+
async function closeSession(session, worktreeName, port) {
|
|
103
|
+
p.intro(bold('Close session'))
|
|
104
|
+
|
|
105
|
+
const label = session.name || session.tmuxName
|
|
106
|
+
|
|
107
|
+
if (session.status === 'live') {
|
|
108
|
+
p.log.info(`Session ${cyan(label)} is live — detaching first...`)
|
|
109
|
+
const detachResult = await apiRequest(
|
|
110
|
+
worktreeName, port,
|
|
111
|
+
`_storyboard/terminal/sessions/${encodeURIComponent(session.tmuxName)}/detach`,
|
|
112
|
+
)
|
|
113
|
+
if (!detachResult.ok) {
|
|
114
|
+
p.log.warn('Could not detach via API, continuing with archive...')
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Orphan the session (archive with grace timer)
|
|
119
|
+
const orphanResult = await apiRequest(
|
|
120
|
+
worktreeName, port,
|
|
121
|
+
`_storyboard/terminal/sessions/${encodeURIComponent(session.tmuxName)}/orphan`,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if (orphanResult.ok) {
|
|
125
|
+
p.log.success(`Session ${cyan(label)} archived. Tmux session kept alive with grace timer.`)
|
|
126
|
+
} else {
|
|
127
|
+
p.log.error(`Failed to archive session ${cyan(label)}`)
|
|
128
|
+
process.exit(1)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
p.outro('')
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── Open (attach) ──
|
|
135
|
+
|
|
136
|
+
async function openSession(session, _worktreeName, _port) {
|
|
137
|
+
void _worktreeName
|
|
138
|
+
void _port
|
|
139
|
+
p.intro(bold('Open session'))
|
|
140
|
+
|
|
141
|
+
const label = session.name || session.tmuxName
|
|
142
|
+
const currentSb = getCurrentTmuxSession()
|
|
143
|
+
|
|
144
|
+
if (session.status === 'live' && session.tmuxName !== currentSb) {
|
|
145
|
+
p.log.warn(
|
|
146
|
+
`Session ${cyan(label)} is currently live on widget ${dim(session.widgetId)} ` +
|
|
147
|
+
`in canvas ${cyan(session.canvasId)}.`
|
|
148
|
+
)
|
|
149
|
+
const confirm = await p.confirm({
|
|
150
|
+
message: 'Attach anyway? This may cause conflicts with the live widget.',
|
|
151
|
+
})
|
|
152
|
+
if (p.isCancel(confirm) || !confirm) {
|
|
153
|
+
p.outro(dim('Cancelled'))
|
|
154
|
+
process.exit(0)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// If we're inside a storyboard tmux session, use switch-client.
|
|
159
|
+
// Otherwise always use attach-session (even if user has their own tmux).
|
|
160
|
+
if (currentSb) {
|
|
161
|
+
p.outro(`Switching to ${bold(label)}...`)
|
|
162
|
+
try {
|
|
163
|
+
execSync(`tmux switch-client -t "${session.tmuxName}"`, { stdio: 'inherit' })
|
|
164
|
+
} catch {
|
|
165
|
+
p.log.error('Failed to switch tmux client')
|
|
166
|
+
process.exit(1)
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
p.outro(`Attaching to ${bold(label)}...`)
|
|
170
|
+
try {
|
|
171
|
+
execSync(`tmux attach-session -t "${session.tmuxName}"`, { stdio: 'inherit' })
|
|
172
|
+
} catch {
|
|
173
|
+
p.log.error('Failed to attach to tmux session')
|
|
174
|
+
process.exit(1)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ── Remove (destroy) ──
|
|
180
|
+
|
|
181
|
+
async function removeSession(session, worktreeName, port) {
|
|
182
|
+
p.intro(bold('Remove session'))
|
|
183
|
+
|
|
184
|
+
const label = session.name || session.tmuxName
|
|
185
|
+
|
|
186
|
+
const widgetNote = session.widgetId && session.widgetId !== 'unknown'
|
|
187
|
+
? `\n This will also ${yellow('remove the terminal widget')} from canvas ${cyan(session.canvasId)}.`
|
|
188
|
+
: ''
|
|
189
|
+
|
|
190
|
+
const confirm = await p.confirm({
|
|
191
|
+
message: `Permanently destroy session ${bold(label)}?${widgetNote}\n The tmux session and all running processes inside it will be lost.`,
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
if (p.isCancel(confirm) || !confirm) {
|
|
195
|
+
p.outro(dim('Cancelled'))
|
|
196
|
+
process.exit(0)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Kill via API
|
|
200
|
+
const killResult = await apiRequest(
|
|
201
|
+
worktreeName, port,
|
|
202
|
+
`_storyboard/terminal/sessions/${encodeURIComponent(session.tmuxName)}`,
|
|
203
|
+
'DELETE',
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if (killResult.ok) {
|
|
207
|
+
p.log.success(`Session ${cyan(label)} destroyed`)
|
|
208
|
+
} else {
|
|
209
|
+
p.log.warn('API call failed, killing tmux session directly...')
|
|
210
|
+
try {
|
|
211
|
+
execSync(`tmux kill-session -t "${session.tmuxName}" 2>/dev/null`, { stdio: 'ignore' })
|
|
212
|
+
p.log.success(`Session ${cyan(label)} killed via tmux`)
|
|
213
|
+
} catch {
|
|
214
|
+
p.log.error('Failed to kill session')
|
|
215
|
+
process.exit(1)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Remove widget from canvas
|
|
220
|
+
if (session.widgetId && session.widgetId !== 'unknown' && session.canvasId && session.canvasId !== 'unknown') {
|
|
221
|
+
const removeResult = await apiRequest(
|
|
222
|
+
worktreeName, port,
|
|
223
|
+
'_storyboard/canvas/widget',
|
|
224
|
+
'DELETE',
|
|
225
|
+
{ name: session.canvasId, widgetId: session.widgetId },
|
|
226
|
+
)
|
|
227
|
+
if (removeResult.ok) {
|
|
228
|
+
p.log.success(`Widget ${dim(session.widgetId)} removed from canvas ${cyan(session.canvasId)}`)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
p.outro('')
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Main ──
|
|
236
|
+
|
|
237
|
+
async function main() {
|
|
238
|
+
if (missing.length > 0 || errors.length > 0) {
|
|
239
|
+
p.intro(bold('Terminal'))
|
|
240
|
+
if (errors.length) errors.forEach(e => p.log.error(e))
|
|
241
|
+
if (missing.length) p.log.error(`Missing required flag: ${bold('--id')}`)
|
|
242
|
+
p.log.info(`Usage: ${cyan(`storyboard terminal ${subcommand} --id <name-or-id>`)}`)
|
|
243
|
+
p.outro('')
|
|
244
|
+
process.exit(1)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const worktreeName = detectWorktreeName()
|
|
248
|
+
const port = resolveRunningPort(worktreeName)
|
|
249
|
+
const result = await fetchSessions(worktreeName, port)
|
|
250
|
+
|
|
251
|
+
if (!result) {
|
|
252
|
+
p.intro(bold('Terminal'))
|
|
253
|
+
p.log.error('Could not connect to dev server. Is it running?')
|
|
254
|
+
p.outro('')
|
|
255
|
+
process.exit(1)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const session = resolveSession(result.sessions, flags.id)
|
|
259
|
+
if (!session) {
|
|
260
|
+
p.intro(bold('Terminal'))
|
|
261
|
+
p.log.error(`No session found matching ${bold(flags.id)}`)
|
|
262
|
+
p.log.info(`Try ${cyan('storyboard terminal')} to browse sessions.`)
|
|
263
|
+
p.outro('')
|
|
264
|
+
process.exit(1)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
switch (subcommand) {
|
|
268
|
+
case 'close':
|
|
269
|
+
case 'archive':
|
|
270
|
+
await closeSession(session, worktreeName, port)
|
|
271
|
+
break
|
|
272
|
+
case 'open':
|
|
273
|
+
await openSession(session, worktreeName, port)
|
|
274
|
+
break
|
|
275
|
+
case 'remove':
|
|
276
|
+
await removeSession(session, worktreeName, port)
|
|
277
|
+
break
|
|
278
|
+
default:
|
|
279
|
+
p.log.error(`Unknown subcommand: ${subcommand}`)
|
|
280
|
+
process.exit(1)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
main().catch((err) => {
|
|
285
|
+
p.log.error(err.message)
|
|
286
|
+
process.exit(1)
|
|
287
|
+
})
|