@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,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helpers for subscribing to URL hash changes in React hooks.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Subscribe to hash changes so React re-renders when the hash updates.
|
|
7
|
+
* Compatible with useSyncExternalStore.
|
|
8
|
+
*/
|
|
9
|
+
export function subscribeToHash(callback) {
|
|
10
|
+
window.addEventListener('hashchange', callback)
|
|
11
|
+
return () => window.removeEventListener('hashchange', callback)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Snapshot of the full hash string for useSyncExternalStore.
|
|
16
|
+
*/
|
|
17
|
+
export function getHashSnapshot() {
|
|
18
|
+
return window.location.hash
|
|
19
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { subscribeToHash, getHashSnapshot } from './hashSubscribe.js'
|
|
2
|
+
|
|
3
|
+
describe('subscribeToHash', () => {
|
|
4
|
+
it('returns an unsubscribe function', () => {
|
|
5
|
+
const unsub = subscribeToHash(() => {})
|
|
6
|
+
expect(typeof unsub).toBe('function')
|
|
7
|
+
unsub()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('callback fires on hashchange event', () => {
|
|
11
|
+
const cb = vi.fn()
|
|
12
|
+
const unsub = subscribeToHash(cb)
|
|
13
|
+
|
|
14
|
+
window.dispatchEvent(new Event('hashchange'))
|
|
15
|
+
expect(cb).toHaveBeenCalledTimes(1)
|
|
16
|
+
|
|
17
|
+
unsub()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('callback does NOT fire after unsubscribe', () => {
|
|
21
|
+
const cb = vi.fn()
|
|
22
|
+
const unsub = subscribeToHash(cb)
|
|
23
|
+
unsub()
|
|
24
|
+
|
|
25
|
+
window.dispatchEvent(new Event('hashchange'))
|
|
26
|
+
expect(cb).not.toHaveBeenCalled()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('multiple subscribers all fire', () => {
|
|
30
|
+
const cb1 = vi.fn()
|
|
31
|
+
const cb2 = vi.fn()
|
|
32
|
+
const unsub1 = subscribeToHash(cb1)
|
|
33
|
+
const unsub2 = subscribeToHash(cb2)
|
|
34
|
+
|
|
35
|
+
window.dispatchEvent(new Event('hashchange'))
|
|
36
|
+
expect(cb1).toHaveBeenCalledTimes(1)
|
|
37
|
+
expect(cb2).toHaveBeenCalledTimes(1)
|
|
38
|
+
|
|
39
|
+
unsub1()
|
|
40
|
+
unsub2()
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe('getHashSnapshot', () => {
|
|
45
|
+
it('returns current hash string', () => {
|
|
46
|
+
window.location.hash = '#foo=bar'
|
|
47
|
+
expect(getHashSnapshot()).toBe('#foo=bar')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('returns empty string when no hash', () => {
|
|
51
|
+
window.location.hash = ''
|
|
52
|
+
expect(getHashSnapshot()).toBe('')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('reflects hash changes immediately', () => {
|
|
56
|
+
expect(getHashSnapshot()).toBe('')
|
|
57
|
+
window.location.hash = '#a=1'
|
|
58
|
+
expect(getHashSnapshot()).toBe('#a=1')
|
|
59
|
+
window.location.hash = '#b=2'
|
|
60
|
+
expect(getHashSnapshot()).toBe('#b=2')
|
|
61
|
+
})
|
|
62
|
+
})
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hide-mode + undo/redo override history.
|
|
3
|
+
*
|
|
4
|
+
* In normal mode, overrides live in the URL hash (#key=value).
|
|
5
|
+
* In hide mode, overrides live in localStorage and the URL stays clean.
|
|
6
|
+
*
|
|
7
|
+
* The localStorage mirror stores three keys under storyboard:
|
|
8
|
+
*
|
|
9
|
+
* historyState → [[0, "/route", "params"], [1, "/other", "params"], ...]
|
|
10
|
+
* Ordered chronologically: 0 = first state, 1, 2, 3…
|
|
11
|
+
* Each entry is [position, route, paramString].
|
|
12
|
+
*
|
|
13
|
+
* currentState → index into historyState for the active snapshot
|
|
14
|
+
*
|
|
15
|
+
* nextState → index into historyState for redo target (null if none).
|
|
16
|
+
* Cleared when the user creates a new state that doesn't
|
|
17
|
+
* match previousState or nextState (i.e. a "branch").
|
|
18
|
+
*
|
|
19
|
+
* Undo moves currentState backward (currentState - 1 = previousState).
|
|
20
|
+
* Redo moves currentState forward to nextState.
|
|
21
|
+
* A fresh override write appends to historyState, advances currentState,
|
|
22
|
+
* and clears nextState (forking the timeline).
|
|
23
|
+
*
|
|
24
|
+
* The hide flag is stored as: storyboard:__hide__
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { getLocal, setLocal, removeLocal, notifyChange } from './localStorage.js'
|
|
28
|
+
import { setParam } from './session.js'
|
|
29
|
+
|
|
30
|
+
const HIDE_FLAG = '__hide__'
|
|
31
|
+
const HISTORY_KEY = 'historyState'
|
|
32
|
+
const CURRENT_KEY = 'currentState'
|
|
33
|
+
const NEXT_KEY = 'nextState'
|
|
34
|
+
const MAX_HISTORY = 200
|
|
35
|
+
|
|
36
|
+
// ── Hide Mode ──
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Check whether hide mode is currently active.
|
|
40
|
+
* @returns {boolean}
|
|
41
|
+
*/
|
|
42
|
+
export function isHideMode() {
|
|
43
|
+
return getLocal(HIDE_FLAG) === '1'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Activate hide mode:
|
|
48
|
+
* 1. Snapshot current state into the history
|
|
49
|
+
* 2. Set the hide flag
|
|
50
|
+
* 3. Clear the URL hash and strip ?hide
|
|
51
|
+
*/
|
|
52
|
+
export function activateHideMode() {
|
|
53
|
+
pushSnapshot()
|
|
54
|
+
setLocal(HIDE_FLAG, '1')
|
|
55
|
+
|
|
56
|
+
const url = new URL(window.location.href)
|
|
57
|
+
url.searchParams.delete('hide')
|
|
58
|
+
url.hash = ''
|
|
59
|
+
window.history.replaceState(window.history.state, '', url.toString())
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Deactivate hide mode:
|
|
64
|
+
* 1. Restore current snapshot params → URL hash
|
|
65
|
+
* 2. Remove hide flag
|
|
66
|
+
* 3. Strip ?show from the URL
|
|
67
|
+
* (History is never cleared)
|
|
68
|
+
*/
|
|
69
|
+
export function deactivateHideMode() {
|
|
70
|
+
const params = getCurrentSnapshot()
|
|
71
|
+
if (params) {
|
|
72
|
+
window.location.hash = ''
|
|
73
|
+
const parsed = new URLSearchParams(params)
|
|
74
|
+
for (const [key, value] of parsed.entries()) {
|
|
75
|
+
setParam(key, value)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
removeLocal(HIDE_FLAG)
|
|
80
|
+
stripSearchParam('show')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ── History Stack ──
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the current route pathname.
|
|
87
|
+
* @returns {string}
|
|
88
|
+
*/
|
|
89
|
+
function currentRoute() {
|
|
90
|
+
return window.location.pathname
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get the current hash as a normalized param string.
|
|
95
|
+
* @returns {string}
|
|
96
|
+
*/
|
|
97
|
+
function currentParams() {
|
|
98
|
+
return new URLSearchParams(window.location.hash.replace(/^#/, '')).toString()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Push a new snapshot onto the history stack.
|
|
103
|
+
*
|
|
104
|
+
* Appends after currentState, advances currentState, and clears nextState
|
|
105
|
+
* (forking the timeline — any redo future is discarded).
|
|
106
|
+
*
|
|
107
|
+
* @param {string} [paramString] - Override param string. Defaults to current hash.
|
|
108
|
+
* @param {string} [route] - Route pathname. Defaults to current pathname.
|
|
109
|
+
*/
|
|
110
|
+
export function pushSnapshot(paramString, route) {
|
|
111
|
+
const params = paramString !== undefined ? paramString : currentParams()
|
|
112
|
+
const routePath = route !== undefined ? route : currentRoute()
|
|
113
|
+
|
|
114
|
+
const history = getOverrideHistory()
|
|
115
|
+
const current = getCurrentIndex()
|
|
116
|
+
|
|
117
|
+
// Don't push duplicates of the current state (same route + params)
|
|
118
|
+
if (current !== null && history[current]) {
|
|
119
|
+
const [, prevRoute, prevParams] = history[current]
|
|
120
|
+
if (prevRoute === routePath && prevParams === params) return
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Trim any entries after currentState (fork the timeline)
|
|
124
|
+
const base = current !== null ? history.slice(0, current + 1) : history
|
|
125
|
+
|
|
126
|
+
// Append new entry: [position, route, params]
|
|
127
|
+
const newIndex = base.length
|
|
128
|
+
const entry = [newIndex, routePath, params]
|
|
129
|
+
|
|
130
|
+
// Enforce max history by dropping oldest entries
|
|
131
|
+
const updated = [...base, entry]
|
|
132
|
+
if (updated.length > MAX_HISTORY) {
|
|
133
|
+
const trimmed = updated.slice(updated.length - MAX_HISTORY)
|
|
134
|
+
for (let i = 0; i < trimmed.length; i++) {
|
|
135
|
+
trimmed[i] = [i, trimmed[i][1], trimmed[i][2]]
|
|
136
|
+
}
|
|
137
|
+
setLocal(HISTORY_KEY, JSON.stringify(trimmed))
|
|
138
|
+
setLocal(CURRENT_KEY, String(trimmed.length - 1))
|
|
139
|
+
} else {
|
|
140
|
+
setLocal(HISTORY_KEY, JSON.stringify(updated))
|
|
141
|
+
setLocal(CURRENT_KEY, String(newIndex))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// New state forks the timeline — clear redo target
|
|
145
|
+
removeLocal(NEXT_KEY)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Undo: move currentState back by one. Sets nextState to the old current
|
|
150
|
+
* so redo can return.
|
|
151
|
+
* @returns {{ route: string, params: string }|null}
|
|
152
|
+
*/
|
|
153
|
+
export function undo() {
|
|
154
|
+
const current = getCurrentIndex()
|
|
155
|
+
if (current === null || current <= 0) return null
|
|
156
|
+
|
|
157
|
+
const prevIndex = current - 1
|
|
158
|
+
const history = getOverrideHistory()
|
|
159
|
+
if (!history[prevIndex]) return null
|
|
160
|
+
|
|
161
|
+
setLocal(NEXT_KEY, String(current))
|
|
162
|
+
setLocal(CURRENT_KEY, String(prevIndex))
|
|
163
|
+
|
|
164
|
+
const [, route, params] = history[prevIndex]
|
|
165
|
+
applySnapshot(params)
|
|
166
|
+
return { route, params }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Redo: move currentState forward to nextState.
|
|
171
|
+
* @returns {{ route: string, params: string }|null}
|
|
172
|
+
*/
|
|
173
|
+
export function redo() {
|
|
174
|
+
const nextIndex = getNextIndex()
|
|
175
|
+
if (nextIndex === null) return null
|
|
176
|
+
|
|
177
|
+
const history = getOverrideHistory()
|
|
178
|
+
if (!history[nextIndex]) return null
|
|
179
|
+
|
|
180
|
+
const followingIndex = nextIndex + 1
|
|
181
|
+
if (history[followingIndex]) {
|
|
182
|
+
setLocal(NEXT_KEY, String(followingIndex))
|
|
183
|
+
} else {
|
|
184
|
+
removeLocal(NEXT_KEY)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
setLocal(CURRENT_KEY, String(nextIndex))
|
|
188
|
+
|
|
189
|
+
const [, route, params] = history[nextIndex]
|
|
190
|
+
applySnapshot(params)
|
|
191
|
+
return { route, params }
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get the full override history stack.
|
|
196
|
+
* @returns {Array<[number, string, string]>} Array of [index, route, paramString]
|
|
197
|
+
*/
|
|
198
|
+
export function getOverrideHistory() {
|
|
199
|
+
const raw = getLocal(HISTORY_KEY)
|
|
200
|
+
if (!raw) return []
|
|
201
|
+
try {
|
|
202
|
+
const parsed = JSON.parse(raw)
|
|
203
|
+
return Array.isArray(parsed) ? parsed : []
|
|
204
|
+
} catch {
|
|
205
|
+
return []
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get the current state index.
|
|
211
|
+
* @returns {number|null}
|
|
212
|
+
*/
|
|
213
|
+
export function getCurrentIndex() {
|
|
214
|
+
const raw = getLocal(CURRENT_KEY)
|
|
215
|
+
if (raw === null) return null
|
|
216
|
+
const n = parseInt(raw, 10)
|
|
217
|
+
return Number.isNaN(n) ? null : n
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Get the next state index (redo target).
|
|
222
|
+
* @returns {number|null}
|
|
223
|
+
*/
|
|
224
|
+
export function getNextIndex() {
|
|
225
|
+
const raw = getLocal(NEXT_KEY)
|
|
226
|
+
if (raw === null) return null
|
|
227
|
+
const n = parseInt(raw, 10)
|
|
228
|
+
return Number.isNaN(n) ? null : n
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get the current snapshot's param string.
|
|
233
|
+
* @returns {string|null}
|
|
234
|
+
*/
|
|
235
|
+
export function getCurrentSnapshot() {
|
|
236
|
+
const current = getCurrentIndex()
|
|
237
|
+
if (current === null) return null
|
|
238
|
+
const history = getOverrideHistory()
|
|
239
|
+
if (!history[current]) return null
|
|
240
|
+
return history[current][2]
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get the current snapshot's route.
|
|
245
|
+
* @returns {string|null}
|
|
246
|
+
*/
|
|
247
|
+
export function getCurrentRoute() {
|
|
248
|
+
const current = getCurrentIndex()
|
|
249
|
+
if (current === null) return null
|
|
250
|
+
const history = getOverrideHistory()
|
|
251
|
+
if (!history[current]) return null
|
|
252
|
+
return history[current][1]
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Check if undo is available.
|
|
257
|
+
* @returns {boolean}
|
|
258
|
+
*/
|
|
259
|
+
export function canUndo() {
|
|
260
|
+
const current = getCurrentIndex()
|
|
261
|
+
return current !== null && current > 0
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Check if redo is available.
|
|
266
|
+
* @returns {boolean}
|
|
267
|
+
*/
|
|
268
|
+
export function canRedo() {
|
|
269
|
+
return getNextIndex() !== null
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ── Shadow Read/Write (used by useOverride in hide mode) ──
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Read a single shadow value from the current snapshot.
|
|
276
|
+
* @param {string} key - Dot-notation path
|
|
277
|
+
* @returns {string|null}
|
|
278
|
+
*/
|
|
279
|
+
export function getShadow(key) {
|
|
280
|
+
const params = getCurrentSnapshot()
|
|
281
|
+
if (!params) return null
|
|
282
|
+
return new URLSearchParams(params).get(key)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Write a shadow value by pushing a new snapshot with the key updated.
|
|
287
|
+
* @param {string} key
|
|
288
|
+
* @param {string} value
|
|
289
|
+
*/
|
|
290
|
+
export function setShadow(key, value) {
|
|
291
|
+
const params = getCurrentSnapshot() || ''
|
|
292
|
+
const parsed = new URLSearchParams(params)
|
|
293
|
+
parsed.set(key, String(value))
|
|
294
|
+
pushSnapshot(parsed.toString(), getCurrentRoute() || currentRoute())
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Remove a shadow value by pushing a new snapshot without the key.
|
|
299
|
+
* @param {string} key
|
|
300
|
+
*/
|
|
301
|
+
export function removeShadow(key) {
|
|
302
|
+
const params = getCurrentSnapshot() || ''
|
|
303
|
+
const parsed = new URLSearchParams(params)
|
|
304
|
+
parsed.delete(key)
|
|
305
|
+
pushSnapshot(parsed.toString(), getCurrentRoute() || currentRoute())
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Return all shadow entries from the current snapshot as a plain object.
|
|
310
|
+
* @returns {Record<string, string>}
|
|
311
|
+
*/
|
|
312
|
+
export function getAllShadows() {
|
|
313
|
+
const params = getCurrentSnapshot()
|
|
314
|
+
if (!params) return {}
|
|
315
|
+
const parsed = new URLSearchParams(params)
|
|
316
|
+
const result = {}
|
|
317
|
+
for (const [key, value] of parsed.entries()) {
|
|
318
|
+
result[key] = value
|
|
319
|
+
}
|
|
320
|
+
return result
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ── Hash ↔ History Sync ──
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Sync localStorage currentState when the URL changes externally
|
|
327
|
+
* (e.g. browser back/forward). Matches on route + params.
|
|
328
|
+
*
|
|
329
|
+
* - Adjacent move backward → treated as undo (sets nextState)
|
|
330
|
+
* - Adjacent move forward to nextState → treated as redo (advances nextState)
|
|
331
|
+
* - Non-adjacent jump → clears nextState and truncates history
|
|
332
|
+
* - No match in history → pushes as new snapshot
|
|
333
|
+
*/
|
|
334
|
+
export function syncHashToHistory() {
|
|
335
|
+
if (isHideMode()) return
|
|
336
|
+
|
|
337
|
+
const route = currentRoute()
|
|
338
|
+
const params = currentParams()
|
|
339
|
+
|
|
340
|
+
const history = getOverrideHistory()
|
|
341
|
+
const current = getCurrentIndex()
|
|
342
|
+
|
|
343
|
+
// Empty state and empty history — nothing to sync
|
|
344
|
+
if (!params && !route && history.length === 0) return
|
|
345
|
+
|
|
346
|
+
// Find matching entry (same route AND params)
|
|
347
|
+
const matchIndex = history.findIndex(
|
|
348
|
+
([, r, p]) => r === route && p === params
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
if (matchIndex === -1) {
|
|
352
|
+
pushSnapshot(params, route)
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (matchIndex === current) return
|
|
357
|
+
|
|
358
|
+
const prevIndex = current !== null ? current - 1 : null
|
|
359
|
+
const nextIndex = getNextIndex()
|
|
360
|
+
|
|
361
|
+
if (prevIndex !== null && matchIndex === prevIndex) {
|
|
362
|
+
setLocal(NEXT_KEY, String(current))
|
|
363
|
+
setLocal(CURRENT_KEY, String(matchIndex))
|
|
364
|
+
} else if (nextIndex !== null && matchIndex === nextIndex) {
|
|
365
|
+
const followingIndex = nextIndex + 1
|
|
366
|
+
if (history[followingIndex]) {
|
|
367
|
+
setLocal(NEXT_KEY, String(followingIndex))
|
|
368
|
+
} else {
|
|
369
|
+
removeLocal(NEXT_KEY)
|
|
370
|
+
}
|
|
371
|
+
setLocal(CURRENT_KEY, String(matchIndex))
|
|
372
|
+
} else {
|
|
373
|
+
// Non-adjacent jump — clear nextState and truncate
|
|
374
|
+
removeLocal(NEXT_KEY)
|
|
375
|
+
setLocal(CURRENT_KEY, String(matchIndex))
|
|
376
|
+
const trimmed = history.slice(0, matchIndex + 1)
|
|
377
|
+
setLocal(HISTORY_KEY, JSON.stringify(trimmed))
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
notifyChange()
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Install listeners that keep localStorage history in sync
|
|
385
|
+
* with browser navigation. Call once at app startup.
|
|
386
|
+
* Also records the initial page load as the first history entry.
|
|
387
|
+
*/
|
|
388
|
+
export function installHistorySync() {
|
|
389
|
+
// Record initial page state — but skip in hide mode where the hash
|
|
390
|
+
// is intentionally empty; pushing it would clobber the real shadow data.
|
|
391
|
+
if (!isHideMode()) {
|
|
392
|
+
pushSnapshot()
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
window.addEventListener('hashchange', () => syncHashToHistory())
|
|
396
|
+
window.addEventListener('popstate', () => syncHashToHistory())
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ── Internal ──
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Apply a snapshot param string to the active override target.
|
|
403
|
+
* In hide mode, localStorage is already updated — just notify.
|
|
404
|
+
* In normal mode, write to the URL hash.
|
|
405
|
+
* @param {string} params
|
|
406
|
+
*/
|
|
407
|
+
function applySnapshot(params) {
|
|
408
|
+
if (isHideMode()) {
|
|
409
|
+
notifyChange()
|
|
410
|
+
} else {
|
|
411
|
+
window.location.hash = params
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Remove a search param from the URL without triggering a navigation.
|
|
417
|
+
* @param {string} param
|
|
418
|
+
*/
|
|
419
|
+
function stripSearchParam(param) {
|
|
420
|
+
const url = new URL(window.location.href)
|
|
421
|
+
if (!url.searchParams.has(param)) return
|
|
422
|
+
url.searchParams.delete(param)
|
|
423
|
+
window.history.replaceState(window.history.state, '', url.toString())
|
|
424
|
+
}
|