@dfosco/storyboard 0.7.1 → 0.8.1

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.
Files changed (254) hide show
  1. package/dist/storyboard-ui.css +1 -1
  2. package/dist/storyboard-ui.js +13907 -10816
  3. package/dist/storyboard-ui.js.map +1 -1
  4. package/dist/tailwind.css +1 -1
  5. package/package.json +138 -128
  6. package/scaffold/AGENTS.md +9 -7
  7. package/scaffold/README.md +18 -0
  8. package/scaffold/deploy.yml +21 -0
  9. package/scaffold/gitignore +8 -14
  10. package/scaffold/preview.yml +40 -0
  11. package/scaffold/{manifest.json → scaffold.config.json} +73 -3
  12. package/scaffold/scripts/aggregate-artifacts.mjs +257 -0
  13. package/scaffold/skills/canvas/SKILL.md +1 -1
  14. package/scaffold/skills/migrate-0.5.0/SKILL.md +1 -1
  15. package/scaffold/skills/ship/SKILL.md +2 -2
  16. package/scaffold/skills/worktree/SKILL.md +1 -1
  17. package/scaffold/src/_prototype.jsx +17 -0
  18. package/scaffold/src/_story.jsx +13 -0
  19. package/scaffold/src/library/README.md +34 -0
  20. package/scaffold/src/library/_app.jsx +53 -0
  21. package/scaffold/src/library/_app.module.css +8 -0
  22. package/scaffold/src/library/create.jsx +10 -0
  23. package/scaffold/src/library/index.jsx +14 -0
  24. package/scaffold/src/library/mount.jsx +52 -0
  25. package/scaffold/src/library/prototypes-entry.jsx +57 -0
  26. package/scaffold/src/library/routes.jsx +205 -0
  27. package/scaffold/src/library/viewfinder.jsx +13 -0
  28. package/scaffold/src/library/workspace.jsx +17 -0
  29. package/scripts/aggregate-artifacts.mjs +257 -0
  30. package/src/core/artifact/operations.js +19 -13
  31. package/src/core/autosync/server.test.js +1 -1
  32. package/src/core/canvas/hot-pool.js +10 -11
  33. package/src/core/canvas/materializer.js +39 -13
  34. package/src/core/canvas/server.js +324 -69
  35. package/src/core/canvas/server.test.js +365 -2
  36. package/src/core/canvas/terminal-server.js +144 -57
  37. package/src/core/canvas/undoRedo.js +174 -0
  38. package/src/core/canvas/undoRedo.test.js +250 -0
  39. package/src/core/cli/buildManifest.js +34 -0
  40. package/src/core/cli/canvasRedo.js +44 -0
  41. package/src/core/cli/canvasUndo.js +40 -0
  42. package/src/core/cli/create.js +116 -18
  43. package/src/core/cli/dev.js +23 -35
  44. package/src/core/cli/index.js +21 -0
  45. package/src/core/cli/schemas.js +2 -3
  46. package/src/core/cli/serverUrl.js +24 -0
  47. package/src/core/cli/setup.js +14 -25
  48. package/src/core/cli/terminal-welcome.js +12 -6
  49. package/src/core/cli/updateVersion.js +42 -2
  50. package/src/core/cli/workleaf.js +140 -0
  51. package/src/core/comments/api.js +1 -1
  52. package/src/core/comments/queries.js +3 -0
  53. package/src/core/comments/ui/CommentWindow.jsx +3 -2
  54. package/src/core/data/artifactIndex.js +235 -0
  55. package/src/core/data/artifactIndex.test.js +203 -0
  56. package/src/core/data/artifactManifest.js +290 -0
  57. package/src/core/data/artifactManifest.test.js +207 -0
  58. package/src/core/data/viewfinder.js +47 -7
  59. package/src/core/index.js +6 -1
  60. package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +2 -2
  61. package/src/core/lib/components/ui/avatar/avatar-image.jsx +2 -2
  62. package/src/core/lib/components/ui/avatar/avatar.jsx +2 -2
  63. package/src/core/lib/components/ui/checkbox/checkbox.jsx +12 -9
  64. package/src/core/lib/components/ui/dialog/dialog-content.jsx +45 -31
  65. package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +9 -5
  66. package/src/core/lib/components/ui/dialog/dialog.jsx +12 -3
  67. package/src/core/lib/components/ui/dialog/dialog.module.css +118 -0
  68. package/src/core/lib/components/ui/dialog/index.js +4 -34
  69. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +3 -3
  70. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +19 -9
  71. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +8 -8
  72. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +2 -2
  73. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +2 -2
  74. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +15 -4
  75. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +2 -2
  76. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +2 -2
  77. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +14 -7
  78. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +2 -2
  79. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +8 -4
  80. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +4 -4
  81. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +2 -2
  82. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +8 -2
  83. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +2 -2
  84. package/src/core/lib/components/ui/label/label.jsx +1 -2
  85. package/src/core/lib/components/ui/panel/panel-close.jsx +7 -4
  86. package/src/core/lib/components/ui/panel/panel-content.jsx +7 -9
  87. package/src/core/lib/components/ui/panel/panel-title.jsx +3 -3
  88. package/src/core/lib/components/ui/panel/panel.jsx +11 -2
  89. package/src/core/lib/components/ui/separator/separator.jsx +6 -4
  90. package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +7 -6
  91. package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +13 -6
  92. package/src/core/lib/components/ui/tooltip/tooltip.jsx +5 -5
  93. package/src/core/mountStoryboardCore.js +234 -74
  94. package/src/core/mountStoryboardCore.test.js +174 -0
  95. package/src/core/rename-watcher/watcher.js +329 -36
  96. package/src/core/rename-watcher/watcher.test.js +253 -0
  97. package/src/core/scaffold/fragments.js +287 -0
  98. package/src/core/scaffold/fragments.test.js +401 -0
  99. package/src/core/scaffold/index.js +291 -0
  100. package/src/core/scaffold/migrateLegacyGitignore.js +157 -0
  101. package/src/core/scaffold/scaffold-integration.test.js +247 -0
  102. package/src/core/scaffold.js +4 -97
  103. package/src/core/stores/configSchema.js +41 -2
  104. package/src/core/stores/recentArtifacts.js +49 -2
  105. package/src/core/stores/recentArtifacts.test.js +59 -6
  106. package/src/core/stores/themeBootstrap.js +264 -0
  107. package/src/core/stores/themeBootstrap.test.js +211 -0
  108. package/src/core/stores/themeStore.test.js +154 -0
  109. package/src/core/stores/themeStore.ts +235 -220
  110. package/src/core/styles/generateThemesCss.js +63 -0
  111. package/src/core/styles/tailwind.css +12 -4
  112. package/src/core/styles/workspace-chrome.css +30 -0
  113. package/src/core/tools/handlers/paletteTheme.js +11 -8
  114. package/src/core/ui/ActionMenuButton.jsx +2 -1
  115. package/src/core/ui/CanvasCreateMenu.jsx +1 -19
  116. package/src/core/ui/CoreUIBar.jsx +10 -7
  117. package/src/core/ui/ThemeMenuButton.jsx +45 -30
  118. package/src/core/vite/server-plugin.js +13 -0
  119. package/src/core/workleaf/cleanup.js +102 -0
  120. package/src/core/workleaf/create.js +199 -0
  121. package/src/core/workleaf/delete.js +101 -0
  122. package/src/core/workleaf/list.js +57 -0
  123. package/src/core/workleaf/merge.js +238 -0
  124. package/src/core/workleaf/paths.js +146 -0
  125. package/src/core/workleaf/paths.test.js +230 -0
  126. package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +28 -21
  127. package/src/core/workshop/features/createFlow/server.js +5 -1
  128. package/src/core/workshop/features/createPrototype/server.js +5 -1
  129. package/src/core/workshop/features/templateIndex.js +28 -1
  130. package/src/core/worktree/port.js +48 -5
  131. package/src/core/worktree/port.test.js +42 -0
  132. package/src/internals/ArtifactForm/ArtifactForm.jsx +173 -112
  133. package/src/internals/ArtifactForm/ArtifactForm.module.css +48 -66
  134. package/src/internals/ArtifactForm/artifactSchemas.js +40 -5
  135. package/src/internals/BaseUiForm/BaseUiForm.jsx +293 -0
  136. package/src/internals/BaseUiForm/BaseUiForm.module.css +542 -0
  137. package/src/internals/BranchesDropdown/BranchesDropdown.jsx +69 -0
  138. package/src/internals/BranchesDropdown/BranchesDropdown.module.css +84 -0
  139. package/src/internals/CommandPalette/CommandPalette.jsx +25 -19
  140. package/src/internals/CommandPalette/CreateArtifactForm.jsx +150 -0
  141. package/src/internals/CommandPalette/CreateDialog.jsx +20 -110
  142. package/src/internals/CommandPalette/WidgetArtifactDialog.jsx +1 -1
  143. package/src/internals/CommandPalette/createHelpers.js +30 -0
  144. package/src/internals/CreatePage/CreatePage.jsx +57 -0
  145. package/src/internals/CreatePage/CreatePage.module.css +34 -0
  146. package/src/internals/Dialog.jsx +51 -0
  147. package/src/internals/SimpleWorkspace/SimpleWorkspace.jsx +533 -0
  148. package/src/internals/SimpleWorkspace/SimpleWorkspace.module.css +323 -0
  149. package/src/internals/Tooltip.jsx +39 -0
  150. package/src/internals/Viewfinder.jsx +196 -48
  151. package/src/internals/Viewfinder.module.css +72 -1
  152. package/src/internals/canvas/CanvasPage.jsx +267 -124
  153. package/src/internals/canvas/CanvasPage.multiselect.test.jsx +39 -26
  154. package/src/internals/canvas/PageSelector.jsx +15 -1
  155. package/src/internals/canvas/PageSelector.module.css +14 -0
  156. package/src/internals/canvas/canvasApi.js +40 -5
  157. package/src/internals/canvas/isolation/createIsolationMiddleware.js +68 -0
  158. package/src/internals/canvas/isolation/mountIsolationApp.jsx +88 -0
  159. package/src/internals/canvas/isolation/prototypeRoutes.jsx +131 -0
  160. package/src/internals/canvas/isolation/prototypesEntry.jsx +28 -0
  161. package/src/internals/canvas/isolation/storiesEntry.jsx +20 -0
  162. package/src/internals/canvas/isolation/storyRoutes.jsx +55 -0
  163. package/src/internals/canvas/useUndoRedo.js +75 -60
  164. package/src/internals/canvas/useUndoRedo.test.js +46 -178
  165. package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +1 -1
  166. package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +30 -21
  167. package/src/internals/canvas/widgets/ImageWidget.jsx +7 -2
  168. package/src/internals/canvas/widgets/LinkPreview.module.css +2 -2
  169. package/src/internals/canvas/widgets/PrototypeEmbed.jsx +69 -10
  170. package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +114 -0
  171. package/src/internals/canvas/widgets/StorySetWidget.jsx +8 -4
  172. package/src/internals/canvas/widgets/StoryWidget.jsx +21 -38
  173. package/src/internals/canvas/widgets/TerminalReadWidget.jsx +13 -15
  174. package/src/internals/canvas/widgets/WidgetChrome.jsx +1 -1
  175. package/src/internals/canvas/widgets/WidgetChrome.module.css +15 -0
  176. package/src/internals/canvas/widgets/embedTheme.js +2 -2
  177. package/src/internals/canvas/widgets/expandUtils.js +19 -19
  178. package/src/internals/canvas/widgets/normalizeLegacyEmbedSrc.js +74 -0
  179. package/src/internals/context.jsx +22 -1
  180. package/src/internals/context.test.jsx +47 -0
  181. package/src/internals/hooks/useArtifactIndex.js +31 -0
  182. package/src/internals/hooks/useArtifactIndex.test.js +45 -0
  183. package/src/internals/hooks/usePrototypeReloadGuard.js +14 -8
  184. package/src/internals/index.js +6 -0
  185. package/src/internals/loadConsumerWrapper.js +60 -0
  186. package/src/internals/story/ComponentSetPage.jsx +75 -34
  187. package/src/internals/story/StoryPage.jsx +16 -25
  188. package/src/internals/vite/data-plugin.js +313 -148
  189. package/src/internals/vite/data-plugin.test.js +11 -11
  190. package/{scaffold/storyboard.config.json → storyboard.config.json} +25 -0
  191. package/terminal.config.json +1 -1
  192. package/src/core/lib/components/ui/card/card-action.jsx +0 -11
  193. package/src/core/lib/components/ui/card/card-content.jsx +0 -11
  194. package/src/core/lib/components/ui/card/card-description.jsx +0 -11
  195. package/src/core/lib/components/ui/card/card-footer.jsx +0 -11
  196. package/src/core/lib/components/ui/card/card-header.jsx +0 -19
  197. package/src/core/lib/components/ui/card/card-title.jsx +0 -11
  198. package/src/core/lib/components/ui/card/card.jsx +0 -17
  199. package/src/core/lib/components/ui/card/index.js +0 -25
  200. package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +0 -7
  201. package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +0 -7
  202. package/src/core/lib/components/ui/collapsible/collapsible.jsx +0 -7
  203. package/src/core/lib/components/ui/collapsible/index.js +0 -13
  204. package/src/core/lib/components/ui/dialog/dialog-close.jsx +0 -7
  205. package/src/core/lib/components/ui/dialog/dialog-description.jsx +0 -15
  206. package/src/core/lib/components/ui/dialog/dialog-footer.jsx +0 -23
  207. package/src/core/lib/components/ui/dialog/dialog-header.jsx +0 -11
  208. package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +0 -15
  209. package/src/core/lib/components/ui/dialog/dialog-portal.jsx +0 -4
  210. package/src/core/lib/components/ui/dialog/dialog-title.jsx +0 -15
  211. package/src/core/lib/components/ui/popover/index.js +0 -28
  212. package/src/core/lib/components/ui/popover/popover-close.jsx +0 -7
  213. package/src/core/lib/components/ui/popover/popover-content.jsx +0 -22
  214. package/src/core/lib/components/ui/popover/popover-description.jsx +0 -11
  215. package/src/core/lib/components/ui/popover/popover-header.jsx +0 -11
  216. package/src/core/lib/components/ui/popover/popover-portal.jsx +0 -4
  217. package/src/core/lib/components/ui/popover/popover-title.jsx +0 -11
  218. package/src/core/lib/components/ui/popover/popover-trigger.jsx +0 -8
  219. package/src/core/lib/components/ui/popover/popover.jsx +0 -4
  220. package/src/core/lib/components/ui/select/index.js +0 -37
  221. package/src/core/lib/components/ui/select/select-content.jsx +0 -30
  222. package/src/core/lib/components/ui/select/select-group-heading.jsx +0 -17
  223. package/src/core/lib/components/ui/select/select-group.jsx +0 -15
  224. package/src/core/lib/components/ui/select/select-item.jsx +0 -26
  225. package/src/core/lib/components/ui/select/select-label.jsx +0 -11
  226. package/src/core/lib/components/ui/select/select-portal.jsx +0 -4
  227. package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +0 -18
  228. package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +0 -18
  229. package/src/core/lib/components/ui/select/select-separator.jsx +0 -15
  230. package/src/core/lib/components/ui/select/select-trigger.jsx +0 -25
  231. package/src/core/lib/components/ui/select/select.jsx +0 -4
  232. package/src/core/lib/components/ui/sheet/index.js +0 -34
  233. package/src/core/lib/components/ui/sheet/sheet-close.jsx +0 -7
  234. package/src/core/lib/components/ui/sheet/sheet-content.jsx +0 -35
  235. package/src/core/lib/components/ui/sheet/sheet-description.jsx +0 -15
  236. package/src/core/lib/components/ui/sheet/sheet-footer.jsx +0 -11
  237. package/src/core/lib/components/ui/sheet/sheet-header.jsx +0 -11
  238. package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +0 -15
  239. package/src/core/lib/components/ui/sheet/sheet-portal.jsx +0 -4
  240. package/src/core/lib/components/ui/sheet/sheet-title.jsx +0 -15
  241. package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +0 -7
  242. package/src/core/lib/components/ui/sheet/sheet.jsx +0 -4
  243. package/src/core/lib/components/ui/toggle/index.js +0 -8
  244. package/src/core/lib/components/ui/toggle/toggle.jsx +0 -36
  245. package/src/core/lib/components/ui/toggle-group/index.js +0 -10
  246. package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +0 -29
  247. package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +0 -43
  248. package/src/internals/canvas/StoryErrorBoundary.jsx +0 -47
  249. package/src/internals/canvas/componentIsolate.jsx +0 -144
  250. package/src/internals/canvas/componentSetIsolate.jsx +0 -300
  251. package/src/internals/canvas/widgets/InlineStoryRenderer.jsx +0 -129
  252. package/src/internals/canvas/widgets/InlineStoryRenderer.test.jsx +0 -68
  253. package/src/primer/ThemeSync.jsx +0 -73
  254. package/src/primer/index.js +0 -1
