@dfosco/storyboard 0.5.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (592) hide show
  1. package/commandpalette.config.json +152 -0
  2. package/dist/storyboard-ui.css +1 -0
  3. package/dist/storyboard-ui.js +21328 -0
  4. package/dist/storyboard-ui.js.map +1 -0
  5. package/dist/tailwind.css +2 -0
  6. package/dist/tiny-canvas.css +1 -0
  7. package/dist/tiny-canvas.js +389 -0
  8. package/package.json +121 -0
  9. package/paste.config.json +67 -0
  10. package/scaffold/AGENTS.md +432 -0
  11. package/scaffold/agents/prompt-agent.agent.md +181 -0
  12. package/scaffold/agents/terminal-agent.agent.md +351 -0
  13. package/scaffold/codex/config.toml +246 -0
  14. package/scaffold/deploy.yml +103 -0
  15. package/scaffold/githooks/pre-push +114 -0
  16. package/scaffold/gitignore +64 -0
  17. package/scaffold/manifest.json +56 -0
  18. package/scaffold/preview.yml +181 -0
  19. package/scaffold/scripts/link.sh +26 -0
  20. package/scaffold/scripts/unlink.sh +10 -0
  21. package/scaffold/skills/agent-browser/SKILL.md +260 -0
  22. package/scaffold/skills/canvas/SKILL.md +364 -0
  23. package/scaffold/skills/create/SKILL.md +501 -0
  24. package/scaffold/skills/ship/SKILL.md +237 -0
  25. package/scaffold/skills/storyboard/SKILL.md +360 -0
  26. package/scaffold/skills/update-storyboard/SKILL.md +16 -0
  27. package/scaffold/skills/update-storyboard/update-storyboard-packages.sh +26 -0
  28. package/scaffold/skills/vitest/GENERATION.md +5 -0
  29. package/scaffold/skills/vitest/SKILL.md +52 -0
  30. package/scaffold/skills/vitest/references/advanced-environments.md +264 -0
  31. package/scaffold/skills/vitest/references/advanced-projects.md +300 -0
  32. package/scaffold/skills/vitest/references/advanced-type-testing.md +237 -0
  33. package/scaffold/skills/vitest/references/advanced-vi.md +249 -0
  34. package/scaffold/skills/vitest/references/core-cli.md +166 -0
  35. package/scaffold/skills/vitest/references/core-config.md +174 -0
  36. package/scaffold/skills/vitest/references/core-describe.md +193 -0
  37. package/scaffold/skills/vitest/references/core-expect.md +219 -0
  38. package/scaffold/skills/vitest/references/core-hooks.md +244 -0
  39. package/scaffold/skills/vitest/references/core-test-api.md +233 -0
  40. package/scaffold/skills/vitest/references/features-concurrency.md +250 -0
  41. package/scaffold/skills/vitest/references/features-context.md +238 -0
  42. package/scaffold/skills/vitest/references/features-coverage.md +207 -0
  43. package/scaffold/skills/vitest/references/features-filtering.md +211 -0
  44. package/scaffold/skills/vitest/references/features-mocking.md +265 -0
  45. package/scaffold/skills/vitest/references/features-snapshots.md +207 -0
  46. package/scaffold/skills/worktree/SKILL.md +93 -0
  47. package/scaffold/storyboard.config.json +44 -0
  48. package/src/canvas/Canvas.jsx +78 -0
  49. package/src/canvas/Draggable.jsx +235 -0
  50. package/src/canvas/index.d.ts +41 -0
  51. package/src/canvas/index.js +6 -0
  52. package/src/canvas/style.css +118 -0
  53. package/src/canvas/useResetCanvas.js +17 -0
  54. package/src/canvas/utils.js +136 -0
  55. package/src/core/assets/fonts/IoskeleyMono-Bold.woff2 +0 -0
  56. package/src/core/assets/fonts/IoskeleyMono-Italic.woff2 +0 -0
  57. package/src/core/assets/fonts/IoskeleyMono-Medium.woff2 +0 -0
  58. package/src/core/assets/fonts/IoskeleyMono-Regular.woff2 +0 -0
  59. package/src/core/assets/fonts/IoskeleyMono-SemiBold.woff2 +0 -0
  60. package/src/core/autosync/server.js +714 -0
  61. package/src/core/autosync/server.test.js +158 -0
  62. package/src/core/canvas/__tests__/agent-integration.test.js +596 -0
  63. package/src/core/canvas/__tests__/helpers/browser.js +95 -0
  64. package/src/core/canvas/__tests__/helpers/canvas-api.js +129 -0
  65. package/src/core/canvas/__tests__/helpers/perf.js +118 -0
  66. package/src/core/canvas/__tests__/helpers/setup.js +176 -0
  67. package/src/core/canvas/__tests__/helpers/tmux.js +130 -0
  68. package/src/core/canvas/__tests__/helpers/transcript.js +132 -0
  69. package/src/core/canvas/__tests__/terminal-integration.test.js +177 -0
  70. package/src/core/canvas/collision.js +292 -0
  71. package/src/core/canvas/collision.test.js +371 -0
  72. package/src/core/canvas/compact.js +83 -0
  73. package/src/core/canvas/deriveCanvasId.test.js +40 -0
  74. package/src/core/canvas/githubEmbeds.js +527 -0
  75. package/src/core/canvas/githubEmbeds.test.js +302 -0
  76. package/src/core/canvas/hot-pool.js +766 -0
  77. package/src/core/canvas/identity.js +107 -0
  78. package/src/core/canvas/identity.test.js +100 -0
  79. package/src/core/canvas/materializer.js +259 -0
  80. package/src/core/canvas/materializer.test.js +356 -0
  81. package/src/core/canvas/selectedWidgets.js +270 -0
  82. package/src/core/canvas/selectedWidgets.test.js +321 -0
  83. package/src/core/canvas/server.js +3134 -0
  84. package/src/core/canvas/server.test.js +379 -0
  85. package/src/core/canvas/terminal-config.js +330 -0
  86. package/src/core/canvas/terminal-registry.js +465 -0
  87. package/src/core/canvas/terminal-server.js +1436 -0
  88. package/src/core/canvas/writeGuard.js +53 -0
  89. package/src/core/cli/agent.js +85 -0
  90. package/src/core/cli/branch.js +386 -0
  91. package/src/core/cli/canvasAdd.js +241 -0
  92. package/src/core/cli/canvasBatch.js +98 -0
  93. package/src/core/cli/canvasBounds.js +160 -0
  94. package/src/core/cli/canvasRead.js +236 -0
  95. package/src/core/cli/canvasUpdate.js +179 -0
  96. package/src/core/cli/code.js +67 -0
  97. package/src/core/cli/compact.js +62 -0
  98. package/src/core/cli/create.js +674 -0
  99. package/src/core/cli/dev-helpers.js +53 -0
  100. package/src/core/cli/dev-helpers.test.js +53 -0
  101. package/src/core/cli/dev.js +430 -0
  102. package/src/core/cli/exit.js +38 -0
  103. package/src/core/cli/flags.js +174 -0
  104. package/src/core/cli/flags.test.js +155 -0
  105. package/src/core/cli/index.js +233 -0
  106. package/src/core/cli/intro.js +37 -0
  107. package/src/core/cli/proxy.js +319 -0
  108. package/src/core/cli/proxy.test.js +63 -0
  109. package/src/core/cli/schemas.js +223 -0
  110. package/src/core/cli/server.js +192 -0
  111. package/src/core/cli/serverUrl.js +61 -0
  112. package/src/core/cli/sessions.js +459 -0
  113. package/src/core/cli/setup.js +404 -0
  114. package/src/core/cli/terminal-commands.js +287 -0
  115. package/src/core/cli/terminal-messaging.js +231 -0
  116. package/src/core/cli/terminal-welcome.js +515 -0
  117. package/src/core/cli/updateVersion.js +124 -0
  118. package/src/core/comments/api.js +284 -0
  119. package/src/core/comments/api.test.js +282 -0
  120. package/src/core/comments/auth.js +151 -0
  121. package/src/core/comments/auth.test.js +167 -0
  122. package/src/core/comments/commentCache.js +109 -0
  123. package/src/core/comments/commentCache.test.js +48 -0
  124. package/src/core/comments/commentDrafts.js +68 -0
  125. package/src/core/comments/commentMode.js +63 -0
  126. package/src/core/comments/commentMode.test.js +90 -0
  127. package/src/core/comments/config.js +47 -0
  128. package/src/core/comments/config.test.js +77 -0
  129. package/src/core/comments/graphql.js +65 -0
  130. package/src/core/comments/graphql.test.js +95 -0
  131. package/src/core/comments/index.js +42 -0
  132. package/src/core/comments/metadata.js +52 -0
  133. package/src/core/comments/metadata.test.js +110 -0
  134. package/src/core/comments/queries.js +245 -0
  135. package/src/core/comments/ui/AuthModal.jsx +114 -0
  136. package/src/core/comments/ui/CommentOverlay.js +52 -0
  137. package/src/core/comments/ui/CommentWindow.jsx +329 -0
  138. package/src/core/comments/ui/CommentsDrawer.jsx +102 -0
  139. package/src/core/comments/ui/Composer.jsx +64 -0
  140. package/src/core/comments/ui/authModal.js +66 -0
  141. package/src/core/comments/ui/authModal.test.js +76 -0
  142. package/src/core/comments/ui/comment-cursor-dark.svg +1 -0
  143. package/src/core/comments/ui/comment-cursor.svg +1 -0
  144. package/src/core/comments/ui/comment-layout.css +142 -0
  145. package/src/core/comments/ui/commentWindow.js +121 -0
  146. package/src/core/comments/ui/comments.css +242 -0
  147. package/src/core/comments/ui/commentsDrawer.js +84 -0
  148. package/src/core/comments/ui/composer.js +136 -0
  149. package/src/core/comments/ui/index.js +14 -0
  150. package/src/core/comments/ui/mount.js +687 -0
  151. package/src/core/comments/ui/mount.test.js +336 -0
  152. package/src/core/data/dotPath.js +53 -0
  153. package/src/core/data/dotPath.test.js +114 -0
  154. package/src/core/data/loader.js +409 -0
  155. package/src/core/data/loader.test.js +599 -0
  156. package/src/core/data/viewfinder.js +363 -0
  157. package/src/core/data/viewfinder.test.js +456 -0
  158. package/src/core/devtools/devtools-consumer.js +28 -0
  159. package/src/core/devtools/devtools.js +144 -0
  160. package/src/core/devtools/devtools.test.js +75 -0
  161. package/src/core/devtools/sceneDebug.js +112 -0
  162. package/src/core/devtools/sceneDebug.test.js +141 -0
  163. package/src/core/index.js +124 -0
  164. package/src/core/inspector/fiberWalker.js +239 -0
  165. package/src/core/inspector/highlighter.js +275 -0
  166. package/src/core/inspector/mouseMode.js +259 -0
  167. package/src/core/lib/components/ui/alert/alert-action.jsx +11 -0
  168. package/src/core/lib/components/ui/alert/alert-description.jsx +11 -0
  169. package/src/core/lib/components/ui/alert/alert-title.jsx +11 -0
  170. package/src/core/lib/components/ui/alert/alert.jsx +25 -0
  171. package/src/core/lib/components/ui/alert/index.js +17 -0
  172. package/src/core/lib/components/ui/avatar/avatar-badge.jsx +22 -0
  173. package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +18 -0
  174. package/src/core/lib/components/ui/avatar/avatar-group-count.jsx +19 -0
  175. package/src/core/lib/components/ui/avatar/avatar-group.jsx +19 -0
  176. package/src/core/lib/components/ui/avatar/avatar-image.jsx +15 -0
  177. package/src/core/lib/components/ui/avatar/avatar.jsx +19 -0
  178. package/src/core/lib/components/ui/avatar/index.js +22 -0
  179. package/src/core/lib/components/ui/badge/badge.jsx +31 -0
  180. package/src/core/lib/components/ui/badge/index.js +2 -0
  181. package/src/core/lib/components/ui/button/button.jsx +100 -0
  182. package/src/core/lib/components/ui/button/index.js +12 -0
  183. package/src/core/lib/components/ui/card/card-action.jsx +11 -0
  184. package/src/core/lib/components/ui/card/card-content.jsx +11 -0
  185. package/src/core/lib/components/ui/card/card-description.jsx +11 -0
  186. package/src/core/lib/components/ui/card/card-footer.jsx +11 -0
  187. package/src/core/lib/components/ui/card/card-header.jsx +19 -0
  188. package/src/core/lib/components/ui/card/card-title.jsx +11 -0
  189. package/src/core/lib/components/ui/card/card.jsx +17 -0
  190. package/src/core/lib/components/ui/card/index.js +25 -0
  191. package/src/core/lib/components/ui/checkbox/checkbox.jsx +29 -0
  192. package/src/core/lib/components/ui/checkbox/index.js +6 -0
  193. package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +7 -0
  194. package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +7 -0
  195. package/src/core/lib/components/ui/collapsible/collapsible.jsx +7 -0
  196. package/src/core/lib/components/ui/collapsible/index.js +13 -0
  197. package/src/core/lib/components/ui/dialog/dialog-close.jsx +7 -0
  198. package/src/core/lib/components/ui/dialog/dialog-content.jsx +34 -0
  199. package/src/core/lib/components/ui/dialog/dialog-description.jsx +15 -0
  200. package/src/core/lib/components/ui/dialog/dialog-footer.jsx +23 -0
  201. package/src/core/lib/components/ui/dialog/dialog-header.jsx +11 -0
  202. package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +15 -0
  203. package/src/core/lib/components/ui/dialog/dialog-portal.jsx +4 -0
  204. package/src/core/lib/components/ui/dialog/dialog-title.jsx +15 -0
  205. package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +7 -0
  206. package/src/core/lib/components/ui/dialog/dialog.jsx +4 -0
  207. package/src/core/lib/components/ui/dialog/index.js +34 -0
  208. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +8 -0
  209. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +30 -0
  210. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +22 -0
  211. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +16 -0
  212. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +7 -0
  213. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +20 -0
  214. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-label.jsx +17 -0
  215. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +4 -0
  216. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +7 -0
  217. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +29 -0
  218. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +15 -0
  219. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.jsx +16 -0
  220. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +15 -0
  221. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +23 -0
  222. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +4 -0
  223. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +7 -0
  224. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +4 -0
  225. package/src/core/lib/components/ui/dropdown-menu/index.js +54 -0
  226. package/src/core/lib/components/ui/input/index.js +7 -0
  227. package/src/core/lib/components/ui/input/input.jsx +19 -0
  228. package/src/core/lib/components/ui/label/index.js +7 -0
  229. package/src/core/lib/components/ui/label/label.jsx +19 -0
  230. package/src/core/lib/components/ui/panel/index.js +24 -0
  231. package/src/core/lib/components/ui/panel/panel-body.jsx +11 -0
  232. package/src/core/lib/components/ui/panel/panel-close.jsx +16 -0
  233. package/src/core/lib/components/ui/panel/panel-content.jsx +29 -0
  234. package/src/core/lib/components/ui/panel/panel-footer.jsx +11 -0
  235. package/src/core/lib/components/ui/panel/panel-header.jsx +11 -0
  236. package/src/core/lib/components/ui/panel/panel-title.jsx +12 -0
  237. package/src/core/lib/components/ui/panel/panel.jsx +4 -0
  238. package/src/core/lib/components/ui/popover/index.js +28 -0
  239. package/src/core/lib/components/ui/popover/popover-close.jsx +7 -0
  240. package/src/core/lib/components/ui/popover/popover-content.jsx +22 -0
  241. package/src/core/lib/components/ui/popover/popover-description.jsx +11 -0
  242. package/src/core/lib/components/ui/popover/popover-header.jsx +11 -0
  243. package/src/core/lib/components/ui/popover/popover-portal.jsx +4 -0
  244. package/src/core/lib/components/ui/popover/popover-title.jsx +11 -0
  245. package/src/core/lib/components/ui/popover/popover-trigger.jsx +8 -0
  246. package/src/core/lib/components/ui/popover/popover.jsx +4 -0
  247. package/src/core/lib/components/ui/searchable-list.jsx +160 -0
  248. package/src/core/lib/components/ui/select/index.js +37 -0
  249. package/src/core/lib/components/ui/select/select-content.jsx +30 -0
  250. package/src/core/lib/components/ui/select/select-group-heading.jsx +17 -0
  251. package/src/core/lib/components/ui/select/select-group.jsx +15 -0
  252. package/src/core/lib/components/ui/select/select-item.jsx +26 -0
  253. package/src/core/lib/components/ui/select/select-label.jsx +11 -0
  254. package/src/core/lib/components/ui/select/select-portal.jsx +4 -0
  255. package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +18 -0
  256. package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +18 -0
  257. package/src/core/lib/components/ui/select/select-separator.jsx +15 -0
  258. package/src/core/lib/components/ui/select/select-trigger.jsx +25 -0
  259. package/src/core/lib/components/ui/select/select.jsx +4 -0
  260. package/src/core/lib/components/ui/separator/index.js +7 -0
  261. package/src/core/lib/components/ui/separator/separator.jsx +22 -0
  262. package/src/core/lib/components/ui/sheet/index.js +34 -0
  263. package/src/core/lib/components/ui/sheet/sheet-close.jsx +7 -0
  264. package/src/core/lib/components/ui/sheet/sheet-content.jsx +35 -0
  265. package/src/core/lib/components/ui/sheet/sheet-description.jsx +15 -0
  266. package/src/core/lib/components/ui/sheet/sheet-footer.jsx +11 -0
  267. package/src/core/lib/components/ui/sheet/sheet-header.jsx +11 -0
  268. package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +15 -0
  269. package/src/core/lib/components/ui/sheet/sheet-portal.jsx +4 -0
  270. package/src/core/lib/components/ui/sheet/sheet-title.jsx +15 -0
  271. package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +7 -0
  272. package/src/core/lib/components/ui/sheet/sheet.jsx +4 -0
  273. package/src/core/lib/components/ui/textarea/index.js +7 -0
  274. package/src/core/lib/components/ui/textarea/textarea.jsx +18 -0
  275. package/src/core/lib/components/ui/toggle/index.js +8 -0
  276. package/src/core/lib/components/ui/toggle/toggle.jsx +36 -0
  277. package/src/core/lib/components/ui/toggle-group/index.js +10 -0
  278. package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +29 -0
  279. package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +43 -0
  280. package/src/core/lib/components/ui/tooltip/index.js +3 -0
  281. package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +21 -0
  282. package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +23 -0
  283. package/src/core/lib/components/ui/tooltip/tooltip.jsx +11 -0
  284. package/src/core/lib/components/ui/trigger-button/index.js +6 -0
  285. package/src/core/lib/components/ui/trigger-button/trigger-button.css +38 -0
  286. package/src/core/lib/components/ui/trigger-button/trigger-button.jsx +63 -0
  287. package/src/core/lib/utils/index.js +6 -0
  288. package/src/core/logger/devLogger.js +238 -0
  289. package/src/core/logger/devLogger.test.js +193 -0
  290. package/src/core/modes/modes.css +98 -0
  291. package/src/core/modes/modes.js +492 -0
  292. package/src/core/modes/modes.test.js +562 -0
  293. package/src/core/mountStoryboardCore.js +478 -0
  294. package/src/core/rename-watcher/config.json +23 -0
  295. package/src/core/rename-watcher/watcher.js +531 -0
  296. package/src/core/scaffold.js +100 -0
  297. package/src/core/server/index.js +391 -0
  298. package/src/core/session/bodyClasses.js +128 -0
  299. package/src/core/session/bodyClasses.test.js +192 -0
  300. package/src/core/session/hashSubscribe.js +19 -0
  301. package/src/core/session/hashSubscribe.test.js +62 -0
  302. package/src/core/session/hideMode.js +424 -0
  303. package/src/core/session/hideMode.test.js +268 -0
  304. package/src/core/session/interceptHideParams.js +35 -0
  305. package/src/core/session/interceptHideParams.test.js +90 -0
  306. package/src/core/session/localStorage.js +134 -0
  307. package/src/core/session/localStorage.test.js +148 -0
  308. package/src/core/session/session.js +76 -0
  309. package/src/core/session/session.test.js +91 -0
  310. package/src/core/stores/canvasConfig.js +134 -0
  311. package/src/core/stores/canvasConfig.test.js +120 -0
  312. package/src/core/stores/commandActions.js +284 -0
  313. package/src/core/stores/commandPaletteConfig.js +31 -0
  314. package/src/core/stores/configSchema.js +232 -0
  315. package/src/core/stores/configSchema.test.js +72 -0
  316. package/src/core/stores/configStore.js +161 -0
  317. package/src/core/stores/customerModeConfig.js +30 -0
  318. package/src/core/stores/featureFlags.js +127 -0
  319. package/src/core/stores/paletteProviders.js +360 -0
  320. package/src/core/stores/paletteProviders.test.js +186 -0
  321. package/src/core/stores/plugins.js +40 -0
  322. package/src/core/stores/plugins.test.js +68 -0
  323. package/src/core/stores/recentArtifacts.js +68 -0
  324. package/src/core/stores/recentArtifacts.test.js +71 -0
  325. package/src/core/stores/sidePanelStore.ts +143 -0
  326. package/src/core/stores/themeStore.ts +291 -0
  327. package/src/core/stores/toolRegistry.js +227 -0
  328. package/src/core/stores/toolStateStore.js +183 -0
  329. package/src/core/stores/toolStateStore.test.js +220 -0
  330. package/src/core/stores/toolbarConfigStore.js +165 -0
  331. package/src/core/stores/uiConfig.js +64 -0
  332. package/src/core/stores/uiConfig.test.js +63 -0
  333. package/src/core/styles/tailwind.css +204 -0
  334. package/src/core/tools/handlers/autosync.js +12 -0
  335. package/src/core/tools/handlers/canvasAddWidget.js +11 -0
  336. package/src/core/tools/handlers/canvasAgents.js +20 -0
  337. package/src/core/tools/handlers/canvasToolbar.js +56 -0
  338. package/src/core/tools/handlers/commandPalette.js +9 -0
  339. package/src/core/tools/handlers/comments.js +16 -0
  340. package/src/core/tools/handlers/create.js +39 -0
  341. package/src/core/tools/handlers/devtools.js +122 -0
  342. package/src/core/tools/handlers/devtools.test.js +87 -0
  343. package/src/core/tools/handlers/featureFlags.js +21 -0
  344. package/src/core/tools/handlers/flows.js +68 -0
  345. package/src/core/tools/handlers/hideChrome.js +9 -0
  346. package/src/core/tools/handlers/hideToolbars.js +25 -0
  347. package/src/core/tools/handlers/inspector.js +19 -0
  348. package/src/core/tools/handlers/paletteTheme.js +35 -0
  349. package/src/core/tools/handlers/theme.js +9 -0
  350. package/src/core/tools/registry.js +26 -0
  351. package/src/core/tools/surfaces/canvasToolbar.js +10 -0
  352. package/src/core/tools/surfaces/commandList.js +10 -0
  353. package/src/core/tools/surfaces/mainToolbar.js +11 -0
  354. package/src/core/tools/surfaces/registry.js +19 -0
  355. package/src/core/ui/ActionMenuButton.jsx +114 -0
  356. package/src/core/ui/AutosyncMenuButton.css +67 -0
  357. package/src/core/ui/AutosyncMenuButton.jsx +242 -0
  358. package/src/core/ui/BranchSelect.jsx +29 -0
  359. package/src/core/ui/BranchSelect.module.css +30 -0
  360. package/src/core/ui/CanvasAgentsMenu.jsx +89 -0
  361. package/src/core/ui/CanvasCreateMenu.jsx +611 -0
  362. package/src/core/ui/CanvasSnap.css +27 -0
  363. package/src/core/ui/CanvasSnap.jsx +51 -0
  364. package/src/core/ui/CanvasUndoRedo.css +36 -0
  365. package/src/core/ui/CanvasUndoRedo.jsx +62 -0
  366. package/src/core/ui/CanvasZoomControl.css +53 -0
  367. package/src/core/ui/CanvasZoomControl.jsx +49 -0
  368. package/src/core/ui/CanvasZoomToFit.css +18 -0
  369. package/src/core/ui/CanvasZoomToFit.jsx +26 -0
  370. package/src/core/ui/CommandMenu.css +8 -0
  371. package/src/core/ui/CommandMenu.jsx +287 -0
  372. package/src/core/ui/CommandPalette.jsx +35 -0
  373. package/src/core/ui/CommandPaletteTrigger.jsx +25 -0
  374. package/src/core/ui/CommentsMenuButton.jsx +40 -0
  375. package/src/core/ui/CoreUIBar.css +47 -0
  376. package/src/core/ui/CoreUIBar.jsx +905 -0
  377. package/src/core/ui/CreateMenuButton.jsx +117 -0
  378. package/src/core/ui/HideChromeTrigger.jsx +48 -0
  379. package/src/core/ui/Icon.jsx +279 -0
  380. package/src/core/ui/InspectorPanel.css +109 -0
  381. package/src/core/ui/InspectorPanel.jsx +632 -0
  382. package/src/core/ui/PwaInstallBanner.css +42 -0
  383. package/src/core/ui/PwaInstallBanner.jsx +124 -0
  384. package/src/core/ui/SidePanel.jsx +261 -0
  385. package/src/core/ui/ThemeMenuButton.jsx +139 -0
  386. package/src/core/ui/core-ui-colors.css +129 -0
  387. package/src/core/ui/design-modes.ts +7 -0
  388. package/src/core/ui/sidepanel.css +301 -0
  389. package/src/core/ui/viewfinder.ts +7 -0
  390. package/src/core/ui-entry.js +30 -0
  391. package/src/core/utils/fuzzySearch.js +117 -0
  392. package/src/core/utils/fuzzySearch.test.js +119 -0
  393. package/src/core/utils/mobileViewport.js +57 -0
  394. package/src/core/utils/mobileViewport.test.js +68 -0
  395. package/src/core/utils/prodMode.js +38 -0
  396. package/src/core/utils/smoothCorners.js +20 -0
  397. package/src/core/vite/docs-handler.js +155 -0
  398. package/src/core/vite/server-plugin.js +797 -0
  399. package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +260 -0
  400. package/src/core/workshop/features/createCanvas/index.js +14 -0
  401. package/src/core/workshop/features/createFlow/CreateFlowForm.jsx +334 -0
  402. package/src/core/workshop/features/createFlow/index.js +19 -0
  403. package/src/core/workshop/features/createFlow/server.js +663 -0
  404. package/src/core/workshop/features/createPage/CreatePageForm.jsx +304 -0
  405. package/src/core/workshop/features/createPage/index.js +11 -0
  406. package/src/core/workshop/features/createPrototype/CreatePrototypeForm.jsx +289 -0
  407. package/src/core/workshop/features/createPrototype/index.js +19 -0
  408. package/src/core/workshop/features/createPrototype/server.js +433 -0
  409. package/src/core/workshop/features/createStory/CreateStoryForm.jsx +208 -0
  410. package/src/core/workshop/features/createStory/index.js +14 -0
  411. package/src/core/workshop/features/registry-server.js +22 -0
  412. package/src/core/workshop/features/registry.js +28 -0
  413. package/src/core/workshop/features/templateIndex.js +155 -0
  414. package/src/core/workshop/ui/WorkshopPanel.jsx +98 -0
  415. package/src/core/workshop/ui/mount.ts +6 -0
  416. package/src/core/worktree/port.js +268 -0
  417. package/src/core/worktree/port.test.js +222 -0
  418. package/src/core/worktree/serverRegistry.js +120 -0
  419. package/src/internals/AuthModal/AuthModal.jsx +132 -0
  420. package/src/internals/AuthModal/AuthModal.module.css +221 -0
  421. package/src/internals/BranchBar/BranchBar.jsx +87 -0
  422. package/src/internals/BranchBar/BranchBar.module.css +247 -0
  423. package/src/internals/BranchBar/useBranches.js +93 -0
  424. package/src/internals/BranchBar/useBranches.test.js +68 -0
  425. package/src/internals/CommandPalette/CommandPalette.jsx +1361 -0
  426. package/src/internals/CommandPalette/CreateDialog.jsx +219 -0
  427. package/src/internals/CommandPalette/command-palette.css +180 -0
  428. package/src/internals/FlowError.module.css +30 -0
  429. package/src/internals/Icon.jsx +279 -0
  430. package/src/internals/StoryboardContext.js +3 -0
  431. package/src/internals/Viewfinder.jsx +1479 -0
  432. package/src/internals/Viewfinder.module.css +1540 -0
  433. package/src/internals/Workspace.jsx +7 -0
  434. package/src/internals/__mocks__/virtual-storyboard-data-index.js +4 -0
  435. package/src/internals/canvas/CanvasControls.jsx +112 -0
  436. package/src/internals/canvas/CanvasControls.module.css +135 -0
  437. package/src/internals/canvas/CanvasPage.bridge.test.jsx +387 -0
  438. package/src/internals/canvas/CanvasPage.dragdrop.test.jsx +350 -0
  439. package/src/internals/canvas/CanvasPage.jsx +3092 -0
  440. package/src/internals/canvas/CanvasPage.module.css +187 -0
  441. package/src/internals/canvas/CanvasPage.multiselect.test.jsx +358 -0
  442. package/src/internals/canvas/CanvasToolbar.jsx +73 -0
  443. package/src/internals/canvas/CanvasToolbar.module.css +92 -0
  444. package/src/internals/canvas/ComponentErrorBoundary.jsx +50 -0
  445. package/src/internals/canvas/ConnectorLayer.jsx +208 -0
  446. package/src/internals/canvas/ConnectorLayer.module.css +129 -0
  447. package/src/internals/canvas/MarqueeOverlay.jsx +20 -0
  448. package/src/internals/canvas/PageSelector.jsx +587 -0
  449. package/src/internals/canvas/PageSelector.module.css +261 -0
  450. package/src/internals/canvas/PageSelector.test.jsx +113 -0
  451. package/src/internals/canvas/WebGLContextPool.jsx +292 -0
  452. package/src/internals/canvas/WebGLContextPool.test.jsx +165 -0
  453. package/src/internals/canvas/canvasApi.js +164 -0
  454. package/src/internals/canvas/canvasReloadGuard.js +37 -0
  455. package/src/internals/canvas/canvasReloadGuard.test.js +27 -0
  456. package/src/internals/canvas/canvasTheme.js +118 -0
  457. package/src/internals/canvas/componentIsolate.jsx +165 -0
  458. package/src/internals/canvas/componentSetIsolate.jsx +257 -0
  459. package/src/internals/canvas/computeCanvasBounds.test.js +121 -0
  460. package/src/internals/canvas/connectorGeometry.js +132 -0
  461. package/src/internals/canvas/hotPoolDevLogs.js +25 -0
  462. package/src/internals/canvas/textSelection.js +10 -0
  463. package/src/internals/canvas/textSelection.test.js +26 -0
  464. package/src/internals/canvas/useCanvas.js +126 -0
  465. package/src/internals/canvas/useCanvas.test.js +26 -0
  466. package/src/internals/canvas/useMarqueeSelect.js +213 -0
  467. package/src/internals/canvas/useMarqueeSelect.test.js +78 -0
  468. package/src/internals/canvas/useUndoRedo.js +86 -0
  469. package/src/internals/canvas/useUndoRedo.test.js +231 -0
  470. package/src/internals/canvas/widgets/CodePenEmbed.jsx +293 -0
  471. package/src/internals/canvas/widgets/CodePenEmbed.module.css +161 -0
  472. package/src/internals/canvas/widgets/ComponentSetWidget.jsx +2 -0
  473. package/src/internals/canvas/widgets/ComponentSetWidget.module.css +89 -0
  474. package/src/internals/canvas/widgets/ComponentWidget.jsx +14 -0
  475. package/src/internals/canvas/widgets/ComponentWidget.module.css +0 -0
  476. package/src/internals/canvas/widgets/CropOverlay.jsx +179 -0
  477. package/src/internals/canvas/widgets/CropOverlay.module.css +154 -0
  478. package/src/internals/canvas/widgets/ExpandedPane.jsx +474 -0
  479. package/src/internals/canvas/widgets/ExpandedPane.module.css +179 -0
  480. package/src/internals/canvas/widgets/ExpandedPane.test.jsx +240 -0
  481. package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +111 -0
  482. package/src/internals/canvas/widgets/ExpandedPaneTopBar.module.css +59 -0
  483. package/src/internals/canvas/widgets/ExpandedPaneTopBar.test.jsx +45 -0
  484. package/src/internals/canvas/widgets/FigmaEmbed.jsx +296 -0
  485. package/src/internals/canvas/widgets/FigmaEmbed.module.css +222 -0
  486. package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +151 -0
  487. package/src/internals/canvas/widgets/FrozenTerminalOverlay.module.css +83 -0
  488. package/src/internals/canvas/widgets/ImageWidget.jsx +287 -0
  489. package/src/internals/canvas/widgets/ImageWidget.module.css +81 -0
  490. package/src/internals/canvas/widgets/LinkPreview.jsx +439 -0
  491. package/src/internals/canvas/widgets/LinkPreview.module.css +585 -0
  492. package/src/internals/canvas/widgets/LinkPreview.test.jsx +193 -0
  493. package/src/internals/canvas/widgets/MarkdownBlock.jsx +354 -0
  494. package/src/internals/canvas/widgets/MarkdownBlock.module.css +377 -0
  495. package/src/internals/canvas/widgets/MarkdownBlock.test.jsx +92 -0
  496. package/src/internals/canvas/widgets/PromptWidget.jsx +428 -0
  497. package/src/internals/canvas/widgets/PromptWidget.module.css +273 -0
  498. package/src/internals/canvas/widgets/PrototypeEmbed.jsx +463 -0
  499. package/src/internals/canvas/widgets/PrototypeEmbed.module.css +579 -0
  500. package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +10 -0
  501. package/src/internals/canvas/widgets/ResizeHandle.jsx +67 -0
  502. package/src/internals/canvas/widgets/ResizeHandle.module.css +29 -0
  503. package/src/internals/canvas/widgets/StickyNote.jsx +92 -0
  504. package/src/internals/canvas/widgets/StickyNote.module.css +70 -0
  505. package/src/internals/canvas/widgets/StickyNote.test.jsx +116 -0
  506. package/src/internals/canvas/widgets/StorySetWidget.jsx +208 -0
  507. package/src/internals/canvas/widgets/StorySetWidget.module.css +89 -0
  508. package/src/internals/canvas/widgets/StoryWidget.jsx +334 -0
  509. package/src/internals/canvas/widgets/StoryWidget.module.css +211 -0
  510. package/src/internals/canvas/widgets/TerminalReadWidget.jsx +146 -0
  511. package/src/internals/canvas/widgets/TerminalReadWidget.module.css +94 -0
  512. package/src/internals/canvas/widgets/TerminalWidget.jsx +704 -0
  513. package/src/internals/canvas/widgets/TerminalWidget.module.css +444 -0
  514. package/src/internals/canvas/widgets/TilesWidget.jsx +300 -0
  515. package/src/internals/canvas/widgets/TilesWidget.module.css +133 -0
  516. package/src/internals/canvas/widgets/WidgetChrome.jsx +580 -0
  517. package/src/internals/canvas/widgets/WidgetChrome.module.css +421 -0
  518. package/src/internals/canvas/widgets/WidgetWrapper.jsx +15 -0
  519. package/src/internals/canvas/widgets/WidgetWrapper.module.css +25 -0
  520. package/src/internals/canvas/widgets/codepenUrl.js +75 -0
  521. package/src/internals/canvas/widgets/codepenUrl.test.js +76 -0
  522. package/src/internals/canvas/widgets/embedInteraction.test.jsx +173 -0
  523. package/src/internals/canvas/widgets/embedOverlay.module.css +35 -0
  524. package/src/internals/canvas/widgets/embedTheme.js +148 -0
  525. package/src/internals/canvas/widgets/expandUtils.js +559 -0
  526. package/src/internals/canvas/widgets/expandUtils.test.js +155 -0
  527. package/src/internals/canvas/widgets/figmaUrl.js +118 -0
  528. package/src/internals/canvas/widgets/figmaUrl.test.js +139 -0
  529. package/src/internals/canvas/widgets/githubUrl.js +82 -0
  530. package/src/internals/canvas/widgets/githubUrl.test.js +74 -0
  531. package/src/internals/canvas/widgets/iframeDevLogs.js +49 -0
  532. package/src/internals/canvas/widgets/iframeDevLogs.test.jsx +81 -0
  533. package/src/internals/canvas/widgets/index.js +42 -0
  534. package/src/internals/canvas/widgets/pasteRules.js +295 -0
  535. package/src/internals/canvas/widgets/pasteRules.test.js +474 -0
  536. package/src/internals/canvas/widgets/snapshotDisplay.test.jsx +211 -0
  537. package/src/internals/canvas/widgets/tilePool.js +23 -0
  538. package/src/internals/canvas/widgets/tiles/diagonal-bl.png +0 -0
  539. package/src/internals/canvas/widgets/tiles/diagonal-br.png +0 -0
  540. package/src/internals/canvas/widgets/tiles/diagonal-tl.png +0 -0
  541. package/src/internals/canvas/widgets/tiles/leaf.png +0 -0
  542. package/src/internals/canvas/widgets/tiles/quarter-tl.png +0 -0
  543. package/src/internals/canvas/widgets/tiles/quarter-tr.png +0 -0
  544. package/src/internals/canvas/widgets/tiles/solid-a.png +0 -0
  545. package/src/internals/canvas/widgets/tiles/solid-b.png +0 -0
  546. package/src/internals/canvas/widgets/widgetConfig.js +291 -0
  547. package/src/internals/canvas/widgets/widgetConfig.test.js +68 -0
  548. package/src/internals/canvas/widgets/widgetIcons.jsx +190 -0
  549. package/src/internals/canvas/widgets/widgetProps.js +133 -0
  550. package/src/internals/context/FormContext.js +13 -0
  551. package/src/internals/context/FormContext.test.js +48 -0
  552. package/src/internals/context.jsx +481 -0
  553. package/src/internals/context.test.jsx +296 -0
  554. package/src/internals/hashPreserver.js +73 -0
  555. package/src/internals/hashPreserver.test.js +107 -0
  556. package/src/internals/hooks/useConfig.js +14 -0
  557. package/src/internals/hooks/useFeatureFlag.js +14 -0
  558. package/src/internals/hooks/useFlows.js +50 -0
  559. package/src/internals/hooks/useFlows.test.js +134 -0
  560. package/src/internals/hooks/useHideMode.js +31 -0
  561. package/src/internals/hooks/useHideMode.test.js +43 -0
  562. package/src/internals/hooks/useLocalStorage.js +57 -0
  563. package/src/internals/hooks/useLocalStorage.test.js +75 -0
  564. package/src/internals/hooks/useMode.js +43 -0
  565. package/src/internals/hooks/useObject.js +101 -0
  566. package/src/internals/hooks/useObject.test.js +74 -0
  567. package/src/internals/hooks/useOverride.js +84 -0
  568. package/src/internals/hooks/useOverride.test.js +71 -0
  569. package/src/internals/hooks/usePrototypeReloadGuard.js +64 -0
  570. package/src/internals/hooks/useRecord.js +158 -0
  571. package/src/internals/hooks/useRecord.test.js +221 -0
  572. package/src/internals/hooks/useScene.js +38 -0
  573. package/src/internals/hooks/useScene.test.js +66 -0
  574. package/src/internals/hooks/useSceneData.js +108 -0
  575. package/src/internals/hooks/useSceneData.test.js +136 -0
  576. package/src/internals/hooks/useSession.js +4 -0
  577. package/src/internals/hooks/useSession.test.js +8 -0
  578. package/src/internals/hooks/useThemeState.js +61 -0
  579. package/src/internals/hooks/useThemeState.test.js +66 -0
  580. package/src/internals/hooks/useUndoRedo.js +28 -0
  581. package/src/internals/hooks/useUndoRedo.test.js +64 -0
  582. package/src/internals/index.js +58 -0
  583. package/src/internals/story/ComponentSetPage.jsx +198 -0
  584. package/src/internals/story/ComponentSetPage.module.css +129 -0
  585. package/src/internals/story/StoryPage.jsx +147 -0
  586. package/src/internals/story/StoryPage.module.css +18 -0
  587. package/src/internals/test-utils.js +45 -0
  588. package/src/internals/vite/data-plugin.js +1508 -0
  589. package/src/internals/vite/data-plugin.test.js +1223 -0
  590. package/src/test-utils.js +44 -0
  591. package/toolbar.config.json +271 -0
  592. package/widgets.config.json +1537 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * URL hash-based utilities for storyboard session state.
