@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,319 @@
1
+ /**
2
+ * storyboard proxy — Manage the Caddy reverse proxy.
3
+ *
4
+ * Usage:
5
+ * storyboard proxy start Start or reload Caddy proxy
6
+ * storyboard proxy close Stop Caddy proxy
7
+ *
8
+ * Caddy listens on port 80 for storyboard.localhost, routing:
9
+ * /storyboard/* → main (port 1234)
10
+ * /<branchname>/storyboard/* → worktree (assigned port)
11
+ *
12
+ * Requires Caddy to be installed (brew install caddy).
13
+ */
14
+
15
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'
16
+ import { execSync } from 'child_process'
17
+ import { dirname, resolve } from 'path'
18
+ import { portsFilePath } from '../worktree/port.js'
19
+ import { list as listRunningServers } from '../worktree/serverRegistry.js'
20
+
21
+ export function readDevDomain(cwd) {
22
+ try {
23
+ const configPath = resolve(cwd || process.cwd(), 'storyboard.config.json')
24
+ const config = JSON.parse(readFileSync(configPath, 'utf8'))
25
+ return `${config.devDomain || 'storyboard'}.localhost`
26
+ } catch {
27
+ return 'storyboard.localhost'
28
+ }
29
+ }
30
+
31
+ const DOMAIN = readDevDomain()
32
+
33
+ /**
34
+ * Build branch→port map from running servers (servers.json) + explicit overrides.
35
+ * Only includes branches with live processes — prevents stale routes from
36
+ * creating redirect loops when ports get reused by different Vite instances.
37
+ * When multiple servers run for the same worktree, latest startedAt wins.
38
+ */
39
+ function livePortMap(portOverrides = {}) {
40
+ const map = {}
41
+ try {
42
+ for (const srv of listRunningServers()) {
43
+ if (srv.worktree === 'main') continue
44
+ const existing = map[srv.worktree]
45
+ if (!existing || srv.startedAt > existing.startedAt) {
46
+ map[srv.worktree] = { port: srv.port, startedAt: srv.startedAt }
47
+ }
48
+ }
49
+ } catch { /* registry unavailable — rely on overrides only */ }
50
+
51
+ // Build final branch→port, overrides always win
52
+ const result = {}
53
+ for (const [name, info] of Object.entries(map)) result[name] = info.port
54
+ Object.assign(result, portOverrides)
55
+ // Remove 'main' if accidentally passed — handled separately
56
+ delete result.main
57
+ return result
58
+ }
59
+
60
+ export function generateCaddyfile(portOverrides = {}) {
61
+ const portsFile = portsFilePath()
62
+ const dir = dirname(portsFile)
63
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
64
+
65
+ // Main port from ports.json (always 1234 unless overridden)
66
+ let mainPort = 1234
67
+ if (existsSync(portsFile)) {
68
+ try {
69
+ const ports = JSON.parse(readFileSync(portsFile, 'utf8'))
70
+ mainPort = ports.main || 1234
71
+ } catch { /* use default */ }
72
+ }
73
+
74
+ const branches = Object.entries(livePortMap(portOverrides))
75
+
76
+ // Build Caddy route blocks — branches first (more specific), main last (fallback)
77
+ const branchBlocks = branches
78
+ .map(([name, port]) => `\t# Worktree: ${name}\n\thandle /branch--${name}/* {\n\t\treverse_proxy localhost:${port}\n\t}`)
79
+ .join('\n\n')
80
+
81
+ const caddyfile = `# Auto-generated by storyboard CLI — do not edit manually.
82
+ # Regenerated on every \`storyboard dev\` or \`storyboard proxy\`.
83
+
84
+ http://${DOMAIN} {
85
+ ${branchBlocks ? branchBlocks + '\n\n' : ''}\t# Main
86
+ \thandle {
87
+ \t\treverse_proxy localhost:${mainPort}
88
+ \t}
89
+ }
90
+ `
91
+
92
+ const caddyfilePath = portsFile.replace('ports.json', 'Caddyfile')
93
+ writeFileSync(caddyfilePath, caddyfile)
94
+ return caddyfilePath
95
+ }
96
+
97
+ export function isCaddyInstalled() {
98
+ try {
99
+ execSync('caddy version', { stdio: 'ignore' })
100
+ return true
101
+ } catch {
102
+ return false
103
+ }
104
+ }
105
+
106
+ export function isCaddyRunning() {
107
+ try {
108
+ // Caddy admin API on localhost:2019
109
+ execSync('curl -sf http://localhost:2019/config/ >/dev/null 2>&1', { timeout: 2000 })
110
+ return true
111
+ } catch {
112
+ return false
113
+ }
114
+ }
115
+
116
+ export function reloadCaddy(caddyfilePath) {
117
+ try {
118
+ execSync(`caddy reload --config "${caddyfilePath}" 2>/dev/null`, { stdio: 'ignore', timeout: 5000 })
119
+ return true
120
+ } catch {
121
+ return false
122
+ }
123
+ }
124
+
125
+ // ── Caddy admin API (additive route upsert) ──
126
+
127
+ const CADDY_ADMIN = 'http://localhost:2019'
128
+
129
+ /**
130
+ * Build a Caddy JSON route object for this repo's devDomain.
131
+ * Tagged with @id so it can be upserted independently of other repos.
132
+ */
133
+ export function generateRouteConfig(portOverrides = {}) {
134
+ // Main port from ports.json
135
+ const portsFile = portsFilePath()
136
+ let mainPort = 1234
137
+ if (existsSync(portsFile)) {
138
+ try {
139
+ const ports = JSON.parse(readFileSync(portsFile, 'utf8'))
140
+ mainPort = ports.main || 1234
141
+ } catch { /* use default */ }
142
+ }
143
+
144
+ const branches = Object.entries(livePortMap(portOverrides))
145
+
146
+ // Branch subroutes first (more specific), then main fallback
147
+ const subroutes = branches.map(([name, port]) => ({
148
+ match: [{ path: [`/branch--${name}/*`] }],
149
+ handle: [{ handler: 'reverse_proxy', upstreams: [{ dial: `localhost:${port}` }] }],
150
+ }))
151
+ subroutes.push({
152
+ handle: [{ handler: 'reverse_proxy', upstreams: [{ dial: `localhost:${mainPort}` }] }],
153
+ })
154
+
155
+ return {
156
+ '@id': DOMAIN.replace('.localhost', ''),
157
+ match: [{ host: [DOMAIN] }],
158
+ handle: [{ handler: 'subroute', routes: subroutes }],
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Find indices of stale routes that match the same host but lack an @id.
164
+ * Returns indices sorted descending (highest first) for safe deletion.
165
+ * Exported for testing.
166
+ */
167
+ export function findStaleRouteIndices(routes, keepId, host) {
168
+ const indices = []
169
+ for (let i = 0; i < routes.length; i++) {
170
+ const route = routes[i]
171
+ if (route['@id'] === keepId) continue
172
+ if (route['@id']) continue // different intentional route — leave it
173
+ const routeHosts = route.match?.[0]?.host || []
174
+ if (routeHosts.includes(host)) {
175
+ indices.push(i)
176
+ }
177
+ }
178
+ return indices.reverse()
179
+ }
180
+
181
+ /**
182
+ * Remove stale routes that match the same host but lack an @id.
183
+ * These are leftovers from Caddyfile reloads that shadow admin-API routes.
184
+ * Deletes from highest index to lowest to preserve indices during removal.
185
+ * Best-effort — warns on failure but does not throw.
186
+ */
187
+ function cleanupDuplicateRoutes(keepId, host) {
188
+ try {
189
+ const config = execSync(
190
+ `curl -sf '${CADDY_ADMIN}/config/apps/http/servers/srv0/routes'`,
191
+ { encoding: 'utf-8', timeout: 5000 },
192
+ )
193
+ const routes = JSON.parse(config)
194
+ const staleIndices = findStaleRouteIndices(routes, keepId, host)
195
+ for (const idx of staleIndices) {
196
+ try {
197
+ execSync(
198
+ `curl -sf -X DELETE '${CADDY_ADMIN}/config/apps/http/servers/srv0/routes/${idx}'`,
199
+ { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] },
200
+ )
201
+ } catch {
202
+ console.warn(`[storyboard] failed to remove stale proxy route at index ${idx}`)
203
+ }
204
+ }
205
+ } catch {
206
+ console.warn('[storyboard] failed to clean up stale proxy routes — branch URLs may not work via proxy')
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Upsert this repo's route in the running Caddy instance via admin API.
212
+ * Uses PATCH if the @id exists, POST if it doesn't.
213
+ * After a successful upsert, removes any stale non-@id routes for the
214
+ * same host (leftovers from prior Caddyfile reloads).
215
+ * Returns true on success, false on failure.
216
+ */
217
+ export function upsertCaddyRoute(routeConfig) {
218
+ const id = routeConfig['@id']
219
+ const host = routeConfig.match?.[0]?.host?.[0]
220
+ const payload = JSON.stringify(routeConfig)
221
+
222
+ // Try PATCH first (update existing route by @id)
223
+ try {
224
+ execSync(
225
+ `curl -sf -X PATCH '${CADDY_ADMIN}/id/${id}' -H 'Content-Type: application/json' -d '${payload.replace(/'/g, "'\\''")}'`,
226
+ { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] },
227
+ )
228
+ if (host) cleanupDuplicateRoutes(id, host)
229
+ return true
230
+ } catch {
231
+ // @id doesn't exist yet — POST a new route
232
+ try {
233
+ execSync(
234
+ `curl -sf -X POST '${CADDY_ADMIN}/config/apps/http/servers/srv0/routes' -H 'Content-Type: application/json' -d '${payload.replace(/'/g, "'\\''")}'`,
235
+ { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] },
236
+ )
237
+ if (host) cleanupDuplicateRoutes(id, host)
238
+ return true
239
+ } catch {
240
+ return false
241
+ }
242
+ }
243
+ }
244
+
245
+ export function startCaddy(caddyfilePath) {
246
+ try {
247
+ execSync(`caddy start --config "${caddyfilePath}" >/dev/null 2>&1`, { stdio: ['inherit', 'pipe', 'pipe'] })
248
+ return true
249
+ } catch {
250
+ return false
251
+ }
252
+ }
253
+
254
+ export function stopCaddy() {
255
+ try {
256
+ execSync('curl -sf -X POST http://localhost:2019/stop', { timeout: 5000, stdio: 'ignore' })
257
+ return true
258
+ } catch {
259
+ return false
260
+ }
261
+ }
262
+
263
+ // When run directly as `storyboard proxy` (not imported by setup.js)
264
+ const isDirectRun = process.argv[2] === 'proxy'
265
+ if (isDirectRun) {
266
+ const { intro, outro, log, spinner } = await import('@clack/prompts')
267
+ const subcommand = process.argv[3]
268
+
269
+ if (!isCaddyInstalled()) {
270
+ intro('storyboard proxy')
271
+ log.error('Caddy is not installed. Run: brew install caddy')
272
+ process.exit(1)
273
+ }
274
+
275
+ if (subcommand === 'close' || subcommand === 'stop') {
276
+ intro('storyboard proxy close')
277
+ if (!isCaddyRunning()) {
278
+ log.info('Proxy is not running.')
279
+ outro('')
280
+ } else if (stopCaddy()) {
281
+ log.success('Proxy stopped.')
282
+ outro('')
283
+ } else {
284
+ log.error('Failed to stop proxy.')
285
+ process.exit(1)
286
+ }
287
+ } else if (subcommand === 'start' || !subcommand) {
288
+ intro('storyboard proxy start')
289
+ const caddyfilePath = generateCaddyfile()
290
+
291
+ const s = spinner()
292
+ if (isCaddyRunning()) {
293
+ s.start('Updating proxy routes...')
294
+ const routeConfig = generateRouteConfig()
295
+ if (upsertCaddyRoute(routeConfig)) {
296
+ s.stop('Proxy routes updated (admin API)')
297
+ } else if (reloadCaddy(caddyfilePath)) {
298
+ s.stop('Proxy reloaded (Caddyfile fallback)')
299
+ } else {
300
+ s.stop('Failed to update proxy')
301
+ process.exit(1)
302
+ }
303
+ } else {
304
+ s.start('Starting proxy...')
305
+ if (startCaddy(caddyfilePath)) {
306
+ s.stop('Proxy started')
307
+ } else {
308
+ s.stop('Failed to start proxy')
309
+ process.exit(1)
310
+ }
311
+ }
312
+ outro(`Proxy ready at http://${DOMAIN}/`)
313
+ } else {
314
+ intro('storyboard proxy')
315
+ log.error(`Unknown subcommand: "${subcommand}"`)
316
+ log.info('Available: start, close')
317
+ process.exit(1)
318
+ }
319
+ }
@@ -0,0 +1,63 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { findStaleRouteIndices } from './proxy.js'
3
+
4
+ describe('findStaleRouteIndices', () => {
5
+ it('identifies stale non-@id routes matching the same host', () => {
6
+ const routes = [
7
+ { match: [{ host: ['storyboard.localhost'] }], terminal: true }, // index 0: stale
8
+ { '@id': 'storyboard', match: [{ host: ['storyboard.localhost'] }] }, // index 1: ours
9
+ ]
10
+ const indices = findStaleRouteIndices(routes, 'storyboard', 'storyboard.localhost')
11
+ expect(indices).toEqual([0])
12
+ })
13
+
14
+ it('preserves routes with a different @id for the same host', () => {
15
+ const routes = [
16
+ { '@id': 'other-app', match: [{ host: ['storyboard.localhost'] }] },
17
+ { '@id': 'storyboard', match: [{ host: ['storyboard.localhost'] }] },
18
+ ]
19
+ const indices = findStaleRouteIndices(routes, 'storyboard', 'storyboard.localhost')
20
+ expect(indices).toEqual([])
21
+ })
22
+
23
+ it('returns multiple stale indices in descending order', () => {
24
+ const routes = [
25
+ { match: [{ host: ['storyboard.localhost'] }] }, // index 0: stale
26
+ { '@id': 'storyboard-core', match: [{ host: ['storyboard-core.localhost'] }] }, // index 1: different host
27
+ { '@id': 'storyboard', match: [{ host: ['storyboard.localhost'] }] }, // index 2: ours
28
+ { match: [{ host: ['storyboard.localhost'] }] }, // index 3: stale
29
+ ]
30
+ const indices = findStaleRouteIndices(routes, 'storyboard', 'storyboard.localhost')
31
+ expect(indices).toEqual([3, 0]) // descending for safe deletion
32
+ })
33
+
34
+ it('ignores routes for a different host', () => {
35
+ const routes = [
36
+ { match: [{ host: ['other.localhost'] }] },
37
+ { '@id': 'storyboard', match: [{ host: ['storyboard.localhost'] }] },
38
+ ]
39
+ const indices = findStaleRouteIndices(routes, 'storyboard', 'storyboard.localhost')
40
+ expect(indices).toEqual([])
41
+ })
42
+
43
+ it('handles routes with no match field', () => {
44
+ const routes = [
45
+ { handle: [{ handler: 'reverse_proxy' }] }, // no match
46
+ { '@id': 'storyboard', match: [{ host: ['storyboard.localhost'] }] },
47
+ ]
48
+ const indices = findStaleRouteIndices(routes, 'storyboard', 'storyboard.localhost')
49
+ expect(indices).toEqual([])
50
+ })
51
+
52
+ it('returns empty array when no stale routes exist', () => {
53
+ const routes = [
54
+ { '@id': 'storyboard', match: [{ host: ['storyboard.localhost'] }] },
55
+ ]
56
+ const indices = findStaleRouteIndices(routes, 'storyboard', 'storyboard.localhost')
57
+ expect(indices).toEqual([])
58
+ })
59
+
60
+ it('handles empty routes array', () => {
61
+ expect(findStaleRouteIndices([], 'storyboard', 'storyboard.localhost')).toEqual([])
62
+ })
63
+ })
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Creation schemas — define flags for each `storyboard create` subcommand.
3
+ *
4
+ * Each schema is a plain object mapping flag names to definitions.
5
+ * Schemas serve as validation, documentation, and help-text source.
6
+ *
7
+ * @typedef {import('./flags.js').FlagSchema} FlagSchema
8
+ */
9
+
10
+ /** @type {FlagSchema} */
11
+ export const prototypeSchema = {
12
+ name: {
13
+ type: 'string',
14
+ required: true,
15
+ description: 'Prototype name (kebab-case)',
16
+ aliases: ['n'],
17
+ },
18
+ title: {
19
+ type: 'string',
20
+ description: 'Display title',
21
+ aliases: ['t'],
22
+ },
23
+ folder: {
24
+ type: 'string',
25
+ description: 'Parent .folder directory',
26
+ aliases: ['f'],
27
+ },
28
+ partial: {
29
+ type: 'string',
30
+ description: 'Template/recipe name',
31
+ aliases: ['p'],
32
+ },
33
+ author: {
34
+ type: 'string',
35
+ description: 'Author name(s)',
36
+ aliases: ['a'],
37
+ },
38
+ description: {
39
+ type: 'string',
40
+ description: 'Prototype description',
41
+ aliases: ['d'],
42
+ },
43
+ url: {
44
+ type: 'string',
45
+ description: 'External URL (makes it an external prototype)',
46
+ },
47
+ flow: {
48
+ type: 'boolean',
49
+ default: false,
50
+ description: 'Create a default flow file',
51
+ },
52
+ }
53
+
54
+ /** @type {FlagSchema} */
55
+ export const canvasSchema = {
56
+ name: {
57
+ type: 'string',
58
+ required: true,
59
+ description: 'Canvas name (kebab-case)',
60
+ aliases: ['n'],
61
+ },
62
+ title: {
63
+ type: 'string',
64
+ description: 'Display title',
65
+ aliases: ['t'],
66
+ },
67
+ folder: {
68
+ type: 'string',
69
+ description: 'Parent .folder directory',
70
+ aliases: ['f'],
71
+ },
72
+ grid: {
73
+ type: 'boolean',
74
+ default: true,
75
+ description: 'Show dot grid',
76
+ },
77
+ jsx: {
78
+ type: 'boolean',
79
+ default: false,
80
+ description: 'Include JSX companion file',
81
+ },
82
+ description: {
83
+ type: 'string',
84
+ description: 'Optional description',
85
+ aliases: ['d'],
86
+ },
87
+ }
88
+
89
+ /** @type {FlagSchema} */
90
+ export const flowSchema = {
91
+ name: {
92
+ type: 'string',
93
+ required: true,
94
+ description: 'Flow name (kebab-case)',
95
+ aliases: ['n'],
96
+ },
97
+ prototype: {
98
+ type: 'string',
99
+ required: true,
100
+ description: 'Target prototype name',
101
+ aliases: ['p'],
102
+ },
103
+ title: {
104
+ type: 'string',
105
+ description: 'Display title',
106
+ aliases: ['t'],
107
+ },
108
+ folder: {
109
+ type: 'string',
110
+ description: 'Parent .folder directory',
111
+ aliases: ['f'],
112
+ },
113
+ globals: {
114
+ type: 'array',
115
+ description: '$global object names',
116
+ aliases: ['g'],
117
+ },
118
+ 'copy-from': {
119
+ type: 'string',
120
+ description: 'Existing flow to copy from',
121
+ },
122
+ author: {
123
+ type: 'string',
124
+ description: 'Author name(s)',
125
+ aliases: ['a'],
126
+ },
127
+ description: {
128
+ type: 'string',
129
+ description: 'Flow description',
130
+ aliases: ['d'],
131
+ },
132
+ 'starting-page': {
133
+ type: 'string',
134
+ description: 'Starting page path',
135
+ },
136
+ }
137
+
138
+ /** @type {FlagSchema} */
139
+ export const pageSchema = {
140
+ prototype: {
141
+ type: 'string',
142
+ required: true,
143
+ description: 'Target prototype name',
144
+ aliases: ['p'],
145
+ },
146
+ path: {
147
+ type: 'string',
148
+ required: true,
149
+ description: 'Page path (e.g. settings/general)',
150
+ },
151
+ folder: {
152
+ type: 'string',
153
+ description: 'Parent .folder directory',
154
+ aliases: ['f'],
155
+ },
156
+ template: {
157
+ type: 'string',
158
+ description: 'Page template name',
159
+ aliases: ['t'],
160
+ },
161
+ }
162
+
163
+ /** @type {FlagSchema} */
164
+ export const widgetSchema = {
165
+ canvas: {
166
+ type: 'string',
167
+ required: true,
168
+ description: 'Target canvas name',
169
+ aliases: ['c'],
170
+ },
171
+ x: {
172
+ type: 'number',
173
+ description: 'X position (omit for auto-positioning)',
174
+ },
175
+ y: {
176
+ type: 'number',
177
+ description: 'Y position (omit for auto-positioning)',
178
+ },
179
+ near: {
180
+ type: 'string',
181
+ description: 'Place near this widget ID (default: auto-selects last widget). Use --near false to disable',
182
+ },
183
+ direction: {
184
+ type: 'string',
185
+ default: 'right',
186
+ description: 'Direction from reference widget: right, left, above, below',
187
+ aliases: ['dir'],
188
+ },
189
+ resolve: {
190
+ type: 'boolean',
191
+ default: false,
192
+ description: 'Run server-side collision detection on the target position',
193
+ },
194
+ props: {
195
+ type: 'string',
196
+ description: 'Widget props as JSON string',
197
+ },
198
+ 'props-file': {
199
+ type: 'string',
200
+ description: 'Path to a JSON file containing widget props (avoids shell escaping)',
201
+ aliases: ['pf'],
202
+ },
203
+ json: {
204
+ type: 'boolean',
205
+ default: false,
206
+ description: 'Output result as JSON (includes widget id)',
207
+ },
208
+ }
209
+
210
+ /** @type {FlagSchema} */
211
+ export const componentSchema = {
212
+ name: {
213
+ type: 'string',
214
+ required: true,
215
+ description: 'Component name (kebab-case)',
216
+ aliases: ['n'],
217
+ },
218
+ directory: {
219
+ type: 'string',
220
+ description: 'Subdirectory inside src/components/',
221
+ aliases: ['d'],
222
+ },
223
+ }