@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,531 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rename Watcher
|
|
3
|
+
*
|
|
4
|
+
* Detects file and directory renames under watched directories and updates
|
|
5
|
+
* canvas embed URLs (prototype and canvas widgets) to stay current.
|
|
6
|
+
* Auto-commits the changes with a configurable prefix.
|
|
7
|
+
*
|
|
8
|
+
* Uses snapshot-based diffing: on any fs event, re-scans the watched
|
|
9
|
+
* directories and compares old vs new file sets. Only unambiguous renames
|
|
10
|
+
* (1:1 file or directory mappings) are acted on.
|
|
11
|
+
*
|
|
12
|
+
* Configuration is loaded from config.json in this directory.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'node:fs'
|
|
16
|
+
import path from 'node:path'
|
|
17
|
+
import { execFileSync } from 'node:child_process'
|
|
18
|
+
import { materializeFromText } from '../canvas/materializer.js'
|
|
19
|
+
import { toCanvasId } from '../canvas/identity.js'
|
|
20
|
+
|
|
21
|
+
// ─── Logging ─────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`
|
|
24
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`
|
|
25
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`
|
|
26
|
+
|
|
27
|
+
function log(msg) { console.log(dim(` ◈ rename-watcher: ${msg}`)) }
|
|
28
|
+
function logSuccess(msg) { console.log(green(` ✓ rename-watcher: ${msg}`)) }
|
|
29
|
+
function logWarn(msg) { console.log(yellow(` ⚠ rename-watcher: ${msg}`)) }
|
|
30
|
+
|
|
31
|
+
// ─── Config ──────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
function loadConfig() {
|
|
34
|
+
const configPath = new URL('./config.json', import.meta.url)
|
|
35
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── File scanning ───────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Scan a watched directory and return a Set of relative file paths
|
|
42
|
+
* matching the configured extensions and exclusions.
|
|
43
|
+
*/
|
|
44
|
+
function scanDirectory(root, watchEntry, config) {
|
|
45
|
+
const results = new Set()
|
|
46
|
+
const absDir = path.join(root, watchEntry.path)
|
|
47
|
+
|
|
48
|
+
if (!fs.existsSync(absDir)) return results
|
|
49
|
+
|
|
50
|
+
const excludeDirs = new Set(config.exclude.directories)
|
|
51
|
+
const excludePrefixes = config.exclude.filePrefixes
|
|
52
|
+
const extensions = watchEntry.extensions
|
|
53
|
+
|
|
54
|
+
function walk(dir, rel) {
|
|
55
|
+
let entries
|
|
56
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return }
|
|
57
|
+
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
if (excludeDirs.has(entry.name)) continue
|
|
60
|
+
const relPath = rel ? `${rel}/${entry.name}` : entry.name
|
|
61
|
+
|
|
62
|
+
if (entry.isDirectory()) {
|
|
63
|
+
walk(path.join(dir, entry.name), relPath)
|
|
64
|
+
} else {
|
|
65
|
+
if (excludePrefixes.some((p) => entry.name.startsWith(p))) continue
|
|
66
|
+
if (extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
67
|
+
results.add(relPath)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
walk(absDir, '')
|
|
74
|
+
return results
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Route computation ───────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Compute the route path for a prototype file (relative to src/prototypes/).
|
|
81
|
+
* Mirrors the route regex in src/routes.jsx.
|
|
82
|
+
*/
|
|
83
|
+
function prototypeRoute(relPath) {
|
|
84
|
+
let route = relPath
|
|
85
|
+
.replace(/[^/]*\.folder\//g, '')
|
|
86
|
+
.replace(/\.(jsx|tsx|mdx)$/, '')
|
|
87
|
+
.replace(/\/index$/, '')
|
|
88
|
+
|
|
89
|
+
if (!route.startsWith('/')) route = '/' + route
|
|
90
|
+
return route || '/'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Compute the route path for a canvas file (relative to src/canvas/).
|
|
95
|
+
* Uses toCanvasId() for proper folder handling.
|
|
96
|
+
*/
|
|
97
|
+
function canvasRoute(relPath) {
|
|
98
|
+
// relPath is relative to src/canvas/, prepend prefix for toCanvasId()
|
|
99
|
+
const canvasId = toCanvasId('src/canvas/' + relPath)
|
|
100
|
+
return '/canvas/' + canvasId
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function computeRoute(relPath, watchType) {
|
|
104
|
+
if (watchType === 'prototype') return prototypeRoute(relPath)
|
|
105
|
+
if (watchType === 'canvas') return canvasRoute(relPath)
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ─── Rename detection ────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get compound extension (e.g. '.canvas.jsonl' not just '.jsonl').
|
|
113
|
+
*/
|
|
114
|
+
function getCompoundExt(filePath) {
|
|
115
|
+
const base = path.basename(filePath)
|
|
116
|
+
const parts = base.split('.')
|
|
117
|
+
if (parts.length >= 3) return '.' + parts.slice(-2).join('.')
|
|
118
|
+
return path.extname(filePath)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function groupByDir(paths) {
|
|
122
|
+
const groups = new Map()
|
|
123
|
+
for (const p of paths) {
|
|
124
|
+
const dir = path.dirname(p)
|
|
125
|
+
if (!groups.has(dir)) groups.set(dir, [])
|
|
126
|
+
groups.get(dir).push(p)
|
|
127
|
+
}
|
|
128
|
+
return groups
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Find the longest common directory prefix of an array of paths.
|
|
133
|
+
*/
|
|
134
|
+
function commonDirPrefix(paths) {
|
|
135
|
+
if (paths.length === 0) return ''
|
|
136
|
+
if (paths.length === 1) {
|
|
137
|
+
const dir = path.dirname(paths[0])
|
|
138
|
+
return dir === '.' ? '' : dir + '/'
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const sorted = [...paths].sort()
|
|
142
|
+
const first = sorted[0]
|
|
143
|
+
const last = sorted[sorted.length - 1]
|
|
144
|
+
|
|
145
|
+
let i = 0
|
|
146
|
+
while (i < first.length && i < last.length && first[i] === last[i]) i++
|
|
147
|
+
|
|
148
|
+
const prefix = first.slice(0, i)
|
|
149
|
+
const lastSlash = prefix.lastIndexOf('/')
|
|
150
|
+
return lastSlash >= 0 ? prefix.slice(0, lastSlash + 1) : ''
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Detect renames by diffing old and new file snapshots.
|
|
155
|
+
* Only returns unambiguous renames to avoid false positives.
|
|
156
|
+
*
|
|
157
|
+
* Phase 1 — File renames: same directory, same extension, exactly 1 removed + 1 added.
|
|
158
|
+
* Phase 2 — Directory renames: a single directory-prefix swap explains all remaining changes.
|
|
159
|
+
*/
|
|
160
|
+
function detectRenames(oldSnapshot, newSnapshot, watchType) {
|
|
161
|
+
const removed = [...oldSnapshot].filter((f) => !newSnapshot.has(f))
|
|
162
|
+
const added = [...newSnapshot].filter((f) => !oldSnapshot.has(f))
|
|
163
|
+
|
|
164
|
+
if (removed.length === 0 || added.length === 0) return []
|
|
165
|
+
|
|
166
|
+
const renames = []
|
|
167
|
+
const matchedRemoved = new Set()
|
|
168
|
+
const matchedAdded = new Set()
|
|
169
|
+
|
|
170
|
+
// Phase 1: File-level renames
|
|
171
|
+
const removedByDir = groupByDir(removed)
|
|
172
|
+
const addedByDir = groupByDir(added)
|
|
173
|
+
|
|
174
|
+
for (const [dir, dirRemoved] of removedByDir) {
|
|
175
|
+
const dirAdded = addedByDir.get(dir) || []
|
|
176
|
+
if (dirRemoved.length !== 1 || dirAdded.length !== 1) continue
|
|
177
|
+
if (getCompoundExt(dirRemoved[0]) !== getCompoundExt(dirAdded[0])) continue
|
|
178
|
+
|
|
179
|
+
const oldRoute = computeRoute(dirRemoved[0], watchType)
|
|
180
|
+
const newRoute = computeRoute(dirAdded[0], watchType)
|
|
181
|
+
|
|
182
|
+
if (oldRoute === newRoute) continue
|
|
183
|
+
|
|
184
|
+
renames.push({ oldPath: dirRemoved[0], newPath: dirAdded[0], oldRoute, newRoute })
|
|
185
|
+
matchedRemoved.add(dirRemoved[0])
|
|
186
|
+
matchedAdded.add(dirAdded[0])
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Phase 2: Directory-level renames
|
|
190
|
+
const unmatchedRemoved = removed.filter((f) => !matchedRemoved.has(f))
|
|
191
|
+
const unmatchedAdded = added.filter((f) => !matchedAdded.has(f))
|
|
192
|
+
|
|
193
|
+
if (unmatchedRemoved.length > 0 && unmatchedRemoved.length === unmatchedAdded.length) {
|
|
194
|
+
const oldPrefix = commonDirPrefix(unmatchedRemoved)
|
|
195
|
+
const newPrefix = commonDirPrefix(unmatchedAdded)
|
|
196
|
+
|
|
197
|
+
if (oldPrefix && newPrefix && oldPrefix !== newPrefix) {
|
|
198
|
+
const expectedAdded = new Set(
|
|
199
|
+
unmatchedRemoved.map((f) => newPrefix + f.slice(oldPrefix.length)),
|
|
200
|
+
)
|
|
201
|
+
const allMatch = unmatchedAdded.every((f) => expectedAdded.has(f))
|
|
202
|
+
|
|
203
|
+
if (allMatch) {
|
|
204
|
+
for (const oldFile of unmatchedRemoved) {
|
|
205
|
+
const newFile = newPrefix + oldFile.slice(oldPrefix.length)
|
|
206
|
+
const oldRoute = computeRoute(oldFile, watchType)
|
|
207
|
+
const newRoute = computeRoute(newFile, watchType)
|
|
208
|
+
if (oldRoute !== newRoute) {
|
|
209
|
+
renames.push({ oldPath: oldFile, newPath: newFile, oldRoute, newRoute })
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return renames
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ─── Canvas embed updating ───────────────────────────────────────────
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Find all .canvas.jsonl files in the project.
|
|
223
|
+
*/
|
|
224
|
+
function findAllCanvasFiles(root) {
|
|
225
|
+
const results = []
|
|
226
|
+
const ignore = new Set(['node_modules', 'dist', '.git', '.worktrees'])
|
|
227
|
+
|
|
228
|
+
function walk(dir) {
|
|
229
|
+
let entries
|
|
230
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return }
|
|
231
|
+
for (const entry of entries) {
|
|
232
|
+
if (ignore.has(entry.name)) continue
|
|
233
|
+
const fullPath = path.join(dir, entry.name)
|
|
234
|
+
if (entry.isDirectory()) walk(fullPath)
|
|
235
|
+
else if (entry.name.endsWith('.canvas.jsonl')) results.push(fullPath)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
walk(root)
|
|
240
|
+
return results
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Rewrite a URL's pathname if it matches a renamed route.
|
|
245
|
+
* Uses segment-boundary matching to avoid partial matches (e.g. /Signup vs /Signup2).
|
|
246
|
+
*/
|
|
247
|
+
function rewriteUrl(src, renames) {
|
|
248
|
+
const hashIdx = src.indexOf('#')
|
|
249
|
+
const queryIdx = src.indexOf('?')
|
|
250
|
+
|
|
251
|
+
let pathname, suffix
|
|
252
|
+
if (hashIdx >= 0 && (queryIdx < 0 || hashIdx < queryIdx)) {
|
|
253
|
+
pathname = src.slice(0, hashIdx)
|
|
254
|
+
suffix = src.slice(hashIdx)
|
|
255
|
+
} else if (queryIdx >= 0) {
|
|
256
|
+
pathname = src.slice(0, queryIdx)
|
|
257
|
+
suffix = src.slice(queryIdx)
|
|
258
|
+
} else {
|
|
259
|
+
pathname = src
|
|
260
|
+
suffix = ''
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
for (const { oldRoute, newRoute } of renames) {
|
|
264
|
+
if (!oldRoute || !newRoute) continue
|
|
265
|
+
|
|
266
|
+
// Exact match
|
|
267
|
+
if (pathname === oldRoute) return newRoute + suffix
|
|
268
|
+
// Segment-boundary prefix match
|
|
269
|
+
if (pathname.startsWith(oldRoute + '/')) {
|
|
270
|
+
return newRoute + pathname.slice(oldRoute.length) + suffix
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return src
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Update widget embed refs for detected renames.
|
|
279
|
+
* Returns the updated widgets array if any changed, null if unchanged.
|
|
280
|
+
*/
|
|
281
|
+
function updateWidgetRefs(widgets, renames) {
|
|
282
|
+
let changed = false
|
|
283
|
+
|
|
284
|
+
const updated = widgets.map((widget) => {
|
|
285
|
+
// Only process embed widget types with a src prop
|
|
286
|
+
if (widget.type !== 'prototype' && widget.type !== 'canvas') return widget
|
|
287
|
+
|
|
288
|
+
const src = widget.props?.src
|
|
289
|
+
if (!src || typeof src !== 'string') return widget
|
|
290
|
+
|
|
291
|
+
const newSrc = rewriteUrl(src, renames)
|
|
292
|
+
if (newSrc === src) return widget
|
|
293
|
+
|
|
294
|
+
changed = true
|
|
295
|
+
return { ...widget, props: { ...widget.props, src: newSrc } }
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
return changed ? updated : null
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ─── Auto-commit ─────────────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
// Lock file placed in .git/ during commit. Both the rename watcher and
|
|
304
|
+
// autosync share the same working tree and git index, so this file
|
|
305
|
+
// lets autosync's isRepoBusy() (or any future tool) detect that the
|
|
306
|
+
// rename watcher is mid-commit and defer its own cycle.
|
|
307
|
+
const LOCK_FILENAME = 'storyboard-autofix.lock'
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Resolve the .git directory, handling both regular repos and worktrees.
|
|
311
|
+
* In a worktree, `.git` is a file pointing to the real git dir.
|
|
312
|
+
*/
|
|
313
|
+
function resolveGitDir(root) {
|
|
314
|
+
const dotGit = path.join(root, '.git')
|
|
315
|
+
try {
|
|
316
|
+
const stat = fs.statSync(dotGit)
|
|
317
|
+
if (stat.isDirectory()) return dotGit
|
|
318
|
+
// Worktree: .git is a file like "gitdir: /path/to/.git/worktrees/name"
|
|
319
|
+
const content = fs.readFileSync(dotGit, 'utf-8').trim()
|
|
320
|
+
const match = content.match(/^gitdir:\s*(.+)$/)
|
|
321
|
+
if (match) return path.resolve(root, match[1])
|
|
322
|
+
} catch { /* fallback */ }
|
|
323
|
+
return dotGit
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Check if the repo is in a busy state that would conflict with a commit.
|
|
328
|
+
*
|
|
329
|
+
* Mirrors autosync's isRepoBusy() guards so both systems respect the same
|
|
330
|
+
* signals: index.lock, rebase, merge, cherry-pick, and the autofix lock file.
|
|
331
|
+
* Since both autosync and the rename watcher commit directly on the current
|
|
332
|
+
* branch (same working tree, same index), index.lock is the primary mutex.
|
|
333
|
+
*/
|
|
334
|
+
function isRepoBusy(root) {
|
|
335
|
+
const gitDir = resolveGitDir(root)
|
|
336
|
+
|
|
337
|
+
if (fs.existsSync(path.join(gitDir, 'index.lock'))) {
|
|
338
|
+
logWarn('Auto-commit deferred: index.lock present')
|
|
339
|
+
return true
|
|
340
|
+
}
|
|
341
|
+
if (fs.existsSync(path.join(gitDir, 'MERGE_HEAD'))) {
|
|
342
|
+
logWarn('Auto-commit deferred: merge in progress')
|
|
343
|
+
return true
|
|
344
|
+
}
|
|
345
|
+
if (fs.existsSync(path.join(gitDir, 'CHERRY_PICK_HEAD'))) {
|
|
346
|
+
logWarn('Auto-commit deferred: cherry-pick in progress')
|
|
347
|
+
return true
|
|
348
|
+
}
|
|
349
|
+
if (fs.existsSync(path.join(gitDir, 'rebase-merge')) || fs.existsSync(path.join(gitDir, 'rebase-apply'))) {
|
|
350
|
+
logWarn('Auto-commit deferred: rebase in progress')
|
|
351
|
+
return true
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Check for our own stale lock (crash recovery)
|
|
355
|
+
const lockPath = path.join(gitDir, LOCK_FILENAME)
|
|
356
|
+
if (fs.existsSync(lockPath)) {
|
|
357
|
+
try {
|
|
358
|
+
const lockAge = Date.now() - fs.statSync(lockPath).mtimeMs
|
|
359
|
+
if (lockAge < 10_000) {
|
|
360
|
+
logWarn('Auto-commit deferred: previous autofix still in progress')
|
|
361
|
+
return true
|
|
362
|
+
}
|
|
363
|
+
// Stale lock (>10s) — remove it
|
|
364
|
+
fs.unlinkSync(lockPath)
|
|
365
|
+
} catch { /* race — fine */ }
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return false
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Auto-commit modified canvas files using git commit --only to isolate
|
|
373
|
+
* from any other staged or unstaged user work.
|
|
374
|
+
*
|
|
375
|
+
* Coordinates with autosync (which shares the same working tree + index) via:
|
|
376
|
+
* - Lock file (.git/storyboard-autofix.lock) to signal mid-commit
|
|
377
|
+
* - Same repo-busy guards autosync checks (index.lock, merge, rebase, cherry-pick)
|
|
378
|
+
* - git commit --only uses a temporary index, so it won't interfere with
|
|
379
|
+
* files autosync may have staged independently
|
|
380
|
+
*
|
|
381
|
+
* If the commit is deferred (busy repo), the canvas JSONL update is still
|
|
382
|
+
* persisted on disk — autosync's next cycle will pick it up naturally.
|
|
383
|
+
*/
|
|
384
|
+
function autocommit(root, modifiedFiles, renames, config) {
|
|
385
|
+
if (!config.autocommit.enabled || modifiedFiles.length === 0) return
|
|
386
|
+
|
|
387
|
+
const relPaths = modifiedFiles.map((f) => path.relative(root, f))
|
|
388
|
+
|
|
389
|
+
if (isRepoBusy(root)) return
|
|
390
|
+
|
|
391
|
+
const gitDir = resolveGitDir(root)
|
|
392
|
+
const lockPath = path.join(gitDir, LOCK_FILENAME)
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
// Acquire lock so autosync (or other tools) can defer
|
|
396
|
+
fs.writeFileSync(lockPath, `${process.pid}\n${Date.now()}\n`, 'utf-8')
|
|
397
|
+
|
|
398
|
+
execFileSync('git', ['add', '--', ...relPaths], { cwd: root, stdio: 'pipe' })
|
|
399
|
+
|
|
400
|
+
const summary = [...new Set(
|
|
401
|
+
renames
|
|
402
|
+
.filter((r) => r.oldRoute && r.newRoute)
|
|
403
|
+
.map((r) => `${r.oldRoute} → ${r.newRoute}`),
|
|
404
|
+
)].join(', ')
|
|
405
|
+
|
|
406
|
+
const message = `${config.autocommit.prefix} Update embed URLs: ${summary}`
|
|
407
|
+
|
|
408
|
+
execFileSync('git', ['commit', '--only', '--', ...relPaths, '-m', message], {
|
|
409
|
+
cwd: root,
|
|
410
|
+
stdio: 'pipe',
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
logSuccess(`Auto-committed: ${summary}`)
|
|
414
|
+
} catch (err) {
|
|
415
|
+
logWarn(`Auto-commit skipped: ${(err.message || '').split('\n')[0]}`)
|
|
416
|
+
} finally {
|
|
417
|
+
// Release lock
|
|
418
|
+
try { fs.unlinkSync(lockPath) } catch { /* already gone */ }
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ─── Main ────────────────────────────────────────────────────────────
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Process a detected change — rescan, detect renames, update embeds, commit.
|
|
426
|
+
*/
|
|
427
|
+
function processChange(root, snapshots, config) {
|
|
428
|
+
const allRenames = []
|
|
429
|
+
|
|
430
|
+
for (const entry of config.watch) {
|
|
431
|
+
const oldSnapshot = snapshots.get(entry.path)
|
|
432
|
+
const newSnapshot = scanDirectory(root, entry, config)
|
|
433
|
+
const renames = detectRenames(oldSnapshot, newSnapshot, entry.type)
|
|
434
|
+
if (renames.length > 0) allRenames.push(...renames)
|
|
435
|
+
snapshots.set(entry.path, newSnapshot)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (allRenames.length === 0) return
|
|
439
|
+
|
|
440
|
+
// Deduplicate by route pair
|
|
441
|
+
const uniqueRenames = []
|
|
442
|
+
const seen = new Set()
|
|
443
|
+
for (const r of allRenames) {
|
|
444
|
+
const key = `${r.oldRoute}→${r.newRoute}`
|
|
445
|
+
if (!seen.has(key)) {
|
|
446
|
+
seen.add(key)
|
|
447
|
+
uniqueRenames.push(r)
|
|
448
|
+
log(`Detected: ${r.oldRoute} → ${r.newRoute}`)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Scan all canvas files and update embed references
|
|
453
|
+
const canvasFiles = findAllCanvasFiles(root)
|
|
454
|
+
const modifiedFiles = []
|
|
455
|
+
|
|
456
|
+
for (const canvasFile of canvasFiles) {
|
|
457
|
+
try {
|
|
458
|
+
const text = fs.readFileSync(canvasFile, 'utf-8')
|
|
459
|
+
const state = materializeFromText(text)
|
|
460
|
+
|
|
461
|
+
if (!state.widgets || state.widgets.length === 0) continue
|
|
462
|
+
|
|
463
|
+
const updatedWidgets = updateWidgetRefs(state.widgets, uniqueRenames)
|
|
464
|
+
if (updatedWidgets) {
|
|
465
|
+
const event = {
|
|
466
|
+
event: 'widgets_replaced',
|
|
467
|
+
timestamp: new Date().toISOString(),
|
|
468
|
+
widgets: updatedWidgets,
|
|
469
|
+
}
|
|
470
|
+
fs.appendFileSync(canvasFile, JSON.stringify(event) + '\n', 'utf-8')
|
|
471
|
+
modifiedFiles.push(canvasFile)
|
|
472
|
+
log(`Updated embeds in ${path.relative(root, canvasFile)}`)
|
|
473
|
+
}
|
|
474
|
+
} catch (err) {
|
|
475
|
+
logWarn(`Failed to update ${path.basename(canvasFile)}: ${err.message}`)
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
autocommit(root, modifiedFiles, uniqueRenames, config)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Start the rename watcher.
|
|
484
|
+
* @param {string} root — project root directory
|
|
485
|
+
* @returns {{ close: () => void }}
|
|
486
|
+
*/
|
|
487
|
+
export function startRenameWatcher(root) {
|
|
488
|
+
const config = loadConfig()
|
|
489
|
+
const watchers = []
|
|
490
|
+
const snapshots = new Map()
|
|
491
|
+
|
|
492
|
+
for (const entry of config.watch) {
|
|
493
|
+
snapshots.set(entry.path, scanDirectory(root, entry, config))
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
let debounceTimer = null
|
|
497
|
+
|
|
498
|
+
function handleChange() {
|
|
499
|
+
if (debounceTimer) clearTimeout(debounceTimer)
|
|
500
|
+
debounceTimer = setTimeout(() => {
|
|
501
|
+
try {
|
|
502
|
+
processChange(root, snapshots, config)
|
|
503
|
+
} catch (err) {
|
|
504
|
+
logWarn(`Error: ${err.message}`)
|
|
505
|
+
}
|
|
506
|
+
}, config.debounceMs)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
for (const entry of config.watch) {
|
|
510
|
+
const absDir = path.join(root, entry.path)
|
|
511
|
+
if (!fs.existsSync(absDir)) {
|
|
512
|
+
logWarn(`Skipping ${entry.path} (not found)`)
|
|
513
|
+
continue
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
const watcher = fs.watch(absDir, { recursive: true }, handleChange)
|
|
518
|
+
watcher.on('error', (err) => logWarn(`${entry.path}: ${err.message}`))
|
|
519
|
+
watchers.push(watcher)
|
|
520
|
+
} catch (err) {
|
|
521
|
+
logWarn(`Could not watch ${entry.path}: ${err.message}`)
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
close() {
|
|
527
|
+
if (debounceTimer) clearTimeout(debounceTimer)
|
|
528
|
+
for (const w of watchers) w.close()
|
|
529
|
+
},
|
|
530
|
+
}
|
|
531
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* storyboard-scaffold — sync scaffold files from @dfosco/storyboard.
|
|
4
|
+
*
|
|
5
|
+
* Reads scaffold/manifest.json and copies files to the consumer repo:
|
|
6
|
+
* - "scaffold" mode: only if target doesn't exist (never overwrites config)
|
|
7
|
+
* - "updateable" mode: always overwrites with latest (skills, scripts)
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npx storyboard-scaffold
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from 'node:fs'
|
|
14
|
+
import path from 'node:path'
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(new URL(import.meta.url).pathname)
|
|
17
|
+
const scaffoldRoot = path.resolve(__dirname, '..', '..', 'scaffold')
|
|
18
|
+
const consumerRoot = process.cwd()
|
|
19
|
+
|
|
20
|
+
const manifestPath = path.join(scaffoldRoot, 'manifest.json')
|
|
21
|
+
if (!fs.existsSync(manifestPath)) {
|
|
22
|
+
console.error('❌ Could not find scaffold/manifest.json in @dfosco/storyboard-core')
|
|
23
|
+
process.exit(1)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'))
|
|
27
|
+
|
|
28
|
+
function copyFileSync(src, dest) {
|
|
29
|
+
const dir = path.dirname(dest)
|
|
30
|
+
if (!fs.existsSync(dir)) {
|
|
31
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
32
|
+
}
|
|
33
|
+
fs.copyFileSync(src, dest)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function copyDirSync(src, dest) {
|
|
37
|
+
if (!fs.existsSync(dest)) {
|
|
38
|
+
fs.mkdirSync(dest, { recursive: true })
|
|
39
|
+
}
|
|
40
|
+
const entries = fs.readdirSync(src, { withFileTypes: true })
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
const srcPath = path.join(src, entry.name)
|
|
43
|
+
const destPath = path.join(dest, entry.name)
|
|
44
|
+
if (entry.isDirectory()) {
|
|
45
|
+
copyDirSync(srcPath, destPath)
|
|
46
|
+
} else {
|
|
47
|
+
copyFileSync(srcPath, destPath)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let created = 0
|
|
53
|
+
let updated = 0
|
|
54
|
+
let skipped = 0
|
|
55
|
+
|
|
56
|
+
for (const file of manifest.files) {
|
|
57
|
+
const srcPath = path.join(scaffoldRoot, path.relative('scaffold', file.source))
|
|
58
|
+
const destPath = path.join(consumerRoot, file.target)
|
|
59
|
+
|
|
60
|
+
if (file.directory) {
|
|
61
|
+
if (file.mode === 'updateable') {
|
|
62
|
+
copyDirSync(srcPath, destPath)
|
|
63
|
+
updated++
|
|
64
|
+
console.log(` ✔ Updated ${file.target} (sync)`)
|
|
65
|
+
} else {
|
|
66
|
+
if (!fs.existsSync(destPath)) {
|
|
67
|
+
copyDirSync(srcPath, destPath)
|
|
68
|
+
created++
|
|
69
|
+
console.log(` ✔ Created ${file.target} (scaffold)`)
|
|
70
|
+
} else {
|
|
71
|
+
skipped++
|
|
72
|
+
console.log(` ⏭ Skipped ${file.target} (already exists)`)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (file.mode === 'scaffold') {
|
|
79
|
+
if (fs.existsSync(destPath)) {
|
|
80
|
+
skipped++
|
|
81
|
+
console.log(` ⏭ Skipped ${file.target} (already exists)`)
|
|
82
|
+
} else {
|
|
83
|
+
copyFileSync(srcPath, destPath)
|
|
84
|
+
created++
|
|
85
|
+
console.log(` ✔ Created ${file.target} (scaffold)`)
|
|
86
|
+
}
|
|
87
|
+
} else if (file.mode === 'updateable') {
|
|
88
|
+
copyFileSync(srcPath, destPath)
|
|
89
|
+
updated++
|
|
90
|
+
console.log(` ✔ Updated ${file.target} (sync)`)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Make shell scripts executable
|
|
94
|
+
if (file.target.endsWith('.sh')) {
|
|
95
|
+
fs.chmodSync(destPath, 0o755)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log('')
|
|
100
|
+
console.log(`✔ Scaffold complete: ${created} created, ${updated} updated, ${skipped} skipped.`)
|