@dfosco/storyboard 0.7.1 → 0.9.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.
Files changed (323) hide show
  1. package/dist/storyboard-ui.css +1 -1
  2. package/dist/storyboard-ui.js +14128 -10953
  3. package/dist/storyboard-ui.js.map +1 -1
  4. package/dist/tailwind.css +1 -1
  5. package/dist/tiny-canvas.css +1 -1
  6. package/package.json +138 -128
  7. package/scaffold/AGENTS.md +9 -7
  8. package/scaffold/README.md +18 -0
  9. package/scaffold/deploy.yml +21 -0
  10. package/scaffold/gitignore +8 -14
  11. package/scaffold/preview.yml +40 -0
  12. package/scaffold/{manifest.json → scaffold.config.json} +73 -3
  13. package/scaffold/scripts/aggregate-artifacts.mjs +257 -0
  14. package/scaffold/skills/canvas/SKILL.md +1 -1
  15. package/scaffold/skills/migrate-0.5.0/SKILL.md +1 -1
  16. package/scaffold/skills/ship/SKILL.md +2 -2
  17. package/scaffold/skills/worktree/SKILL.md +1 -1
  18. package/scaffold/src/_prototype.jsx +17 -0
  19. package/scaffold/src/_story.jsx +13 -0
  20. package/scaffold/src/library/README.md +34 -0
  21. package/scaffold/src/library/_app.jsx +41 -0
  22. package/scaffold/src/library/_app.module.css +2 -0
  23. package/scaffold/src/library/create.jsx +10 -0
  24. package/scaffold/src/library/index.jsx +14 -0
  25. package/scaffold/src/library/mount.jsx +51 -0
  26. package/scaffold/src/library/prototypes-entry.jsx +57 -0
  27. package/scaffold/src/library/routes.jsx +205 -0
  28. package/scaffold/src/library/viewfinder.jsx +13 -0
  29. package/scaffold/src/library/workspace.jsx +17 -0
  30. package/scripts/aggregate-artifacts.mjs +257 -0
  31. package/src/canvas/style.css +16 -16
  32. package/src/core/artifact/operations.js +54 -15
  33. package/src/core/artifact/schemas/canvas.schema.json +7 -1
  34. package/src/core/autosync/server.test.js +1 -1
  35. package/src/core/canvas/hot-pool.js +21 -33
  36. package/src/core/canvas/materializer.js +39 -13
  37. package/src/core/canvas/selectedWidgets.js +1 -1
  38. package/src/core/canvas/server.js +376 -100
  39. package/src/core/canvas/server.test.js +472 -2
  40. package/src/core/canvas/terminal-server.js +144 -57
  41. package/src/core/canvas/undoRedo.js +174 -0
  42. package/src/core/canvas/undoRedo.test.js +250 -0
  43. package/src/core/cli/artifact.js +2 -0
  44. package/src/core/cli/buildManifest.js +34 -0
  45. package/src/core/cli/canvasRedo.js +44 -0
  46. package/src/core/cli/canvasUndo.js +40 -0
  47. package/src/core/cli/create.js +151 -23
  48. package/src/core/cli/dev.js +23 -35
  49. package/src/core/cli/index.js +21 -0
  50. package/src/core/cli/schemas.js +8 -4
  51. package/src/core/cli/serverUrl.js +24 -0
  52. package/src/core/cli/setup.js +14 -25
  53. package/src/core/cli/terminal-welcome.js +12 -6
  54. package/src/core/cli/updateVersion.js +42 -2
  55. package/src/core/cli/workleaf.js +140 -0
  56. package/src/core/comments/api.js +1 -1
  57. package/src/core/comments/queries.js +3 -0
  58. package/src/core/comments/ui/CommentWindow.jsx +3 -2
  59. package/src/core/comments/ui/comment-layout.css +14 -14
  60. package/src/core/data/artifactIndex.js +235 -0
  61. package/src/core/data/artifactIndex.test.js +203 -0
  62. package/src/core/data/artifactManifest.js +290 -0
  63. package/src/core/data/artifactManifest.test.js +207 -0
  64. package/src/core/data/viewfinder.js +47 -7
  65. package/src/core/index.js +24 -1
  66. package/src/core/inspector/highlighter.js +1 -1
  67. package/src/core/inspector/mouseMode.js +2 -2
  68. package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +2 -2
  69. package/src/core/lib/components/ui/avatar/avatar-image.jsx +2 -2
  70. package/src/core/lib/components/ui/avatar/avatar.jsx +2 -2
  71. package/src/core/lib/components/ui/checkbox/checkbox.jsx +12 -9
  72. package/src/core/lib/components/ui/dialog/dialog-content.jsx +45 -31
  73. package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +9 -5
  74. package/src/core/lib/components/ui/dialog/dialog.jsx +12 -3
  75. package/src/core/lib/components/ui/dialog/dialog.module.css +118 -0
  76. package/src/core/lib/components/ui/dialog/index.js +4 -34
  77. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +3 -3
  78. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +19 -9
  79. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +8 -8
  80. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +2 -2
  81. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +2 -2
  82. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +15 -4
  83. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +2 -2
  84. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +2 -2
  85. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +14 -7
  86. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +2 -2
  87. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +8 -4
  88. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +4 -4
  89. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +2 -2
  90. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +8 -2
  91. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +2 -2
  92. package/src/core/lib/components/ui/label/label.jsx +1 -2
  93. package/src/core/lib/components/ui/panel/panel-close.jsx +7 -4
  94. package/src/core/lib/components/ui/panel/panel-content.jsx +7 -9
  95. package/src/core/lib/components/ui/panel/panel-title.jsx +3 -3
  96. package/src/core/lib/components/ui/panel/panel.jsx +11 -2
  97. package/src/core/lib/components/ui/separator/separator.jsx +6 -4
  98. package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +7 -6
  99. package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +13 -6
  100. package/src/core/lib/components/ui/tooltip/tooltip.jsx +5 -5
  101. package/src/core/modes/modes.css +4 -4
  102. package/src/core/mountStoryboardCore.js +287 -83
  103. package/src/core/mountStoryboardCore.test.js +198 -0
  104. package/src/core/rename-watcher/config.json +1 -1
  105. package/src/core/rename-watcher/watcher.js +330 -37
  106. package/src/core/rename-watcher/watcher.test.js +253 -0
  107. package/src/core/scaffold/fragments.js +287 -0
  108. package/src/core/scaffold/fragments.test.js +401 -0
  109. package/src/core/scaffold/index.js +291 -0
  110. package/src/core/scaffold/migrateLegacyGitignore.js +157 -0
  111. package/src/core/scaffold/scaffold-integration.test.js +247 -0
  112. package/src/core/scaffold.js +4 -97
  113. package/src/core/stores/canvasInteractionStore.js +120 -0
  114. package/src/core/stores/canvasInteractionStore.test.js +126 -0
  115. package/src/core/stores/configSchema.js +64 -8
  116. package/src/core/stores/configSchema.test.js +3 -0
  117. package/src/core/stores/customerModeConfig.js +24 -3
  118. package/src/core/stores/customerModeConfig.test.js +46 -0
  119. package/src/core/stores/presentationStore.js +118 -0
  120. package/src/core/stores/presentationStore.test.js +114 -0
  121. package/src/core/stores/recentArtifacts.js +49 -2
  122. package/src/core/stores/recentArtifacts.test.js +59 -6
  123. package/src/core/stores/themeBootstrap.js +264 -0
  124. package/src/core/stores/themeBootstrap.test.js +211 -0
  125. package/src/core/stores/themeStore.test.js +154 -0
  126. package/src/core/stores/themeStore.ts +235 -220
  127. package/src/core/stores/uiConfig.js +10 -2
  128. package/src/core/styles/generateThemesCss.js +63 -0
  129. package/src/core/styles/tailwind.css +150 -94
  130. package/src/core/styles/workspace-chrome.css +13 -0
  131. package/src/core/tools/handlers/paletteTheme.js +11 -8
  132. package/src/core/ui/ActionMenuButton.jsx +2 -1
  133. package/src/core/ui/AutosyncMenuButton.css +7 -7
  134. package/src/core/ui/BranchSelect.module.css +5 -5
  135. package/src/core/ui/CanvasCreateMenu.jsx +1 -19
  136. package/src/core/ui/ChromeSlot.jsx +92 -0
  137. package/src/core/ui/ChromeSlot.test.jsx +140 -0
  138. package/src/core/ui/CoreUIBar.css +5 -5
  139. package/src/core/ui/CoreUIBar.jsx +170 -142
  140. package/src/core/ui/InspectorPanel.css +5 -5
  141. package/src/core/ui/InspectorPanel.jsx +9 -9
  142. package/src/core/ui/PwaInstallBanner.css +5 -5
  143. package/src/core/ui/PwaInstallBanner.jsx +5 -5
  144. package/src/core/ui/ThemeMenuButton.jsx +45 -30
  145. package/src/core/ui/core-ui-colors.css +28 -66
  146. package/src/core/ui/sidepanel.css +12 -12
  147. package/src/core/vite/server-plugin.js +13 -0
  148. package/src/core/workleaf/cleanup.js +102 -0
  149. package/src/core/workleaf/create.js +199 -0
  150. package/src/core/workleaf/delete.js +101 -0
  151. package/src/core/workleaf/list.js +57 -0
  152. package/src/core/workleaf/merge.js +238 -0
  153. package/src/core/workleaf/paths.js +146 -0
  154. package/src/core/workleaf/paths.test.js +230 -0
  155. package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +130 -34
  156. package/src/core/workshop/features/createFlow/server.js +5 -1
  157. package/src/core/workshop/features/createPrototype/server.js +5 -1
  158. package/src/core/workshop/features/templateIndex.js +28 -1
  159. package/src/core/worktree/port.js +48 -5
  160. package/src/core/worktree/port.test.js +42 -0
  161. package/src/internals/ArtifactForm/ArtifactForm.jsx +173 -112
  162. package/src/internals/ArtifactForm/ArtifactForm.module.css +51 -69
  163. package/src/internals/ArtifactForm/artifactSchemas.js +40 -5
  164. package/src/internals/AuthModal/AuthModal.module.css +34 -34
  165. package/src/internals/BaseUiForm/BaseUiForm.jsx +293 -0
  166. package/src/internals/BaseUiForm/BaseUiForm.module.css +542 -0
  167. package/src/internals/BranchBar/BranchBar.module.css +4 -4
  168. package/src/internals/BranchesDropdown/BranchesDropdown.jsx +69 -0
  169. package/src/internals/BranchesDropdown/BranchesDropdown.module.css +84 -0
  170. package/src/internals/CommandPalette/CommandPalette.jsx +41 -29
  171. package/src/internals/CommandPalette/CreateArtifactForm.jsx +150 -0
  172. package/src/internals/CommandPalette/CreateDialog.jsx +20 -110
  173. package/src/internals/CommandPalette/WidgetArtifactDialog.css +16 -16
  174. package/src/internals/CommandPalette/WidgetArtifactDialog.jsx +1 -1
  175. package/src/internals/CommandPalette/command-palette.css +14 -14
  176. package/src/internals/CommandPalette/createHelpers.js +30 -0
  177. package/src/internals/CreatePage/CreatePage.jsx +57 -0
  178. package/src/internals/CreatePage/CreatePage.module.css +34 -0
  179. package/src/internals/Dialog.jsx +51 -0
  180. package/src/internals/FlowError.module.css +5 -5
  181. package/src/internals/PrototypeErrorBoundary.module.css +15 -15
  182. package/src/internals/SimpleWorkspace/SimpleWorkspace.jsx +534 -0
  183. package/src/internals/SimpleWorkspace/SimpleWorkspace.module.css +323 -0
  184. package/src/internals/Tooltip.jsx +39 -0
  185. package/src/internals/Viewfinder.jsx +229 -64
  186. package/src/internals/Viewfinder.module.css +253 -182
  187. package/src/internals/canvas/CanvasControls.module.css +11 -11
  188. package/src/internals/canvas/CanvasPage.bridge.test.jsx +11 -37
  189. package/src/internals/canvas/CanvasPage.jsx +358 -217
  190. package/src/internals/canvas/CanvasPage.module.css +20 -19
  191. package/src/internals/canvas/CanvasPage.multiselect.test.jsx +39 -26
  192. package/src/internals/canvas/CanvasToolbar.module.css +11 -11
  193. package/src/internals/canvas/ConnectorLayer.jsx +39 -2
  194. package/src/internals/canvas/ConnectorLayer.module.css +10 -10
  195. package/src/internals/canvas/PageSelector.jsx +15 -1
  196. package/src/internals/canvas/PageSelector.module.css +41 -27
  197. package/src/internals/canvas/canvasApi.js +40 -5
  198. package/src/internals/canvas/canvasTheme.js +22 -111
  199. package/src/internals/canvas/isolation/createIsolationMiddleware.js +68 -0
  200. package/src/internals/canvas/isolation/mountIsolationApp.jsx +88 -0
  201. package/src/internals/canvas/isolation/prototypeRoutes.jsx +140 -0
  202. package/src/internals/canvas/isolation/prototypesEntry.jsx +28 -0
  203. package/src/internals/canvas/isolation/storiesEntry.jsx +20 -0
  204. package/src/internals/canvas/isolation/storyRoutes.jsx +55 -0
  205. package/src/internals/canvas/useUndoRedo.js +75 -60
  206. package/src/internals/canvas/useUndoRedo.test.js +46 -178
  207. package/src/internals/canvas/widgets/CodePenEmbed.jsx +16 -36
  208. package/src/internals/canvas/widgets/CodePenEmbed.module.css +12 -12
  209. package/src/internals/canvas/widgets/CropOverlay.module.css +23 -23
  210. package/src/internals/canvas/widgets/ExpandedPane.module.css +8 -8
  211. package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +1 -1
  212. package/src/internals/canvas/widgets/FigmaEmbed.jsx +15 -36
  213. package/src/internals/canvas/widgets/FigmaEmbed.module.css +15 -15
  214. package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +30 -21
  215. package/src/internals/canvas/widgets/FrozenTerminalOverlay.module.css +5 -5
  216. package/src/internals/canvas/widgets/ImageWidget.jsx +7 -2
  217. package/src/internals/canvas/widgets/ImageWidget.module.css +3 -3
  218. package/src/internals/canvas/widgets/LinkPreview.module.css +79 -80
  219. package/src/internals/canvas/widgets/MarkdownBlock.module.css +31 -31
  220. package/src/internals/canvas/widgets/PromptWidget.jsx +2 -16
  221. package/src/internals/canvas/widgets/PromptWidget.module.css +33 -33
  222. package/src/internals/canvas/widgets/PrototypeEmbed.jsx +92 -17
  223. package/src/internals/canvas/widgets/PrototypeEmbed.module.css +58 -56
  224. package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +118 -4
  225. package/src/internals/canvas/widgets/ResizeHandle.module.css +4 -4
  226. package/src/internals/canvas/widgets/StorySetWidget.jsx +53 -25
  227. package/src/internals/canvas/widgets/StoryWidget.jsx +71 -56
  228. package/src/internals/canvas/widgets/StoryWidget.module.css +22 -20
  229. package/src/internals/canvas/widgets/TerminalReadWidget.jsx +13 -15
  230. package/src/internals/canvas/widgets/TerminalReadWidget.module.css +4 -4
  231. package/src/internals/canvas/widgets/TerminalWidget.jsx +56 -45
  232. package/src/internals/canvas/widgets/TerminalWidget.module.css +10 -10
  233. package/src/internals/canvas/widgets/TilesWidget.module.css +17 -17
  234. package/src/internals/canvas/widgets/WidgetChrome.jsx +1 -1
  235. package/src/internals/canvas/widgets/WidgetChrome.module.css +76 -61
  236. package/src/internals/canvas/widgets/WidgetWrapper.module.css +2 -2
  237. package/src/internals/canvas/widgets/embedOverlay.module.css +14 -4
  238. package/src/internals/canvas/widgets/embedTheme.js +12 -104
  239. package/src/internals/canvas/widgets/expandUtils.js +19 -19
  240. package/src/internals/canvas/widgets/normalizeLegacyEmbedSrc.js +74 -0
  241. package/src/internals/canvas/widgets/widgetConfig.js +5 -5
  242. package/src/internals/context.jsx +22 -1
  243. package/src/internals/context.test.jsx +47 -0
  244. package/src/internals/hooks/useArtifactIndex.js +31 -0
  245. package/src/internals/hooks/useArtifactIndex.test.js +45 -0
  246. package/src/internals/hooks/usePrototypeReloadGuard.js +14 -8
  247. package/src/internals/index.js +6 -0
  248. package/src/internals/loadConsumerWrapper.js +60 -0
  249. package/src/internals/story/ComponentSetPage.jsx +75 -34
  250. package/src/internals/story/ComponentSetPage.module.css +14 -14
  251. package/src/internals/story/StoryPage.jsx +16 -25
  252. package/src/internals/story/StoryPage.module.css +2 -2
  253. package/src/internals/vite/data-plugin.js +317 -149
  254. package/src/internals/vite/data-plugin.test.js +11 -11
  255. package/{scaffold/storyboard.config.json → storyboard.config.json} +25 -0
  256. package/terminal.config.json +1 -1
  257. package/src/core/lib/components/ui/card/card-action.jsx +0 -11
  258. package/src/core/lib/components/ui/card/card-content.jsx +0 -11
  259. package/src/core/lib/components/ui/card/card-description.jsx +0 -11
  260. package/src/core/lib/components/ui/card/card-footer.jsx +0 -11
  261. package/src/core/lib/components/ui/card/card-header.jsx +0 -19
  262. package/src/core/lib/components/ui/card/card-title.jsx +0 -11
  263. package/src/core/lib/components/ui/card/card.jsx +0 -17
  264. package/src/core/lib/components/ui/card/index.js +0 -25
  265. package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +0 -7
  266. package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +0 -7
  267. package/src/core/lib/components/ui/collapsible/collapsible.jsx +0 -7
  268. package/src/core/lib/components/ui/collapsible/index.js +0 -13
  269. package/src/core/lib/components/ui/dialog/dialog-close.jsx +0 -7
  270. package/src/core/lib/components/ui/dialog/dialog-description.jsx +0 -15
  271. package/src/core/lib/components/ui/dialog/dialog-footer.jsx +0 -23
  272. package/src/core/lib/components/ui/dialog/dialog-header.jsx +0 -11
  273. package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +0 -15
  274. package/src/core/lib/components/ui/dialog/dialog-portal.jsx +0 -4
  275. package/src/core/lib/components/ui/dialog/dialog-title.jsx +0 -15
  276. package/src/core/lib/components/ui/popover/index.js +0 -28
  277. package/src/core/lib/components/ui/popover/popover-close.jsx +0 -7
  278. package/src/core/lib/components/ui/popover/popover-content.jsx +0 -22
  279. package/src/core/lib/components/ui/popover/popover-description.jsx +0 -11
  280. package/src/core/lib/components/ui/popover/popover-header.jsx +0 -11
  281. package/src/core/lib/components/ui/popover/popover-portal.jsx +0 -4
  282. package/src/core/lib/components/ui/popover/popover-title.jsx +0 -11
  283. package/src/core/lib/components/ui/popover/popover-trigger.jsx +0 -8
  284. package/src/core/lib/components/ui/popover/popover.jsx +0 -4
  285. package/src/core/lib/components/ui/select/index.js +0 -37
  286. package/src/core/lib/components/ui/select/select-content.jsx +0 -30
  287. package/src/core/lib/components/ui/select/select-group-heading.jsx +0 -17
  288. package/src/core/lib/components/ui/select/select-group.jsx +0 -15
  289. package/src/core/lib/components/ui/select/select-item.jsx +0 -26
  290. package/src/core/lib/components/ui/select/select-label.jsx +0 -11
  291. package/src/core/lib/components/ui/select/select-portal.jsx +0 -4
  292. package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +0 -18
  293. package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +0 -18
  294. package/src/core/lib/components/ui/select/select-separator.jsx +0 -15
  295. package/src/core/lib/components/ui/select/select-trigger.jsx +0 -25
  296. package/src/core/lib/components/ui/select/select.jsx +0 -4
  297. package/src/core/lib/components/ui/sheet/index.js +0 -34
  298. package/src/core/lib/components/ui/sheet/sheet-close.jsx +0 -7
  299. package/src/core/lib/components/ui/sheet/sheet-content.jsx +0 -35
  300. package/src/core/lib/components/ui/sheet/sheet-description.jsx +0 -15
  301. package/src/core/lib/components/ui/sheet/sheet-footer.jsx +0 -11
  302. package/src/core/lib/components/ui/sheet/sheet-header.jsx +0 -11
  303. package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +0 -15
  304. package/src/core/lib/components/ui/sheet/sheet-portal.jsx +0 -4
  305. package/src/core/lib/components/ui/sheet/sheet-title.jsx +0 -15
  306. package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +0 -7
  307. package/src/core/lib/components/ui/sheet/sheet.jsx +0 -4
  308. package/src/core/lib/components/ui/toggle/index.js +0 -8
  309. package/src/core/lib/components/ui/toggle/toggle.jsx +0 -36
  310. package/src/core/lib/components/ui/toggle-group/index.js +0 -10
  311. package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +0 -29
  312. package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +0 -43
  313. package/src/internals/canvas/StoryErrorBoundary.jsx +0 -47
  314. package/src/internals/canvas/WebGLContextPool.jsx +0 -291
  315. package/src/internals/canvas/WebGLContextPool.test.jsx +0 -165
  316. package/src/internals/canvas/componentIsolate.jsx +0 -144
  317. package/src/internals/canvas/componentSetIsolate.jsx +0 -300
  318. package/src/internals/canvas/widgets/ComponentSetWidget.module.css +0 -89
  319. package/src/internals/canvas/widgets/InlineStoryRenderer.jsx +0 -129
  320. package/src/internals/canvas/widgets/InlineStoryRenderer.test.jsx +0 -68
  321. package/src/internals/canvas/widgets/StorySetWidget.module.css +0 -89
  322. package/src/primer/ThemeSync.jsx +0 -73
  323. package/src/primer/index.js +0 -1
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Self-heal canvases that persisted a dev-isolation loader URL as the prototype
3
+ * widget src. Three legacy shapes exist (all caused by older bugs in the embed
4
+ * iframe broadcaster — see commit history of PrototypeEmbed.jsx + mountStoryboardCore.js):
5
+ *
6
+ * 1. `/prototypes.html#/MyProto?...`
7
+ * — 0.6.13 and older, when the iframe broadcast pathname+hash and
8
+ * routing lived in the hash. Inner is a clean path.
9
+ *
10
+ * 2. `/prototypes.html#%2FMyProto=&cqOpen=true&...`
11
+ * — 0.6.13/0.6.14 with storyboard URL state. The iframe's hash was
12
+ * `#/MyProto` (router route), then storyboard's session.writeHash
13
+ * did `URLSearchParams.toString()` on the entire hash, which
14
+ * URL-encoded `/` to `%2F` and turned `/MyProto` into a key with
15
+ * an empty value. This is the most common shape seen in the wild.
16
+ *
17
+ * 3. `/prototypes.html/MyProto...`
18
+ * — defensive: if a future broadcaster ever forwards the loader
19
+ * path verbatim with the path-based router layout (0.6.15+).
20
+ *
21
+ * Without normalization the URL builder in PrototypeEmbed treats
22
+ * "prototypes.html" as the prototype name and produces a blank iframe — and
23
+ * cmd+D duplicates inherit the broken src.
24
+ *
25
+ * Pure, deterministic, safe to call on every render. The PrototypeEmbed
26
+ * component also writes the normalized form back to the canvas via onUpdate
27
+ * so the persisted JSONL stops carrying the broken URL forever.
28
+ *
29
+ * @param {unknown} src
30
+ * @returns {unknown} normalized src, or the original value when not a string
31
+ */
32
+ export function normalizeLegacyEmbedSrc(src) {
33
+ if (typeof src !== 'string' || !src) return src
34
+ // Hash-form (legacy 0.6.13 router-in-hash). Match both clean and
35
+ // URLSearchParams-mangled inner contents.
36
+ const hashMatch = src.match(/^\/?(?:[^#]*\/)?prototypes\.html#(.*)$/)
37
+ if (hashMatch) {
38
+ const inner = hashMatch[1] || ''
39
+ if (!inner) return '/'
40
+ // Clean form: `#/MyProto?...` — inner already starts with `/`.
41
+ if (inner.startsWith('/')) return inner
42
+ // URLSearchParams-mangled form: parse and find the key whose decoded
43
+ // name starts with `/` (that's the route). Surviving params become
44
+ // the storyboard URL state on the recovered hash.
45
+ try {
46
+ const params = new URLSearchParams(inner)
47
+ let route = ''
48
+ const restPairs = []
49
+ for (const [key, value] of params) {
50
+ if (!route && key.startsWith('/')) {
51
+ // Storyboard never persists state with `/`-prefixed keys, and the
52
+ // mangled form always puts the route first — so first match wins.
53
+ // The route had no `=` in the original URL, so value is empty.
54
+ // If a `?query=...` survived alongside, append it back.
55
+ route = value ? `${key}?${key}=${value}` : key
56
+ } else {
57
+ restPairs.push(`${key}=${value}`)
58
+ }
59
+ }
60
+ if (route) {
61
+ return restPairs.length > 0 ? `${route}#${restPairs.join('&')}` : route
62
+ }
63
+ } catch { /* fall through */ }
64
+ // Last-resort: prefix `/` so we at least produce a routable path.
65
+ return `/${inner}`
66
+ }
67
+ // Path-form: `/prototypes.html/inner...` — defensive.
68
+ const pathMatch = src.match(/^\/?(?:[^#?]*\/)?prototypes\.html(\/[^]*)$/)
69
+ if (pathMatch) {
70
+ const inner = pathMatch[1] || ''
71
+ return inner || '/'
72
+ }
73
+ return src
74
+ }
@@ -259,16 +259,16 @@ export function getConnectorDefaults() {
259
259
  const defaults = widgetsConfig.connectorDefaults ?? {}
260
260
  return {
261
261
  controlOffset: defaults.controlOffset ?? 80,
262
- stroke: defaults.stroke ?? 'var(--fgColor-accent, #0969da)',
262
+ stroke: defaults.stroke ?? 'var(--color-foreground-accent, #0969da)',
263
263
  strokeWidth: defaults.strokeWidth ?? 4,
264
- hoverStroke: defaults.hoverStroke ?? 'var(--fgColor-danger, #cf222e)',
264
+ hoverStroke: defaults.hoverStroke ?? 'var(--color-foreground-danger, #cf222e)',
265
265
  hoverStrokeWidth: defaults.hoverStrokeWidth ?? 5,
266
266
  endpointRadius: defaults.endpointRadius ?? 6,
267
- endpointFill: defaults.endpointFill ?? 'var(--fgColor-accent, #0969da)',
268
- endpointStroke: defaults.endpointStroke ?? 'var(--bgColor-default, #ffffff)',
267
+ endpointFill: defaults.endpointFill ?? 'var(--color-foreground-accent, #0969da)',
268
+ endpointStroke: defaults.endpointStroke ?? 'var(--color-background, #ffffff)',
269
269
  endpointStrokeWidth: defaults.endpointStrokeWidth ?? 3,
270
270
  hitAreaStrokeWidth: defaults.hitAreaStrokeWidth ?? 24,
271
- dragStroke: defaults.dragStroke ?? 'var(--fgColor-accent, #0969da)',
271
+ dragStroke: defaults.dragStroke ?? 'var(--color-foreground-accent, #0969da)',
272
272
  dragStrokeWidth: defaults.dragStrokeWidth ?? 2,
273
273
  dragDasharray: defaults.dragDasharray ?? '6 4',
274
274
  dragOpacity: defaults.dragOpacity ?? 0.7,
@@ -70,6 +70,7 @@ function getCanvasGroupMap() {
70
70
  name,
71
71
  route,
72
72
  title: data?.title || name.split('/').pop(),
73
+ isPrivate: !!data?._isPrivate,
73
74
  _canvasMeta: data?._canvasMeta || null,
74
75
  })
75
76
  }
@@ -131,6 +132,14 @@ function matchStoryRoute(pathname) {
131
132
  * Strip the app's sub-path prefix (e.g. /storyboard) from the pathname.
132
133
  * React Router's basename strips the branch prefix but not the app name prefix
133
134
  * when the app runs under a nested base path.
135
+ *
136
+ * Two prefixing scenarios are handled:
137
+ * 1. Branch deploys — BASE_URL is `/branch--xxx/{devDomain}/`, so we strip
138
+ * whatever app sub-path remains after the branch prefix.
139
+ * 2. Local dev via Caddy proxy — BASE_URL is `/` (Caddy strips `/{devDomain}/`
140
+ * before forwarding to the dev server), but window.location.pathname is
141
+ * still `/{devDomain}/...`. React Router's basename `/` doesn't strip it,
142
+ * so we rely on the injected `__SB_DEV_DOMAIN__` global instead.
134
143
  */
135
144
  function stripBasePath(pathname) {
136
145
  let p = pathname.replace(/\/+$/, '') || '/'
@@ -145,6 +154,18 @@ function stripBasePath(pathname) {
145
154
  p = p.slice(subPath.length) || '/'
146
155
  }
147
156
  }
157
+ // Local dev via Caddy proxy: window.location.pathname includes the
158
+ // /{devDomain}/ prefix that Caddy strips before reaching the dev server.
159
+ // BASE_URL is `/` in this scenario, so the block above is a no-op.
160
+ if (typeof window !== 'undefined' && window.__SB_LOCAL_DEV__ && window.__SB_DEV_DOMAIN__) {
161
+ const devDomain = String(window.__SB_DEV_DOMAIN__).replace(/^\/+|\/+$/g, '')
162
+ if (devDomain) {
163
+ const devPrefix = '/' + devDomain
164
+ if (p === devPrefix || p.startsWith(devPrefix + '/')) {
165
+ p = p.slice(devPrefix.length) || '/'
166
+ }
167
+ }
168
+ }
148
169
  return p
149
170
  }
150
171
 
@@ -428,7 +449,7 @@ function StoryboardProviderInner({ flowName, sceneName, recordName, recordParam,
428
449
  const _hmrTick = canvasIndexKey
429
450
  const siblingPages = group
430
451
  ? getCanvasGroupMap().get(group) || []
431
- : [{ name: canvasId, route: canvasData?._route || `/canvas/${canvasId}`, title: canvasData?.title || canvasId.split('/').pop() }]
452
+ : [{ name: canvasId, route: canvasData?._route || `/canvas/${canvasId}`, title: canvasData?.title || canvasId.split('/').pop(), isPrivate: !!canvasData?._isPrivate }]
432
453
  const canvasMeta = canvasData?._canvasMeta || null
433
454
  const canvasValue = {
434
455
  data: null,
@@ -293,4 +293,51 @@ describe('StoryboardProvider', () => {
293
293
  expect(screen.getByText('Canvas not found')).toBeInTheDocument()
294
294
  expect(screen.getByRole('link', { name: /go to index page/i })).toHaveAttribute('href', '/')
295
295
  })
296
+
297
+ it('strips dev-domain prefix in local dev (Caddy proxy) so canvas routes resolve', () => {
298
+ // Local dev via Caddy: window.location.pathname includes the /{devDomain}/
299
+ // prefix even though BASE_URL is `/` (the proxy strips it before forwarding
300
+ // to the dev server). Without stripping, isCanvasPath returns false and the
301
+ // 404 never renders — the canvas page just stays blank.
302
+ const origLocalDev = window.__SB_LOCAL_DEV__
303
+ const origDomain = window.__SB_DEV_DOMAIN__
304
+ window.__SB_LOCAL_DEV__ = true
305
+ window.__SB_DEV_DOMAIN__ = 'storyboard'
306
+ try {
307
+ mockUseLocation.mockReturnValue({ pathname: '/storyboard/canvas/unknown-board', search: '', hash: '' })
308
+
309
+ render(
310
+ <StoryboardProvider>
311
+ <ContextReader />
312
+ </StoryboardProvider>,
313
+ )
314
+
315
+ expect(screen.getByText('Canvas not found')).toBeInTheDocument()
316
+ } finally {
317
+ window.__SB_LOCAL_DEV__ = origLocalDev
318
+ window.__SB_DEV_DOMAIN__ = origDomain
319
+ }
320
+ })
321
+
322
+ it('does not strip dev-domain prefix when __SB_LOCAL_DEV__ is not set', () => {
323
+ // Same URL shape, but production-like: prefix should be left alone so it
324
+ // doesn't accidentally match canvas routes for unrelated path segments.
325
+ const origDomain = window.__SB_DEV_DOMAIN__
326
+ window.__SB_DEV_DOMAIN__ = 'storyboard'
327
+ try {
328
+ mockUseLocation.mockReturnValue({ pathname: '/storyboard/canvas/unknown-board', search: '', hash: '' })
329
+
330
+ render(
331
+ <StoryboardProvider>
332
+ <ContextReader />
333
+ </StoryboardProvider>,
334
+ )
335
+
336
+ // isCanvasPath sees `/storyboard/canvas/...` → does NOT start with `/canvas/`
337
+ // → no 404, just renders flow context
338
+ expect(screen.queryByText('Canvas not found')).not.toBeInTheDocument()
339
+ } finally {
340
+ window.__SB_DEV_DOMAIN__ = origDomain
341
+ }
342
+ })
296
343
  })
@@ -0,0 +1,31 @@
1
+ import { useEffect, useMemo, useState } from 'react'
2
+ import { fetchArtifactIndex } from '../../core/data/artifactIndex.js'
3
+
4
+ function isLocalDev() {
5
+ return typeof window !== 'undefined' && window.__SB_LOCAL_DEV__ === true
6
+ }
7
+
8
+ export function useArtifactIndex(basePath) {
9
+ const normalizedBasePath = useMemo(() => basePath || '/', [basePath])
10
+ const localDev = isLocalDev()
11
+ const [state, setState] = useState(() => ({ artifactIndex: null, loading: !localDev }))
12
+
13
+ useEffect(() => {
14
+ if (localDev) return undefined
15
+
16
+ let cancelled = false
17
+
18
+ fetchArtifactIndex(normalizedBasePath)
19
+ .then(index => {
20
+ if (!cancelled) setState({ artifactIndex: index, loading: false })
21
+ })
22
+ .catch(() => {
23
+ if (!cancelled) setState({ artifactIndex: null, loading: false })
24
+ })
25
+
26
+ return () => { cancelled = true }
27
+ }, [localDev, normalizedBasePath])
28
+
29
+ if (localDev) return { artifactIndex: null, loading: false }
30
+ return state
31
+ }
@@ -0,0 +1,45 @@
1
+ import { renderHook, waitFor } from '@testing-library/react'
2
+ import { useArtifactIndex } from './useArtifactIndex.js'
3
+
4
+ describe('useArtifactIndex', () => {
5
+ afterEach(() => {
6
+ delete window.__SB_LOCAL_DEV__
7
+ vi.unstubAllGlobals()
8
+ })
9
+
10
+ it('short-circuits in local dev without fetching', () => {
11
+ window.__SB_LOCAL_DEV__ = true
12
+ const fetchMock = vi.fn()
13
+ vi.stubGlobal('fetch', fetchMock)
14
+
15
+ const { result } = renderHook(() => useArtifactIndex('/storyboard/'))
16
+
17
+ expect(result.current).toEqual({ artifactIndex: null, loading: false })
18
+ expect(fetchMock).not.toHaveBeenCalled()
19
+ })
20
+
21
+ it('returns the parsed index after fetching', async () => {
22
+ const index = { schemaVersion: 1, branches: [], prototypes: {}, canvases: {} }
23
+ const fetchMock = vi.fn(async () => ({ ok: true, json: async () => index }))
24
+ vi.stubGlobal('fetch', fetchMock)
25
+
26
+ const { result } = renderHook(() => useArtifactIndex('/storyboard/branch--feature-foo/'))
27
+
28
+ await waitFor(() => {
29
+ expect(result.current.artifactIndex).toBe(index)
30
+ expect(result.current.loading).toBe(false)
31
+ })
32
+ expect(fetchMock).toHaveBeenCalledWith('/storyboard/artifacts.index.json')
33
+ })
34
+
35
+ it('returns null on fetch failure without throwing', async () => {
36
+ vi.stubGlobal('fetch', vi.fn(async () => { throw new Error('offline') }))
37
+
38
+ const { result } = renderHook(() => useArtifactIndex('/storyboard/'))
39
+
40
+ await waitFor(() => {
41
+ expect(result.current.loading).toBe(false)
42
+ })
43
+ expect(result.current.artifactIndex).toBeNull()
44
+ })
45
+ })
@@ -1,19 +1,24 @@
1
1
  /**
2
- * Prototype reload guard — suppresses Vite HMR full-reloads for non-canvas pages.
2
+ * Embed reload guard — suppresses Vite HMR full-reloads for iframe-embedded
3
+ * pages (prototypes and stories alike).
3
4
  *
4
5
  * Controlled by the "prototype-auto-reload" feature flag (default: false).
6
+ * The flag name predates story-widget support; it now toggles the guard
7
+ * for both prototypes AND stories, which share the policy: drop
8
+ * `full-reload` payloads so in-page state survives unrelated file edits,
9
+ * but let `update` payloads through so React Fast Refresh works.
5
10
  *
6
11
  * The default is false (guard ON) because the dev server emits frequent
7
12
  * full-reload broadcasts from data-plugin watchers; without the guard,
8
- * prototype pages get stuck in a reload loop. Users can opt in via the
13
+ * embedded pages get stuck in a reload loop. Users can opt in via the
9
14
  * devtools menu.
10
- * When the flag is false (user opted out), sends heartbeat messages to the
11
- * Vite dev server which suppresses full-reload and update payloads for this
12
- * client. Custom storyboard events (canvas file changes, story changes, etc.)
13
- * always pass through.
14
15
  *
15
16
  * Heartbeats are sent every 3s and auto-expire server-side after 5s, so
16
17
  * closed tabs never leave the guard stuck.
18
+ *
19
+ * @param {object} [opts]
20
+ * @param {boolean} [opts.enabled=true] — when false, no heartbeats are sent
21
+ * (used by pages that only want to guard when in embed mode).
17
22
  */
18
23
  import { useEffect } from 'react'
19
24
  import { getFlag, subscribeToStorage } from '../../core/index.js'
@@ -21,8 +26,9 @@ import { getFlag, subscribeToStorage } from '../../core/index.js'
21
26
  const FLAG_KEY = 'prototype-auto-reload'
22
27
  const HEARTBEAT_MS = 3000
23
28
 
24
- export default function usePrototypeReloadGuard() {
29
+ export default function usePrototypeReloadGuard({ enabled = true } = {}) {
25
30
  useEffect(() => {
31
+ if (!enabled) return
26
32
  if (!import.meta.hot) return
27
33
 
28
34
  let interval = null
@@ -64,5 +70,5 @@ export default function usePrototypeReloadGuard() {
64
70
  stop()
65
71
  unsub()
66
72
  }
67
- }, [])
73
+ }, [enabled])
68
74
  }
@@ -41,9 +41,15 @@ export { default as Workspace } from './Workspace.jsx'
41
41
  // Deprecated alias — use Workspace instead
42
42
  export { default as Viewfinder } from './Workspace.jsx'
43
43
 
44
+ // Simplified workspace — dev-only "old index" view, falls back to Workspace in prod
45
+ export { default as SimpleWorkspace } from './SimpleWorkspace/SimpleWorkspace.jsx'
46
+
44
47
  // Command Palette (includes BranchBar automatically)
45
48
  export { default as StoryboardCommandPalette } from './CommandPalette/CommandPalette.jsx'
46
49
 
50
+ // Create-only page (used by `storyboard create --ui`)
51
+ export { default as CreatePage } from './CreatePage/CreatePage.jsx'
52
+
47
53
  // Branch Bar (standalone, for consumers who don't use CommandPalette)
48
54
  export { default as BranchBar } from './BranchBar/BranchBar.jsx'
49
55
 
@@ -0,0 +1,60 @@
1
+ /**
2
+ * loadConsumerWrapper — resolves the consumer's `src/_prototype.jsx` or
3
+ * `src/_story.jsx` at runtime so storyboard internals (prototype routes,
4
+ * story page, inline story renderer, isolate iframes) can wrap rendered
5
+ * content with the consumer's design-system providers.
6
+ *
7
+ * These two files are documented as **the** seam between storyboard
8
+ * scaffolding and consumer source code. See plan
9
+ * `.github/plans/style-separation.md`.
10
+ *
11
+ * Falls back to a Fragment pass-through when the wrapper file is absent —
12
+ * which corresponds to "blank slate", the new default.
13
+ *
14
+ * Why a glob: `import.meta.glob` resolves paths against the *consumer*
15
+ * Vite project root, so the same code path works in source-repo dev and in
16
+ * published-package consumers.
17
+ *
18
+ * Why lazy (no eager): we want to defer wrapper-file evaluation until
19
+ * something actually needs to render. Eager evaluation would pull every
20
+ * wrapper module into the test environment (the wrappers typically import
21
+ * design-system code that doesn't mock cleanly), and would inflate the
22
+ * initial bundle even when no story/prototype is visible.
23
+ */
24
+ import { Fragment, lazy, createElement } from 'react'
25
+
26
+ const PROTOTYPE_MODULES = import.meta.glob(
27
+ ['/src/_prototype.jsx', '/src/_prototype.tsx'],
28
+ )
29
+ const STORY_MODULES = import.meta.glob(
30
+ ['/src/_story.jsx', '/src/_story.tsx'],
31
+ )
32
+
33
+ function PassThrough({ children }) {
34
+ return createElement(Fragment, null, children)
35
+ }
36
+
37
+ function makeLazyWrapper(modules) {
38
+ const keys = Object.keys(modules)
39
+ if (keys.length === 0) return PassThrough
40
+ return lazy(async () => {
41
+ const mod = await modules[keys[0]]()
42
+ if (typeof mod?.default === 'function') return { default: mod.default }
43
+ return { default: PassThrough }
44
+ })
45
+ }
46
+
47
+ let _protoCache
48
+ let _storyCache
49
+
50
+ /** Wrapper for prototype routes (workspace SPA + iframe entries). */
51
+ export function getPrototypeWrapper() {
52
+ if (!_protoCache) _protoCache = makeLazyWrapper(PROTOTYPE_MODULES)
53
+ return _protoCache
54
+ }
55
+
56
+ /** Wrapper for story renders (StoryPage, ComponentSetPage, isolates, inline renderer). */
57
+ export function getStoryWrapper() {
58
+ if (!_storyCache) _storyCache = makeLazyWrapper(STORY_MODULES)
59
+ return _storyCache
60
+ }
@@ -12,12 +12,15 @@
12
12
  * `storyboard:component-set:select` to the parent window so the
13
13
  * canvas widget can track which export the user picked.
14
14
  */
15
- import { useState, useEffect, useLayoutEffect, useMemo, useCallback, useRef } from 'react'
15
+ import { useState, useEffect, useLayoutEffect, useMemo, useCallback, useRef, Suspense } from 'react'
16
16
  import { useLocation, useNavigate } from 'react-router-dom'
17
17
  import { getStoryData } from '../../core/index.js'
18
- import { ThemeProvider, BaseStyles } from '@primer/react'
18
+ import { getStoryWrapper } from '../loadConsumerWrapper.js'
19
+ import usePrototypeReloadGuard from '../hooks/usePrototypeReloadGuard.js'
19
20
  import styles from './ComponentSetPage.module.css'
20
21
 
22
+ const StoryWrapper = getStoryWrapper()
23
+
21
24
  export default function ComponentSetPage({ name }) {
22
25
  const location = useLocation()
23
26
  const navigate = useNavigate()
@@ -28,19 +31,8 @@ export default function ComponentSetPage({ name }) {
28
31
  const selected = searchParams.get('selected') || ''
29
32
  const isEmbed = searchParams.has('_sb_embed')
30
33
 
31
- // Suppress HMR full-reloads when embedded as a canvas iframe
32
- useEffect(() => {
33
- if (!isEmbed || !import.meta.hot) return
34
- const msg = { active: true }
35
- import.meta.hot.send('storyboard:canvas-hmr-guard', msg)
36
- const interval = setInterval(() => {
37
- import.meta.hot.send('storyboard:canvas-hmr-guard', msg)
38
- }, 3000)
39
- return () => {
40
- clearInterval(interval)
41
- import.meta.hot.send('storyboard:canvas-hmr-guard', { active: false })
42
- }
43
- }, [isEmbed])
34
+ // Same reload-guard policy as StoryPage / prototype embeds.
35
+ usePrototypeReloadGuard({ enabled: isEmbed })
44
36
 
45
37
  const story = useMemo(() => getStoryData(name), [name])
46
38
  const [exports, setExports] = useState(null)
@@ -89,28 +81,77 @@ export default function ComponentSetPage({ name }) {
89
81
 
90
82
  const gridRef = useRef(null)
91
83
 
92
- // Post total grid size to parent widget so the canvas can auto-fit.
93
- // No snap-to-max: each cell sizes to its own content.
84
+ // Post size info to parent widget so the canvas can auto-fit on first
85
+ // render AND snap-to-content on resize end.
86
+ //
87
+ // Two messages, mirrored from the legacy componentSetIsolate.jsx:
88
+ // - :initial-size — squarish layout hint computed from the *max* cell
89
+ // dimensions, used by StorySetWidget when the widget has no width/height
90
+ // set yet.
91
+ // - :content-size — live cell-bounding-box extent, used by StorySetWidget
92
+ // to snap width/height down to fit after a user resize. Measured from
93
+ // bounding boxes (not scrollWidth/scrollHeight) so the reported size
94
+ // reflects the *natural* cell extent — scrollWidth always matches the
95
+ // container width when the widget is wider than needed, which would
96
+ // defeat width-snapping.
94
97
  useLayoutEffect(() => {
95
98
  const grid = gridRef.current
96
99
  if (!grid || !exports) return
100
+ if (!isEmbed || window.parent === window) return
101
+
102
+ let postedInitial = false
103
+ function postInitialSize() {
104
+ if (postedInitial) return
105
+ const cells = grid.querySelectorAll(`.${styles.cell}`)
106
+ if (cells.length === 0) return
107
+ let maxW = 0
108
+ let maxH = 0
109
+ cells.forEach((cell) => {
110
+ const r = cell.getBoundingClientRect()
111
+ if (r.width > maxW) maxW = r.width
112
+ if (r.height > maxH) maxH = r.height
113
+ })
114
+ if (maxW < 10 || maxH < 10) return
115
+ const count = cells.length
116
+ const cols = Math.max(1, Math.min(count, Math.ceil(Math.sqrt(count))))
117
+ const rows = Math.ceil(count / cols)
118
+ const gap = 16
119
+ const pad = 32 // 16px padding on both sides
120
+ const width = Math.ceil(maxW * cols + gap * (cols - 1) + pad)
121
+ const height = Math.ceil(maxH * rows + gap * (rows - 1) + pad)
122
+ postedInitial = true
123
+ window.parent.postMessage({
124
+ type: 'storyboard:component-set:initial-size',
125
+ width,
126
+ height,
127
+ }, '*')
128
+ }
97
129
 
98
- function measure() {
99
- if (isEmbed && window.parent !== window) {
100
- requestAnimationFrame(() => {
101
- window.parent.postMessage({
102
- type: 'storyboard:component-set:resize',
103
- width: grid.scrollWidth,
104
- height: grid.scrollHeight,
105
- }, '*')
106
- })
107
- }
130
+ function postContentSize() {
131
+ const cells = grid.querySelectorAll(`.${styles.cell}`)
132
+ if (!cells.length) return
133
+ const gridRect = grid.getBoundingClientRect()
134
+ let maxRight = 0
135
+ let maxBottom = 0
136
+ cells.forEach((cell) => {
137
+ const r = cell.getBoundingClientRect()
138
+ const right = r.right - gridRect.left
139
+ const bottom = r.bottom - gridRect.top
140
+ if (right > maxRight) maxRight = right
141
+ if (bottom > maxBottom) maxBottom = bottom
142
+ })
143
+ const pad = 16 // mirror grid padding
144
+ window.parent.postMessage({
145
+ type: 'storyboard:component-set:content-size',
146
+ width: Math.ceil(maxRight + pad),
147
+ height: Math.ceil(maxBottom + pad),
148
+ }, '*')
108
149
  }
109
150
 
110
- measure()
111
- document.fonts.ready.then(() => requestAnimationFrame(measure))
151
+ requestAnimationFrame(() => requestAnimationFrame(postInitialSize))
152
+ document.fonts.ready.then(() => requestAnimationFrame(postInitialSize))
112
153
 
113
- const ro = new ResizeObserver(measure)
154
+ const ro = new ResizeObserver(postContentSize)
114
155
  ro.observe(grid)
115
156
  return () => ro.disconnect()
116
157
  }, [exports, layout, density, isEmbed])
@@ -151,8 +192,8 @@ export default function ComponentSetPage({ name }) {
151
192
  const exportNames = Object.keys(exports)
152
193
 
153
194
  return (
154
- <ThemeProvider colorMode="day">
155
- <BaseStyles>
195
+ <Suspense fallback={null}>
196
+ <StoryWrapper>
156
197
  <div
157
198
  ref={gridRef}
158
199
  className={styles.grid}
@@ -188,7 +229,7 @@ export default function ComponentSetPage({ name }) {
188
229
  )
189
230
  })}
190
231
  </div>
191
- </BaseStyles>
192
- </ThemeProvider>
232
+ </StoryWrapper>
233
+ </Suspense>
193
234
  )
194
235
  }
@@ -8,12 +8,12 @@
8
8
  * widget never reveals the underlying white page. */
9
9
  :global(html),
10
10
  :global(body) {
11
- background: var(--bgColor-muted, #f6f8fa);
11
+ background: var(--color-background-muted, #f6f8fa);
12
12
  min-height: 100%;
13
13
  }
14
14
 
15
15
  .grid {
16
- background: var(--bgColor-muted, #f6f8fa);
16
+ background: var(--color-background-muted, #f6f8fa);
17
17
  display: grid;
18
18
  gap: 16px;
19
19
  padding: 16px;
@@ -57,8 +57,8 @@
57
57
  }
58
58
 
59
59
  .cell {
60
- background: var(--bgColor-default, #ffffff);
61
- border: 1px solid var(--borderColor-default, #d0d7de);
60
+ background: var(--color-background, #ffffff);
61
+ border: 1px solid var(--color-border, #d0d7de);
62
62
  border-radius: 8px;
63
63
  padding: 16px;
64
64
  display: flex;
@@ -70,8 +70,8 @@
70
70
  }
71
71
 
72
72
  .cell[data-selected] {
73
- border-color: var(--fgColor-accent, #0969da);
74
- box-shadow: 0 0 0 1px var(--fgColor-accent, #0969da);
73
+ border-color: var(--color-foreground-accent, #0969da);
74
+ box-shadow: 0 0 0 1px var(--color-foreground-accent, #0969da);
75
75
  }
76
76
 
77
77
  .cellLabel {
@@ -82,7 +82,7 @@
82
82
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
83
83
  font-size: 12px;
84
84
  font-weight: 600;
85
- color: var(--fgColor-muted, #59636e);
85
+ color: var(--color-foreground-muted, #59636e);
86
86
  cursor: pointer;
87
87
  user-select: none;
88
88
  transition: color 100ms ease;
@@ -90,11 +90,11 @@
90
90
  }
91
91
 
92
92
  .cellLabel:hover {
93
- color: var(--fgColor-default, #1f2328);
93
+ color: var(--color-foreground, #1f2328);
94
94
  }
95
95
 
96
96
  .cellLabel[data-selected] {
97
- color: var(--fgColor-accent, #0969da);
97
+ color: var(--color-foreground-accent, #0969da);
98
98
  }
99
99
 
100
100
  .cellRadio {
@@ -104,14 +104,14 @@
104
104
  width: 12px;
105
105
  height: 12px;
106
106
  border-radius: 50%;
107
- border: 2px solid var(--borderColor-default, #d0d7de);
107
+ border: 2px solid var(--color-border, #d0d7de);
108
108
  flex-shrink: 0;
109
109
  transition: border-color 100ms ease, background 100ms ease;
110
110
  }
111
111
 
112
112
  .cellRadio[data-selected] {
113
- border-color: var(--fgColor-accent, #0969da);
114
- background: var(--fgColor-accent, #0969da);
113
+ border-color: var(--color-foreground-accent, #0969da);
114
+ background: var(--color-foreground-accent, #0969da);
115
115
  }
116
116
 
117
117
  .cellContent {
@@ -127,7 +127,7 @@
127
127
  flex-direction: column;
128
128
  gap: 4px;
129
129
  padding: 1rem;
130
- color: var(--fgColor-danger, #cf222e);
130
+ color: var(--color-foreground-danger, #cf222e);
131
131
  font-size: 0.875rem;
132
132
  line-height: 1.5;
133
133
  }
@@ -137,6 +137,6 @@
137
137
  align-items: center;
138
138
  justify-content: center;
139
139
  padding: 3rem;
140
- color: var(--fgColor-muted, #656d76);
140
+ color: var(--color-foreground-muted, #656d76);
141
141
  font-size: 0.875rem;
142
142
  }