@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
@@ -1,847 +0,0 @@
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
- <script lang="ts">
13
- import { onMount, onDestroy, untrack } from 'svelte'
14
- import './core-ui-colors.css'
15
- import CommandPalette from './CommandPalette.svelte'
16
- import * as Panel from './lib/components/ui/panel/index.js'
17
- import PwaInstallBanner from './PwaInstallBanner.svelte'
18
- import { TriggerButton } from './lib/components/ui/trigger-button/index.js'
19
- import * as Tooltip from './lib/components/ui/tooltip/index.js'
20
- import Icon from './svelte-plugin-ui/components/Icon.svelte'
21
- import { modeState } from './svelte-plugin-ui/stores/modeStore.js'
22
- import { sidePanelState, togglePanel } from './stores/sidePanelStore.js'
23
- import { initCommandActions, registerCommandAction, getActionChildren, hasChildrenProvider, isExcludedByRoute, setRoutingBasePath, setDynamicActions, clearDynamicActions } from './commandActions.js'
24
- import { isMobile, subscribeToMobile } from './mobileViewport.js'
25
- import { isMenuHidden } from './uiConfig.js'
26
- import { subscribeToToolbarConfig, getToolbarConfig } from './toolbarConfigStore.js'
27
- import { initToolbarToolStates, getToolbarToolState, isToolbarToolLocalOnly, subscribeToToolbarToolStates } from './toolStateStore.js'
28
- import defaultToolbarConfig from '../toolbar.config.json'
29
-
30
- interface Props { basePath?: string; toolbarConfig?: any; customHandlers?: Record<string, () => Promise<any>> }
31
- let { basePath = '/', toolbarConfig, customHandlers = {} }: Props = $props()
32
-
33
- // Reactive toolbar config — subscribes to the config store for prototype overrides.
34
- // Falls back to the prop (for backward compat) or the bundled defaults.
35
- let storeConfig = $state(getToolbarConfig())
36
- let unsubConfig: (() => void) | null = null
37
-
38
- $effect(() => {
39
- unsubConfig = subscribeToToolbarConfig((cfg: any) => { storeConfig = cfg })
40
- return () => { if (unsubConfig) unsubConfig() }
41
- })
42
-
43
- $effect(() => {
44
- const unsub = subscribeToToolbarToolStates(() => { toolStateVersion++ })
45
- return unsub
46
- })
47
-
48
- // Use store config if available, otherwise fall back to prop or defaults
49
- const config = $derived(
50
- (storeConfig && Object.keys(storeConfig).length > 0)
51
- ? storeConfig
52
- : (toolbarConfig || defaultToolbarConfig)
53
- )
54
-
55
- // Re-seed tool states whenever config changes (e.g. prototype override on navigation)
56
- $effect(() => {
57
- const tools = config.tools || {}
58
- // untrack so the synchronous _notify() → toolStateVersion++ inside
59
- // initToolbarToolStates doesn't get tracked as a dependency of this effect
60
- untrack(() => initToolbarToolStates(tools, { isLocalDev }))
61
- })
62
-
63
- let visible = $state(true)
64
- // Hide the entire toolbar when loaded inside a prototype embed iframe
65
- const isEmbed = typeof window !== 'undefined' && new URLSearchParams(window.location.search).has('_sb_embed')
66
- let commandMenuOpen = $state(false)
67
- let toolComponents: Record<string, any> = $state({})
68
- let toolData: Record<string, any> = $state({})
69
- let navVersion = $state(0)
70
- let origPushState: typeof history.pushState
71
- let origReplaceState: typeof history.replaceState
72
- let bumpNav: () => void
73
- let chromeObserver: MutationObserver | null = null
74
- let SidePanel: any = $state(null)
75
- let toolbarEl: HTMLElement | null = $state(null)
76
- let canvasActive = $state(false)
77
- let activeCanvasId = $state('')
78
- let canvasZoom = $state(100)
79
- let toolStateVersion = $state(0)
80
-
81
- // Mobile viewport state — on narrow screens, toolbar tools move into the command menu
82
- let isMobileState = $state(isMobile())
83
- let unsubMobile: (() => void) | null = null
84
- let mobileActionsRegistered = false
85
-
86
- // Roving tabindex: only one button in the toolbar is tabbable at a time
87
- let activeToolbarIndex = $state(-1)
88
-
89
- const isLocalDev = typeof window !== 'undefined' && (window as any).__SB_LOCAL_DEV__ === true && !new URLSearchParams(window.location.search).has('prodMode')
90
-
91
- /**
92
- * Resolve a handler reference to a module loader function.
93
- * Format: "core:name" → core registry, "custom:name" → client handlers
94
- */
95
- function resolveHandlerModule(
96
- ref: string,
97
- coreModules: Record<string, Function>,
98
- custom: Record<string, () => Promise<any>>
99
- ): Function | null {
100
- const colonIdx = ref.indexOf(':')
101
- if (colonIdx === -1) return coreModules[ref] || null
102
- const prefix = ref.slice(0, colonIdx)
103
- const name = ref.slice(colonIdx + 1)
104
- if (prefix === 'core') return coreModules[name] || null
105
- if (prefix === 'custom') return custom[name] || null
106
- return null
107
- }
108
-
109
- // Resolve tools → menus compatibility layer.
110
- // New config uses `tools` (flat map with toolbar target); legacy uses `menus`.
111
- // When `tools` exists, derive the menus-compatible structures from it.
112
- function resolveMenus(cfg: any): Record<string, any> {
113
- if (cfg.tools) {
114
- const result: Record<string, any> = {}
115
- for (const [key, tool] of Object.entries(cfg.tools as Record<string, any>)) {
116
- if (tool.surface === 'command-list' || tool.surface === 'canvas-toolbar') continue
117
- // Map new render/toolbar fields to legacy menu fields for rendering compat
118
- const menu: any = { ...tool }
119
- if (tool.render === 'menu' && tool.handler) {
120
- menu.action = tool.handler
121
- }
122
- result[key] = { ...menu, _toolId: key }
123
- }
124
- return result
125
- }
126
- return cfg.menus || {}
127
- }
128
-
129
- function resolveCommandConfig(cfg: any): any {
130
- if (cfg.command) {
131
- // Build command menu config from new schema
132
- const actions: any[] = []
133
- actions.push({ type: 'header', label: 'Command Menu' })
134
-
135
- // Add command-list tools as actions
136
- if (cfg.tools) {
137
- for (const [toolKey, tool] of Object.entries(cfg.tools as Record<string, any>)) {
138
- if (tool.surface !== 'command-list') continue
139
- if (tool.render === 'separator') {
140
- actions.push({ type: 'separator' })
141
- continue
142
- }
143
- actions.push({
144
- id: tool.handler || `core/${tool.label?.toLowerCase().replace(/\s+/g, '-')}`,
145
- label: tool.label || tool.ariaLabel,
146
- type: tool.render || 'default',
147
- url: tool.url || null,
148
- modes: tool.modes || ['*'],
149
- toolKey,
150
- localOnly: !tool.prod,
151
- })
152
- }
153
- }
154
-
155
- return {
156
- ariaLabel: 'Command Menu',
157
- trigger: 'command',
158
- icon: cfg.command.icon,
159
- meta: cfg.command.meta,
160
- default: true,
161
- modes: ['*'],
162
- actions,
163
- }
164
- }
165
- return cfg.menus?.command || null
166
- }
167
-
168
- const commandMenuConfig = $derived(
169
- isMenuHidden('command') ? null : resolveCommandConfig(config)
170
- )
171
- const shortcutsConfig = $derived({
172
- ...((config as any).shortcuts || {}),
173
- ...(config.command?.shortcut ? { openCommandMenu: config.command.shortcut } : {}),
174
- })
175
-
176
- // Build ordered menu list from JSON key order (excluding command, which is always rightmost)
177
- const allMenus = $derived(resolveMenus(config))
178
- const orderedMenus = $derived(Object.entries(allMenus)
179
- .filter(([key]) => key !== 'command')
180
- .filter(([key]) => !isMenuHidden(key))
181
- .filter(([key]) => {
182
- void toolStateVersion
183
- return getToolbarToolState(key) !== 'disabled'
184
- })
185
- .map(([key, menu]) => ({ key, ...menu })))
186
-
187
- // Discover menus with sidepanel property
188
- const sidepanelMenus = $derived(orderedMenus.filter(menu => menu.sidepanel))
189
-
190
- // Canvas toolbar tools — only visible when a canvas page is active
191
- const canvasMenus = $derived(
192
- config.tools
193
- ? Object.entries(config.tools as Record<string, any>)
194
- .filter(([, tool]) => tool.surface === 'canvas-toolbar')
195
- .filter(([, tool]) => tool.prod || isLocalDev)
196
- .map(([key, tool]) => ({ key, ...tool }))
197
- : []
198
- )
199
-
200
- function menuVisibleInMode(menu: any, mode: string): boolean {
201
- if (!menu?.modes) return false
202
- if (isExcludedByRoute(menu)) return false
203
- return menu.modes.includes('*') || menu.modes.includes(mode)
204
- }
205
-
206
- // Menus that are visible in the current mode, reversed so JSON top→bottom = right→left
207
- const visibleMenus = $derived(
208
- orderedMenus
209
- .filter(menu => {
210
- void navVersion
211
- void toolStateVersion
212
- const toolState = getToolbarToolState(menu.key)
213
- if (toolState === 'hidden') return false
214
- if (menu.render === 'separator') return true
215
- if (!menuVisibleInMode(menu, $modeState.mode)) return false
216
- if (menu.render === 'sidepanel') return true
217
- // For tools with components, check if loaded
218
- if (!toolComponents[menu.key]) return false
219
- // For action-menu tools (those with a getChildren handler), hide when empty.
220
- // Custom-component menus (e.g. ThemeMenuButton) render their own content.
221
- const actionId = menu.handler || menu.action
222
- if (actionId && menu.render === 'menu' && hasChildrenProvider(actionId)) {
223
- return getActionChildren(actionId).length > 0
224
- }
225
- return true
226
- })
227
- .reverse()
228
- )
229
-
230
- // Clean separators: remove leading, trailing, and consecutive
231
- const cleanedMenus = $derived.by(() => {
232
- const result: typeof visibleMenus = []
233
- for (const item of visibleMenus) {
234
- if (item.render === 'separator') {
235
- // Skip if first item or previous was also a separator
236
- if (result.length === 0 || result[result.length - 1].render === 'separator') continue
237
- result.push(item)
238
- } else {
239
- result.push(item)
240
- }
241
- }
242
- // Remove trailing separator
243
- while (result.length > 0 && result[result.length - 1].render === 'separator') result.pop()
244
- return result
245
- })
246
-
247
- // Total toolbar item count (visible menus + command menu if present)
248
- const toolbarItemCount = $derived(
249
- cleanedMenus.filter(m => m.render !== 'separator').length + (commandMenuConfig ? 1 : 0)
250
- )
251
-
252
- // Command menu is always the last item (rightmost)
253
- const commandMenuIndex = $derived(
254
- commandMenuConfig ? cleanedMenus.filter(m => m.render !== 'separator').length : -1
255
- )
256
-
257
- function getTabindex(index: number): number {
258
- if (activeToolbarIndex < 0) {
259
- // No item focused yet — make the last item (command menu) tabbable as default
260
- return index === toolbarItemCount - 1 ? 0 : -1
261
- }
262
- return index === activeToolbarIndex ? 0 : -1
263
- }
264
-
265
- function focusToolbarItem(index: number) {
266
- activeToolbarIndex = index
267
- if (!toolbarEl) return
268
- const buttons = toolbarEl.querySelectorAll<HTMLElement>('[data-slot="button"]')
269
- buttons[index]?.focus()
270
- }
271
-
272
- function handleToolbarKeydown(e: KeyboardEvent) {
273
- if (toolbarItemCount === 0) return
274
- const current = activeToolbarIndex < 0 ? toolbarItemCount - 1 : activeToolbarIndex
275
-
276
- if (e.key === 'ArrowRight') {
277
- e.preventDefault()
278
- focusToolbarItem((current + 1) % toolbarItemCount)
279
- } else if (e.key === 'ArrowLeft') {
280
- e.preventDefault()
281
- focusToolbarItem((current - 1 + toolbarItemCount) % toolbarItemCount)
282
- } else if (e.key === 'Home') {
283
- e.preventDefault()
284
- focusToolbarItem(0)
285
- } else if (e.key === 'End') {
286
- e.preventDefault()
287
- focusToolbarItem(toolbarItemCount - 1)
288
- } else if (e.key === 'ArrowDown') {
289
- // Menus open upward — block Bits UI's default ArrowDown-to-open
290
- e.preventDefault()
291
- e.stopPropagation()
292
- } else if (e.key === 'ArrowUp') {
293
- e.preventDefault()
294
- // If a menu is already open, focus its last item
295
- const openContent = document.querySelector<HTMLElement>('[data-slot="dropdown-menu-content"]')
296
- if (openContent) {
297
- const items = openContent.querySelectorAll<HTMLElement>('[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]')
298
- if (items.length > 0) items[items.length - 1].focus()
299
- return
300
- }
301
- // Otherwise, open the focused button's dropdown (if it has one)
302
- const focusedBtn = toolbarEl?.querySelector<HTMLElement>('[data-slot="button"]:focus')
303
- if (focusedBtn?.getAttribute('aria-haspopup')) {
304
- focusedBtn.click()
305
- // After menu renders, focus its last item
306
- requestAnimationFrame(() => {
307
- const content = document.querySelector<HTMLElement>('[data-slot="dropdown-menu-content"]')
308
- if (content) {
309
- const items = content.querySelectorAll<HTMLElement>('[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"]')
310
- if (items.length > 0) items[items.length - 1].focus()
311
- }
312
- })
313
- }
314
- }
315
- }
316
-
317
- function handleKeydown(e: KeyboardEvent) {
318
- const hideKey = shortcutsConfig.hideChrome?.key || '.'
319
- const openKey = shortcutsConfig.openCommandMenu?.key
320
-
321
- if (e.key === hideKey && (e.metaKey || e.ctrlKey)) {
322
- e.preventDefault()
323
- visible = !visible
324
- document.documentElement.classList.toggle('storyboard-chrome-hidden', !visible)
325
- }
326
- // Configurable shortcut to open the command menu (works even when hidden)
327
- if (openKey && e.key === openKey && (e.metaKey || e.ctrlKey)) {
328
- e.preventDefault()
329
- document.dispatchEvent(new CustomEvent('storyboard:toggle-palette'))
330
- }
331
- // Config-driven tool shortcuts (e.g. Cmd+D for docs, Cmd+I for inspector)
332
- for (const menu of cleanedMenus) {
333
- const shortcut = menu.shortcut
334
- if (!shortcut?.key) continue
335
- if (e.key === shortcut.key && (e.metaKey || e.ctrlKey) && !e.shiftKey) {
336
- const toolState = getToolbarToolState(menu.key)
337
- // Inactive and disabled tools don't respond to shortcuts
338
- if (toolState === 'inactive' || toolState === 'disabled') break
339
- if (menu.sidepanel) {
340
- e.preventDefault()
341
- togglePanel(menu.sidepanel)
342
- }
343
- break
344
- }
345
- }
346
- }
347
-
348
- onMount(async () => {
349
- window.addEventListener('keydown', handleKeydown)
350
- setRoutingBasePath(basePath)
351
-
352
- // Sync visible state when storyboard-chrome-hidden is toggled externally
353
- // (e.g. from command palette or other UI)
354
- chromeObserver = new MutationObserver(() => {
355
- const hidden = document.documentElement.classList.contains('storyboard-chrome-hidden')
356
- if (visible === !hidden) return
357
- visible = !hidden
358
- })
359
- chromeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
360
-
361
- // Re-evaluate action menus and prototype toolbar config on SPA navigation
362
- const { getPrototypeMetadata } = await import('./loader.js')
363
- const { setPrototypeToolbarConfig, clearPrototypeToolbarConfig } = await import('./toolbarConfigStore.js')
364
-
365
- function syncPrototypeToolbar() {
366
- let pathname = window.location.pathname
367
- const base = basePath.replace(/\/+$/, '')
368
- if (base && pathname.startsWith(base)) pathname = pathname.slice(base.length)
369
- const firstSegment = pathname.replace(/^\//, '').split('/')[0] || null
370
- if (firstSegment) {
371
- const meta = getPrototypeMetadata(firstSegment)
372
- if (meta?.toolbarConfig) {
373
- setPrototypeToolbarConfig(meta.toolbarConfig)
374
- } else {
375
- clearPrototypeToolbarConfig()
376
- }
377
- } else {
378
- clearPrototypeToolbarConfig()
379
- }
380
- }
381
-
382
- bumpNav = () => { navVersion++; syncPrototypeToolbar(); syncMobileActions() }
383
- window.addEventListener('popstate', bumpNav)
384
- origPushState = history.pushState.bind(history)
385
- history.pushState = (...args: any[]) => { origPushState(...args); bumpNav() }
386
- origReplaceState = history.replaceState.bind(history)
387
- history.replaceState = (...args: any[]) => { origReplaceState(...args); bumpNav() }
388
-
389
- // Apply prototype toolbar config for the initial route
390
- syncPrototypeToolbar()
391
-
392
- // Seed the command action registry from config
393
- if (commandMenuConfig) {
394
- initCommandActions(commandMenuConfig)
395
- }
396
-
397
- // Register sidepanel toggle actions
398
- for (const menu of sidepanelMenus) {
399
- registerCommandAction(`core:${menu.key}`, () => {
400
- togglePanel(menu.sidepanel)
401
- })
402
- }
403
-
404
- // Load all tool modules from the registry
405
- const { coreHandlers } = await import('./tools/registry.js')
406
- const toolConfigs = config.tools || {}
407
- const ctx = { basePath, showFlowInfoDialog }
408
-
409
- for (const [toolId, toolConfig] of Object.entries(toolConfigs as Record<string, any>)) {
410
- // Skip non-tool entries (separators have no handler)
411
- if (toolConfig.render === 'separator') continue
412
-
413
- // Skip disabled tools — don't load their modules at all
414
- if (getToolbarToolState(toolId) === 'disabled') continue
415
-
416
- // Resolve handler module via core:/custom: prefix
417
- const handlerRef = toolConfig.handler || `core:${toolId}`
418
- const loadModule = resolveHandlerModule(handlerRef, coreHandlers, customHandlers)
419
- if (!loadModule) continue
420
-
421
- try {
422
- const mod = await loadModule()
423
- const toolCtx = { ...ctx, config: toolConfig }
424
-
425
- // Run guard — skip if guard returns false
426
- if (mod.guard) {
427
- const ok = await mod.guard(toolCtx)
428
- if (!ok) continue
429
- }
430
-
431
- // Run setup
432
- if (mod.setup) {
433
- const setupResult = await mod.setup(toolCtx)
434
- if (setupResult) {
435
- toolData[toolId] = setupResult
436
- }
437
- }
438
-
439
- // Register handler as command action
440
- if (mod.handler) {
441
- const handlerResult = await mod.handler(toolCtx)
442
- const actionId = toolConfig.handler || `core:${toolId}`
443
- // Store handler result in toolData for component access
444
- if (handlerResult && !handlerResult.getChildren) {
445
- toolData[toolId] = { ...(toolData[toolId] || {}), ...handlerResult }
446
- }
447
- registerCommandAction(actionId, handlerResult)
448
- }
449
-
450
- // Load component
451
- if (mod.component) {
452
- const component = await mod.component(toolConfig.render)
453
- toolComponents[toolId] = component
454
- }
455
- } catch { /* tool failed to load — skip gracefully */ }
456
- }
457
-
458
- // Load side panel component
459
- try {
460
- if (sidepanelMenus.length > 0) {
461
- const mod = await import('./SidePanel.svelte')
462
- SidePanel = mod.default
463
- }
464
- } catch {}
465
-
466
- // Listen for canvas mount/unmount events (React↔Svelte bridge)
467
- document.addEventListener('storyboard:canvas:mounted', handleCanvasMounted)
468
- document.addEventListener('storyboard:canvas:unmounted', handleCanvasUnmounted)
469
- document.addEventListener('storyboard:canvas:zoom-changed', handleZoomChanged)
470
- document.addEventListener('storyboard:canvas:status', handleCanvasMounted)
471
- syncCanvasBridgeState()
472
-
473
- // Subscribe to mobile viewport changes and sync mobile command actions
474
- syncMobileActions()
475
- unsubMobile = subscribeToMobile((mobile: boolean) => {
476
- isMobileState = mobile
477
- syncMobileActions()
478
- })
479
- })
480
-
481
- onDestroy(() => {
482
- window.removeEventListener('keydown', handleKeydown)
483
- if (bumpNav) window.removeEventListener('popstate', bumpNav)
484
- if (origPushState) history.pushState = origPushState
485
- if (origReplaceState) history.replaceState = origReplaceState
486
- if (unsubMobile) unsubMobile()
487
- clearDynamicActions('mobile-toolbar')
488
- chromeObserver?.disconnect()
489
- document.removeEventListener('storyboard:canvas:mounted', handleCanvasMounted)
490
- document.removeEventListener('storyboard:canvas:unmounted', handleCanvasUnmounted)
491
- document.removeEventListener('storyboard:canvas:zoom-changed', handleZoomChanged)
492
- document.removeEventListener('storyboard:canvas:status', handleCanvasMounted)
493
- })
494
-
495
- function handleCanvasMounted(e: Event) {
496
- canvasActive = true
497
- const detail = (e as CustomEvent).detail
498
- activeCanvasId = detail?.canvasId || detail?.name || ''
499
- canvasZoom = detail?.zoom ?? 100
500
- syncMobileActions()
501
- }
502
-
503
- function handleCanvasUnmounted() {
504
- canvasActive = false
505
- activeCanvasId = ''
506
- canvasZoom = 100
507
- syncMobileActions()
508
- }
509
-
510
- function handleZoomChanged(e: Event) {
511
- if (!canvasActive) canvasActive = true
512
- canvasZoom = (e as CustomEvent).detail?.zoom ?? canvasZoom
513
- }
514
-
515
- function syncCanvasBridgeState() {
516
- if (typeof window === 'undefined') return
517
- const state = (window as any).__storyboardCanvasBridgeState
518
- if (state && typeof state === 'object') {
519
- canvasActive = state.active === true
520
- activeCanvasId = state.canvasId || state.name || ''
521
- canvasZoom = typeof state.zoom === 'number' ? state.zoom : 100
522
- }
523
- if (!canvasActive) {
524
- document.dispatchEvent(new CustomEvent('storyboard:canvas:status-request'))
525
- }
526
- }
527
-
528
- /**
529
- * Sync mobile command actions — when in mobile viewport, toolbar tools
530
- * become dynamic command actions in the ⌘ menu.
531
- */
532
- async function syncMobileActions() {
533
- if (!isMobileState) {
534
- if (mobileActionsRegistered) {
535
- clearDynamicActions('mobile-toolbar')
536
- mobileActionsRegistered = false
537
- }
538
- return
539
- }
540
-
541
- const actions: any[] = []
542
- const handlers: Record<string, any> = {}
543
- const toolConfigs = config.tools || {}
544
-
545
- // Main-toolbar tools → command actions
546
- actions.push({ type: 'header', label: 'Tools', id: '_mobile_header' })
547
-
548
- for (const [key, tool] of Object.entries(toolConfigs as Record<string, any>)) {
549
- if (tool.surface !== 'main-toolbar') continue
550
- if (tool.render === 'separator') continue
551
- if (!tool.prod && !isLocalDev) continue
552
- if (getToolbarToolState(key) === 'disabled') continue
553
- if (isExcludedByRoute(tool)) continue
554
-
555
- // Always use mobile:-prefixed ids to avoid clobbering shared desktop handlers
556
- const mobileId = `mobile:${key}`
557
- const desktopActionId = tool.handler || `core:${key}`
558
-
559
- // Menu tools with getChildren → submenu (delegate to existing desktop handler)
560
- if (tool.render === 'menu' && hasChildrenProvider(desktopActionId)) {
561
- actions.push({
562
- id: mobileId,
563
- label: tool.label || tool.ariaLabel,
564
- type: 'submenu',
565
- modes: tool.modes || ['*'],
566
- excludeRoutes: tool.excludeRoutes,
567
- toolKey: key,
568
- localOnly: !tool.prod,
569
- })
570
- handlers[mobileId] = {
571
- getChildren: () => getActionChildren(desktopActionId),
572
- }
573
- }
574
- // Sidepanel tools → default action (toggle panel)
575
- else if (tool.render === 'sidepanel') {
576
- actions.push({
577
- id: mobileId,
578
- label: tool.ariaLabel || tool.label || key,
579
- type: 'default',
580
- modes: tool.modes || ['*'],
581
- excludeRoutes: tool.excludeRoutes,
582
- toolKey: key,
583
- localOnly: !tool.prod,
584
- })
585
- handlers[mobileId] = () => { togglePanel(tool.sidepanel) }
586
- }
587
- // Button tools (e.g. comments) — only if the desktop guard passed (component loaded)
588
- else if (tool.render === 'button' && toolComponents[key]) {
589
- try {
590
- if (key === 'comments') {
591
- const { toggleCommentMode, isCommentModeActive } = await import('./comments/commentMode.js')
592
- const { isAuthenticated } = await import('./comments/auth.js')
593
- const { openAuthModal } = await import('./comments/ui/authModal.js')
594
- actions.push({
595
- id: mobileId,
596
- label: tool.ariaLabel || 'Comments',
597
- type: 'toggle',
598
- modes: tool.modes || ['*'],
599
- excludeRoutes: tool.excludeRoutes,
600
- toolKey: key,
601
- localOnly: !tool.prod,
602
- })
603
- handlers[mobileId] = {
604
- execute: async () => {
605
- if (!isAuthenticated()) {
606
- const user = await openAuthModal()
607
- if (!user) return
608
- }
609
- toggleCommentMode()
610
- },
611
- getState: () => isCommentModeActive(),
612
- }
613
- }
614
- } catch { /* comments module not available */ }
615
- }
616
- }
617
-
618
- // Theme tool — special handling (component-only, no handler in registry)
619
- if (toolConfigs.theme && toolComponents.theme) {
620
- // Only add if not already handled above (theme has no getChildren in registry)
621
- if (!actions.some(a => a.id === 'mobile:theme')) {
622
- try {
623
- const { themeState, setTheme, THEMES } = await import('./stores/themeStore.js')
624
- actions.push({
625
- id: 'mobile:theme',
626
- label: 'Theme',
627
- type: 'submenu',
628
- modes: ['*'],
629
- toolKey: 'theme',
630
- localOnly: !toolConfigs.theme.prod,
631
- })
632
- handlers['mobile:theme'] = {
633
- getChildren: () => {
634
- const current = themeState.theme
635
- return THEMES.map((t: any) => ({
636
- id: `theme:${t.value}`,
637
- label: t.label,
638
- type: 'toggle' as const,
639
- active: current === t.value,
640
- execute: () => setTheme(t.value),
641
- }))
642
- },
643
- }
644
- } catch { /* theme store not available */ }
645
- }
646
- }
647
-
648
- // Canvas toolbar stays visible on mobile — no need to duplicate canvas tools here
649
-
650
- setDynamicActions('mobile-toolbar', actions, handlers)
651
- mobileActionsRegistered = true
652
- }
653
-
654
- // Flow info dialog state — driven by core/show-flow-info action
655
- let flowDialogOpen = $state(false)
656
- let flowName = $state('default')
657
- let flowJson = $state('')
658
- let flowError: string | null = $state(null)
659
-
660
- function showFlowInfoDialog(name: string, json: string, error: string | null) {
661
- flowName = name
662
- flowJson = json
663
- flowError = error
664
- flowDialogOpen = true
665
- }
666
- </script>
667
-
668
- {#if !isEmbed}
669
- {#if visible && canvasActive && canvasMenus.length > 0}
670
- <div
671
- class="fixed bottom-6 left-6 z-[9999] font-sans flex items-center gap-3"
672
- role="toolbar"
673
- aria-label="Canvas toolbar"
674
- >
675
- {#each canvasMenus as canvasTool (canvasTool.key)}
676
- {#if toolComponents[canvasTool.key]}
677
- {@const CanvasToolComponent = toolComponents[canvasTool.key]}
678
- {#if canvasTool.render === 'menu'}
679
- <Tooltip.Root>
680
- <Tooltip.Trigger>
681
- <span data-local-only={isToolbarToolLocalOnly(canvasTool.key) || undefined}>
682
- <CanvasToolComponent
683
- config={canvasTool}
684
- data={toolData[canvasTool.key]}
685
- canvasName={activeCanvasId}
686
- zoom={canvasZoom}
687
- tabindex={0}
688
- />
689
- </span>
690
- </Tooltip.Trigger>
691
- <Tooltip.Content side="top">{canvasTool.ariaLabel || canvasTool.key}</Tooltip.Content>
692
- </Tooltip.Root>
693
- {:else}
694
- <CanvasToolComponent
695
- config={canvasTool}
696
- data={toolData[canvasTool.key]}
697
- canvasName={activeCanvasId}
698
- zoom={canvasZoom}
699
- tabindex={0}
700
- />
701
- {/if}
702
- {/if}
703
- {/each}
704
- </div>
705
- {/if}
706
- <div
707
- id="storyboard-controls"
708
- class="fixed bottom-6 right-6 z-[9999] font-sans flex items-end gap-3"
709
- data-core-ui-bar
710
- role="toolbar"
711
- tabindex="0"
712
- aria-label="Storyboard controls"
713
- onkeydown={handleToolbarKeydown}
714
- bind:this={toolbarEl}
715
- >
716
- {#if visible && !isMobileState}
717
- {#each cleanedMenus as menu, i (menu.key)}
718
- {#if menu.render === 'separator'}
719
- <div class="toolbar-separator" aria-hidden="true"></div>
720
- {:else}
721
- <Tooltip.Root>
722
- <Tooltip.Trigger>
723
- {#if menu.render === 'sidepanel'}
724
- {@const toolState = getToolbarToolState(menu.key)}
725
- <TriggerButton
726
- active={$sidePanelState.open && $sidePanelState.activeTab === menu.sidepanel}
727
- inactive={toolState === 'inactive'}
728
- dimmed={toolState === 'dimmed'}
729
- localOnly={isToolbarToolLocalOnly(menu.key)}
730
- size="icon-xl"
731
- aria-label={menu.ariaLabel || menu.key}
732
- tabindex={getTabindex(i)}
733
- onfocus={() => { activeToolbarIndex = i }}
734
- onclick={() => togglePanel(menu.sidepanel)}
735
- >
736
- <Icon name={menu.icon || menu.key} size={16} {...(menu.meta || {})} />
737
- </TriggerButton>
738
- {:else if toolComponents[menu.key]}
739
- {@const toolState = getToolbarToolState(menu.key)}
740
- {@const ToolComponent = toolComponents[menu.key]}
741
- <span
742
- data-tool-state={toolState}
743
- data-local-only={isToolbarToolLocalOnly(menu.key) || undefined}
744
- class={toolState === 'inactive' ? 'tool-inactive' : toolState === 'dimmed' ? 'tool-dimmed' : ''}
745
- >
746
- <ToolComponent
747
- config={menu}
748
- data={toolData[menu.key]}
749
- tabindex={getTabindex(i)}
750
- localOnly={isToolbarToolLocalOnly(menu.key)}
751
- {basePath}
752
- />
753
- </span>
754
- {/if}
755
- </Tooltip.Trigger>
756
- <Tooltip.Content side="top">{menu.ariaLabel || menu.key}</Tooltip.Content>
757
- </Tooltip.Root>
758
- {/if}
759
- {/each}
760
- {/if}
761
- {#if commandMenuConfig}
762
- <div class={visible ? '' : 'default-button-dimmed'}>
763
- <Tooltip.Root>
764
- <Tooltip.Trigger>
765
- <CommandPalette tabindex={getTabindex(commandMenuIndex)} icon={commandMenuConfig.icon} iconMeta={commandMenuConfig.meta} />
766
- </Tooltip.Trigger>
767
- <Tooltip.Content side="top">Command Menu</Tooltip.Content>
768
- </Tooltip.Root>
769
- </div>
770
- {/if}
771
- </div>
772
- {/if}
773
-
774
- {#if !isEmbed && SidePanel}
775
- <SidePanel onClose={() => focusToolbarItem(activeToolbarIndex < 0 ? toolbarItemCount - 1 : activeToolbarIndex)} />
776
- {/if}
777
-
778
- {#if !isEmbed}
779
- <PwaInstallBanner />
780
- {/if}
781
-
782
- <!-- Flow info panel (previously inside CommandMenu) -->
783
- <Panel.Root bind:open={flowDialogOpen}>
784
- <Panel.Content>
785
- <Panel.Header>
786
- <Panel.Title>Flow: {flowName}</Panel.Title>
787
- <Panel.Close />
788
- </Panel.Header>
789
- <Panel.Body>
790
- {#if flowError}
791
- <span class="text-destructive text-sm">{flowError}</span>
792
- {:else}
793
- <pre class="m-0 bg-transparent text-sm font-mono leading-relaxed whitespace-pre-wrap break-words">{flowJson}</pre>
794
- {/if}
795
- </Panel.Body>
796
- </Panel.Content>
797
- </Panel.Root>
798
-
799
- <style>
800
- .toolbar-separator {
801
- width: 1px;
802
- height: 20px;
803
- background: var(--sb--trigger-border, var(--color-slate-400));
804
- opacity: 0.4;
805
- flex-shrink: 0;
806
- }
807
-
808
- .default-button-dimmed {
809
- opacity: 0.3;
810
- transition: opacity 200ms;
811
- }
812
-
813
- .default-button-dimmed:hover,
814
- .default-button-dimmed:focus-within {
815
- opacity: 1;
816
- }
817
-
818
- .tool-inactive {
819
- opacity: 0.45;
820
- pointer-events: none;
821
- }
822
- .tool-dimmed {
823
- opacity: 0.3;
824
- transition: opacity 200ms;
825
- }
826
- .tool-dimmed:hover,
827
- .tool-dimmed:focus-within {
828
- opacity: 1;
829
- }
830
- [data-local-only] {
831
- position: relative;
832
- }
833
- [data-local-only]::after {
834
- content: '';
835
- position: absolute;
836
- top: -1px;
837
- right: -1px;
838
- width: 8px;
839
- height: 8px;
840
- background: hsl(212, 92%, 45%);
841
- border-radius: 50%;
842
- border: 2px solid var(--sb--sc-border-color, transparent);
843
- box-sizing: content-box;
844
- pointer-events: none;
845
- z-index: 1;
846
- }
847
- </style>