@@ -0,0 +1,53 @@
1
+ import { Suspense } from 'react'
2
+ import { Outlet } from 'react-router-dom'
3
+ // Direct imports bypass the barrel re-export in @dfosco/storyboard/index.js.
4
+ // The barrel eagerly re-exports CanvasPage (3K lines), CommandPalette (1.3K lines),
5
+ // and Viewfinder (1.5K lines) — all resolved by Vite dev even though _app only
6
+ // needs StoryboardProvider and useFeatureFlag.
7
+ import StoryboardProvider from '@dfosco/storyboard/context'
8
+ import { useFeatureFlag } from '@dfosco/storyboard/hooks/useFeatureFlag'
9
+ import { AppErrorBoundary } from '@dfosco/storyboard/error-boundary'
10
+ import appStyles from './_app.module.css'
11
+ import '@dfosco/storyboard/canvas/style.css'
12
+
13
+ function PageLoading() {
14
+ return (
15
+ <div style={{
16
+ display: 'flex',
17
+ alignItems: 'center',
18
+ justifyContent: 'center',
19
+ minHeight: '100vh',
20
+ backgroundColor: 'var(--bgColor-default, #0d1117)',
21
+ }}>
22
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" style={{ animation: 'spin 0.8s linear infinite' }}>
23
+ <circle cx="12" cy="12" r="10" stroke="var(--fgColor-muted, #484f58)" strokeWidth="2.5" opacity="0.25" />
24
+ <path d="M12 2a10 10 0 0 1 10 10" stroke="var(--fgColor-default, #e6edf3)" strokeWidth="2.5" strokeLinecap="round" />
25
+ </svg>
26
+ <style>{`@keyframes spin { to { transform: rotate(360deg) } }`}</style>
27
+ </div>
28
+ )
29
+ }
30
+
31
+ export default function App() {
32
+ return (
33
+ <StoryboardProvider>
34
+ <FeatureFlagBanner />
35
+ <Suspense fallback={<PageLoading />}>
36
+ <Outlet />
37
+ </Suspense>
38
+ </StoryboardProvider>
39
+ )
40
+ }
41
+
42
+ // Last-resort error boundary — generouted wires this via _app?.Catch
43
+ export const Catch = AppErrorBoundary
44
+
45
+ function FeatureFlagBanner() {
46
+ const showBanner = useFeatureFlag('show-banner')
47
+ if (!showBanner) return null
48
+ return (
49
+ <div className={appStyles.banner}>
50
+ 🚩 Feature flag <strong>show-banner</strong> is enabled — toggle it off in the Command Menu on the bottom right.
51
+ </div>
52
+ )
53
+ }
@@ -0,0 +1,8 @@
1
+ .banner {
2
+ padding: 18px 16px;
3
+ background-color: var(--bgColor-accent-emphasis, #1f6feb);
4
+ color: var(--fgColor-onEmphasis, #ffffff);
5
+ font-size: 14px;
6
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
7
+ text-align: center;
8
+ }
@@ -0,0 +1,10 @@
1
+ import { CreatePage } from '@dfosco/storyboard'
2
+
3
+ /**
4
+ * `/create` route — standalone schema-driven artifact creator.
5
+ * Opened by `storyboard create --ui` to give a focused creation surface
6
+ * without the workspace chrome.
7
+ */
8
+ export default function CreateRoute() {
9
+ return <CreatePage basePath={import.meta.env.BASE_URL} />
10
+ }
@@ -0,0 +1,14 @@
1
+ import { SimpleWorkspace } from '@dfosco/storyboard'
2
+
3
+ const pageModules = import.meta.glob('/src/prototypes/*/*.jsx')
4
+
5
+ export default function IndexPage() {
6
+ return (
7
+ <SimpleWorkspace
8
+ title="Storyboard"
9
+ subtitle="Collaborative workspace for design & code"
10
+ pageModules={pageModules}
11
+ basePath={import.meta.env.BASE_URL}
12
+ />
13
+ )
14
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * DO NOT EDIT — storyboard-owned library file.
3
+ *
4
+ * Synced from @dfosco/storyboard. Customize via storyboard.config.json,
5
+ * src/_prototype.jsx, src/_story.jsx, or src/library/_app.jsx.
6
+ * See src/library/README.md for the full customization surface.
7
+ *
8
+ * Workspace SPA entry. Mounts the React Router with the file-based prototype
9
+ * routes (see ./routes.jsx) and bootstraps storyboard core (devtools, comments,
10
+ * theming, etc.) via mountStoryboardCore.
11
+ */
12
+ import { StrictMode } from 'react'
13
+ import { createRoot } from 'react-dom/client'
14
+ import { RouterProvider, createBrowserRouter } from 'react-router-dom'
15
+ import { routes } from './routes'
16
+
17
+ import { installHashPreserver } from '@dfosco/storyboard/hash-preserver'
18
+ import { mountStoryboardCore } from '@dfosco/storyboard/core'
19
+ import '@dfosco/storyboard/styles/workspace-chrome.css'
20
+ import '@dfosco/storyboard/comments/ui/comment-layout.css'
21
+ import storyboardConfig from '../../storyboard.config.json'
22
+ import { installPrototypeOverlayIsolation } from '../prototypeOverlayIsolation.js'
23
+
24
+ // Suppress Vite error overlays that originate in /src/prototypes/ so a
25
+ // broken prototype can't take down the canvas surface — see
26
+ // .agents/plans/vite-isolation.md and src/prototypeOverlayIsolation.js.
27
+ installPrototypeOverlayIsolation()
28
+
29
+ // Redirect after canvas creation — Vite full-reloads when a new
30
+ // .canvas.jsonl is created. The form sets ?redirect=/canvas/name
31
+ // which survives the reload. We navigate once the route is registered.
32
+ const redirectParam = new URLSearchParams(window.location.search).get('redirect')
33
+ if (redirectParam) {
34
+ const base = (import.meta.env.BASE_URL || '/').replace(/\/$/, '')
35
+ window.location.replace(base + redirectParam)
36
+ }
37
+
38
+ const router = createBrowserRouter(routes, {
39
+ basename: import.meta.env.BASE_URL,
40
+ })
41
+
42
+ installHashPreserver(router, import.meta.env.BASE_URL)
43
+ mountStoryboardCore(storyboardConfig, { basePath: import.meta.env.BASE_URL })
44
+
45
+ const rootElement = document.getElementById('root')
46
+ const root = createRoot(rootElement)
47
+
48
+ root.render(
49
+ <StrictMode>
50
+ <RouterProvider router={router} />
51
+ </StrictMode>
52
+ )
@@ -0,0 +1,57 @@
1
+ /**
2
+ * DO NOT EDIT — storyboard-owned library file.
3
+ *
4
+ * Synced from @dfosco/storyboard. Customize via src/_prototype.jsx.
5
+ * See src/library/README.md for the full customization surface.
6
+ *
7
+ * Isolated prototypes entry point (see .agents/plans/vite-isolation.md).
8
+ *
9
+ * This is the second Vite HTML entry — prototypes.html loads this file.
10
+ * Goal: physically separate the prototypes module graph + Rollup chunk graph
11
+ * from the canvas app so a broken prototype cannot poison canvas surfaces.
12
+ *
13
+ * Uses createHashRouter so prototype routes live in the URL hash of
14
+ * prototypes.html (e.g. prototypes.html#/MyProto/SignupForm). This avoids
15
+ * needing any server rewrite — every iframe load resolves to the same
16
+ * prototypes.html document, then the router renders the matched prototype.
17
+ *
18
+ * Does NOT install the workspace-level hash preserver — that hook is the
19
+ * canvas/workshop SPA concern, not relevant inside an isolated prototype.
20
+ *
21
+ * The prototype provider chain (Primer / Tailwind / fonts / design tokens)
22
+ * comes from the consumer's `src/_prototype.jsx` — wired by the route layout
23
+ * inside ./routes.jsx. When absent, prototypes render with no shared providers.
24
+ */
25
+ import { StrictMode } from 'react'
26
+ import { createRoot } from 'react-dom/client'
27
+ import { RouterProvider, createHashRouter } from 'react-router-dom'
28
+ import { routes, getRoutesForProto } from './routes'
29
+ import { mountStoryboardCore } from '@dfosco/storyboard/core'
30
+ import '@dfosco/storyboard/comments/ui/comment-layout.css'
31
+ import storyboardConfig from '../../storyboard.config.json'
32
+
33
+ // Mount storyboard core. In embed mode (_sb_embed in the URL — always true
34
+ // for canvas iframes) this short-circuits past UI mounting and only:
35
+ // - honors the canvas-aware theme (_sb_theme_target, _sb_canvas_theme)
36
+ // - sets up the iframe→parent postMessage navigation bridge
37
+ mountStoryboardCore(storyboardConfig, { basePath: import.meta.env.BASE_URL })
38
+
39
+ // Per-prototype filtering: when PrototypeEmbed loads us with ?proto=Name in
40
+ // dev, narrow the router to that prototype's subtree. Other prototypes'
41
+ // lazy() entries never appear in the route table, so a broken sibling
42
+ // prototype cannot leak into this iframe's module graph or HMR scope. Falls
43
+ // back to the full route tree when no proto param is set (production builds
44
+ // + direct-load flows).
45
+ const protoParam = new URLSearchParams(window.location.search).get('proto')
46
+ const activeRoutes = protoParam ? getRoutesForProto(protoParam) : routes
47
+
48
+ const router = createHashRouter(activeRoutes)
49
+
50
+ const rootElement = document.getElementById('root')
51
+ const root = createRoot(rootElement)
52
+
53
+ root.render(
54
+ <StrictMode>
55
+ <RouterProvider router={router} />
56
+ </StrictMode>
57
+ )
@@ -0,0 +1,205 @@
1
+ /**
2
+ * DO NOT EDIT — storyboard-owned library file.
3
+ *
4
+ * Synced from @dfosco/storyboard. Customize via src/_prototype.jsx (wraps
5
+ * every prototype route) or src/library/_app.jsx (workspace SPA shell).
6
+ * See src/library/README.md for the full customization surface.
7
+ *
8
+ * Custom generouted routes entry for storyboard.
9
+ *
10
+ * generouted hardcodes /src/pages/ in both its import.meta.glob patterns and
11
+ * its route-path regex. This module mirrors the generouted runtime but scans
12
+ * two directories:
13
+ * - /src/library/ — storyboard's workspace pages (workspace, viewfinder,
14
+ * create, index) plus _app.jsx (SPA shell wrapper).
15
+ * - /src/prototypes/ — the consumer's actual prototypes.
16
+ *
17
+ * Both directories have their prefix segment stripped from the URL — so
18
+ * src/library/workspace.jsx mounts at /workspace, and
19
+ * src/prototypes/MyProto/index.jsx mounts at /MyProto.
20
+ *
21
+ * Every PROTOTYPE route (anything originating from /src/prototypes/) is
22
+ * wrapped in the consumer's src/_prototype.jsx (resolved via
23
+ * `getPrototypeWrapper`). LIBRARY routes (workspace, viewfinder, create,
24
+ * index) are NOT wrapped — they own their own provider chain inherited
25
+ * from _app.jsx.
26
+ */
27
+ import { Fragment, Suspense } from 'react'
28
+ import { Outlet, useLocation } from 'react-router-dom'
29
+ import PrototypeErrorBoundary, { ImportErrorFallback } from '@dfosco/storyboard/error-boundary'
30
+ import { getPrototypeWrapper } from '@dfosco/storyboard/wrappers'
31
+ import {
32
+ generatePreservedRoutes,
33
+ generateRegularRoutes,
34
+ generateModalRoutes,
35
+ patterns,
36
+ } from '@generouted/react-router/core'
37
+
38
+ // Patch the route regex to strip src/library/, src/prototypes/, .folder/,
39
+ // and `drafts/` segments. A `drafts/` dir is a gitignored scratch root —
40
+ // prototypes inside route at the same URL as a non-prefixed sibling (so
41
+ // `prototypes/drafts/Foo/` and `prototypes/Foo/` both resolve at `/Foo`).
42
+ patterns.route = [/^.*\/src\/(library|prototypes)\/|^\/(library|prototypes)\/|[^/]*\.folder\/|(?<=^|\/)drafts\/|\.(jsx|tsx|mdx)$/g, '']
43
+
44
+ // `_app.jsx` lives in src/library/ — it's storyboard's SPA shell
45
+ // (StoryboardProvider + suspense + error boundary). Consumers can copy
46
+ // it to src/library/_app.jsx and customize freely. `404` is currently a
47
+ // no-op convention, kept for parity with generouted preserved routes.
48
+ const PRESERVED = import.meta.glob('/src/library/(_app|404).{jsx,tsx}', { eager: true })
49
+ const MODALS = import.meta.glob('/src/prototypes/**/[+]*.{jsx,tsx}', { eager: true })
50
+
51
+ const PrototypeWrapper = getPrototypeWrapper()
52
+
53
+ // Library workspace pages — non-prototype routes (Workspace, Viewfinder,
54
+ // Create, landing index). Bypasses the PrototypeWrapper.
55
+ const LIBRARY_ROUTES = import.meta.glob(
56
+ ['/src/library/*.{jsx,tsx}', '!/src/library/(_app|404|mount|prototypes-entry|routes).{jsx,tsx}'],
57
+ )
58
+
59
+ // Lazy-load prototype routes — only the matched route's module is fetched.
60
+ // This prevents story/canvas URLs from paying for every prototype page's
61
+ // imports (Primer, Reshaped, user components, etc.) on first load.
62
+ const PROTOTYPE_ROUTES = import.meta.glob(
63
+ ['/src/prototypes/**/[\\w[-]*.{jsx,tsx,mdx}', '!/src/prototypes/**/(_!(layout)*(/*)?|_app|404)*'],
64
+ )
65
+
66
+ const preservedRoutes = generatePreservedRoutes(PRESERVED)
67
+ const modalRoutes = generateModalRoutes(MODALS)
68
+
69
+ function makeLazyRoute(importFn, key) {
70
+ const index = /index\.(jsx|tsx|mdx)$/.test(key) && !key.includes('prototypes/index') && !key.includes('library/index')
71
+ ? { index: true }
72
+ : {}
73
+ return {
74
+ ...index,
75
+ lazy: async () => {
76
+ try {
77
+ const module = await importFn()
78
+ const Default = module?.default || Fragment
79
+ const Page = () =>
80
+ module?.Pending ? (
81
+ <Suspense fallback={<module.Pending />}>
82
+ <Default />
83
+ </Suspense>
84
+ ) : (
85
+ <Default />
86
+ )
87
+ return {
88
+ Component: Page,
89
+ ErrorBoundary: module?.Catch || PrototypeErrorBoundary,
90
+ loader: module?.Loader,
91
+ action: module?.Action,
92
+ }
93
+ } catch (err) {
94
+ // Import itself failed (syntax error, broken dependency, etc.)
95
+ return {
96
+ Component: () => <ImportErrorFallback error={err} route={key} />,
97
+ }
98
+ }
99
+ },
100
+ }
101
+ }
102
+
103
+ const libraryRoutes = generateRegularRoutes(LIBRARY_ROUTES, makeLazyRoute)
104
+ const prototypeRoutes = generateRegularRoutes(PROTOTYPE_ROUTES, makeLazyRoute)
105
+
106
+ const _app = preservedRoutes?.['_app']
107
+ const _404 = preservedRoutes?.['404']
108
+ const Default = _app?.default || Outlet
109
+
110
+ // eslint-disable-next-line react-refresh/only-export-components
111
+ const Modals_ = () => {
112
+ const Modal = modalRoutes[useLocation().state?.modal] || Fragment
113
+ return <Modal />
114
+ }
115
+
116
+ // eslint-disable-next-line react-refresh/only-export-components
117
+ const Layout = () => (
118
+ <>
119
+ <Default /> <Modals_ />
120
+ </>
121
+ )
122
+
123
+ // eslint-disable-next-line react-refresh/only-export-components
124
+ const App = () =>
125
+ _app?.Pending ? (
126
+ <Suspense fallback={<_app.Pending />}>
127
+ <Layout />
128
+ </Suspense>
129
+ ) : (
130
+ <Layout />
131
+ )
132
+
133
+ const app = { Component: _app?.default ? App : Layout, ErrorBoundary: _app?.Catch, loader: _app?.Loader }
134
+ const fallback = { path: '*', Component: _404?.default || Fragment }
135
+
136
+ // Wrap every PROTOTYPE route in <PrototypeWrapper> via a layout route.
137
+ // The wrapper resolves to consumer's src/_prototype.jsx — or a Fragment
138
+ // pass-through when absent (truly blank slate).
139
+ //
140
+ // The wrapping <div data-storyboard-prototype> uses `display: contents` so
141
+ // it never affects layout — it only exposes a CSS-selector hook
142
+ // (`[data-storyboard-prototype] selector`) that consumers can use to scope
143
+ // prototype-side styles.
144
+ //
145
+ // eslint-disable-next-line react-refresh/only-export-components
146
+ function PrototypeLayout() {
147
+ return (
148
+ <Suspense fallback={null}>
149
+ <PrototypeWrapper>
150
+ <div data-storyboard-prototype style={{ display: 'contents' }}>
151
+ <Outlet />
152
+ </div>
153
+ </PrototypeWrapper>
154
+ </Suspense>
155
+ )
156
+ }
157
+
158
+ const prototypeLayoutRoute = {
159
+ Component: PrototypeLayout,
160
+ children: prototypeRoutes,
161
+ }
162
+
163
+ export const routes = [{
164
+ ...app,
165
+ children: [...libraryRoutes, prototypeLayoutRoute, fallback],
166
+ }]
167
+
168
+ // Return a routes tree filtered to a single prototype subtree. Used by the
169
+ // per-prototype iframe entry (see prototypes-entry.jsx + PrototypeEmbed
170
+ // dev URL builder). Filtering at this layer keeps a broken sibling
171
+ // prototype from ever appearing in the matched route's lazy() chain — only
172
+ // the requested prototype's modules are reachable from the router.
173
+ //
174
+ // Library routes (workspace, viewfinder, create) are NEVER in the iframe
175
+ // path because the iframe entry uses a different routes module
176
+ // (prototypeRoutes.jsx inside the library). So filtering here only needs
177
+ // to walk the prototype subtree.
178
+ export function getRoutesForProto(proto) {
179
+ if (!proto) return routes
180
+ const matchesProto = (route) => {
181
+ const seg = (route.path || '').split('/').filter(Boolean)[0]
182
+ return seg === proto
183
+ }
184
+ const filter = (children) => children
185
+ .map((r) => {
186
+ if (r.path === '*') return r
187
+ if (!r.path) {
188
+ if (r.children) return { ...r, children: filter(r.children) }
189
+ return r
190
+ }
191
+ if (matchesProto(r)) {
192
+ return r.children ? { ...r, children: filter(r.children) } : r
193
+ }
194
+ return null
195
+ })
196
+ .filter(Boolean)
197
+ return [{
198
+ ...routes[0],
199
+ children: routes[0].children.map((child) => {
200
+ // Only the prototype-layout child has nested children we want to filter.
201
+ if (child === prototypeLayoutRoute) return { ...child, children: filter(child.children || []) }
202
+ return child
203
+ }),
204
+ }]
205
+ }
@@ -0,0 +1,13 @@
1
+ import { useEffect } from 'react'
2
+
3
+ /**
4
+ * Backward-compat redirect: /viewfinder → /workspace
5
+ * Kept for one release cycle so existing bookmarks and links still work.
6
+ */
7
+ export default function ViewfinderRedirect() {
8
+ useEffect(() => {
9
+ const base = (import.meta.env.BASE_URL || '/').replace(/\/+$/, '')
10
+ window.location.replace(`${base}/workspace${window.location.hash}`)
11
+ }, [])
12
+ return null
13
+ }
@@ -0,0 +1,17 @@
1
+ import { flows } from 'virtual:storyboard-data-index'
2
+ import { Workspace } from '@dfosco/storyboard'
3
+
4
+ const pageModules = import.meta.glob('/src/prototypes/*.jsx')
5
+
6
+ export default function WorkspacePage() {
7
+ return (
8
+ <Workspace
9
+ title="Storyboard"
10
+ subtitle="Where design work goes"
11
+ flows={flows}
12
+ hideDefaultFlow={true}
13
+ pageModules={pageModules}
14
+ basePath={import.meta.env.BASE_URL}
15
+ />
16
+ )
17
+ }