@dfosco/storyboard-core 4.2.0-beta.3 → 4.2.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 (419) hide show
  1. package/commandpalette.config.json +109 -24
  2. package/dist/storyboard-ui.css +1 -1
  3. package/dist/storyboard-ui.js +16711 -27239
  4. package/dist/storyboard-ui.js.map +1 -1
  5. package/dist/tailwind.css +1 -1
  6. package/package.json +5 -2
  7. package/scaffold/agents/prompt-agent.agent.md +181 -0
  8. package/scaffold/agents/terminal-agent.agent.md +351 -0
  9. package/scaffold/codex/config.toml +246 -0
  10. package/scaffold/manifest.json +5 -10
  11. package/scaffold/skills/canvas/SKILL.md +5 -4
  12. package/scaffold/skills/ship/SKILL.md +1 -1
  13. package/scaffold/storyboard.config.json +11 -1
  14. package/src/ActionMenuButton.jsx +103 -0
  15. package/src/AutosyncMenuButton.css +67 -0
  16. package/src/AutosyncMenuButton.jsx +242 -0
  17. package/src/BranchSelect.jsx +29 -0
  18. package/src/BranchSelect.module.css +30 -0
  19. package/src/CanvasAgentsMenu.jsx +89 -0
  20. package/src/CanvasCreateMenu.jsx +611 -0
  21. package/src/CanvasSnap.css +27 -0
  22. package/src/CanvasSnap.jsx +51 -0
  23. package/src/CanvasUndoRedo.css +36 -0
  24. package/src/CanvasUndoRedo.jsx +62 -0
  25. package/src/CanvasZoomControl.css +53 -0
  26. package/src/CanvasZoomControl.jsx +49 -0
  27. package/src/CanvasZoomToFit.css +18 -0
  28. package/src/CanvasZoomToFit.jsx +26 -0
  29. package/src/CommandMenu.css +8 -0
  30. package/src/CommandMenu.jsx +287 -0
  31. package/src/CommandPalette.jsx +35 -0
  32. package/src/CommandPaletteTrigger.jsx +25 -0
  33. package/src/CommentsMenuButton.jsx +40 -0
  34. package/src/CoreUIBar.css +47 -0
  35. package/src/CoreUIBar.jsx +858 -0
  36. package/src/CreateMenuButton.jsx +117 -0
  37. package/src/HideChromeTrigger.jsx +40 -0
  38. package/src/InspectorPanel.css +109 -0
  39. package/src/InspectorPanel.jsx +632 -0
  40. package/src/PwaInstallBanner.css +42 -0
  41. package/src/PwaInstallBanner.jsx +124 -0
  42. package/src/SidePanel.jsx +261 -0
  43. package/src/ThemeMenuButton.jsx +139 -0
  44. package/src/autosync/server.js +202 -5
  45. package/src/autosync/server.test.js +111 -0
  46. package/src/canvas/__tests__/agent-integration.test.js +596 -0
  47. package/src/canvas/__tests__/helpers/browser.js +95 -0
  48. package/src/canvas/__tests__/helpers/canvas-api.js +129 -0
  49. package/src/canvas/__tests__/helpers/perf.js +118 -0
  50. package/src/canvas/__tests__/helpers/setup.js +176 -0
  51. package/src/canvas/__tests__/helpers/tmux.js +130 -0
  52. package/src/canvas/__tests__/helpers/transcript.js +132 -0
  53. package/src/canvas/__tests__/terminal-integration.test.js +177 -0
  54. package/src/canvas/hot-pool.js +756 -0
  55. package/src/canvas/materializer.js +31 -0
  56. package/src/canvas/materializer.test.js +56 -0
  57. package/src/canvas/selectedWidgets.js +65 -7
  58. package/src/canvas/server.js +1802 -22
  59. package/src/canvas/server.test.js +239 -0
  60. package/src/canvas/terminal-config.js +330 -0
  61. package/src/canvas/terminal-registry.js +41 -3
  62. package/src/canvas/terminal-server.js +1098 -37
  63. package/src/canvas/writeGuard.js +51 -3
  64. package/src/canvasConfig.js +67 -1
  65. package/src/canvasConfig.test.js +79 -1
  66. package/src/cli/agent.js +85 -0
  67. package/src/cli/branch.js +232 -0
  68. package/src/cli/canvasAdd.js +61 -14
  69. package/src/cli/canvasBatch.js +98 -0
  70. package/src/cli/canvasBounds.js +1 -1
  71. package/src/cli/canvasRead.js +1 -1
  72. package/src/cli/canvasUpdate.js +179 -0
  73. package/src/cli/compact.js +0 -2
  74. package/src/cli/create.js +40 -16
  75. package/src/cli/dev.js +158 -84
  76. package/src/cli/exit.js +23 -24
  77. package/src/cli/index.js +55 -2
  78. package/src/cli/proxy.js +96 -37
  79. package/src/cli/schemas.js +22 -4
  80. package/src/cli/server.js +149 -26
  81. package/src/cli/serverUrl.js +8 -3
  82. package/src/cli/sessions.js +133 -7
  83. package/src/cli/setup.js +138 -12
  84. package/src/cli/terminal-commands.js +20 -9
  85. package/src/cli/terminal-messaging.js +231 -0
  86. package/src/cli/terminal-welcome.js +449 -34
  87. package/src/cli/updateVersion.js +1 -1
  88. package/src/commandActions.js +1 -0
  89. package/src/commandPaletteConfig.js +9 -0
  90. package/src/comments/auth.js +2 -1
  91. package/src/comments/ui/AuthModal.jsx +114 -0
  92. package/src/comments/ui/CommentWindow.jsx +329 -0
  93. package/src/comments/ui/CommentsDrawer.jsx +102 -0
  94. package/src/comments/ui/Composer.jsx +64 -0
  95. package/src/comments/ui/authModal.test.js +1 -1
  96. package/src/comments/ui/commentWindow.js +16 -17
  97. package/src/comments/ui/commentsDrawer.js +25 -26
  98. package/src/comments/ui/composer.js +23 -24
  99. package/src/comments/ui/index.js +2 -3
  100. package/src/configSchema.js +59 -1
  101. package/src/configStore.js +161 -0
  102. package/src/core-ui-colors.css +12 -0
  103. package/src/devtools.js +17 -19
  104. package/src/devtools.test.js +18 -9
  105. package/src/featureFlags.js +12 -5
  106. package/src/fuzzySearch.test.js +10 -0
  107. package/src/index.js +15 -3
  108. package/src/lib/components/ui/alert/alert-action.jsx +11 -0
  109. package/src/lib/components/ui/alert/alert-description.jsx +11 -0
  110. package/src/lib/components/ui/alert/alert-title.jsx +11 -0
  111. package/src/lib/components/ui/alert/alert.jsx +25 -0
  112. package/src/lib/components/ui/alert/index.js +15 -15
  113. package/src/lib/components/ui/avatar/avatar-badge.jsx +22 -0
  114. package/src/lib/components/ui/avatar/avatar-fallback.jsx +18 -0
  115. package/src/lib/components/ui/avatar/avatar-group-count.jsx +19 -0
  116. package/src/lib/components/ui/avatar/avatar-group.jsx +19 -0
  117. package/src/lib/components/ui/avatar/avatar-image.jsx +15 -0
  118. package/src/lib/components/ui/avatar/avatar.jsx +19 -0
  119. package/src/lib/components/ui/avatar/index.js +20 -20
  120. package/src/lib/components/ui/badge/badge.jsx +31 -0
  121. package/src/lib/components/ui/badge/index.js +2 -2
  122. package/src/lib/components/ui/button/button.jsx +100 -0
  123. package/src/lib/components/ui/button/index.js +9 -9
  124. package/src/lib/components/ui/card/card-action.jsx +11 -0
  125. package/src/lib/components/ui/card/card-content.jsx +11 -0
  126. package/src/lib/components/ui/card/card-description.jsx +11 -0
  127. package/src/lib/components/ui/card/card-footer.jsx +11 -0
  128. package/src/lib/components/ui/card/card-header.jsx +19 -0
  129. package/src/lib/components/ui/card/card-title.jsx +11 -0
  130. package/src/lib/components/ui/card/card.jsx +17 -0
  131. package/src/lib/components/ui/card/index.js +23 -23
  132. package/src/lib/components/ui/checkbox/checkbox.jsx +29 -0
  133. package/src/lib/components/ui/checkbox/index.js +5 -5
  134. package/src/lib/components/ui/collapsible/collapsible-content.jsx +7 -0
  135. package/src/lib/components/ui/collapsible/collapsible-trigger.jsx +7 -0
  136. package/src/lib/components/ui/collapsible/collapsible.jsx +7 -0
  137. package/src/lib/components/ui/collapsible/index.js +11 -11
  138. package/src/lib/components/ui/dialog/dialog-close.jsx +7 -0
  139. package/src/lib/components/ui/dialog/dialog-content.jsx +34 -0
  140. package/src/lib/components/ui/dialog/dialog-description.jsx +15 -0
  141. package/src/lib/components/ui/dialog/dialog-footer.jsx +23 -0
  142. package/src/lib/components/ui/dialog/dialog-header.jsx +11 -0
  143. package/src/lib/components/ui/dialog/dialog-overlay.jsx +15 -0
  144. package/src/lib/components/ui/dialog/dialog-portal.jsx +4 -0
  145. package/src/lib/components/ui/dialog/dialog-title.jsx +15 -0
  146. package/src/lib/components/ui/dialog/dialog-trigger.jsx +7 -0
  147. package/src/lib/components/ui/dialog/dialog.jsx +4 -0
  148. package/src/lib/components/ui/dialog/index.js +32 -32
  149. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +8 -0
  150. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +30 -0
  151. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +22 -0
  152. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +16 -0
  153. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +7 -0
  154. package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +20 -0
  155. package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.jsx +17 -0
  156. package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +4 -0
  157. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +7 -0
  158. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +29 -0
  159. package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +15 -0
  160. package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.jsx +16 -0
  161. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +15 -0
  162. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +23 -0
  163. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +4 -0
  164. package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +7 -0
  165. package/src/lib/components/ui/dropdown-menu/dropdown-menu.jsx +4 -0
  166. package/src/lib/components/ui/dropdown-menu/index.js +52 -52
  167. package/src/lib/components/ui/input/index.js +5 -5
  168. package/src/lib/components/ui/input/input.jsx +19 -0
  169. package/src/lib/components/ui/label/index.js +5 -5
  170. package/src/lib/components/ui/label/label.jsx +19 -0
  171. package/src/lib/components/ui/panel/index.js +21 -21
  172. package/src/lib/components/ui/panel/panel-body.jsx +11 -0
  173. package/src/lib/components/ui/panel/panel-close.jsx +16 -0
  174. package/src/lib/components/ui/panel/panel-content.jsx +29 -0
  175. package/src/lib/components/ui/panel/panel-footer.jsx +11 -0
  176. package/src/lib/components/ui/panel/panel-header.jsx +11 -0
  177. package/src/lib/components/ui/panel/panel-title.jsx +12 -0
  178. package/src/lib/components/ui/panel/panel.jsx +4 -0
  179. package/src/lib/components/ui/popover/index.js +26 -26
  180. package/src/lib/components/ui/popover/popover-close.jsx +7 -0
  181. package/src/lib/components/ui/popover/popover-content.jsx +22 -0
  182. package/src/lib/components/ui/popover/popover-description.jsx +11 -0
  183. package/src/lib/components/ui/popover/popover-header.jsx +11 -0
  184. package/src/lib/components/ui/popover/popover-portal.jsx +4 -0
  185. package/src/lib/components/ui/popover/popover-title.jsx +11 -0
  186. package/src/lib/components/ui/popover/popover-trigger.jsx +8 -0
  187. package/src/lib/components/ui/popover/popover.jsx +4 -0
  188. package/src/lib/components/ui/searchable-list.jsx +160 -0
  189. package/src/lib/components/ui/select/index.js +35 -35
  190. package/src/lib/components/ui/select/select-content.jsx +30 -0
  191. package/src/lib/components/ui/select/select-group-heading.jsx +17 -0
  192. package/src/lib/components/ui/select/select-group.jsx +15 -0
  193. package/src/lib/components/ui/select/select-item.jsx +26 -0
  194. package/src/lib/components/ui/select/select-label.jsx +11 -0
  195. package/src/lib/components/ui/select/select-portal.jsx +4 -0
  196. package/src/lib/components/ui/select/select-scroll-down-button.jsx +18 -0
  197. package/src/lib/components/ui/select/select-scroll-up-button.jsx +18 -0
  198. package/src/lib/components/ui/select/select-separator.jsx +15 -0
  199. package/src/lib/components/ui/select/select-trigger.jsx +25 -0
  200. package/src/lib/components/ui/select/select.jsx +4 -0
  201. package/src/lib/components/ui/separator/index.js +5 -5
  202. package/src/lib/components/ui/separator/separator.jsx +22 -0
  203. package/src/lib/components/ui/sheet/index.js +32 -32
  204. package/src/lib/components/ui/sheet/sheet-close.jsx +7 -0
  205. package/src/lib/components/ui/sheet/sheet-content.jsx +35 -0
  206. package/src/lib/components/ui/sheet/sheet-description.jsx +15 -0
  207. package/src/lib/components/ui/sheet/sheet-footer.jsx +11 -0
  208. package/src/lib/components/ui/sheet/sheet-header.jsx +11 -0
  209. package/src/lib/components/ui/sheet/sheet-overlay.jsx +15 -0
  210. package/src/lib/components/ui/sheet/sheet-portal.jsx +4 -0
  211. package/src/lib/components/ui/sheet/sheet-title.jsx +15 -0
  212. package/src/lib/components/ui/sheet/sheet-trigger.jsx +7 -0
  213. package/src/lib/components/ui/sheet/sheet.jsx +4 -0
  214. package/src/lib/components/ui/textarea/index.js +5 -5
  215. package/src/lib/components/ui/textarea/textarea.jsx +18 -0
  216. package/src/lib/components/ui/toggle/index.js +6 -9
  217. package/src/lib/components/ui/toggle/toggle.jsx +36 -0
  218. package/src/lib/components/ui/toggle-group/index.js +8 -8
  219. package/src/lib/components/ui/toggle-group/toggle-group-item.jsx +29 -0
  220. package/src/lib/components/ui/toggle-group/toggle-group.jsx +43 -0
  221. package/src/lib/components/ui/tooltip/index.js +3 -3
  222. package/src/lib/components/ui/tooltip/tooltip-content.jsx +21 -0
  223. package/src/lib/components/ui/tooltip/tooltip-trigger.jsx +23 -0
  224. package/src/lib/components/ui/tooltip/tooltip.jsx +11 -0
  225. package/src/lib/components/ui/trigger-button/index.js +3 -3
  226. package/src/lib/components/ui/trigger-button/trigger-button.css +38 -0
  227. package/src/lib/components/ui/trigger-button/trigger-button.jsx +63 -0
  228. package/src/logger/devLogger.js +238 -0
  229. package/src/logger/devLogger.test.js +193 -0
  230. package/src/modes.test.js +4 -4
  231. package/src/mountStoryboardCore.js +133 -35
  232. package/src/paletteProviders.js +3 -0
  233. package/src/paletteProviders.test.js +2 -2
  234. package/src/server/index.js +104 -40
  235. package/src/sidepanel.css +214 -0
  236. package/src/smoothCorners.js +1 -1
  237. package/src/styles/tailwind.css +1 -1
  238. package/src/svelte-plugin-ui/__tests__/ModeSwitch.test.ts +8 -8
  239. package/src/svelte-plugin-ui/__tests__/ToolbarShell.test.ts +11 -10
  240. package/src/svelte-plugin-ui/components/Icon.css +11 -0
  241. package/src/svelte-plugin-ui/components/Icon.jsx +281 -0
  242. package/src/svelte-plugin-ui/components/ModeSwitch.css +90 -0
  243. package/src/svelte-plugin-ui/components/ModeSwitch.jsx +47 -0
  244. package/src/svelte-plugin-ui/components/ToolbarShell.css +80 -0
  245. package/src/svelte-plugin-ui/components/ToolbarShell.jsx +84 -0
  246. package/src/svelte-plugin-ui/components/Viewfinder.css +412 -0
  247. package/src/svelte-plugin-ui/components/Viewfinder.jsx +513 -0
  248. package/src/svelte-plugin-ui/mount.ts +12 -16
  249. package/src/toolRegistry.js +4 -4
  250. package/src/toolbarConfigStore.js +30 -0
  251. package/src/tools/handlers/autosync.js +1 -1
  252. package/src/tools/handlers/canvasAddWidget.js +1 -1
  253. package/src/tools/handlers/canvasAgents.js +20 -0
  254. package/src/tools/handlers/canvasToolbar.js +8 -8
  255. package/src/tools/handlers/commandPalette.js +9 -0
  256. package/src/tools/handlers/comments.js +2 -2
  257. package/src/tools/handlers/create.js +1 -1
  258. package/src/tools/handlers/devtools.js +19 -3
  259. package/src/tools/handlers/devtools.test.js +38 -0
  260. package/src/tools/handlers/featureFlags.js +1 -1
  261. package/src/tools/handlers/flows.js +3 -3
  262. package/src/tools/handlers/hideChrome.js +9 -0
  263. package/src/tools/handlers/paletteTheme.js +35 -0
  264. package/src/tools/handlers/theme.js +1 -1
  265. package/src/tools/registry.js +4 -1
  266. package/src/tools/surfaces/commandList.js +3 -3
  267. package/src/tools/surfaces/mainToolbar.js +3 -3
  268. package/src/tools/surfaces/registry.js +4 -4
  269. package/src/ui/design-modes.ts +2 -2
  270. package/src/ui/viewfinder.ts +1 -1
  271. package/src/vite/server-plugin.js +243 -61
  272. package/src/workshop/features/createCanvas/CreateCanvasForm.jsx +260 -0
  273. package/src/workshop/features/createCanvas/index.js +1 -1
  274. package/src/workshop/features/createFlow/CreateFlowForm.jsx +334 -0
  275. package/src/workshop/features/createFlow/index.js +1 -1
  276. package/src/workshop/features/createPage/CreatePageForm.jsx +304 -0
  277. package/src/workshop/features/createPage/index.js +1 -1
  278. package/src/workshop/features/createPrototype/CreatePrototypeForm.jsx +289 -0
  279. package/src/workshop/features/createPrototype/index.js +1 -1
  280. package/src/workshop/features/createPrototype/server.js +98 -0
  281. package/src/workshop/features/createStory/CreateStoryForm.jsx +208 -0
  282. package/src/workshop/features/createStory/index.js +1 -1
  283. package/src/workshop/ui/WorkshopPanel.jsx +98 -0
  284. package/src/workshop/ui/mount.ts +1 -1
  285. package/src/worktree/port.js +48 -0
  286. package/src/worktree/serverRegistry.js +120 -0
  287. package/toolbar.config.json +93 -42
  288. package/widgets.config.json +580 -12
  289. package/scaffold/commandpalette.config.json +0 -4
  290. package/scaffold/toolbar.config.json +0 -4
  291. package/src/ActionMenuButton.svelte +0 -119
  292. package/src/AutosyncMenuButton.svelte +0 -397
  293. package/src/CanvasCreateMenu.svelte +0 -295
  294. package/src/CanvasSnap.svelte +0 -87
  295. package/src/CanvasUndoRedo.svelte +0 -108
  296. package/src/CanvasZoomControl.svelte +0 -111
  297. package/src/CanvasZoomToFit.svelte +0 -52
  298. package/src/CommandMenu.svelte +0 -249
  299. package/src/CommandPalette.svelte +0 -33
  300. package/src/CommentsMenuButton.svelte +0 -53
  301. package/src/CoreUIBar.svelte +0 -847
  302. package/src/CreateMenuButton.svelte +0 -133
  303. package/src/DocPanel.svelte +0 -299
  304. package/src/InspectorPanel.svelte +0 -745
  305. package/src/PwaInstallBanner.svelte +0 -124
  306. package/src/SidePanel.svelte +0 -480
  307. package/src/ThemeMenuButton.svelte +0 -132
  308. package/src/comments/ui/AuthModal.svelte +0 -108
  309. package/src/comments/ui/CommentWindow.svelte +0 -333
  310. package/src/comments/ui/CommentsDrawer.svelte +0 -96
  311. package/src/comments/ui/Composer.svelte +0 -65
  312. package/src/lib/components/ui/alert/alert-action.svelte +0 -19
  313. package/src/lib/components/ui/alert/alert-description.svelte +0 -22
  314. package/src/lib/components/ui/alert/alert-title.svelte +0 -22
  315. package/src/lib/components/ui/alert/alert.svelte +0 -38
  316. package/src/lib/components/ui/avatar/avatar-badge.svelte +0 -25
  317. package/src/lib/components/ui/avatar/avatar-fallback.svelte +0 -20
  318. package/src/lib/components/ui/avatar/avatar-group-count.svelte +0 -22
  319. package/src/lib/components/ui/avatar/avatar-group.svelte +0 -22
  320. package/src/lib/components/ui/avatar/avatar-image.svelte +0 -17
  321. package/src/lib/components/ui/avatar/avatar.svelte +0 -24
  322. package/src/lib/components/ui/badge/badge.svelte +0 -44
  323. package/src/lib/components/ui/button/button.svelte +0 -108
  324. package/src/lib/components/ui/card/card-action.svelte +0 -21
  325. package/src/lib/components/ui/card/card-content.svelte +0 -19
  326. package/src/lib/components/ui/card/card-description.svelte +0 -19
  327. package/src/lib/components/ui/card/card-footer.svelte +0 -18
  328. package/src/lib/components/ui/card/card-header.svelte +0 -21
  329. package/src/lib/components/ui/card/card-title.svelte +0 -14
  330. package/src/lib/components/ui/card/card.svelte +0 -21
  331. package/src/lib/components/ui/checkbox/checkbox.svelte +0 -39
  332. package/src/lib/components/ui/collapsible/collapsible-content.svelte +0 -7
  333. package/src/lib/components/ui/collapsible/collapsible-trigger.svelte +0 -7
  334. package/src/lib/components/ui/collapsible/collapsible.svelte +0 -11
  335. package/src/lib/components/ui/dialog/dialog-close.svelte +0 -11
  336. package/src/lib/components/ui/dialog/dialog-content.svelte +0 -42
  337. package/src/lib/components/ui/dialog/dialog-description.svelte +0 -17
  338. package/src/lib/components/ui/dialog/dialog-footer.svelte +0 -29
  339. package/src/lib/components/ui/dialog/dialog-header.svelte +0 -19
  340. package/src/lib/components/ui/dialog/dialog-overlay.svelte +0 -17
  341. package/src/lib/components/ui/dialog/dialog-portal.svelte +0 -7
  342. package/src/lib/components/ui/dialog/dialog-title.svelte +0 -17
  343. package/src/lib/components/ui/dialog/dialog-trigger.svelte +0 -11
  344. package/src/lib/components/ui/dialog/dialog.svelte +0 -7
  345. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +0 -16
  346. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -40
  347. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +0 -27
  348. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +0 -18
  349. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +0 -7
  350. package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +0 -24
  351. package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +0 -20
  352. package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +0 -7
  353. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +0 -16
  354. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +0 -34
  355. package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +0 -17
  356. package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +0 -19
  357. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +0 -17
  358. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +0 -27
  359. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +0 -7
  360. package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +0 -7
  361. package/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +0 -7
  362. package/src/lib/components/ui/input/input.svelte +0 -40
  363. package/src/lib/components/ui/label/label.svelte +0 -20
  364. package/src/lib/components/ui/panel/panel-body.svelte +0 -13
  365. package/src/lib/components/ui/panel/panel-close.svelte +0 -16
  366. package/src/lib/components/ui/panel/panel-content.svelte +0 -33
  367. package/src/lib/components/ui/panel/panel-footer.svelte +0 -13
  368. package/src/lib/components/ui/panel/panel-header.svelte +0 -16
  369. package/src/lib/components/ui/panel/panel-title.svelte +0 -14
  370. package/src/lib/components/ui/panel/panel.svelte +0 -15
  371. package/src/lib/components/ui/popover/popover-close.svelte +0 -7
  372. package/src/lib/components/ui/popover/popover-content.svelte +0 -27
  373. package/src/lib/components/ui/popover/popover-description.svelte +0 -19
  374. package/src/lib/components/ui/popover/popover-header.svelte +0 -19
  375. package/src/lib/components/ui/popover/popover-portal.svelte +0 -7
  376. package/src/lib/components/ui/popover/popover-title.svelte +0 -19
  377. package/src/lib/components/ui/popover/popover-trigger.svelte +0 -17
  378. package/src/lib/components/ui/popover/popover.svelte +0 -7
  379. package/src/lib/components/ui/select/select-content.svelte +0 -40
  380. package/src/lib/components/ui/select/select-group-heading.svelte +0 -19
  381. package/src/lib/components/ui/select/select-group.svelte +0 -17
  382. package/src/lib/components/ui/select/select-item.svelte +0 -38
  383. package/src/lib/components/ui/select/select-label.svelte +0 -18
  384. package/src/lib/components/ui/select/select-portal.svelte +0 -7
  385. package/src/lib/components/ui/select/select-scroll-down-button.svelte +0 -20
  386. package/src/lib/components/ui/select/select-scroll-up-button.svelte +0 -20
  387. package/src/lib/components/ui/select/select-separator.svelte +0 -17
  388. package/src/lib/components/ui/select/select-trigger.svelte +0 -27
  389. package/src/lib/components/ui/select/select.svelte +0 -11
  390. package/src/lib/components/ui/separator/separator.svelte +0 -23
  391. package/src/lib/components/ui/sheet/sheet-close.svelte +0 -7
  392. package/src/lib/components/ui/sheet/sheet-content.svelte +0 -43
  393. package/src/lib/components/ui/sheet/sheet-description.svelte +0 -17
  394. package/src/lib/components/ui/sheet/sheet-footer.svelte +0 -18
  395. package/src/lib/components/ui/sheet/sheet-header.svelte +0 -19
  396. package/src/lib/components/ui/sheet/sheet-overlay.svelte +0 -17
  397. package/src/lib/components/ui/sheet/sheet-portal.svelte +0 -7
  398. package/src/lib/components/ui/sheet/sheet-title.svelte +0 -17
  399. package/src/lib/components/ui/sheet/sheet-trigger.svelte +0 -7
  400. package/src/lib/components/ui/sheet/sheet.svelte +0 -7
  401. package/src/lib/components/ui/textarea/textarea.svelte +0 -21
  402. package/src/lib/components/ui/toggle/toggle.svelte +0 -45
  403. package/src/lib/components/ui/toggle-group/toggle-group-item.svelte +0 -35
  404. package/src/lib/components/ui/toggle-group/toggle-group.svelte +0 -63
  405. package/src/lib/components/ui/tooltip/tooltip-content.svelte +0 -24
  406. package/src/lib/components/ui/tooltip/tooltip-trigger.svelte +0 -27
  407. package/src/lib/components/ui/tooltip/tooltip.svelte +0 -9
  408. package/src/lib/components/ui/trigger-button/trigger-button.svelte +0 -106
  409. package/src/svelte-plugin-ui/components/Icon.svelte +0 -181
  410. package/src/svelte-plugin-ui/components/ModeSwitch.svelte +0 -121
  411. package/src/svelte-plugin-ui/components/ToolbarShell.svelte +0 -150
  412. package/src/svelte-plugin-ui/components/Viewfinder.svelte +0 -1001
  413. package/src/tools/handlers/docs.js +0 -11
  414. package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +0 -139
  415. package/src/workshop/features/createFlow/CreateFlowForm.svelte +0 -314
  416. package/src/workshop/features/createPage/CreatePageForm.svelte +0 -249
  417. package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +0 -287
  418. package/src/workshop/features/createStory/CreateStoryForm.svelte +0 -161
  419. package/src/workshop/ui/WorkshopPanel.svelte +0 -97
