@dfosco/storyboard-core 4.2.0-beta.2 → 4.2.0-beta.21

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