3
+ *
4
+ * Session params are stored in the URL hash fragment (after #) to avoid
5
+ * triggering React Router re-renders. React Router (used by generouted)
6
+ * patches history.replaceState/pushState, so any search-param change
7
+ * causes a full route tree re-render. The hash is invisible to the router.
8
+ *
9
+ * Format: #key1=value1&key2=value2
10
+ * Example: /page?scene=default#user.name=Alice&settings.theme=dark
11
+ */
12
+
13
+ /**
14
+ * Parse the current hash into a Map of key→value pairs.
15
+ * @returns {URLSearchParams}
16
+ */
17
+ function parseHash() {
18
+ const raw = window.location.hash.replace(/^#/, '')
19
+ return new URLSearchParams(raw)
20
+ }
21
+
22
+ /**
23
+ * Write a URLSearchParams back to the hash.
24
+ * Uses window.location.hash (NOT history.replaceState) because
25
+ * generouted/React Router patches replaceState and would trigger
26
+ * a full route re-render. Native hash assignment only fires
27
+ * 'hashchange' which React Router ignores.
28
+ * @param {URLSearchParams} params
29
+ */
30
+ function writeHash(params) {
31
+ const str = params.toString()
32
+ window.location.hash = str
33
+ }
34
+
35
+ /**
36
+ * Read a single session param value.
37
+ * @param {string} key
38
+ * @returns {string|null}
39
+ */
40
+ export function getParam(key) {
41
+ return parseHash().get(key)
42
+ }
43
+
44
+ /**
45
+ * Write a single session param. Updates the hash in-place.
46
+ * @param {string} key
47
+ * @param {string} value
48
+ */
49
+ export function setParam(key, value) {
50
+ const params = parseHash()
51
+ params.set(key, String(value))
52
+ writeHash(params)
53
+ }
54
+
55
+ /**
56
+ * Return all session params as a plain object.
57
+ * @returns {Record<string, string>}
58
+ */
59
+ export function getAllParams() {
60
+ const params = parseHash()
61
+ const result = {}
62
+ for (const [key, value] of params.entries()) {
63
+ result[key] = value
64
+ }
65
+ return result
66
+ }
67
+
68
+ /**
69
+ * Remove a single session param from the hash.
70
+ * @param {string} key
71
+ */
72
+ export function removeParam(key) {
73
+ const params = parseHash()
74
+ params.delete(key)
75
+ writeHash(params)
76
+ }
@@ -0,0 +1,91 @@
1
+ import { getParam, setParam, getAllParams, removeParam } from './session.js'
2
+
3
+ describe('getParam', () => {
4
+ it('returns null when hash is empty', () => {
5
+ window.location.hash = ''
6
+ expect(getParam('key')).toBeNull()
7
+ })
8
+
9
+ it('returns value for existing param', () => {
10
+ window.location.hash = 'foo=bar'
11
+ expect(getParam('foo')).toBe('bar')
12
+ })
13
+
14
+ it('returns null for missing param', () => {
15
+ window.location.hash = 'foo=bar'
16
+ expect(getParam('missing')).toBeNull()
17
+ })
18
+
19
+ it('handles URL-encoded values', () => {
20
+ window.location.hash = 'name=hello%20world'
21
+ expect(getParam('name')).toBe('hello world')
22
+ })
23
+ })
24
+
25
+ describe('setParam', () => {
26
+ it('sets a new param in hash', () => {
27
+ window.location.hash = ''
28
+ setParam('key', 'value')
29
+ expect(getParam('key')).toBe('value')
30
+ })
31
+
32
+ it('updates existing param', () => {
33
+ window.location.hash = 'key=old'
34
+ setParam('key', 'new')
35
+ expect(getParam('key')).toBe('new')
36
+ })
37
+
38
+ it('preserves other params', () => {
39
+ window.location.hash = 'a=1&b=2'
40
+ setParam('c', '3')
41
+ expect(getParam('a')).toBe('1')
42
+ expect(getParam('b')).toBe('2')
43
+ expect(getParam('c')).toBe('3')
44
+ })
45
+
46
+ it('converts value to string', () => {
47
+ window.location.hash = ''
48
+ setParam('num', 42)
49
+ expect(getParam('num')).toBe('42')
50
+ })
51
+ })
52
+
53
+ describe('getAllParams', () => {
54
+ it('returns empty object for empty hash', () => {
55
+ window.location.hash = ''
56
+ expect(getAllParams()).toEqual({})
57
+ })
58
+
59
+ it('returns all params', () => {
60
+ window.location.hash = 'a=1&b=2'
61
+ expect(getAllParams()).toEqual({ a: '1', b: '2' })
62
+ })
63
+
64
+ it('handles multiple params', () => {
65
+ window.location.hash = 'x=hello&y=world&z=test'
66
+ const params = getAllParams()
67
+ expect(Object.keys(params)).toHaveLength(3)
68
+ expect(params).toEqual({ x: 'hello', y: 'world', z: 'test' })
69
+ })
70
+ })
71
+
72
+ describe('removeParam', () => {
73
+ it('removes existing param', () => {
74
+ window.location.hash = 'a=1&b=2'
75
+ removeParam('a')
76
+ expect(getParam('a')).toBeNull()
77
+ })
78
+
79
+ it('preserves other params', () => {
80
+ window.location.hash = 'a=1&b=2&c=3'
81
+ removeParam('b')
82
+ expect(getParam('a')).toBe('1')
83
+ expect(getParam('c')).toBe('3')
84
+ })
85
+
86
+ it('no-ops for missing param', () => {
87
+ window.location.hash = 'a=1'
88
+ removeParam('nonexistent')
89
+ expect(getParam('a')).toBe('1')
90
+ })
91
+ })
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Canvas Config — project-level overrides for canvas behavior.
3
+ *
4
+ * Client repos use the "canvas" key in storyboard.config.json to customize
5
+ * canvas paste rules and other canvas-level settings.
6
+ *
7
+ * {
8
+ * "canvas": {
9
+ * "pasteRules": [
10
+ * { "pattern": "youtube\\.com/watch", "type": "link-preview", "props": { "url": "$url" } }
11
+ * ]
12
+ * }
13
+ * }
14
+ *
15
+ * Framework-agnostic (zero npm dependencies).
16
+ */
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Internal state
20
+ // ---------------------------------------------------------------------------
21
+
22
+ let _pasteRules = []
23
+ let _terminal = {}
24
+ let _agents = {}
25
+ let _zoom = { min: 10, max: 250, step: 10 }
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Configuration
29
+ // ---------------------------------------------------------------------------
30
+
31
+ /**
32
+ * Initialize canvas config from storyboard.config.json's "canvas" key.
33
+ * Called by mountStoryboardCore.
34
+ *
35
+ * @param {{ pasteRules?: object[], terminal?: object, agents?: object, zoom?: object }} [config]
36
+ */
37
+ export function initCanvasConfig(config = {}) {
38
+ _pasteRules = Array.isArray(config.pasteRules) ? config.pasteRules : []
39
+ _terminal = config.terminal && typeof config.terminal === 'object' ? config.terminal : {}
40
+ _agents = config.agents && typeof config.agents === 'object' ? config.agents : {}
41
+ _zoom = config.zoom && typeof config.zoom === 'object'
42
+ ? { min: config.zoom.min ?? 10, max: config.zoom.max ?? 250, step: config.zoom.step ?? 10 }
43
+ : { min: 10, max: 250, step: 10 }
44
+ }
45
+
46
+ /**
47
+ * Get the configured paste rules (raw config objects).
48
+ *
49
+ * @returns {object[]}
50
+ */
51
+ export function getPasteRules() {
52
+ return _pasteRules
53
+ }
54
+
55
+ /**
56
+ * Get terminal widget configuration.
57
+ *
58
+ * @returns {{ theme?: object, fontSize?: number, fontFamily?: string, prompt?: string }}
59
+ */
60
+ export function getTerminalConfig() {
61
+ return _terminal
62
+ }
63
+
64
+ /**
65
+ * Get agent configurations.
66
+ *
67
+ * @returns {object}
68
+ */
69
+ export function getAgentsConfig() {
70
+ return _agents
71
+ }
72
+
73
+ /**
74
+ * Get canvas zoom configuration (min, max, step).
75
+ *
76
+ * @returns {{ min: number, max: number, step: number }}
77
+ */
78
+ export function getCanvasZoom() {
79
+ return _zoom
80
+ }
81
+
82
+ /**
83
+ * Check if a terminal/agent widget should be resizable based on config.
84
+ * Agent-level `resizable` overrides the base `terminal.resizable`.
85
+ *
86
+ * @param {string|null} [agentId] — agent ID to check for overrides
87
+ * @returns {boolean}
88
+ */
89
+ export function isTerminalResizable(agentId = null) {
90
+ if (agentId) {
91
+ const agentCfg = _agents[agentId]
92
+ if (agentCfg && agentCfg.resizable !== undefined) return agentCfg.resizable
93
+ }
94
+ return _terminal.resizable ?? false
95
+ }
96
+
97
+ /**
98
+ * Get effective default dimensions for a terminal/agent widget.
99
+ * Cascade: agent config > terminal config > provided fallbacks.
100
+ *
101
+ * @param {string|null} [agentId] — agent ID to check for overrides
102
+ * @param {{ width: number, height: number }} [fallback] — schema-level fallbacks
103
+ * @returns {{ width: number, height: number }}
104
+ */
105
+ export function getTerminalDimensions(agentId = null, fallback = { width: 800, height: 450 }) {
106
+ const base = {
107
+ width: _terminal.defaultWidth ?? fallback.width,
108
+ height: _terminal.defaultHeight ?? fallback.height,
109
+ }
110
+ if (agentId) {
111
+ const agentCfg = _agents[agentId]
112
+ if (agentCfg) {
113
+ return {
114
+ width: agentCfg.defaultWidth ?? base.width,
115
+ height: agentCfg.defaultHeight ?? base.height,
116
+ }
117
+ }
118
+ }
119
+ return base
120
+ }
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Test helpers
124
+ // ---------------------------------------------------------------------------
125
+
126
+ /**
127
+ * Reset all internal state. Only for use in tests.
128
+ */
129
+ export function _resetCanvasConfig() {
130
+ _pasteRules = []
131
+ _terminal = {}
132
+ _agents = {}
133
+ _zoom = { min: 10, max: 250, step: 10 }
134
+ }
@@ -0,0 +1,120 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { initCanvasConfig, getPasteRules, isTerminalResizable, getTerminalDimensions, _resetCanvasConfig } from './canvasConfig.js'
3
+
4
+ describe('canvasConfig', () => {
5
+ beforeEach(() => {
6
+ _resetCanvasConfig()
7
+ })
8
+
9
+ it('returns empty array by default', () => {
10
+ expect(getPasteRules()).toEqual([])
11
+ })
12
+
13
+ it('stores paste rules from config', () => {
14
+ const rules = [
15
+ { pattern: 'youtube\\.com', type: 'link-preview', props: { url: '$url' } },
16
+ ]
17
+ initCanvasConfig({ pasteRules: rules })
18
+ expect(getPasteRules()).toEqual(rules)
19
+ })
20
+
21
+ it('handles missing pasteRules gracefully', () => {
22
+ initCanvasConfig({})
23
+ expect(getPasteRules()).toEqual([])
24
+ })
25
+
26
+ it('handles undefined config', () => {
27
+ initCanvasConfig()
28
+ expect(getPasteRules()).toEqual([])
29
+ })
30
+
31
+ it('handles non-array pasteRules', () => {
32
+ initCanvasConfig({ pasteRules: 'not-an-array' })
33
+ expect(getPasteRules()).toEqual([])
34
+ })
35
+
36
+ it('resets on _resetCanvasConfig', () => {
37
+ initCanvasConfig({ pasteRules: [{ pattern: '.', type: 'test' }] })
38
+ expect(getPasteRules()).toHaveLength(1)
39
+ _resetCanvasConfig()
40
+ expect(getPasteRules()).toEqual([])
41
+ })
42
+ })
43
+
44
+ describe('isTerminalResizable', () => {
45
+ beforeEach(() => {
46
+ _resetCanvasConfig()
47
+ })
48
+
49
+ it('returns false by default', () => {
50
+ expect(isTerminalResizable()).toBe(false)
51
+ })
52
+
53
+ it('returns terminal.resizable when set', () => {
54
+ initCanvasConfig({ terminal: { resizable: true } })
55
+ expect(isTerminalResizable()).toBe(true)
56
+ })
57
+
58
+ it('agent overrides terminal resizable', () => {
59
+ initCanvasConfig({
60
+ terminal: { resizable: false },
61
+ agents: { myAgent: { resizable: true } },
62
+ })
63
+ expect(isTerminalResizable('myAgent')).toBe(true)
64
+ expect(isTerminalResizable()).toBe(false)
65
+ })
66
+
67
+ it('agent can disable resizable even when terminal enables it', () => {
68
+ initCanvasConfig({
69
+ terminal: { resizable: true },
70
+ agents: { fixedAgent: { resizable: false } },
71
+ })
72
+ expect(isTerminalResizable('fixedAgent')).toBe(false)
73
+ expect(isTerminalResizable()).toBe(true)
74
+ })
75
+
76
+ it('falls back to terminal config for unknown agent', () => {
77
+ initCanvasConfig({ terminal: { resizable: true } })
78
+ expect(isTerminalResizable('nonexistent')).toBe(true)
79
+ })
80
+ })
81
+
82
+ describe('getTerminalDimensions', () => {
83
+ beforeEach(() => {
84
+ _resetCanvasConfig()
85
+ })
86
+
87
+ it('returns fallback defaults when no config', () => {
88
+ expect(getTerminalDimensions()).toEqual({ width: 800, height: 450 })
89
+ })
90
+
91
+ it('returns terminal config dimensions', () => {
92
+ initCanvasConfig({ terminal: { defaultWidth: 1000, defaultHeight: 600 } })
93
+ expect(getTerminalDimensions()).toEqual({ width: 1000, height: 600 })
94
+ })
95
+
96
+ it('agent overrides terminal dimensions', () => {
97
+ initCanvasConfig({
98
+ terminal: { defaultWidth: 1000, defaultHeight: 600 },
99
+ agents: { bigAgent: { defaultWidth: 1400, defaultHeight: 800 } },
100
+ })
101
+ expect(getTerminalDimensions('bigAgent')).toEqual({ width: 1400, height: 800 })
102
+ })
103
+
104
+ it('agent partial override inherits from terminal for unset dimensions', () => {
105
+ initCanvasConfig({
106
+ terminal: { defaultWidth: 1000, defaultHeight: 600 },
107
+ agents: { wideAgent: { defaultWidth: 1400 } },
108
+ })
109
+ expect(getTerminalDimensions('wideAgent')).toEqual({ width: 1400, height: 600 })
110
+ })
111
+
112
+ it('falls back to terminal config for unknown agent', () => {
113
+ initCanvasConfig({ terminal: { defaultWidth: 900, defaultHeight: 500 } })
114
+ expect(getTerminalDimensions('nonexistent')).toEqual({ width: 900, height: 500 })
115
+ })
116
+
117
+ it('uses custom fallback when provided', () => {
118
+ expect(getTerminalDimensions(null, { width: 1200, height: 450 })).toEqual({ width: 1200, height: 450 })
119
+ })
120
+ })
@@ -0,0 +1,284 @@
1
+ /**
2
+ * Command Actions — config-driven registry for command menu entries.
3
+ *
4
+ * The command section of toolbar.config.json declares action metadata:
5
+ * id, label, type, hideFrom, separatorBefore
6
+ *
7
+ * Handler shapes by type:
8
+ * default: () => void
9
+ * toggle: { execute(), getState() → boolean }
10
+ * submenu: { getChildren() → Array<{ id?, label, type, active?, execute }> }
11
+ */
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Internal state
15
+ // ---------------------------------------------------------------------------
16
+
17
+ let _config = { actions: {}, footer: '' }
18
+
19
+ /** @type {Map<string, any>} id → handler (function or object) */
20
+ const _handlers = new Map()
21
+
22
+ /** @type {Set<Function>} */
23
+ const _listeners = new Set()
24
+
25
+ let _snapshotVersion = 0
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Initialization
29
+ // ---------------------------------------------------------------------------
30
+
31
+ /**
32
+ * Seed the registry from a menu config object.
33
+ * Called once at app startup.
34
+ *
35
+ * @param {{ actions: Array, footer?: string }} config
36
+ */
37
+ export function initCommandActions(config) {
38
+ _config = { ...config }
39
+ _notify()
40
+ }
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // Handler registration
44
+ // ---------------------------------------------------------------------------
45
+
46
+ /**
47
+ * Register a handler for a declared action.
48
+ *
49
+ * @param {string} id Action id (e.g. "core/viewfinder")
50
+ * @param {Function|object} handler
51
+ * - default type: () => void
52
+ * - toggle type: { execute(), getState() }
53
+ * - submenu type: { getChildren() }
54
+ */
55
+ export function registerCommandAction(id, handler) {
56
+ _handlers.set(id, handler)
57
+ _notify()
58
+ }
59
+
60
+ /**
61
+ * Remove a previously registered handler.
62
+ * @param {string} id
63
+ */
64
+ export function unregisterCommandAction(id) {
65
+ _handlers.delete(id)
66
+ _notify()
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Dynamic actions (not in config — registered at runtime)
71
+ // ---------------------------------------------------------------------------
72
+
73
+ /** @type {Array} */
74
+ let _dynamicActions = []
75
+
76
+ /**
77
+ * Add dynamic actions (e.g. comments menu items).
78
+ * These are appended after config-declared actions.
79
+ *
80
+ * @param {string} group Group key for bulk replacement (e.g. "comments")
81
+ * @param {Array<{ id: string, label: string, type?: string, separatorBefore?: boolean }>} actions
82
+ * @param {Record<string, Function|object>} [handlers] id → handler map
83
+ */
84
+ export function setDynamicActions(group, actions, handlers = {}) {
85
+ // Remove previous entries for this group
86
+ _dynamicActions = _dynamicActions.filter(a => a._group !== group)
87
+
88
+ for (const action of actions) {
89
+ _dynamicActions.push({ ...action, type: action.type || 'default', _group: group })
90
+ }
91
+
92
+ // Register handlers
93
+ for (const [id, handler] of Object.entries(handlers)) {
94
+ _handlers.set(id, handler)
95
+ }
96
+
97
+ _notify()
98
+ }
99
+
100
+ /**
101
+ * Remove all dynamic actions for a group.
102
+ * @param {string} group
103
+ */
104
+ export function clearDynamicActions(group) {
105
+ const removed = _dynamicActions.filter(a => a._group === group)
106
+ _dynamicActions = _dynamicActions.filter(a => a._group !== group)
107
+ for (const a of removed) {
108
+ _handlers.delete(a.id)
109
+ }
110
+ _notify()
111
+ }
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // Resolution
115
+ // ---------------------------------------------------------------------------
116
+
117
+ // Base path for route matching — set by CoreUIBar or app layer
118
+ let _basePath = '/'
119
+
120
+ /**
121
+ * Set the base path used by isExcludedByRoute for portable pattern matching.
122
+ * @param {string} basePath
123
+ */
124
+ export function setRoutingBasePath(basePath) {
125
+ _basePath = basePath || '/'
126
+ }
127
+
128
+ /**
129
+ * Check if an item is excluded from the current route.
130
+ * Strips the base path so patterns match against app-relative paths
131
+ * (e.g. "/" for the root, "/Signup" for a prototype page).
132
+ * @param {object} item Action or menu with optional excludeRoutes array
133
+ * @returns {boolean} true if the item should be hidden
134
+ */
135
+ export function isExcludedByRoute(item) {
136
+ const patterns = item.excludeRoutes
137
+ if (!patterns || !Array.isArray(patterns) || patterns.length === 0) return false
138
+ if (typeof window === 'undefined') return false
139
+ let pathname = window.location.pathname
140
+ const base = _basePath.replace(/\/+$/, '')
141
+ if (base && pathname.startsWith(base)) {
142
+ pathname = pathname.slice(base.length) || '/'
143
+ }
144
+ return patterns.some(pattern => new RegExp(pattern).test(pathname))
145
+ }
146
+
147
+ /**
148
+ * Check if an action is visible in a given mode.
149
+ * @param {object} action
150
+ * @param {string} mode
151
+ * @returns {boolean}
152
+ */
153
+ function actionVisibleInMode(action, mode) {
154
+ if (isExcludedByRoute(action)) return false
155
+ const modes = action.modes
156
+ if (!modes) return true
157
+ return modes.includes('*') || modes.includes(mode)
158
+ }
159
+
160
+ /**
161
+ * Get resolved actions for a given mode.
162
+ * Filters by each action's modes array, appends dynamic actions.
163
+ *
164
+ * @param {string} mode Current mode name
165
+ * @returns {Array<{ id, label, type, separatorBefore?, handler?, active? }>}
166
+ */
167
+ export function getActionsForMode(mode) {
168
+ const configActions = Array.isArray(_config.actions) ? _config.actions : []
169
+ const filtered = configActions.filter(a => actionVisibleInMode(a, mode))
170
+ const dynamic = _dynamicActions.filter(a => actionVisibleInMode(a, mode))
171
+
172
+ // Insert dynamic actions before the footer (if present)
173
+ const footerIndex = filtered.findIndex(a => a.type === 'footer')
174
+ let all
175
+ if (footerIndex >= 0 && dynamic.length > 0) {
176
+ all = [
177
+ ...filtered.slice(0, footerIndex),
178
+ ...dynamic,
179
+ ...filtered.slice(footerIndex),
180
+ ]
181
+ } else {
182
+ all = [...filtered, ...dynamic]
183
+ }
184
+
185
+ return all.map(a => {
186
+ const handler = _handlers.get(a.id)
187
+ const isToggle = a.type === 'toggle'
188
+ const active = isToggle && handler?.getState ? handler.getState() : false
189
+
190
+ return {
191
+ id: a.id,
192
+ label: a.label,
193
+ type: a.type || 'default',
194
+ url: a.url || null,
195
+ toolKey: a.toolKey || null,
196
+ localOnly: a.localOnly || false,
197
+ hideFromCommandPaletteSearch: a.hideFromCommandPaletteSearch || false,
198
+ handler,
199
+ active,
200
+ }
201
+ })
202
+ }
203
+
204
+ /**
205
+ * Execute an action by id.
206
+ * @param {string} id
207
+ */
208
+ export function executeAction(id) {
209
+ const handler = _handlers.get(id)
210
+ if (!handler) return
211
+ if (typeof handler === 'function') {
212
+ handler()
213
+ } else if (handler.execute) {
214
+ handler.execute()
215
+ }
216
+ _notify()
217
+ }
218
+
219
+ /**
220
+ * Get submenu children for a submenu-type action.
221
+ * @param {string} id
222
+ * @returns {Array<{ id?, label, type, active?, execute? }>}
223
+ */
224
+ export function getActionChildren(id) {
225
+ const handler = _handlers.get(id)
226
+ if (!handler?.getChildren) return []
227
+ return handler.getChildren()
228
+ }
229
+
230
+ /**
231
+ * Check if a handler provides dynamic children (getChildren).
232
+ * Used by CoreUIBar to distinguish action-menu tools (gate on children count)
233
+ * from custom-component menus (always visible, render their own content).
234
+ * @param {string} id
235
+ * @returns {boolean}
236
+ */
237
+ export function hasChildrenProvider(id) {
238
+ const handler = _handlers.get(id)
239
+ return !!handler?.getChildren
240
+ }
241
+
242
+ // ---------------------------------------------------------------------------
243
+ // Reactivity
244
+ // ---------------------------------------------------------------------------
245
+
246
+ /**
247
+ * Subscribe to action changes. Compatible with useSyncExternalStore.
248
+ * @param {Function} callback
249
+ * @returns {Function} Unsubscribe
250
+ */
251
+ export function subscribeToCommandActions(callback) {
252
+ _listeners.add(callback)
253
+ return () => _listeners.delete(callback)
254
+ }
255
+
256
+ /**
257
+ * Snapshot for useSyncExternalStore.
258
+ * @returns {string}
259
+ */
260
+ export function getCommandActionsSnapshot() {
261
+ return String(_snapshotVersion)
262
+ }
263
+
264
+ function _notify() {
265
+ _snapshotVersion++
266
+ for (const cb of _listeners) {
267
+ try { cb() } catch (err) {
268
+ console.error('[storyboard] Error in command action subscriber:', err)
269
+ }
270
+ }
271
+ }
272
+
273
+ // ---------------------------------------------------------------------------
274
+ // Test helpers
275
+ // ---------------------------------------------------------------------------
276
+
277
+ /** Reset all state. Only for tests. */
278
+ export function _resetCommandActions() {
279
+ _config = { actions: {}, footer: '' }
280
+ _handlers.clear()
281
+ _dynamicActions = []
282
+ _listeners.clear()
283
+ _snapshotVersion = 0
284
+ }