@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,531 @@
1
+ /**
2
+ * Rename Watcher
3
+ *
4
+ * Detects file and directory renames under watched directories and updates
5
+ * canvas embed URLs (prototype and canvas widgets) to stay current.
6
+ * Auto-commits the changes with a configurable prefix.
7
+ *
8
+ * Uses snapshot-based diffing: on any fs event, re-scans the watched
9
+ * directories and compares old vs new file sets. Only unambiguous renames
10
+ * (1:1 file or directory mappings) are acted on.
11
+ *
12
+ * Configuration is loaded from config.json in this directory.
13
+ */
14
+
15
+ import fs from 'node:fs'
16
+ import path from 'node:path'
17
+ import { execFileSync } from 'node:child_process'
18
+ import { materializeFromText } from '../canvas/materializer.js'
19
+ import { toCanvasId } from '../canvas/identity.js'
20
+
21
+ // ─── Logging ─────────────────────────────────────────────────────────
22
+
23
+ const dim = (s) => `\x1b[2m${s}\x1b[0m`
24
+ const green = (s) => `\x1b[32m${s}\x1b[0m`
25
+ const yellow = (s) => `\x1b[33m${s}\x1b[0m`
26
+
27
+ function log(msg) { console.log(dim(` ◈ rename-watcher: ${msg}`)) }
28
+ function logSuccess(msg) { console.log(green(` ✓ rename-watcher: ${msg}`)) }
29
+ function logWarn(msg) { console.log(yellow(` ⚠ rename-watcher: ${msg}`)) }
30
+
31
+ // ─── Config ──────────────────────────────────────────────────────────
32
+
33
+ function loadConfig() {
34
+ const configPath = new URL('./config.json', import.meta.url)
35
+ return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
36
+ }
37
+
38
+ // ─── File scanning ───────────────────────────────────────────────────
39
+
40
+ /**
41
+ * Scan a watched directory and return a Set of relative file paths
42
+ * matching the configured extensions and exclusions.
43
+ */
44
+ function scanDirectory(root, watchEntry, config) {
45
+ const results = new Set()
46
+ const absDir = path.join(root, watchEntry.path)
47
+
48
+ if (!fs.existsSync(absDir)) return results
49
+
50
+ const excludeDirs = new Set(config.exclude.directories)
51
+ const excludePrefixes = config.exclude.filePrefixes
52
+ const extensions = watchEntry.extensions
53
+
54
+ function walk(dir, rel) {
55
+ let entries
56
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return }
57
+
58
+ for (const entry of entries) {
59
+ if (excludeDirs.has(entry.name)) continue
60
+ const relPath = rel ? `${rel}/${entry.name}` : entry.name
61
+
62
+ if (entry.isDirectory()) {
63
+ walk(path.join(dir, entry.name), relPath)
64
+ } else {
65
+ if (excludePrefixes.some((p) => entry.name.startsWith(p))) continue
66
+ if (extensions.some((ext) => entry.name.endsWith(ext))) {
67
+ results.add(relPath)
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ walk(absDir, '')
74
+ return results
75
+ }
76
+
77
+ // ─── Route computation ───────────────────────────────────────────────
78
+
79
+ /**
80
+ * Compute the route path for a prototype file (relative to src/prototypes/).
81
+ * Mirrors the route regex in src/routes.jsx.
82
+ */
83
+ function prototypeRoute(relPath) {
84
+ let route = relPath
85
+ .replace(/[^/]*\.folder\//g, '')
86
+ .replace(/\.(jsx|tsx|mdx)$/, '')
87
+ .replace(/\/index$/, '')
88
+
89
+ if (!route.startsWith('/')) route = '/' + route
90
+ return route || '/'
91
+ }
92
+
93
+ /**
94
+ * Compute the route path for a canvas file (relative to src/canvas/).
95
+ * Uses toCanvasId() for proper folder handling.
96
+ */
97
+ function canvasRoute(relPath) {
98
+ // relPath is relative to src/canvas/, prepend prefix for toCanvasId()
99
+ const canvasId = toCanvasId('src/canvas/' + relPath)
100
+ return '/canvas/' + canvasId
101
+ }
102
+
103
+ function computeRoute(relPath, watchType) {
104
+ if (watchType === 'prototype') return prototypeRoute(relPath)
105
+ if (watchType === 'canvas') return canvasRoute(relPath)
106
+ return null
107
+ }
108
+
109
+ // ─── Rename detection ────────────────────────────────────────────────
110
+
111
+ /**
112
+ * Get compound extension (e.g. '.canvas.jsonl' not just '.jsonl').
113
+ */
114
+ function getCompoundExt(filePath) {
115
+ const base = path.basename(filePath)
116
+ const parts = base.split('.')
117
+ if (parts.length >= 3) return '.' + parts.slice(-2).join('.')
118
+ return path.extname(filePath)
119
+ }
120
+
121
+ function groupByDir(paths) {
122
+ const groups = new Map()
123
+ for (const p of paths) {
124
+ const dir = path.dirname(p)
125
+ if (!groups.has(dir)) groups.set(dir, [])
126
+ groups.get(dir).push(p)
127
+ }
128
+ return groups
129
+ }
130
+
131
+ /**
132
+ * Find the longest common directory prefix of an array of paths.
133
+ */
134
+ function commonDirPrefix(paths) {
135
+ if (paths.length === 0) return ''
136
+ if (paths.length === 1) {
137
+ const dir = path.dirname(paths[0])
138
+ return dir === '.' ? '' : dir + '/'
139
+ }
140
+
141
+ const sorted = [...paths].sort()
142
+ const first = sorted[0]
143
+ const last = sorted[sorted.length - 1]
144
+
145
+ let i = 0
146
+ while (i < first.length && i < last.length && first[i] === last[i]) i++
147
+
148
+ const prefix = first.slice(0, i)
149
+ const lastSlash = prefix.lastIndexOf('/')
150
+ return lastSlash >= 0 ? prefix.slice(0, lastSlash + 1) : ''
151
+ }
152
+
153
+ /**
154
+ * Detect renames by diffing old and new file snapshots.
155
+ * Only returns unambiguous renames to avoid false positives.
156
+ *
157
+ * Phase 1 — File renames: same directory, same extension, exactly 1 removed + 1 added.
158
+ * Phase 2 — Directory renames: a single directory-prefix swap explains all remaining changes.
159
+ */
160
+ function detectRenames(oldSnapshot, newSnapshot, watchType) {
161
+ const removed = [...oldSnapshot].filter((f) => !newSnapshot.has(f))
162
+ const added = [...newSnapshot].filter((f) => !oldSnapshot.has(f))
163
+
164
+ if (removed.length === 0 || added.length === 0) return []
165
+
166
+ const renames = []
167
+ const matchedRemoved = new Set()
168
+ const matchedAdded = new Set()
169
+
170
+ // Phase 1: File-level renames
171
+ const removedByDir = groupByDir(removed)
172
+ const addedByDir = groupByDir(added)
173
+
174
+ for (const [dir, dirRemoved] of removedByDir) {
175
+ const dirAdded = addedByDir.get(dir) || []
176
+ if (dirRemoved.length !== 1 || dirAdded.length !== 1) continue
177
+ if (getCompoundExt(dirRemoved[0]) !== getCompoundExt(dirAdded[0])) continue
178
+
179
+ const oldRoute = computeRoute(dirRemoved[0], watchType)
180
+ const newRoute = computeRoute(dirAdded[0], watchType)
181
+
182
+ if (oldRoute === newRoute) continue
183
+
184
+ renames.push({ oldPath: dirRemoved[0], newPath: dirAdded[0], oldRoute, newRoute })
185
+ matchedRemoved.add(dirRemoved[0])
186
+ matchedAdded.add(dirAdded[0])
187
+ }
188
+
189
+ // Phase 2: Directory-level renames
190
+ const unmatchedRemoved = removed.filter((f) => !matchedRemoved.has(f))
191
+ const unmatchedAdded = added.filter((f) => !matchedAdded.has(f))
192
+
193
+ if (unmatchedRemoved.length > 0 && unmatchedRemoved.length === unmatchedAdded.length) {
194
+ const oldPrefix = commonDirPrefix(unmatchedRemoved)
195
+ const newPrefix = commonDirPrefix(unmatchedAdded)
196
+
197
+ if (oldPrefix && newPrefix && oldPrefix !== newPrefix) {
198
+ const expectedAdded = new Set(
199
+ unmatchedRemoved.map((f) => newPrefix + f.slice(oldPrefix.length)),
200
+ )
201
+ const allMatch = unmatchedAdded.every((f) => expectedAdded.has(f))
202
+
203
+ if (allMatch) {
204
+ for (const oldFile of unmatchedRemoved) {
205
+ const newFile = newPrefix + oldFile.slice(oldPrefix.length)
206
+ const oldRoute = computeRoute(oldFile, watchType)
207
+ const newRoute = computeRoute(newFile, watchType)
208
+ if (oldRoute !== newRoute) {
209
+ renames.push({ oldPath: oldFile, newPath: newFile, oldRoute, newRoute })
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
215
+
216
+ return renames
217
+ }
218
+
219
+ // ─── Canvas embed updating ───────────────────────────────────────────
220
+
221
+ /**
222
+ * Find all .canvas.jsonl files in the project.
223
+ */
224
+ function findAllCanvasFiles(root) {
225
+ const results = []
226
+ const ignore = new Set(['node_modules', 'dist', '.git', '.worktrees'])
227
+
228
+ function walk(dir) {
229
+ let entries
230
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return }
231
+ for (const entry of entries) {
232
+ if (ignore.has(entry.name)) continue
233
+ const fullPath = path.join(dir, entry.name)
234
+ if (entry.isDirectory()) walk(fullPath)
235
+ else if (entry.name.endsWith('.canvas.jsonl')) results.push(fullPath)
236
+ }
237
+ }
238
+
239
+ walk(root)
240
+ return results
241
+ }
242
+
243
+ /**
244
+ * Rewrite a URL's pathname if it matches a renamed route.
245
+ * Uses segment-boundary matching to avoid partial matches (e.g. /Signup vs /Signup2).
246
+ */
247
+ function rewriteUrl(src, renames) {
248
+ const hashIdx = src.indexOf('#')
249
+ const queryIdx = src.indexOf('?')
250
+
251
+ let pathname, suffix
252
+ if (hashIdx >= 0 && (queryIdx < 0 || hashIdx < queryIdx)) {
253
+ pathname = src.slice(0, hashIdx)
254
+ suffix = src.slice(hashIdx)
255
+ } else if (queryIdx >= 0) {
256
+ pathname = src.slice(0, queryIdx)
257
+ suffix = src.slice(queryIdx)
258
+ } else {
259
+ pathname = src
260
+ suffix = ''
261
+ }
262
+
263
+ for (const { oldRoute, newRoute } of renames) {
264
+ if (!oldRoute || !newRoute) continue
265
+
266
+ // Exact match
267
+ if (pathname === oldRoute) return newRoute + suffix
268
+ // Segment-boundary prefix match
269
+ if (pathname.startsWith(oldRoute + '/')) {
270
+ return newRoute + pathname.slice(oldRoute.length) + suffix
271
+ }
272
+ }
273
+
274
+ return src
275
+ }
276
+
277
+ /**
278
+ * Update widget embed refs for detected renames.
279
+ * Returns the updated widgets array if any changed, null if unchanged.
280
+ */
281
+ function updateWidgetRefs(widgets, renames) {
282
+ let changed = false
283
+
284
+ const updated = widgets.map((widget) => {
285
+ // Only process embed widget types with a src prop
286
+ if (widget.type !== 'prototype' && widget.type !== 'canvas') return widget
287
+
288
+ const src = widget.props?.src
289
+ if (!src || typeof src !== 'string') return widget
290
+
291
+ const newSrc = rewriteUrl(src, renames)
292
+ if (newSrc === src) return widget
293
+
294
+ changed = true
295
+ return { ...widget, props: { ...widget.props, src: newSrc } }
296
+ })
297
+
298
+ return changed ? updated : null
299
+ }
300
+
301
+ // ─── Auto-commit ─────────────────────────────────────────────────────
302
+
303
+ // Lock file placed in .git/ during commit. Both the rename watcher and
304
+ // autosync share the same working tree and git index, so this file
305
+ // lets autosync's isRepoBusy() (or any future tool) detect that the
306
+ // rename watcher is mid-commit and defer its own cycle.
307
+ const LOCK_FILENAME = 'storyboard-autofix.lock'
308
+
309
+ /**
310
+ * Resolve the .git directory, handling both regular repos and worktrees.
311
+ * In a worktree, `.git` is a file pointing to the real git dir.
312
+ */
313
+ function resolveGitDir(root) {
314
+ const dotGit = path.join(root, '.git')
315
+ try {
316
+ const stat = fs.statSync(dotGit)
317
+ if (stat.isDirectory()) return dotGit
318
+ // Worktree: .git is a file like "gitdir: /path/to/.git/worktrees/name"
319
+ const content = fs.readFileSync(dotGit, 'utf-8').trim()
320
+ const match = content.match(/^gitdir:\s*(.+)$/)
321
+ if (match) return path.resolve(root, match[1])
322
+ } catch { /* fallback */ }
323
+ return dotGit
324
+ }
325
+
326
+ /**
327
+ * Check if the repo is in a busy state that would conflict with a commit.
328
+ *
329
+ * Mirrors autosync's isRepoBusy() guards so both systems respect the same
330
+ * signals: index.lock, rebase, merge, cherry-pick, and the autofix lock file.
331
+ * Since both autosync and the rename watcher commit directly on the current
332
+ * branch (same working tree, same index), index.lock is the primary mutex.
333
+ */
334
+ function isRepoBusy(root) {
335
+ const gitDir = resolveGitDir(root)
336
+
337
+ if (fs.existsSync(path.join(gitDir, 'index.lock'))) {
338
+ logWarn('Auto-commit deferred: index.lock present')
339
+ return true
340
+ }
341
+ if (fs.existsSync(path.join(gitDir, 'MERGE_HEAD'))) {
342
+ logWarn('Auto-commit deferred: merge in progress')
343
+ return true
344
+ }
345
+ if (fs.existsSync(path.join(gitDir, 'CHERRY_PICK_HEAD'))) {
346
+ logWarn('Auto-commit deferred: cherry-pick in progress')
347
+ return true
348
+ }
349
+ if (fs.existsSync(path.join(gitDir, 'rebase-merge')) || fs.existsSync(path.join(gitDir, 'rebase-apply'))) {
350
+ logWarn('Auto-commit deferred: rebase in progress')
351
+ return true
352
+ }
353
+
354
+ // Check for our own stale lock (crash recovery)
355
+ const lockPath = path.join(gitDir, LOCK_FILENAME)
356
+ if (fs.existsSync(lockPath)) {
357
+ try {
358
+ const lockAge = Date.now() - fs.statSync(lockPath).mtimeMs
359
+ if (lockAge < 10_000) {
360
+ logWarn('Auto-commit deferred: previous autofix still in progress')
361
+ return true
362
+ }
363
+ // Stale lock (>10s) — remove it
364
+ fs.unlinkSync(lockPath)
365
+ } catch { /* race — fine */ }
366
+ }
367
+
368
+ return false
369
+ }
370
+
371
+ /**
372
+ * Auto-commit modified canvas files using git commit --only to isolate
373
+ * from any other staged or unstaged user work.
374
+ *
375
+ * Coordinates with autosync (which shares the same working tree + index) via:
376
+ * - Lock file (.git/storyboard-autofix.lock) to signal mid-commit
377
+ * - Same repo-busy guards autosync checks (index.lock, merge, rebase, cherry-pick)
378
+ * - git commit --only uses a temporary index, so it won't interfere with
379
+ * files autosync may have staged independently
380
+ *
381
+ * If the commit is deferred (busy repo), the canvas JSONL update is still
382
+ * persisted on disk — autosync's next cycle will pick it up naturally.
383
+ */
384
+ function autocommit(root, modifiedFiles, renames, config) {
385
+ if (!config.autocommit.enabled || modifiedFiles.length === 0) return
386
+
387
+ const relPaths = modifiedFiles.map((f) => path.relative(root, f))
388
+
389
+ if (isRepoBusy(root)) return
390
+
391
+ const gitDir = resolveGitDir(root)
392
+ const lockPath = path.join(gitDir, LOCK_FILENAME)
393
+
394
+ try {
395
+ // Acquire lock so autosync (or other tools) can defer
396
+ fs.writeFileSync(lockPath, `${process.pid}\n${Date.now()}\n`, 'utf-8')
397
+
398
+ execFileSync('git', ['add', '--', ...relPaths], { cwd: root, stdio: 'pipe' })
399
+
400
+ const summary = [...new Set(
401
+ renames
402
+ .filter((r) => r.oldRoute && r.newRoute)
403
+ .map((r) => `${r.oldRoute} → ${r.newRoute}`),
404
+ )].join(', ')
405
+
406
+ const message = `${config.autocommit.prefix} Update embed URLs: ${summary}`
407
+
408
+ execFileSync('git', ['commit', '--only', '--', ...relPaths, '-m', message], {
409
+ cwd: root,
410
+ stdio: 'pipe',
411
+ })
412
+
413
+ logSuccess(`Auto-committed: ${summary}`)
414
+ } catch (err) {
415
+ logWarn(`Auto-commit skipped: ${(err.message || '').split('\n')[0]}`)
416
+ } finally {
417
+ // Release lock
418
+ try { fs.unlinkSync(lockPath) } catch { /* already gone */ }
419
+ }
420
+ }
421
+
422
+ // ─── Main ────────────────────────────────────────────────────────────
423
+
424
+ /**
425
+ * Process a detected change — rescan, detect renames, update embeds, commit.
426
+ */
427
+ function processChange(root, snapshots, config) {
428
+ const allRenames = []
429
+
430
+ for (const entry of config.watch) {
431
+ const oldSnapshot = snapshots.get(entry.path)
432
+ const newSnapshot = scanDirectory(root, entry, config)
433
+ const renames = detectRenames(oldSnapshot, newSnapshot, entry.type)
434
+ if (renames.length > 0) allRenames.push(...renames)
435
+ snapshots.set(entry.path, newSnapshot)
436
+ }
437
+
438
+ if (allRenames.length === 0) return
439
+
440
+ // Deduplicate by route pair
441
+ const uniqueRenames = []
442
+ const seen = new Set()
443
+ for (const r of allRenames) {
444
+ const key = `${r.oldRoute}→${r.newRoute}`
445
+ if (!seen.has(key)) {
446
+ seen.add(key)
447
+ uniqueRenames.push(r)
448
+ log(`Detected: ${r.oldRoute} → ${r.newRoute}`)
449
+ }
450
+ }
451
+
452
+ // Scan all canvas files and update embed references
453
+ const canvasFiles = findAllCanvasFiles(root)
454
+ const modifiedFiles = []
455
+
456
+ for (const canvasFile of canvasFiles) {
457
+ try {
458
+ const text = fs.readFileSync(canvasFile, 'utf-8')
459
+ const state = materializeFromText(text)
460
+
461
+ if (!state.widgets || state.widgets.length === 0) continue
462
+
463
+ const updatedWidgets = updateWidgetRefs(state.widgets, uniqueRenames)
464
+ if (updatedWidgets) {
465
+ const event = {
466
+ event: 'widgets_replaced',
467
+ timestamp: new Date().toISOString(),
468
+ widgets: updatedWidgets,
469
+ }
470
+ fs.appendFileSync(canvasFile, JSON.stringify(event) + '\n', 'utf-8')
471
+ modifiedFiles.push(canvasFile)
472
+ log(`Updated embeds in ${path.relative(root, canvasFile)}`)
473
+ }
474
+ } catch (err) {
475
+ logWarn(`Failed to update ${path.basename(canvasFile)}: ${err.message}`)
476
+ }
477
+ }
478
+
479
+ autocommit(root, modifiedFiles, uniqueRenames, config)
480
+ }
481
+
482
+ /**
483
+ * Start the rename watcher.
484
+ * @param {string} root — project root directory
485
+ * @returns {{ close: () => void }}
486
+ */
487
+ export function startRenameWatcher(root) {
488
+ const config = loadConfig()
489
+ const watchers = []
490
+ const snapshots = new Map()
491
+
492
+ for (const entry of config.watch) {
493
+ snapshots.set(entry.path, scanDirectory(root, entry, config))
494
+ }
495
+
496
+ let debounceTimer = null
497
+
498
+ function handleChange() {
499
+ if (debounceTimer) clearTimeout(debounceTimer)
500
+ debounceTimer = setTimeout(() => {
501
+ try {
502
+ processChange(root, snapshots, config)
503
+ } catch (err) {
504
+ logWarn(`Error: ${err.message}`)
505
+ }
506
+ }, config.debounceMs)
507
+ }
508
+
509
+ for (const entry of config.watch) {
510
+ const absDir = path.join(root, entry.path)
511
+ if (!fs.existsSync(absDir)) {
512
+ logWarn(`Skipping ${entry.path} (not found)`)
513
+ continue
514
+ }
515
+
516
+ try {
517
+ const watcher = fs.watch(absDir, { recursive: true }, handleChange)
518
+ watcher.on('error', (err) => logWarn(`${entry.path}: ${err.message}`))
519
+ watchers.push(watcher)
520
+ } catch (err) {
521
+ logWarn(`Could not watch ${entry.path}: ${err.message}`)
522
+ }
523
+ }
524
+
525
+ return {
526
+ close() {
527
+ if (debounceTimer) clearTimeout(debounceTimer)
528
+ for (const w of watchers) w.close()
529
+ },
530
+ }
531
+ }
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * storyboard-scaffold — sync scaffold files from @dfosco/storyboard.
4
+ *
5
+ * Reads scaffold/manifest.json and copies files to the consumer repo:
6
+ * - "scaffold" mode: only if target doesn't exist (never overwrites config)
7
+ * - "updateable" mode: always overwrites with latest (skills, scripts)
8
+ *
9
+ * Usage:
10
+ * npx storyboard-scaffold
11
+ */
12
+
13
+ import fs from 'node:fs'
14
+ import path from 'node:path'
15
+
16
+ const __dirname = path.dirname(new URL(import.meta.url).pathname)
17
+ const scaffoldRoot = path.resolve(__dirname, '..', '..', 'scaffold')
18
+ const consumerRoot = process.cwd()
19
+
20
+ const manifestPath = path.join(scaffoldRoot, 'manifest.json')
21
+ if (!fs.existsSync(manifestPath)) {
22
+ console.error('❌ Could not find scaffold/manifest.json in @dfosco/storyboard-core')
23
+ process.exit(1)
24
+ }
25
+
26
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'))
27
+
28
+ function copyFileSync(src, dest) {
29
+ const dir = path.dirname(dest)
30
+ if (!fs.existsSync(dir)) {
31
+ fs.mkdirSync(dir, { recursive: true })
32
+ }
33
+ fs.copyFileSync(src, dest)
34
+ }
35
+
36
+ function copyDirSync(src, dest) {
37
+ if (!fs.existsSync(dest)) {
38
+ fs.mkdirSync(dest, { recursive: true })
39
+ }
40
+ const entries = fs.readdirSync(src, { withFileTypes: true })
41
+ for (const entry of entries) {
42
+ const srcPath = path.join(src, entry.name)
43
+ const destPath = path.join(dest, entry.name)
44
+ if (entry.isDirectory()) {
45
+ copyDirSync(srcPath, destPath)
46
+ } else {
47
+ copyFileSync(srcPath, destPath)
48
+ }
49
+ }
50
+ }
51
+
52
+ let created = 0
53
+ let updated = 0
54
+ let skipped = 0
55
+
56
+ for (const file of manifest.files) {
57
+ const srcPath = path.join(scaffoldRoot, path.relative('scaffold', file.source))
58
+ const destPath = path.join(consumerRoot, file.target)
59
+
60
+ if (file.directory) {
61
+ if (file.mode === 'updateable') {
62
+ copyDirSync(srcPath, destPath)
63
+ updated++
64
+ console.log(` ✔ Updated ${file.target} (sync)`)
65
+ } else {
66
+ if (!fs.existsSync(destPath)) {
67
+ copyDirSync(srcPath, destPath)
68
+ created++
69
+ console.log(` ✔ Created ${file.target} (scaffold)`)
70
+ } else {
71
+ skipped++
72
+ console.log(` ⏭ Skipped ${file.target} (already exists)`)
73
+ }
74
+ }
75
+ continue
76
+ }
77
+
78
+ if (file.mode === 'scaffold') {
79
+ if (fs.existsSync(destPath)) {
80
+ skipped++
81
+ console.log(` ⏭ Skipped ${file.target} (already exists)`)
82
+ } else {
83
+ copyFileSync(srcPath, destPath)
84
+ created++
85
+ console.log(` ✔ Created ${file.target} (scaffold)`)
86
+ }
87
+ } else if (file.mode === 'updateable') {
88
+ copyFileSync(srcPath, destPath)
89
+ updated++
90
+ console.log(` ✔ Updated ${file.target} (sync)`)
91
+ }
92
+
93
+ // Make shell scripts executable
94
+ if (file.target.endsWith('.sh')) {
95
+ fs.chmodSync(destPath, 0o755)
96
+ }
97
+ }
98
+
99
+ console.log('')
100
+ console.log(`✔ Scaffold complete: ${created} created, ${updated} updated, ${skipped} skipped.`)