@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,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workshop feature registry.
|
|
3
|
+
*
|
|
4
|
+
* Maps feature names (matching keys in storyboard.config.json → workshop.features)
|
|
5
|
+
* to their module paths. The server plugin and client mount use this registry
|
|
6
|
+
* to dynamically load only the enabled features.
|
|
7
|
+
*
|
|
8
|
+
* To add a new feature:
|
|
9
|
+
* 1. Create a directory under features/ with index.js exporting the standard interface
|
|
10
|
+
* 2. Add its import here
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as createPrototype from './createPrototype/index.js'
|
|
14
|
+
import * as createFlow from './createFlow/index.js'
|
|
15
|
+
import * as createPage from './createPage/index.js'
|
|
16
|
+
import * as createCanvas from './createCanvas/index.js'
|
|
17
|
+
import * as createStory from './createStory/index.js'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* All available workshop features, keyed by config name.
|
|
21
|
+
*/
|
|
22
|
+
export const features = {
|
|
23
|
+
createPrototype,
|
|
24
|
+
createFlow,
|
|
25
|
+
createPage,
|
|
26
|
+
createCanvas,
|
|
27
|
+
createStory,
|
|
28
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { parse as parseJsonc } from 'jsonc-parser'
|
|
4
|
+
|
|
5
|
+
const TEMPLATE_DIR_NAMES = new Set(['template', 'templates'])
|
|
6
|
+
const RECIPE_DIR_NAMES = new Set(['recipe', 'recipes'])
|
|
7
|
+
const PARTIAL_DIR_NAMES = new Set([...TEMPLATE_DIR_NAMES, ...RECIPE_DIR_NAMES])
|
|
8
|
+
|
|
9
|
+
function normalizeSlashes(value) {
|
|
10
|
+
return value.replaceAll('\\', '/')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isExternalPrototype(prototypePath) {
|
|
14
|
+
try {
|
|
15
|
+
const raw = fs.readFileSync(prototypePath, 'utf-8')
|
|
16
|
+
const parsed = parseJsonc(raw)
|
|
17
|
+
return typeof parsed?.url === 'string' && parsed.url.trim().length > 0
|
|
18
|
+
} catch {
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isPartialDirName(directory) {
|
|
24
|
+
return PARTIAL_DIR_NAMES.has(directory)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function toPartialKind(directory) {
|
|
28
|
+
if (TEMPLATE_DIR_NAMES.has(directory)) return 'template'
|
|
29
|
+
if (RECIPE_DIR_NAMES.has(directory)) return 'recipe'
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function listPrototypeDirs(root) {
|
|
34
|
+
const prototypesDir = path.join(root, 'src', 'prototypes')
|
|
35
|
+
if (!fs.existsSync(prototypesDir)) return []
|
|
36
|
+
|
|
37
|
+
const results = []
|
|
38
|
+
|
|
39
|
+
function scanDir(dir, folder) {
|
|
40
|
+
if (!fs.existsSync(dir)) return
|
|
41
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
42
|
+
if (!entry.isDirectory()) continue
|
|
43
|
+
if (entry.name.endsWith('.folder')) {
|
|
44
|
+
scanDir(path.join(dir, entry.name), entry.name.replace(/\.folder$/, ''))
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const protoDir = path.join(dir, entry.name)
|
|
49
|
+
const prototypeFile = fs.readdirSync(protoDir).find((f) => f.endsWith('.prototype.json'))
|
|
50
|
+
if (!prototypeFile) continue
|
|
51
|
+
if (isExternalPrototype(path.join(protoDir, prototypeFile))) continue
|
|
52
|
+
|
|
53
|
+
results.push({
|
|
54
|
+
name: entry.name,
|
|
55
|
+
folder,
|
|
56
|
+
dir: protoDir,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
scanDir(prototypesDir)
|
|
62
|
+
return results
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function makeId(prefix, directory, name, prototype, folder) {
|
|
66
|
+
if (prefix === 'global') {
|
|
67
|
+
return `global:${directory}/${name}`
|
|
68
|
+
}
|
|
69
|
+
const folderPart = folder ? `${folder}/` : ''
|
|
70
|
+
return `prototype:${folderPart}${prototype}:${directory}/${name}`
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function buildTemplateRecipeIndex(root, configPartials = []) {
|
|
74
|
+
const srcRoot = path.join(root, 'src')
|
|
75
|
+
const results = []
|
|
76
|
+
const seenIds = new Set()
|
|
77
|
+
|
|
78
|
+
for (const partial of configPartials) {
|
|
79
|
+
if (!partial || !isPartialDirName(partial.directory) || !partial.name) continue
|
|
80
|
+
const kind = toPartialKind(partial.directory)
|
|
81
|
+
if (!kind) continue
|
|
82
|
+
const id = makeId('global', partial.directory, partial.name)
|
|
83
|
+
if (seenIds.has(id)) continue
|
|
84
|
+
seenIds.add(id)
|
|
85
|
+
results.push({
|
|
86
|
+
id,
|
|
87
|
+
name: partial.name,
|
|
88
|
+
directory: partial.directory,
|
|
89
|
+
kind,
|
|
90
|
+
scope: 'global',
|
|
91
|
+
baseDir: partial.directory,
|
|
92
|
+
globals: Array.isArray(partial.globals) ? partial.globals : undefined,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const prototypes = listPrototypeDirs(root)
|
|
97
|
+
for (const proto of prototypes) {
|
|
98
|
+
for (const directory of PARTIAL_DIR_NAMES) {
|
|
99
|
+
const localPartialsDir = path.join(proto.dir, directory)
|
|
100
|
+
if (!fs.existsSync(localPartialsDir)) continue
|
|
101
|
+
|
|
102
|
+
for (const entry of fs.readdirSync(localPartialsDir, { withFileTypes: true })) {
|
|
103
|
+
if (!entry.isDirectory()) continue
|
|
104
|
+
const kind = toPartialKind(directory)
|
|
105
|
+
if (!kind) continue
|
|
106
|
+
|
|
107
|
+
const baseDir = normalizeSlashes(path.relative(srcRoot, localPartialsDir))
|
|
108
|
+
const id = makeId('prototype', directory, entry.name, proto.name, proto.folder)
|
|
109
|
+
if (seenIds.has(id)) continue
|
|
110
|
+
seenIds.add(id)
|
|
111
|
+
results.push({
|
|
112
|
+
id,
|
|
113
|
+
name: entry.name,
|
|
114
|
+
directory,
|
|
115
|
+
kind,
|
|
116
|
+
scope: 'prototype',
|
|
117
|
+
prototype: proto.name,
|
|
118
|
+
...(proto.folder ? { folder: proto.folder } : {}),
|
|
119
|
+
baseDir,
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return results.sort((a, b) => {
|
|
126
|
+
if (a.scope !== b.scope) return a.scope.localeCompare(b.scope)
|
|
127
|
+
if ((a.prototype || '') !== (b.prototype || '')) return (a.prototype || '').localeCompare(b.prototype || '')
|
|
128
|
+
if ((a.folder || '') !== (b.folder || '')) return (a.folder || '').localeCompare(b.folder || '')
|
|
129
|
+
if (a.kind !== b.kind) return a.kind.localeCompare(b.kind)
|
|
130
|
+
return a.name.localeCompare(b.name)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function resolveTemplateRecipeEntry(entries, templateValue, { prototype, folder } = {}) {
|
|
135
|
+
if (!templateValue || typeof templateValue !== 'string') return null
|
|
136
|
+
|
|
137
|
+
const trimmed = templateValue.trim()
|
|
138
|
+
if (!trimmed) return null
|
|
139
|
+
|
|
140
|
+
const byId = entries.find((entry) => entry.id === trimmed)
|
|
141
|
+
if (byId) return byId
|
|
142
|
+
|
|
143
|
+
const exactScoped = entries.find((entry) =>
|
|
144
|
+
entry.scope === 'prototype' &&
|
|
145
|
+
entry.name === trimmed &&
|
|
146
|
+
entry.prototype === prototype &&
|
|
147
|
+
(entry.folder || '') === (folder || '')
|
|
148
|
+
)
|
|
149
|
+
if (exactScoped) return exactScoped
|
|
150
|
+
|
|
151
|
+
const globalMatch = entries.find((entry) => entry.scope === 'global' && entry.name === trimmed)
|
|
152
|
+
if (globalMatch) return globalMatch
|
|
153
|
+
|
|
154
|
+
return entries.find((entry) => entry.name === trimmed) || null
|
|
155
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useState, useMemo, useEffect, useCallback } from 'react'
|
|
2
|
+
|
|
3
|
+
export default function WorkshopPanel({ features = [] }) {
|
|
4
|
+
const [menuOpen, setMenuOpen] = useState(false)
|
|
5
|
+
const [activeOverlay, setActiveOverlay] = useState(null)
|
|
6
|
+
const [visible, setVisible] = useState(true)
|
|
7
|
+
|
|
8
|
+
const activeFeature = useMemo(
|
|
9
|
+
() => (activeOverlay ? features.find((f) => f.overlayId === activeOverlay) ?? null : null),
|
|
10
|
+
[activeOverlay, features]
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
const showOverlay = useCallback((id) => {
|
|
14
|
+
setActiveOverlay(id)
|
|
15
|
+
setMenuOpen(false)
|
|
16
|
+
}, [])
|
|
17
|
+
|
|
18
|
+
const closeOverlay = useCallback(() => setActiveOverlay(null), [])
|
|
19
|
+
|
|
20
|
+
const handleKeydown = useCallback(
|
|
21
|
+
(e) => {
|
|
22
|
+
if (e.key === '.' && (e.metaKey || e.ctrlKey)) {
|
|
23
|
+
setVisible((v) => !v)
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
if (e.key === 'Escape') {
|
|
27
|
+
setActiveOverlay((cur) => {
|
|
28
|
+
if (cur) return null
|
|
29
|
+
setMenuOpen(false)
|
|
30
|
+
return cur
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
[]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const handleClickOutside = useCallback((e) => {
|
|
38
|
+
if (!e.target.closest('[data-workshop-panel]')) {
|
|
39
|
+
setMenuOpen(false)
|
|
40
|
+
}
|
|
41
|
+
}, [])
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
window.addEventListener('keydown', handleKeydown)
|
|
45
|
+
document.addEventListener('click', handleClickOutside)
|
|
46
|
+
return () => {
|
|
47
|
+
window.removeEventListener('keydown', handleKeydown)
|
|
48
|
+
document.removeEventListener('click', handleClickOutside)
|
|
49
|
+
}
|
|
50
|
+
}, [handleKeydown, handleClickOutside])
|
|
51
|
+
|
|
52
|
+
if (!visible) return null
|
|
53
|
+
|
|
54
|
+
const ActiveOverlay = activeFeature?.overlay
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div data-workshop-panel className="fixed bottom-6 right-[76px] z-[9999] font-sans">
|
|
58
|
+
<button
|
|
59
|
+
className="flex items-center p-3 bg-popover text-muted-foreground border border-border rounded-full cursor-pointer shadow-lg hover:scale-105 active:scale-95 transition-transform select-none"
|
|
60
|
+
aria-label="Workshop"
|
|
61
|
+
onClick={() => setMenuOpen((o) => !o)}
|
|
62
|
+
>
|
|
63
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
|
|
64
|
+
<path d="M5.433 2.304A4.494 4.494 0 0 0 3.5 6c0 1.598.832 3.002 2.09 3.802.518.328.929.923.902 1.64v.008l-.164 3.337a.75.75 0 1 1-1.498-.073l.163-3.34c.007-.14-.1-.313-.36-.465A5.986 5.986 0 0 1 2 6a5.994 5.994 0 0 1 2.567-4.92 1.482 1.482 0 0 1 1.673-.04c.462.296.76.827.76 1.423v2.076c0 .332.214.572.491.572.268 0 .492-.24.492-.572V2.463c0-.596.298-1.127.76-1.423a1.482 1.482 0 0 1 1.673.04A5.994 5.994 0 0 1 13 6a5.986 5.986 0 0 1-2.633 4.909c-.26.152-.367.325-.36.465l.164 3.34a.75.75 0 1 1-1.498.073l-.164-3.337v-.008c-.027-.717.384-1.312.902-1.64A4.494 4.494 0 0 0 11.5 6a4.494 4.494 0 0 0-1.933-3.696c-.024.017-.067.067-.067.159v2.076c0 1.074-.84 2.072-1.991 2.072-1.161 0-2.009-.998-2.009-2.072V2.463c0-.092-.043-.142-.067-.16Z" />
|
|
65
|
+
</svg>
|
|
66
|
+
</button>
|
|
67
|
+
|
|
68
|
+
{menuOpen && (
|
|
69
|
+
<div className="absolute bottom-14 right-0 min-w-[200px] bg-popover text-popover-foreground border border-border rounded-xl shadow-lg overflow-hidden">
|
|
70
|
+
<div className="px-4 pt-2 pb-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Workshop</div>
|
|
71
|
+
<div className="h-px bg-border" />
|
|
72
|
+
{features.map((f) => (
|
|
73
|
+
<button
|
|
74
|
+
key={f.overlayId}
|
|
75
|
+
className="flex items-center gap-2 w-full px-4 py-2 text-sm bg-transparent border-none cursor-pointer text-left hover:bg-accent transition-colors"
|
|
76
|
+
onClick={() => showOverlay(f.overlayId)}
|
|
77
|
+
>
|
|
78
|
+
<span className="text-sm">{f.icon || ''}</span> {f.label}
|
|
79
|
+
</button>
|
|
80
|
+
))}
|
|
81
|
+
<div className="h-px bg-border" />
|
|
82
|
+
<div className="px-4 py-1.5 text-[11px] text-muted-foreground">Dev-only tools</div>
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
|
|
86
|
+
{ActiveOverlay && (
|
|
87
|
+
<div
|
|
88
|
+
className="fixed inset-0 z-[10000] flex items-center justify-center bg-black/50"
|
|
89
|
+
onClick={(e) => { if (e.target === e.currentTarget) closeOverlay() }}
|
|
90
|
+
>
|
|
91
|
+
<div className="w-full max-w-[480px]">
|
|
92
|
+
<ActiveOverlay onClose={closeOverlay} />
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
96
|
+
</div>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worktree Port Registry
|
|
3
|
+
*
|
|
4
|
+
* Manages a JSON registry (worktrees/ports.json) that maps worktree names
|
|
5
|
+
* to unique dev-server ports. Main always gets 1234; worktrees get 1235+.
|
|
6
|
+
*
|
|
7
|
+
* This module is published as part of @dfosco/storyboard-core so client
|
|
8
|
+
* repos can use port detection without duplicating the logic.
|
|
9
|
+
*
|
|
10
|
+
* Programmatic API:
|
|
11
|
+
* import { getPort, detectWorktreeName, resolvePort } from '@dfosco/storyboard/worktree/port'
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, realpathSync } from 'fs'
|
|
15
|
+
import { join, dirname, basename } from 'path'
|
|
16
|
+
import { execSync } from 'child_process'
|
|
17
|
+
import { findByWorktree } from './serverRegistry.js'
|
|
18
|
+
|
|
19
|
+
const BASE_PORT = 1234
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the path to worktrees/ports.json.
|
|
23
|
+
*
|
|
24
|
+
* Derives the repo root from directory structure so it works whether
|
|
25
|
+
* running from the repo root or from inside worktrees/<name>/ — even
|
|
26
|
+
* when ports.json does not exist yet.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} [cwd] — override working directory
|
|
29
|
+
* @returns {string} absolute path to ports.json
|
|
30
|
+
*/
|
|
31
|
+
export function portsFilePath(cwd = process.cwd()) {
|
|
32
|
+
const realCwd = realpathSync(cwd)
|
|
33
|
+
|
|
34
|
+
// Check if we're inside worktrees/<name>/
|
|
35
|
+
const worktreeMatch = realCwd.match(/^(.+)[/\\]worktrees[/\\][^/\\]+/)
|
|
36
|
+
if (worktreeMatch) {
|
|
37
|
+
return join(worktreeMatch[1], 'worktrees', 'ports.json')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// We're at the repo root (or somewhere else) — default location
|
|
41
|
+
return join(realCwd, 'worktrees', 'ports.json')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Detect the worktree name from the current git context.
|
|
46
|
+
*
|
|
47
|
+
* Returns 'main' when not inside a worktrees/<name>/ directory.
|
|
48
|
+
*/
|
|
49
|
+
export function detectWorktreeName() {
|
|
50
|
+
try {
|
|
51
|
+
const topLevel = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim()
|
|
52
|
+
const realTop = realpathSync(topLevel)
|
|
53
|
+
|
|
54
|
+
// Check if we're inside a worktrees/<name> directory
|
|
55
|
+
if (realTop.includes('worktrees/') || realTop.includes('worktrees\\')) {
|
|
56
|
+
return basename(realTop)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Also check the cwd pattern
|
|
60
|
+
const realCwd = realpathSync(process.cwd())
|
|
61
|
+
const worktreeMatch = realCwd.match(/worktrees[/\\]([^/\\]+)/)
|
|
62
|
+
if (worktreeMatch) return worktreeMatch[1]
|
|
63
|
+
|
|
64
|
+
// Not a worktree — check the current branch name
|
|
65
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim()
|
|
66
|
+
if (branch && branch !== 'main' && branch !== 'master') return branch
|
|
67
|
+
|
|
68
|
+
return 'main'
|
|
69
|
+
} catch {
|
|
70
|
+
return 'main'
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if a port is in use by another process (synchronous, best-effort via lsof).
|
|
76
|
+
* Skipped when NODE_ENV=test to avoid test flakiness from real port state.
|
|
77
|
+
* @param {number} port
|
|
78
|
+
* @returns {boolean}
|
|
79
|
+
*/
|
|
80
|
+
function isPortInUse(port) {
|
|
81
|
+
if (process.env.NODE_ENV === 'test') return false
|
|
82
|
+
try {
|
|
83
|
+
execSync(`lsof -i :${port} -sTCP:LISTEN`, { stdio: 'ignore', timeout: 2000 })
|
|
84
|
+
return true
|
|
85
|
+
} catch {
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get or assign a port for the given worktree name.
|
|
92
|
+
*
|
|
93
|
+
* Creates worktrees/ports.json if it doesn't exist. Assigns ports
|
|
94
|
+
* starting at BASE_PORT+1 (1235) for non-main worktrees.
|
|
95
|
+
* If the previously assigned port was stolen by another process,
|
|
96
|
+
* reassigns to the next available port.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} worktreeName
|
|
99
|
+
* @returns {number}
|
|
100
|
+
*/
|
|
101
|
+
export function getPort(worktreeName) {
|
|
102
|
+
const portsFile = portsFilePath()
|
|
103
|
+
const dir = dirname(portsFile)
|
|
104
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
105
|
+
|
|
106
|
+
let ports = { main: BASE_PORT }
|
|
107
|
+
if (existsSync(portsFile)) {
|
|
108
|
+
try {
|
|
109
|
+
ports = JSON.parse(readFileSync(portsFile, 'utf8'))
|
|
110
|
+
} catch {
|
|
111
|
+
// Corrupted file — start fresh
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (worktreeName === 'main') return ports.main || BASE_PORT
|
|
116
|
+
|
|
117
|
+
// If port already assigned, verify it's not stolen by another process
|
|
118
|
+
if (ports[worktreeName]) {
|
|
119
|
+
if (!isPortInUse(ports[worktreeName])) {
|
|
120
|
+
return ports[worktreeName]
|
|
121
|
+
}
|
|
122
|
+
// Port is occupied — fall through to reassign
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const usedPorts = Object.values(ports)
|
|
126
|
+
let nextPort = BASE_PORT + 1
|
|
127
|
+
while (usedPorts.includes(nextPort)) nextPort++
|
|
128
|
+
ports[worktreeName] = nextPort
|
|
129
|
+
writeFileSync(portsFile, JSON.stringify(ports, null, 2) + '\n')
|
|
130
|
+
|
|
131
|
+
return ports[worktreeName]
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Release a port assignment for a worktree.
|
|
136
|
+
*
|
|
137
|
+
* Removes the entry from ports.json so the port can be reused.
|
|
138
|
+
* Never removes 'main'.
|
|
139
|
+
*
|
|
140
|
+
* @param {string} worktreeName
|
|
141
|
+
*/
|
|
142
|
+
export function releasePort(worktreeName) {
|
|
143
|
+
if (worktreeName === 'main') return
|
|
144
|
+
|
|
145
|
+
const portsFile = portsFilePath()
|
|
146
|
+
if (!existsSync(portsFile)) return
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const ports = JSON.parse(readFileSync(portsFile, 'utf8'))
|
|
150
|
+
if (!(worktreeName in ports)) return
|
|
151
|
+
delete ports[worktreeName]
|
|
152
|
+
writeFileSync(portsFile, JSON.stringify(ports, null, 2) + '\n')
|
|
153
|
+
} catch { /* ignore corrupt file */ }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Resolve the port for a running dev server.
|
|
158
|
+
*
|
|
159
|
+
* Checks the server registry (servers.json) first for a live process,
|
|
160
|
+
* then falls back to ports.json assignment. Use this when connecting
|
|
161
|
+
* to an already-running server — it returns the real bound port even
|
|
162
|
+
* when Vite rebinds to a different port than originally assigned.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} worktreeName
|
|
165
|
+
* @returns {number}
|
|
166
|
+
*/
|
|
167
|
+
export function resolveRunningPort(worktreeName) {
|
|
168
|
+
try {
|
|
169
|
+
const servers = findByWorktree(worktreeName)
|
|
170
|
+
if (servers.length > 0) {
|
|
171
|
+
const latest = servers.reduce((a, b) =>
|
|
172
|
+
(a.startedAt || '') >= (b.startedAt || '') ? a : b
|
|
173
|
+
)
|
|
174
|
+
return latest.port
|
|
175
|
+
}
|
|
176
|
+
} catch { /* registry unavailable */ }
|
|
177
|
+
|
|
178
|
+
return resolvePort(worktreeName)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Resolve the port for a worktree from worktrees/ports.json
|
|
183
|
+
* without assigning a new one if missing.
|
|
184
|
+
*
|
|
185
|
+
* @param {string} worktreeName
|
|
186
|
+
* @returns {number}
|
|
187
|
+
*/
|
|
188
|
+
export function resolvePort(worktreeName) {
|
|
189
|
+
const portsFile = portsFilePath()
|
|
190
|
+
if (!existsSync(portsFile)) return BASE_PORT
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const ports = JSON.parse(readFileSync(portsFile, 'utf8'))
|
|
194
|
+
return ports[worktreeName] ?? BASE_PORT
|
|
195
|
+
} catch {
|
|
196
|
+
return BASE_PORT
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Slugify a branch name for filesystem and subdomain safety.
|
|
202
|
+
*
|
|
203
|
+
* - lowercase
|
|
204
|
+
* - dots, spaces, underscores, non-alphanumeric (except - and /) → hyphens
|
|
205
|
+
* - collapse consecutive hyphens
|
|
206
|
+
* - trim leading/trailing hyphens per segment
|
|
207
|
+
*/
|
|
208
|
+
export function slugify(name) {
|
|
209
|
+
return name
|
|
210
|
+
.toLowerCase()
|
|
211
|
+
.replace(/[^a-z0-9/-]/g, '-')
|
|
212
|
+
.replace(/-{2,}/g, '-')
|
|
213
|
+
.split('/')
|
|
214
|
+
.map((s) => s.replace(/^-+|-+$/g, ''))
|
|
215
|
+
.join('/')
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Resolve the repo root — the directory that contains `worktrees/`.
|
|
220
|
+
*
|
|
221
|
+
* Works whether cwd is the repo root itself or inside `worktrees/<name>/`.
|
|
222
|
+
*
|
|
223
|
+
* @param {string} [cwd]
|
|
224
|
+
* @returns {string} absolute path to repo root
|
|
225
|
+
*/
|
|
226
|
+
export function repoRoot(cwd = process.cwd()) {
|
|
227
|
+
const realCwd = realpathSync(cwd)
|
|
228
|
+
|
|
229
|
+
const worktreeMatch = realCwd.match(/^(.+)[/\\]worktrees[/\\][^/\\]+/)
|
|
230
|
+
if (worktreeMatch) return worktreeMatch[1]
|
|
231
|
+
|
|
232
|
+
return realCwd
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Resolve the full path to a worktree directory.
|
|
237
|
+
*
|
|
238
|
+
* Returns repo root for 'main', `worktrees/<name>` otherwise.
|
|
239
|
+
*
|
|
240
|
+
* @param {string} name — worktree name
|
|
241
|
+
* @param {string} [cwd]
|
|
242
|
+
* @returns {string} absolute path
|
|
243
|
+
*/
|
|
244
|
+
export function worktreeDir(name, cwd) {
|
|
245
|
+
const root = repoRoot(cwd)
|
|
246
|
+
if (name === 'main') return root
|
|
247
|
+
return join(root, 'worktrees', name)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* List existing worktree directory names from `worktrees/`.
|
|
252
|
+
*
|
|
253
|
+
* Only returns directories that look like real worktrees (contain a `.git` file).
|
|
254
|
+
* Does not include 'main'.
|
|
255
|
+
*
|
|
256
|
+
* @param {string} [cwd]
|
|
257
|
+
* @returns {string[]}
|
|
258
|
+
*/
|
|
259
|
+
export function listWorktrees(cwd) {
|
|
260
|
+
const root = repoRoot(cwd)
|
|
261
|
+
const worktreesDir = join(root, 'worktrees')
|
|
262
|
+
|
|
263
|
+
if (!existsSync(worktreesDir)) return []
|
|
264
|
+
|
|
265
|
+
return readdirSync(worktreesDir, { withFileTypes: true })
|
|
266
|
+
.filter((d) => d.isDirectory() && existsSync(join(worktreesDir, d.name, '.git')))
|
|
267
|
+
.map((d) => d.name)
|
|
268
|
+
}
|