@@ -0,0 +1,858 @@
1
+ /**
2
+ * CoreUIBar — unified floating button bar for the storyboard devtools.
3
+ *
4
+ * Fixed bottom-right. Always shows the ⌘ command button (rightmost).
5
+ * Mode-specific buttons appear to its left at a smaller size.
6
+ * Hue follows the active mode's collar color via --trigger-* CSS custom
7
+ * properties set in modes.css.
8
+ *
9
+ * Initializes the command action registry and registers core handlers.
10
+ */
11
+
12
+ import './CoreUIBar.css';
13
+ import { useState, useEffect, useMemo, useRef, useCallback } from 'react'
14
+ import './core-ui-colors.css'
15
+ import * as Panel from './lib/components/ui/panel/index.js'
16
+ import PwaInstallBanner from './PwaInstallBanner.jsx'
17
+ import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
18
+ import * as Tooltip from './lib/components/ui/tooltip/index.js'
19
+ import Icon from './svelte-plugin-ui/components/Icon.jsx'
20
+ import { modeState } from './svelte-plugin-ui/stores/modeStore.js'
21
+ import { sidePanelState, togglePanel } from './stores/sidePanelStore.js'
22
+ import {
23
+ initCommandActions, registerCommandAction, getActionChildren,
24
+ hasChildrenProvider, isExcludedByRoute, setRoutingBasePath,
25
+ setDynamicActions, clearDynamicActions,
26
+ } from './commandActions.js'
27
+ import { isMobile, subscribeToMobile } from './mobileViewport.js'
28
+ import { isMenuHidden } from './uiConfig.js'
29
+ import { subscribeToToolbarConfig, getToolbarConfig } from './toolbarConfigStore.js'
30
+ import {
31
+ initToolbarToolStates, getToolbarToolState, isToolbarToolLocalOnly,
32
+ subscribeToToolbarToolStates,
33
+ } from './toolStateStore.js'
34
+ import defaultToolbarConfig from '../toolbar.config.json'
35
+ import { getPrototypeMetadata } from './loader.js'
36
+ import { setPrototypeToolbarConfig, clearPrototypeToolbarConfig } from './toolbarConfigStore.js'
37
+ import { setOverrides, clearOverrides } from './configStore.js'
38
+ import { coreHandlers } from './tools/registry.js'
39
+ import { toggleCommentMode, isCommentModeActive } from './comments/commentMode.js'
40
+ import { isAuthenticated } from './comments/auth.js'
41
+ import { openAuthModal } from './comments/ui/authModal.js'
42
+ import { themeState, setTheme, THEMES } from './stores/themeStore.js'
43
+ import SidePanelComponent from './SidePanel.jsx'
44
+
45
+ const isEmbed = typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('_sb_embed')
46
+ const isLocalDev = typeof window !== 'undefined' && window.__SB_LOCAL_DEV__ === true && !new URLSearchParams(window.location.search).has('prodMode')
47
+
48
+ /**
49
+ * Resolve a handler reference to a module loader function.
50
+ * Format: "core:name" → core registry, "custom:name" → client handlers
51
+ */
52
+ function resolveHandlerModule(ref, coreModules, custom) {
53
+ const colonIdx = ref.indexOf(':')
54
+ if (colonIdx === -1) return coreModules[ref] || null
55
+ const prefix = ref.slice(0, colonIdx)
56
+ const name = ref.slice(colonIdx + 1)
57
+ if (prefix === 'core') return coreModules[name] || null
58
+ if (prefix === 'custom') return custom[name] || null
59
+ return null
60
+ }
61
+
62
+ // Resolve tools → menus compatibility layer.
63
+ function resolveMenus(cfg) {
64
+ if (cfg.tools) {
65
+ const result = {}
66
+ for (const [key, tool] of Object.entries(cfg.tools)) {
67
+ if (tool.surface === 'command-palette' || tool.surface === 'canvas-toolbar') continue
68
+ const menu = { ...tool }
69
+ if (tool.render === 'menu' && tool.handler) {
70
+ menu.action = tool.handler
71
+ }
72
+ result[key] = { ...menu, _toolId: key }
73
+ }
74
+ return result
75
+ }
76
+ return cfg.menus || {}
77
+ }
78
+
79
+ function resolveCommandConfig(cfg) {
80
+ if (!cfg.tools) return cfg.menus?.command || null
81
+ const actions = []
82
+ actions.push({ type: 'header', label: 'Command Menu' })
83
+
84
+ for (const [toolKey, tool] of Object.entries(cfg.tools)) {
85
+ if (tool.surface !== 'command-palette') continue
86
+ if (tool.render === 'separator') {
87
+ actions.push({ type: 'separator' })
88
+ continue
89
+ }
90
+ actions.push({
91
+ id: tool.handler || `core/${tool.label?.toLowerCase().replace(/\s+/g, '-')}`,
92
+ label: tool.label || tool.ariaLabel,
93
+ type: tool.render || 'default',
94
+ url: tool.url || null,
95
+ modes: tool.modes || ['*'],
96
+ toolKey,
97
+ localOnly: !tool.prod,
98
+ hideFromCommandPaletteSearch: tool.hideFromCommandPaletteSearch || false,
99
+ })
100
+ }
101
+
102
+ return {
103
+ ariaLabel: 'Command Menu',
104
+ trigger: 'command',
105
+ default: true,
106
+ modes: ['*'],
107
+ actions,
108
+ }
109
+ }
110
+
111
+ function menuVisibleInMode(menu, mode) {
112
+ if (!menu?.modes) return false
113
+ if (isExcludedByRoute(menu)) return false
114
+ return menu.modes.includes('*') || menu.modes.includes(mode)
115
+ }
116
+
117
+ // Hook to subscribe to an external store (modeState, sidePanelState)
118
+ function useExternalStore(store) {
119
+ const [value, setValue] = useState(() => ({ ...store }))
120
+ useEffect(() => {
121
+ // For stores that expose subscribe
122
+ if (store.subscribe) {
123
+ const unsub = store.subscribe((v) => setValue({ ...v }))
124
+ return unsub
125
+ }
126
+ }, [store])
127
+ return value
128
+ }
129
+
130
+ export default function CoreUIBar({ basePath = '/', toolbarConfig, customHandlers = {} }) {
131
+ const [visible, setVisible] = useState(
132
+ () => !document.documentElement.classList.contains('storyboard-chrome-hidden')
133
+ )
134
+ const [toolComponents, setToolComponents] = useState({})
135
+ const [toolData, setToolData] = useState({})
136
+ const [navVersion, setNavVersion] = useState(0)
137
+ const [SidePanelComp, setSidePanelComp] = useState(null)
138
+ const [canvasActive, setCanvasActive] = useState(false)
139
+ const [activeCanvasId, setActiveCanvasId] = useState('')
140
+ const [canvasZoom, setCanvasZoom] = useState(100)
141
+ const [toolStateVersion, setToolStateVersion] = useState(0)
142
+ const [isMobileState, setIsMobileState] = useState(isMobile())
143
+ const [activeToolbarIndex, setActiveToolbarIndex] = useState(-1)
144
+ const [flowDialogOpen, setFlowDialogOpen] = useState(false)
145
+ const [flowName, setFlowName] = useState('default')
146
+ const [flowJson, setFlowJson] = useState('')
147
+ const [flowError, setFlowError] = useState(null)
148
+
149
+ const toolbarEl = useRef(null)
150
+ const mobileActionsRegistered = useRef(false)
151
+ const origPushStateRef = useRef(null)
152
+ const origReplaceStateRef = useRef(null)
153
+ const bumpNavRef = useRef(null)
154
+
155
+ // Subscribe to external stores
156
+ const currentModeState = useExternalStore(modeState)
157
+ const currentSidePanelState = useExternalStore(sidePanelState)
158
+
159
+ // Subscribe to toolbar config store
160
+ const [storeConfig, setStoreConfig] = useState(getToolbarConfig())
161
+ useEffect(() => {
162
+ const unsub = subscribeToToolbarConfig((cfg) => setStoreConfig(cfg))
163
+ return unsub
164
+ }, [])
165
+
166
+ // Subscribe to tool state changes
167
+ useEffect(() => {
168
+ const unsub = subscribeToToolbarToolStates(() => setToolStateVersion((v) => v + 1))
169
+ return unsub
170
+ }, [])
171
+
172
+ // Resolved config
173
+ const config = useMemo(() => {
174
+ return (storeConfig && Object.keys(storeConfig).length > 0)
175
+ ? storeConfig
176
+ : (toolbarConfig || defaultToolbarConfig)
177
+ }, [storeConfig, toolbarConfig])
178
+
179
+ // Re-seed tool states whenever config changes
180
+ useEffect(() => {
181
+ const tools = config.tools || {}
182
+ initToolbarToolStates(tools, { isLocalDev })
183
+ }, [config])
184
+
185
+ const commandMenuConfig = useMemo(() => resolveCommandConfig(config), [config])
186
+
187
+ const shortcutsConfig = useMemo(() => {
188
+ const tools = config.tools || {}
189
+ const hideChromeTool = tools['hide-chrome']
190
+ const commandPaletteTool = tools['command-palette']
191
+ return {
192
+ hideChrome: hideChromeTool?.shortcut || { key: '.', label: '⌘.' },
193
+ openCommandMenu: commandPaletteTool?.shortcut || { key: 'k', label: '⌘K' },
194
+ }
195
+ }, [config])
196
+
197
+ const allMenus = useMemo(() => resolveMenus(config), [config])
198
+
199
+ const orderedMenus = useMemo(() => {
200
+ // Reference toolStateVersion to re-compute when tool states change
201
+ void toolStateVersion
202
+ return Object.entries(allMenus)
203
+ .filter(([key]) => key !== 'command')
204
+ .filter(([key]) => !isMenuHidden(key))
205
+ .filter(([key]) => getToolbarToolState(key) !== 'disabled')
206
+ .map(([key, menu]) => ({ key, ...menu }))
207
+ }, [allMenus, toolStateVersion])
208
+
209
+ const sidepanelMenus = useMemo(
210
+ () => orderedMenus.filter((menu) => menu.sidepanel),
211
+ [orderedMenus],
212
+ )
213
+
214
+ const canvasMenus = useMemo(() => {
215
+ return config.tools
216
+ ? Object.entries(config.tools)
217
+ .filter(([, tool]) => tool.surface === 'canvas-toolbar')
218
+ .filter(([, tool]) => tool.prod || isLocalDev)
219
+ .map(([key, tool]) => ({ key, ...tool }))
220
+ : []
221
+ }, [config])
222
+
223
+ const visibleMenus = useMemo(() => {
224
+ void navVersion
225
+ void toolStateVersion
226
+ return orderedMenus
227
+ .filter((menu) => {
228
+ const toolState = getToolbarToolState(menu.key)
229
+ if (toolState === 'hidden') return false
230
+ if (menu.render === 'separator') return true
231
+ if (!menuVisibleInMode(menu, currentModeState.mode)) return false
232
+ if (menu.render === 'sidepanel') return true
233
+ if (!toolComponents[menu.key]) return false
234
+ const actionId = menu.handler || menu.action
235
+ if (actionId && menu.render === 'menu' && hasChildrenProvider(actionId)) {
236
+ return getActionChildren(actionId).length > 0
237
+ }
238
+ return true
239
+ })
240
+ }, [orderedMenus, navVersion, toolStateVersion, currentModeState.mode, toolComponents])
241
+
242
+ const cleanedMenus = useMemo(() => {
243
+ const result = []
244
+ for (const item of visibleMenus) {
245
+ if (item.render === 'separator') {
246
+ if (result.length === 0 || result[result.length - 1].render === 'separator') continue
247
+ result.push(item)
248
+ } else {
249
+ result.push(item)
250
+ }
251
+ }
252
+ while (result.length > 0 && result[result.length - 1].render === 'separator') result.pop()
253
+ return result
254
+ }, [visibleMenus])
255
+
256
+ const toolbarItemCount = useMemo(
257
+ () => cleanedMenus.filter((m) => m.render !== 'separator').length,
258
+ [cleanedMenus],
259
+ )
260
+
261
+ const lastToolIndex = useMemo(
262
+ () => cleanedMenus.filter((m) => m.render !== 'separator').length - 1,
263
+ [cleanedMenus],
264
+ )
265
+
266
+ function getTabindex(index) {
267
+ if (activeToolbarIndex < 0) {
268
+ return index === lastToolIndex ? 0 : -1
269
+ }
270
+ return index === activeToolbarIndex ? 0 : -1
271
+ }
272
+
273
+ const focusToolbarItem = useCallback((index) => {
274
+ setActiveToolbarIndex(index)
275
+ if (!toolbarEl.current) return
276
+ const buttons = toolbarEl.current.querySelectorAll('[data-slot="button"]')
277
+ buttons[index]?.focus()
278
+ }, [])
279
+
280
+ const handleToolbarKeydown = useCallback((e) => {
281
+ if (toolbarItemCount === 0) return
282
+ const current = activeToolbarIndex < 0 ? toolbarItemCount - 1 : activeToolbarIndex
283
+
284
+ if (e.key === 'ArrowRight') {
285
+ e.preventDefault()
286
+ focusToolbarItem((current + 1) % toolbarItemCount)
287
+ } else if (e.key === 'ArrowLeft') {
288
+ e.preventDefault()
289
+ focusToolbarItem((current - 1 + toolbarItemCount) % toolbarItemCount)
290
+ } else if (e.key === 'Home') {
291
+ e.preventDefault()
292
+ focusToolbarItem(0)
293
+ } else if (e.key === 'End') {
294
+ e.preventDefault()
295
+ focusToolbarItem(toolbarItemCount - 1)
296
+ } else if (e.key === 'ArrowDown') {
297
+ e.preventDefault()
298
+ e.stopPropagation()
299
+ } else if (e.key === 'ArrowUp') {
300
+ e.preventDefault()
301
+ const openContent = document.querySelector('[data-slot="dropdown-menu-content"]')
302
+ if (openContent) {
303
+ const items = openContent.querySelectorAll('[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]')
304
+ if (items.length > 0) items[items.length - 1].focus()
305
+ return
306
+ }
307
+ const focusedBtn = toolbarEl.current?.querySelector('[data-slot="button"]:focus')
308
+ if (focusedBtn?.getAttribute('aria-haspopup')) {
309
+ focusedBtn.click()
310
+ requestAnimationFrame(() => {
311
+ const content = document.querySelector('[data-slot="dropdown-menu-content"]')
312
+ if (content) {
313
+ const items = content.querySelectorAll('[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]')
314
+ if (items.length > 0) items[items.length - 1].focus()
315
+ }
316
+ })
317
+ }
318
+ }
319
+ }, [toolbarItemCount, activeToolbarIndex, focusToolbarItem])
320
+
321
+ const toggleToolsVisibility = useCallback(() => {
322
+ setVisible((v) => {
323
+ const next = !v
324
+ document.documentElement.classList.toggle('storyboard-chrome-hidden', !next)
325
+ return next
326
+ })
327
+ }, [])
328
+
329
+ function showFlowInfoDialog(name, json, error) {
330
+ setFlowName(name)
331
+ setFlowJson(json)
332
+ setFlowError(error)
333
+ setFlowDialogOpen(true)
334
+ }
335
+
336
+ // Stable refs for syncMobileActions
337
+ const configRef = useRef(config)
338
+ // eslint-disable-next-line react-hooks/refs
339
+ configRef.current = config
340
+ const toolComponentsRef = useRef(toolComponents)
341
+ // eslint-disable-next-line react-hooks/refs
342
+ toolComponentsRef.current = toolComponents
343
+ const isMobileStateRef = useRef(isMobileState)
344
+ // eslint-disable-next-line react-hooks/refs
345
+ isMobileStateRef.current = isMobileState
346
+
347
+ const syncMobileActions = useCallback(async () => {
348
+ if (!isMobileStateRef.current) {
349
+ if (mobileActionsRegistered.current) {
350
+ clearDynamicActions('mobile-toolbar')
351
+ mobileActionsRegistered.current = false
352
+ }
353
+ return
354
+ }
355
+
356
+ const actions = []
357
+ const handlers = {}
358
+ const toolConfigs = configRef.current.tools || {}
359
+
360
+ actions.push({ type: 'header', label: 'Tools', id: '_mobile_header' })
361
+
362
+ for (const [key, tool] of Object.entries(toolConfigs)) {
363
+ if (tool.surface !== 'command-toolbar') continue
364
+ if (tool.render === 'separator') continue
365
+ if (!tool.prod && !isLocalDev) continue
366
+ if (getToolbarToolState(key) === 'disabled') continue
367
+ if (isExcludedByRoute(tool)) continue
368
+
369
+ const mobileId = `mobile:${key}`
370
+ const desktopActionId = tool.handler || `core:${key}`
371
+
372
+ if (tool.render === 'menu' && hasChildrenProvider(desktopActionId)) {
373
+ actions.push({
374
+ id: mobileId,
375
+ label: tool.label || tool.ariaLabel,
376
+ type: 'submenu',
377
+ modes: tool.modes || ['*'],
378
+ excludeRoutes: tool.excludeRoutes,
379
+ toolKey: key,
380
+ localOnly: !tool.prod,
381
+ })
382
+ handlers[mobileId] = {
383
+ getChildren: () => getActionChildren(desktopActionId),
384
+ }
385
+ } else if (tool.render === 'sidepanel') {
386
+ actions.push({
387
+ id: mobileId,
388
+ label: tool.ariaLabel || tool.label || key,
389
+ type: 'default',
390
+ modes: tool.modes || ['*'],
391
+ excludeRoutes: tool.excludeRoutes,
392
+ toolKey: key,
393
+ localOnly: !tool.prod,
394
+ })
395
+ handlers[mobileId] = () => { togglePanel(tool.sidepanel) }
396
+ } else if (tool.render === 'button' && toolComponentsRef.current[key]) {
397
+ try {
398
+ if (key === 'comments') {
399
+
400
+
401
+ actions.push({
402
+ id: mobileId,
403
+ label: tool.ariaLabel || 'Comments',
404
+ type: 'toggle',
405
+ modes: tool.modes || ['*'],
406
+ excludeRoutes: tool.excludeRoutes,
407
+ toolKey: key,
408
+ localOnly: !tool.prod,
409
+ })
410
+ handlers[mobileId] = {
411
+ execute: async () => {
412
+ if (!isAuthenticated()) {
413
+ const user = await openAuthModal()
414
+ if (!user) return
415
+ }
416
+ toggleCommentMode()
417
+ },
418
+ getState: () => isCommentModeActive(),
419
+ }
420
+ }
421
+ } catch { /* comments module not available */ }
422
+ }
423
+ }
424
+
425
+ // Theme tool — special handling
426
+ if (toolConfigs.theme && toolComponentsRef.current.theme) {
427
+ if (!actions.some((a) => a.id === 'mobile:theme')) {
428
+ try {
429
+ actions.push({
430
+ id: 'mobile:theme',
431
+ label: 'Theme',
432
+ type: 'submenu',
433
+ modes: ['*'],
434
+ toolKey: 'theme',
435
+ localOnly: !toolConfigs.theme.prod,
436
+ })
437
+ handlers['mobile:theme'] = {
438
+ getChildren: () => {
439
+ const current = themeState.theme
440
+ return THEMES.map((t) => ({
441
+ id: `theme:${t.value}`,
442
+ label: t.label,
443
+ type: 'toggle',
444
+ active: current === t.value,
445
+ execute: () => setTheme(t.value),
446
+ }))
447
+ },
448
+ }
449
+ } catch { /* theme store not available */ }
450
+ }
451
+ }
452
+
453
+ setDynamicActions('mobile-toolbar', actions, handlers)
454
+ mobileActionsRegistered.current = true
455
+ }, [])
456
+
457
+ // Canvas event handlers
458
+ const handleCanvasMounted = useCallback((e) => {
459
+ setCanvasActive(true)
460
+ const detail = e.detail
461
+ setActiveCanvasId(detail?.canvasId || detail?.name || '')
462
+ setCanvasZoom(detail?.zoom ?? 100)
463
+ syncMobileActions()
464
+ }, [syncMobileActions])
465
+
466
+ const handleCanvasUnmounted = useCallback(() => {
467
+ setCanvasActive(false)
468
+ setActiveCanvasId('')
469
+ setCanvasZoom(100)
470
+ syncMobileActions()
471
+ }, [syncMobileActions])
472
+
473
+ const handleZoomChanged = useCallback((e) => {
474
+ setCanvasActive(true)
475
+ setCanvasZoom(e.detail?.zoom ?? 100)
476
+ }, [])
477
+
478
+ // Main mount effect
479
+ useEffect(() => {
480
+ let mounted = true
481
+
482
+ setRoutingBasePath(basePath)
483
+
484
+ // Sync visible state when storyboard-chrome-hidden is toggled externally
485
+ const chromeObserver = new MutationObserver(() => {
486
+ const hidden = document.documentElement.classList.contains('storyboard-chrome-hidden')
487
+ setVisible((v) => {
488
+ if (v === !hidden) return v
489
+ return !hidden
490
+ })
491
+ })
492
+ chromeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
493
+
494
+ ;(async () => {
495
+ if (!mounted) return
496
+
497
+ function syncPrototypeToolbar() {
498
+ let pathname = window.location.pathname
499
+ const base = basePath.replace(/\/+$/, '')
500
+ if (base && pathname.startsWith(base)) pathname = pathname.slice(base.length)
501
+ const firstSegment = pathname.replace(/^\//, '').split('/')[0] || null
502
+ if (firstSegment) {
503
+ const meta = getPrototypeMetadata(firstSegment)
504
+
505
+ if (meta?.toolbarConfig) {
506
+ setPrototypeToolbarConfig(meta.toolbarConfig)
507
+ setOverrides('toolbar', meta.toolbarConfig)
508
+ } else {
509
+ clearPrototypeToolbarConfig()
510
+ clearOverrides('toolbar')
511
+ }
512
+
513
+ const domainMap = [
514
+ ['commandPaletteConfig', 'commandPalette'],
515
+ ['widgetsConfig', 'widgets'],
516
+ ['pasteConfig', 'paste'],
517
+ ]
518
+ for (const [metaKey, domain] of domainMap) {
519
+ if (meta?.[metaKey]) {
520
+ setOverrides(domain, meta[metaKey])
521
+ } else {
522
+ clearOverrides(domain)
523
+ }
524
+ }
525
+ } else {
526
+ clearPrototypeToolbarConfig()
527
+ clearOverrides('toolbar')
528
+ clearOverrides('commandPalette')
529
+ clearOverrides('widgets')
530
+ clearOverrides('paste')
531
+ }
532
+ }
533
+
534
+ const bumpNav = () => {
535
+ setNavVersion((v) => v + 1)
536
+ syncPrototypeToolbar()
537
+ syncMobileActions()
538
+ }
539
+ bumpNavRef.current = bumpNav
540
+
541
+ window.addEventListener('popstate', bumpNav)
542
+ origPushStateRef.current = history.pushState.bind(history)
543
+ history.pushState = (...args) => { origPushStateRef.current(...args); bumpNav() }
544
+ origReplaceStateRef.current = history.replaceState.bind(history)
545
+ history.replaceState = (...args) => { origReplaceStateRef.current(...args); bumpNav() }
546
+
547
+ syncPrototypeToolbar()
548
+
549
+ // Seed the command action registry from config
550
+ if (commandMenuConfig) {
551
+ initCommandActions(commandMenuConfig)
552
+ }
553
+
554
+ // Register sidepanel toggle actions
555
+ for (const menu of sidepanelMenus) {
556
+ registerCommandAction(`core:${menu.key}`, () => {
557
+ togglePanel(menu.sidepanel)
558
+ })
559
+ }
560
+
561
+ // Load all tool modules from the registry — in parallel
562
+
563
+ const toolConfigs = config.tools || {}
564
+ const ctx = { basePath, showFlowInfoDialog }
565
+
566
+ const loadedComponents = {}
567
+ const loadedData = {}
568
+
569
+ await Promise.all(Object.entries(toolConfigs).map(async ([toolId, toolConfig]) => {
570
+ if (toolConfig.render === 'separator') return
571
+ if (getToolbarToolState(toolId) === 'disabled') return
572
+
573
+ const handlerRef = toolConfig.handler || `core:${toolId}`
574
+ const loadModule = resolveHandlerModule(handlerRef, coreHandlers, customHandlers)
575
+ if (!loadModule) return
576
+
577
+ try {
578
+ const mod = await loadModule()
579
+ const toolCtx = { ...ctx, config: toolConfig }
580
+
581
+ if (mod.guard) {
582
+ const ok = await mod.guard(toolCtx)
583
+ if (!ok) return
584
+ }
585
+
586
+ if (mod.setup) {
587
+ const setupResult = await mod.setup(toolCtx)
588
+ if (setupResult) {
589
+ loadedData[toolId] = setupResult
590
+ }
591
+ }
592
+
593
+ if (mod.handler) {
594
+ const handlerResult = await mod.handler(toolCtx)
595
+ const actionId = toolConfig.handler || `core:${toolId}`
596
+ if (handlerResult && !handlerResult.getChildren) {
597
+ loadedData[toolId] = { ...(loadedData[toolId] || {}), ...handlerResult }
598
+ }
599
+ registerCommandAction(actionId, handlerResult)
600
+ }
601
+
602
+ if (mod.component) {
603
+ const component = await mod.component(toolConfig.render)
604
+ loadedComponents[toolId] = component
605
+ }
606
+ } catch (err) {
607
+ console.warn(`[CoreUIBar] Failed to load tool "${toolId}":`, err)
608
+ }
609
+ }))
610
+
611
+ if (mounted) {
612
+ setToolComponents(loadedComponents)
613
+ setToolData(loadedData)
614
+ }
615
+
616
+ // Load side panel component
617
+ if (sidepanelMenus.length > 0 && mounted) {
618
+ setSidePanelComp(() => SidePanelComponent)
619
+ }
620
+
621
+ // Sync canvas bridge state
622
+ if (typeof window !== 'undefined') {
623
+ const state = window.__storyboardCanvasBridgeState
624
+ if (state && typeof state === 'object') {
625
+ setCanvasActive(state.active === true)
626
+ setActiveCanvasId(state.canvasId || state.name || '')
627
+ setCanvasZoom(typeof state.zoom === 'number' ? state.zoom : 100)
628
+ } else {
629
+ document.dispatchEvent(new CustomEvent('storyboard:canvas:status-request'))
630
+ }
631
+ }
632
+
633
+ syncMobileActions()
634
+ })()
635
+
636
+ // Subscribe to mobile viewport changes
637
+ const unsubMobile = subscribeToMobile((mobile) => {
638
+ setIsMobileState(mobile)
639
+ // syncMobileActions will be called by the isMobileState change
640
+ })
641
+
642
+ return () => {
643
+ mounted = false
644
+ if (bumpNavRef.current) window.removeEventListener('popstate', bumpNavRef.current)
645
+ if (origPushStateRef.current) history.pushState = origPushStateRef.current
646
+ if (origReplaceStateRef.current) history.replaceState = origReplaceStateRef.current
647
+ unsubMobile()
648
+ clearDynamicActions('mobile-toolbar')
649
+ chromeObserver.disconnect()
650
+ }
651
+ }, []) // eslint-disable-line react-hooks/exhaustive-deps
652
+
653
+ // Canvas event listeners
654
+ useEffect(() => {
655
+ document.addEventListener('storyboard:canvas:mounted', handleCanvasMounted)
656
+ document.addEventListener('storyboard:canvas:unmounted', handleCanvasUnmounted)
657
+ document.addEventListener('storyboard:canvas:zoom-changed', handleZoomChanged)
658
+ document.addEventListener('storyboard:canvas:status', handleCanvasMounted)
659
+ return () => {
660
+ document.removeEventListener('storyboard:canvas:mounted', handleCanvasMounted)
661
+ document.removeEventListener('storyboard:canvas:unmounted', handleCanvasUnmounted)
662
+ document.removeEventListener('storyboard:canvas:zoom-changed', handleZoomChanged)
663
+ document.removeEventListener('storyboard:canvas:status', handleCanvasMounted)
664
+ }
665
+ }, [handleCanvasMounted, handleCanvasUnmounted, handleZoomChanged])
666
+
667
+ // Sync mobile actions when isMobileState changes
668
+ useEffect(() => {
669
+ syncMobileActions()
670
+ }, [isMobileState, syncMobileActions])
671
+
672
+ // Keyboard shortcut handler
673
+ useEffect(() => {
674
+ function handleKeydown(e) {
675
+ const hideKey = shortcutsConfig.hideChrome?.key || '.'
676
+
677
+ if (e.key === hideKey && (e.metaKey || e.ctrlKey)) {
678
+ e.preventDefault()
679
+ toggleToolsVisibility()
680
+ }
681
+ for (const menu of cleanedMenus) {
682
+ const shortcut = menu.shortcut
683
+ if (!shortcut?.key) continue
684
+ if (e.key === shortcut.key && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
685
+ const toolState = getToolbarToolState(menu.key)
686
+ if (toolState === 'inactive' || toolState === 'disabled') break
687
+ if (menu.sidepanel) {
688
+ e.preventDefault()
689
+ togglePanel(menu.sidepanel)
690
+ }
691
+ break
692
+ }
693
+ }
694
+ }
695
+
696
+ window.addEventListener('keydown', handleKeydown)
697
+ return () => window.removeEventListener('keydown', handleKeydown)
698
+ }, [shortcutsConfig, cleanedMenus, toggleToolsVisibility])
699
+
700
+ if (isEmbed) return null
701
+
702
+ return (
703
+ <>
704
+ {/* Canvas toolbar */}
705
+ {canvasActive && canvasMenus.length > 0 && (
706
+ <div
707
+ className="fixed bottom-6 left-6 z-[9999] font-sans flex items-center gap-3"
708
+ role="toolbar"
709
+ aria-label="Canvas toolbar"
710
+ >
711
+ {canvasMenus.map((canvasTool) => {
712
+ const CanvasToolComponent = toolComponents[canvasTool.key]
713
+ if (!CanvasToolComponent) return null
714
+ if (canvasTool.render === 'menu') {
715
+ return (
716
+ <Tooltip.Root key={canvasTool.key}>
717
+ <Tooltip.Trigger>
718
+ <span data-local-only={isToolbarToolLocalOnly(canvasTool.key) || undefined}>
719
+ <CanvasToolComponent
720
+ config={canvasTool}
721
+ data={toolData[canvasTool.key]}
722
+ canvasName={activeCanvasId}
723
+ zoom={canvasZoom}
724
+ tabIndex={0}
725
+ />
726
+ </span>
727
+ </Tooltip.Trigger>
728
+ <Tooltip.Content side="top">{canvasTool.ariaLabel || canvasTool.key}</Tooltip.Content>
729
+ </Tooltip.Root>
730
+ )
731
+ }
732
+ return (
733
+ <CanvasToolComponent
734
+ key={canvasTool.key}
735
+ config={canvasTool}
736
+ data={toolData[canvasTool.key]}
737
+ canvasName={activeCanvasId}
738
+ zoom={canvasZoom}
739
+ tabIndex={0}
740
+ />
741
+ )
742
+ })}
743
+ </div>
744
+ )}
745
+
746
+ {/* Main toolbar */}
747
+ <div
748
+ id="storyboard-controls"
749
+ className="fixed bottom-6 right-6 z-[9999] font-sans flex items-end gap-3"
750
+ data-core-ui-bar=""
751
+ role="toolbar"
752
+ tabIndex={0}
753
+ aria-label="Storyboard controls"
754
+ onKeyDown={handleToolbarKeydown}
755
+ ref={toolbarEl}
756
+ >
757
+ {/* Always-visible tools (left) */}
758
+ {cleanedMenus.map((menu, i) => {
759
+ if (!menu.alwaysVisible || !toolComponents[menu.key]) return null
760
+ const ToolComponent = toolComponents[menu.key]
761
+ return (
762
+ <Tooltip.Root key={menu.key}>
763
+ <Tooltip.Trigger>
764
+ <ToolComponent
765
+ config={menu}
766
+ data={toolData[menu.key]}
767
+ tabIndex={getTabindex(i)}
768
+ localOnly={isToolbarToolLocalOnly(menu.key)}
769
+ basePath={basePath}
770
+ />
771
+ </Tooltip.Trigger>
772
+ <Tooltip.Content side="top">
773
+ {menu.ariaLabel || menu.key}{menu.shortcut?.label ? ` · ${menu.shortcut.label}` : ''}
774
+ </Tooltip.Content>
775
+ </Tooltip.Root>
776
+ )
777
+ })}
778
+
779
+ {visible && !isMobileState && cleanedMenus.map((menu, i) => {
780
+ if (menu.alwaysVisible) return null
781
+ if (menu.render === 'separator') {
782
+ return <div key={menu.key} className="toolbar-separator" aria-hidden="true" />
783
+ }
784
+ const toolState = getToolbarToolState(menu.key)
785
+ return (
786
+ <Tooltip.Root key={menu.key}>
787
+ <Tooltip.Trigger>
788
+ {menu.render === 'sidepanel' ? (
789
+ <TriggerButton
790
+ active={currentSidePanelState.open && currentSidePanelState.activeTab === menu.sidepanel}
791
+ inactive={toolState === 'inactive'}
792
+ dimmed={toolState === 'dimmed'}
793
+ localOnly={isToolbarToolLocalOnly(menu.key)}
794
+ size="icon-xl"
795
+ aria-label={menu.ariaLabel || menu.key}
796
+ tabIndex={getTabindex(i)}
797
+ onFocus={() => setActiveToolbarIndex(i)}
798
+ onClick={() => togglePanel(menu.sidepanel)}
799
+ >
800
+ <Icon name={menu.icon || menu.key} size={16} {...(menu.meta || {})} />
801
+ </TriggerButton>
802
+ ) : toolComponents[menu.key] ? (() => {
803
+ const ToolComponent = toolComponents[menu.key]
804
+ return (
805
+ <span
806
+ data-tool-state={toolState}
807
+ data-local-only={isToolbarToolLocalOnly(menu.key) || undefined}
808
+ className={toolState === 'inactive' ? 'tool-inactive' : toolState === 'dimmed' ? 'tool-dimmed' : ''}
809
+ >
810
+ <ToolComponent
811
+ config={menu}
812
+ data={toolData[menu.key]}
813
+ tabIndex={getTabindex(i)}
814
+ localOnly={isToolbarToolLocalOnly(menu.key)}
815
+ basePath={basePath}
816
+ />
817
+ </span>
818
+ )
819
+ })() : null}
820
+ </Tooltip.Trigger>
821
+ <Tooltip.Content side="top">
822
+ {menu.ariaLabel || menu.key}{menu.shortcut?.label ? ` · ${menu.shortcut.label}` : ''}
823
+ </Tooltip.Content>
824
+ </Tooltip.Root>
825
+ )
826
+ })}
827
+
828
+ </div>
829
+
830
+ {/* Side panel */}
831
+ {SidePanelComp && (
832
+ <SidePanelComp
833
+ onClose={() => focusToolbarItem(activeToolbarIndex < 0 ? toolbarItemCount - 1 : activeToolbarIndex)}
834
+ />
835
+ )}
836
+
837
+ {/* PWA install banner */}
838
+ <PwaInstallBanner />
839
+
840
+ {/* Flow info panel */}
841
+ <Panel.Root open={flowDialogOpen} onOpenChange={setFlowDialogOpen}>
842
+ <Panel.Content>
843
+ <Panel.Header>
844
+ <Panel.Title>Flow: {flowName}</Panel.Title>
845
+ <Panel.Close />
846
+ </Panel.Header>
847
+ <Panel.Body>
848
+ {flowError ? (
849
+ <span className="text-destructive text-sm">{flowError}</span>
850
+ ) : (
851
+ <pre className="m-0 bg-transparent text-sm font-mono leading-relaxed whitespace-pre-wrap break-words">{flowJson}</pre>
852
+ )}
853
+ </Panel.Body>
854
+ </Panel.Content>
855
+ </Panel.Root>
856
+ </>
857
+ )
858
+ }