@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,28 @@
1
+ /**
2
+ * Workshop feature registry.
3
+ *
4
+ * Maps feature names (matching keys in storyboard.config.json → workshop.features)
5
+ * to their module paths. The server plugin and client mount use this registry
6
+ * to dynamically load only the enabled features.
7
+ *
8
+ * To add a new feature:
9
+ * 1. Create a directory under features/ with index.js exporting the standard interface
10
+ * 2. Add its import here
11
+ */
12
+
13
+ import * as createPrototype from './createPrototype/index.js'
14
+ import * as createFlow from './createFlow/index.js'
15
+ import * as createPage from './createPage/index.js'
16
+ import * as createCanvas from './createCanvas/index.js'
17
+ import * as createStory from './createStory/index.js'
18
+
19
+ /**
20
+ * All available workshop features, keyed by config name.
21
+ */
22
+ export const features = {
23
+ createPrototype,
24
+ createFlow,
25
+ createPage,
26
+ createCanvas,
27
+ createStory,
28
+ }
@@ -0,0 +1,155 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { parse as parseJsonc } from 'jsonc-parser'
4
+
5
+ const TEMPLATE_DIR_NAMES = new Set(['template', 'templates'])
6
+ const RECIPE_DIR_NAMES = new Set(['recipe', 'recipes'])
7
+ const PARTIAL_DIR_NAMES = new Set([...TEMPLATE_DIR_NAMES, ...RECIPE_DIR_NAMES])
8
+
9
+ function normalizeSlashes(value) {
10
+ return value.replaceAll('\\', '/')
11
+ }
12
+
13
+ function isExternalPrototype(prototypePath) {
14
+ try {
15
+ const raw = fs.readFileSync(prototypePath, 'utf-8')
16
+ const parsed = parseJsonc(raw)
17
+ return typeof parsed?.url === 'string' && parsed.url.trim().length > 0
18
+ } catch {
19
+ return false
20
+ }
21
+ }
22
+
23
+ function isPartialDirName(directory) {
24
+ return PARTIAL_DIR_NAMES.has(directory)
25
+ }
26
+
27
+ function toPartialKind(directory) {
28
+ if (TEMPLATE_DIR_NAMES.has(directory)) return 'template'
29
+ if (RECIPE_DIR_NAMES.has(directory)) return 'recipe'
30
+ return null
31
+ }
32
+
33
+ function listPrototypeDirs(root) {
34
+ const prototypesDir = path.join(root, 'src', 'prototypes')
35
+ if (!fs.existsSync(prototypesDir)) return []
36
+
37
+ const results = []
38
+
39
+ function scanDir(dir, folder) {
40
+ if (!fs.existsSync(dir)) return
41
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
42
+ if (!entry.isDirectory()) continue
43
+ if (entry.name.endsWith('.folder')) {
44
+ scanDir(path.join(dir, entry.name), entry.name.replace(/\.folder$/, ''))
45
+ continue
46
+ }
47
+
48
+ const protoDir = path.join(dir, entry.name)
49
+ const prototypeFile = fs.readdirSync(protoDir).find((f) => f.endsWith('.prototype.json'))
50
+ if (!prototypeFile) continue
51
+ if (isExternalPrototype(path.join(protoDir, prototypeFile))) continue
52
+
53
+ results.push({
54
+ name: entry.name,
55
+ folder,
56
+ dir: protoDir,
57
+ })
58
+ }
59
+ }
60
+
61
+ scanDir(prototypesDir)
62
+ return results
63
+ }
64
+
65
+ function makeId(prefix, directory, name, prototype, folder) {
66
+ if (prefix === 'global') {
67
+ return `global:${directory}/${name}`
68
+ }
69
+ const folderPart = folder ? `${folder}/` : ''
70
+ return `prototype:${folderPart}${prototype}:${directory}/${name}`
71
+ }
72
+
73
+ export function buildTemplateRecipeIndex(root, configPartials = []) {
74
+ const srcRoot = path.join(root, 'src')
75
+ const results = []
76
+ const seenIds = new Set()
77
+
78
+ for (const partial of configPartials) {
79
+ if (!partial || !isPartialDirName(partial.directory) || !partial.name) continue
80
+ const kind = toPartialKind(partial.directory)
81
+ if (!kind) continue
82
+ const id = makeId('global', partial.directory, partial.name)
83
+ if (seenIds.has(id)) continue
84
+ seenIds.add(id)
85
+ results.push({
86
+ id,
87
+ name: partial.name,
88
+ directory: partial.directory,
89
+ kind,
90
+ scope: 'global',
91
+ baseDir: partial.directory,
92
+ globals: Array.isArray(partial.globals) ? partial.globals : undefined,
93
+ })
94
+ }
95
+
96
+ const prototypes = listPrototypeDirs(root)
97
+ for (const proto of prototypes) {
98
+ for (const directory of PARTIAL_DIR_NAMES) {
99
+ const localPartialsDir = path.join(proto.dir, directory)
100
+ if (!fs.existsSync(localPartialsDir)) continue
101
+
102
+ for (const entry of fs.readdirSync(localPartialsDir, { withFileTypes: true })) {
103
+ if (!entry.isDirectory()) continue
104
+ const kind = toPartialKind(directory)
105
+ if (!kind) continue
106
+
107
+ const baseDir = normalizeSlashes(path.relative(srcRoot, localPartialsDir))
108
+ const id = makeId('prototype', directory, entry.name, proto.name, proto.folder)
109
+ if (seenIds.has(id)) continue
110
+ seenIds.add(id)
111
+ results.push({
112
+ id,
113
+ name: entry.name,
114
+ directory,
115
+ kind,
116
+ scope: 'prototype',
117
+ prototype: proto.name,
118
+ ...(proto.folder ? { folder: proto.folder } : {}),
119
+ baseDir,
120
+ })
121
+ }
122
+ }
123
+ }
124
+
125
+ return results.sort((a, b) => {
126
+ if (a.scope !== b.scope) return a.scope.localeCompare(b.scope)
127
+ if ((a.prototype || '') !== (b.prototype || '')) return (a.prototype || '').localeCompare(b.prototype || '')
128
+ if ((a.folder || '') !== (b.folder || '')) return (a.folder || '').localeCompare(b.folder || '')
129
+ if (a.kind !== b.kind) return a.kind.localeCompare(b.kind)
130
+ return a.name.localeCompare(b.name)
131
+ })
132
+ }
133
+
134
+ export function resolveTemplateRecipeEntry(entries, templateValue, { prototype, folder } = {}) {
135
+ if (!templateValue || typeof templateValue !== 'string') return null
136
+
137
+ const trimmed = templateValue.trim()
138
+ if (!trimmed) return null
139
+
140
+ const byId = entries.find((entry) => entry.id === trimmed)
141
+ if (byId) return byId
142
+
143
+ const exactScoped = entries.find((entry) =>
144
+ entry.scope === 'prototype' &&
145
+ entry.name === trimmed &&
146
+ entry.prototype === prototype &&
147
+ (entry.folder || '') === (folder || '')
148
+ )
149
+ if (exactScoped) return exactScoped
150
+
151
+ const globalMatch = entries.find((entry) => entry.scope === 'global' && entry.name === trimmed)
152
+ if (globalMatch) return globalMatch
153
+
154
+ return entries.find((entry) => entry.name === trimmed) || null
155
+ }
@@ -0,0 +1,98 @@
1
+ import { useState, useMemo, useEffect, useCallback } from 'react'
2
+
3
+ export default function WorkshopPanel({ features = [] }) {
4
+ const [menuOpen, setMenuOpen] = useState(false)
5
+ const [activeOverlay, setActiveOverlay] = useState(null)
6
+ const [visible, setVisible] = useState(true)
7
+
8
+ const activeFeature = useMemo(
9
+ () => (activeOverlay ? features.find((f) => f.overlayId === activeOverlay) ?? null : null),
10
+ [activeOverlay, features]
11
+ )
12
+
13
+ const showOverlay = useCallback((id) => {
14
+ setActiveOverlay(id)
15
+ setMenuOpen(false)
16
+ }, [])
17
+
18
+ const closeOverlay = useCallback(() => setActiveOverlay(null), [])
19
+
20
+ const handleKeydown = useCallback(
21
+ (e) => {
22
+ if (e.key === '.' && (e.metaKey || e.ctrlKey)) {
23
+ setVisible((v) => !v)
24
+ return
25
+ }
26
+ if (e.key === 'Escape') {
27
+ setActiveOverlay((cur) => {
28
+ if (cur) return null
29
+ setMenuOpen(false)
30
+ return cur
31
+ })
32
+ }
33
+ },
34
+ []
35
+ )
36
+
37
+ const handleClickOutside = useCallback((e) => {
38
+ if (!e.target.closest('[data-workshop-panel]')) {
39
+ setMenuOpen(false)
40
+ }
41
+ }, [])
42
+
43
+ useEffect(() => {
44
+ window.addEventListener('keydown', handleKeydown)
45
+ document.addEventListener('click', handleClickOutside)
46
+ return () => {
47
+ window.removeEventListener('keydown', handleKeydown)
48
+ document.removeEventListener('click', handleClickOutside)
49
+ }
50
+ }, [handleKeydown, handleClickOutside])
51
+
52
+ if (!visible) return null
53
+
54
+ const ActiveOverlay = activeFeature?.overlay
55
+
56
+ return (
57
+ <div data-workshop-panel className="fixed bottom-6 right-[76px] z-[9999] font-sans">
58
+ <button
59
+ className="flex items-center p-3 bg-popover text-muted-foreground border border-border rounded-full cursor-pointer shadow-lg hover:scale-105 active:scale-95 transition-transform select-none"
60
+ aria-label="Workshop"
61
+ onClick={() => setMenuOpen((o) => !o)}
62
+ >
63
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16" fill="currentColor">
64
+ <path d="M5.433 2.304A4.494 4.494 0 0 0 3.5 6c0 1.598.832 3.002 2.09 3.802.518.328.929.923.902 1.64v.008l-.164 3.337a.75.75 0 1 1-1.498-.073l.163-3.34c.007-.14-.1-.313-.36-.465A5.986 5.986 0 0 1 2 6a5.994 5.994 0 0 1 2.567-4.92 1.482 1.482 0 0 1 1.673-.04c.462.296.76.827.76 1.423v2.076c0 .332.214.572.491.572.268 0 .492-.24.492-.572V2.463c0-.596.298-1.127.76-1.423a1.482 1.482 0 0 1 1.673.04A5.994 5.994 0 0 1 13 6a5.986 5.986 0 0 1-2.633 4.909c-.26.152-.367.325-.36.465l.164 3.34a.75.75 0 1 1-1.498.073l-.164-3.337v-.008c-.027-.717.384-1.312.902-1.64A4.494 4.494 0 0 0 11.5 6a4.494 4.494 0 0 0-1.933-3.696c-.024.017-.067.067-.067.159v2.076c0 1.074-.84 2.072-1.991 2.072-1.161 0-2.009-.998-2.009-2.072V2.463c0-.092-.043-.142-.067-.16Z" />
65
+ </svg>
66
+ </button>
67
+
68
+ {menuOpen && (
69
+ <div className="absolute bottom-14 right-0 min-w-[200px] bg-popover text-popover-foreground border border-border rounded-xl shadow-lg overflow-hidden">
70
+ <div className="px-4 pt-2 pb-1 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground">Workshop</div>
71
+ <div className="h-px bg-border" />
72
+ {features.map((f) => (
73
+ <button
74
+ key={f.overlayId}
75
+ className="flex items-center gap-2 w-full px-4 py-2 text-sm bg-transparent border-none cursor-pointer text-left hover:bg-accent transition-colors"
76
+ onClick={() => showOverlay(f.overlayId)}
77
+ >
78
+ <span className="text-sm">{f.icon || ''}</span> {f.label}
79
+ </button>
80
+ ))}
81
+ <div className="h-px bg-border" />
82
+ <div className="px-4 py-1.5 text-[11px] text-muted-foreground">Dev-only tools</div>
83
+ </div>
84
+ )}
85
+
86
+ {ActiveOverlay && (
87
+ <div
88
+ className="fixed inset-0 z-[10000] flex items-center justify-center bg-black/50"
89
+ onClick={(e) => { if (e.target === e.currentTarget) closeOverlay() }}
90
+ >
91
+ <div className="w-full max-w-[480px]">
92
+ <ActiveOverlay onClose={closeOverlay} />
93
+ </div>
94
+ </div>
95
+ )}
96
+ </div>
97
+ )
98
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Workshop panel mount — stub (Svelte UI removed).
3
+ */
4
+
5
+ export function mountWorkshopPanel() {}
6
+ export function unmountWorkshopPanel() {}
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Worktree Port Registry
3
+ *
4
+ * Manages a JSON registry (worktrees/ports.json) that maps worktree names
5
+ * to unique dev-server ports. Main always gets 1234; worktrees get 1235+.
6
+ *
7
+ * This module is published as part of @dfosco/storyboard-core so client
8
+ * repos can use port detection without duplicating the logic.
9
+ *
10
+ * Programmatic API:
11
+ * import { getPort, detectWorktreeName, resolvePort } from '@dfosco/storyboard/worktree/port'
12
+ */
13
+
14
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, realpathSync } from 'fs'
15
+ import { join, dirname, basename } from 'path'
16
+ import { execSync } from 'child_process'
17
+ import { findByWorktree } from './serverRegistry.js'
18
+
19
+ const BASE_PORT = 1234
20
+
21
+ /**
22
+ * Resolve the path to worktrees/ports.json.
23
+ *
24
+ * Derives the repo root from directory structure so it works whether
25
+ * running from the repo root or from inside worktrees/<name>/ — even
26
+ * when ports.json does not exist yet.
27
+ *
28
+ * @param {string} [cwd] — override working directory
29
+ * @returns {string} absolute path to ports.json
30
+ */
31
+ export function portsFilePath(cwd = process.cwd()) {
32
+ const realCwd = realpathSync(cwd)
33
+
34
+ // Check if we're inside worktrees/<name>/
35
+ const worktreeMatch = realCwd.match(/^(.+)[/\\]worktrees[/\\][^/\\]+/)
36
+ if (worktreeMatch) {
37
+ return join(worktreeMatch[1], 'worktrees', 'ports.json')
38
+ }
39
+
40
+ // We're at the repo root (or somewhere else) — default location
41
+ return join(realCwd, 'worktrees', 'ports.json')
42
+ }
43
+
44
+ /**
45
+ * Detect the worktree name from the current git context.
46
+ *
47
+ * Returns 'main' when not inside a worktrees/<name>/ directory.
48
+ */
49
+ export function detectWorktreeName() {
50
+ try {
51
+ const topLevel = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim()
52
+ const realTop = realpathSync(topLevel)
53
+
54
+ // Check if we're inside a worktrees/<name> directory
55
+ if (realTop.includes('worktrees/') || realTop.includes('worktrees\\')) {
56
+ return basename(realTop)
57
+ }
58
+
59
+ // Also check the cwd pattern
60
+ const realCwd = realpathSync(process.cwd())
61
+ const worktreeMatch = realCwd.match(/worktrees[/\\]([^/\\]+)/)
62
+ if (worktreeMatch) return worktreeMatch[1]
63
+
64
+ // Not a worktree — check the current branch name
65
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', { encoding: 'utf8' }).trim()
66
+ if (branch && branch !== 'main' && branch !== 'master') return branch
67
+
68
+ return 'main'
69
+ } catch {
70
+ return 'main'
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Check if a port is in use by another process (synchronous, best-effort via lsof).
76
+ * Skipped when NODE_ENV=test to avoid test flakiness from real port state.
77
+ * @param {number} port
78
+ * @returns {boolean}
79
+ */
80
+ function isPortInUse(port) {
81
+ if (process.env.NODE_ENV === 'test') return false
82
+ try {
83
+ execSync(`lsof -i :${port} -sTCP:LISTEN`, { stdio: 'ignore', timeout: 2000 })
84
+ return true
85
+ } catch {
86
+ return false
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Get or assign a port for the given worktree name.
92
+ *
93
+ * Creates worktrees/ports.json if it doesn't exist. Assigns ports
94
+ * starting at BASE_PORT+1 (1235) for non-main worktrees.
95
+ * If the previously assigned port was stolen by another process,
96
+ * reassigns to the next available port.
97
+ *
98
+ * @param {string} worktreeName
99
+ * @returns {number}
100
+ */
101
+ export function getPort(worktreeName) {
102
+ const portsFile = portsFilePath()
103
+ const dir = dirname(portsFile)
104
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
105
+
106
+ let ports = { main: BASE_PORT }
107
+ if (existsSync(portsFile)) {
108
+ try {
109
+ ports = JSON.parse(readFileSync(portsFile, 'utf8'))
110
+ } catch {
111
+ // Corrupted file — start fresh
112
+ }
113
+ }
114
+
115
+ if (worktreeName === 'main') return ports.main || BASE_PORT
116
+
117
+ // If port already assigned, verify it's not stolen by another process
118
+ if (ports[worktreeName]) {
119
+ if (!isPortInUse(ports[worktreeName])) {
120
+ return ports[worktreeName]
121
+ }
122
+ // Port is occupied — fall through to reassign
123
+ }
124
+
125
+ const usedPorts = Object.values(ports)
126
+ let nextPort = BASE_PORT + 1
127
+ while (usedPorts.includes(nextPort)) nextPort++
128
+ ports[worktreeName] = nextPort
129
+ writeFileSync(portsFile, JSON.stringify(ports, null, 2) + '\n')
130
+
131
+ return ports[worktreeName]
132
+ }
133
+
134
+ /**
135
+ * Release a port assignment for a worktree.
136
+ *
137
+ * Removes the entry from ports.json so the port can be reused.
138
+ * Never removes 'main'.
139
+ *
140
+ * @param {string} worktreeName
141
+ */
142
+ export function releasePort(worktreeName) {
143
+ if (worktreeName === 'main') return
144
+
145
+ const portsFile = portsFilePath()
146
+ if (!existsSync(portsFile)) return
147
+
148
+ try {
149
+ const ports = JSON.parse(readFileSync(portsFile, 'utf8'))
150
+ if (!(worktreeName in ports)) return
151
+ delete ports[worktreeName]
152
+ writeFileSync(portsFile, JSON.stringify(ports, null, 2) + '\n')
153
+ } catch { /* ignore corrupt file */ }
154
+ }
155
+
156
+ /**
157
+ * Resolve the port for a running dev server.
158
+ *
159
+ * Checks the server registry (servers.json) first for a live process,
160
+ * then falls back to ports.json assignment. Use this when connecting
161
+ * to an already-running server — it returns the real bound port even
162
+ * when Vite rebinds to a different port than originally assigned.
163
+ *
164
+ * @param {string} worktreeName
165
+ * @returns {number}
166
+ */
167
+ export function resolveRunningPort(worktreeName) {
168
+ try {
169
+ const servers = findByWorktree(worktreeName)
170
+ if (servers.length > 0) {
171
+ const latest = servers.reduce((a, b) =>
172
+ (a.startedAt || '') >= (b.startedAt || '') ? a : b
173
+ )
174
+ return latest.port
175
+ }
176
+ } catch { /* registry unavailable */ }
177
+
178
+ return resolvePort(worktreeName)
179
+ }
180
+
181
+ /**
182
+ * Resolve the port for a worktree from worktrees/ports.json
183
+ * without assigning a new one if missing.
184
+ *
185
+ * @param {string} worktreeName
186
+ * @returns {number}
187
+ */
188
+ export function resolvePort(worktreeName) {
189
+ const portsFile = portsFilePath()
190
+ if (!existsSync(portsFile)) return BASE_PORT
191
+
192
+ try {
193
+ const ports = JSON.parse(readFileSync(portsFile, 'utf8'))
194
+ return ports[worktreeName] ?? BASE_PORT
195
+ } catch {
196
+ return BASE_PORT
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Slugify a branch name for filesystem and subdomain safety.
202
+ *
203
+ * - lowercase
204
+ * - dots, spaces, underscores, non-alphanumeric (except - and /) → hyphens
205
+ * - collapse consecutive hyphens
206
+ * - trim leading/trailing hyphens per segment
207
+ */
208
+ export function slugify(name) {
209
+ return name
210
+ .toLowerCase()
211
+ .replace(/[^a-z0-9/-]/g, '-')
212
+ .replace(/-{2,}/g, '-')
213
+ .split('/')
214
+ .map((s) => s.replace(/^-+|-+$/g, ''))
215
+ .join('/')
216
+ }
217
+
218
+ /**
219
+ * Resolve the repo root — the directory that contains `worktrees/`.
220
+ *
221
+ * Works whether cwd is the repo root itself or inside `worktrees/<name>/`.
222
+ *
223
+ * @param {string} [cwd]
224
+ * @returns {string} absolute path to repo root
225
+ */
226
+ export function repoRoot(cwd = process.cwd()) {
227
+ const realCwd = realpathSync(cwd)
228
+
229
+ const worktreeMatch = realCwd.match(/^(.+)[/\\]worktrees[/\\][^/\\]+/)
230
+ if (worktreeMatch) return worktreeMatch[1]
231
+
232
+ return realCwd
233
+ }
234
+
235
+ /**
236
+ * Resolve the full path to a worktree directory.
237
+ *
238
+ * Returns repo root for 'main', `worktrees/<name>` otherwise.
239
+ *
240
+ * @param {string} name — worktree name
241
+ * @param {string} [cwd]
242
+ * @returns {string} absolute path
243
+ */
244
+ export function worktreeDir(name, cwd) {
245
+ const root = repoRoot(cwd)
246
+ if (name === 'main') return root
247
+ return join(root, 'worktrees', name)
248
+ }
249
+
250
+ /**
251
+ * List existing worktree directory names from `worktrees/`.
252
+ *
253
+ * Only returns directories that look like real worktrees (contain a `.git` file).
254
+ * Does not include 'main'.
255
+ *
256
+ * @param {string} [cwd]
257
+ * @returns {string[]}
258
+ */
259
+ export function listWorktrees(cwd) {
260
+ const root = repoRoot(cwd)
261
+ const worktreesDir = join(root, 'worktrees')
262
+
263
+ if (!existsSync(worktreesDir)) return []
264
+
265
+ return readdirSync(worktreesDir, { withFileTypes: true })
266
+ .filter((d) => d.isDirectory() && existsSync(join(worktreesDir, d.name, '.git')))
267
+ .map((d) => d.name)
268
+ }