@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,404 @@
1
+ /**
2
+ * storyboard setup — One-time setup for the Storyboard dev environment.
3
+ *
4
+ * Idempotent: safe to run multiple times, only does what's needed.
5
+ *
6
+ * Usage:
7
+ * npx storyboard setup # interactive
8
+ * npx storyboard setup --skip-branch # non-interactive, skip branch prompt
9
+ * npx storyboard setup --branch=<name> # non-interactive, switch to branch after setup
10
+ */
11
+
12
+ import * as p from '@clack/prompts'
13
+ import { existsSync, writeFileSync, readFileSync, mkdirSync, readdirSync, symlinkSync } from 'fs'
14
+ import path from 'path'
15
+ import { execSync } from 'child_process'
16
+ import { generateCaddyfile, isCaddyInstalled, isCaddyRunning, startCaddy, reloadCaddy } from './proxy.js'
17
+ import { gettingStartedLines, dim, magenta, bold, yellow, green } from './intro.js'
18
+ import { parseFlags } from './flags.js'
19
+
20
+ const flagSchema = {
21
+ 'skip-branch': { type: 'boolean', default: false, description: 'Skip the branch prompt at the end' },
22
+ branch: { type: 'string', description: 'Switch to a branch after setup (non-interactive)' },
23
+ }
24
+
25
+ const { flags } = parseFlags(process.argv.slice(3), flagSchema)
26
+
27
+ /**
28
+ * Run a potentially slow task with a spinner that only appears after 500ms.
29
+ * If the task completes quickly, shows the done message immediately.
30
+ */
31
+ async function withSpin(label, doneMsg, fn) {
32
+ const spin = p.spinner()
33
+ const timer = setTimeout(() => spin.start(label), 500)
34
+ try {
35
+ await fn()
36
+ clearTimeout(timer)
37
+ spin.stop(doneMsg)
38
+ } catch (err) {
39
+ clearTimeout(timer)
40
+ spin.stop(`Failed: ${label}`)
41
+ throw err
42
+ }
43
+ }
44
+
45
+ function mascot() {
46
+ const d = dim('·')
47
+ const f = magenta
48
+ const b = dim
49
+ const msg = ` ${bold('Happy prototyping!')} 🎨`
50
+ return [
51
+ ` ${b('╭─────────────────╮')}`,
52
+ ` ${b('│')} ${d} ${f('◠')} ${f('◡')} ${f('◠')} ${d} ${b('│')}${msg}`,
53
+ ` ${b('│')} ${d} ${d} ${d} ${d} ${d} ${b('│')}`,
54
+ ` ${b('╰─────────────────╯')}`,
55
+ ].join('\n')
56
+ }
57
+
58
+ function run(cmd, opts = {}) {
59
+ execSync(cmd, { stdio: 'pipe', ...opts })
60
+ }
61
+
62
+ function isInstalled(cmd) {
63
+ try {
64
+ execSync(`which ${cmd}`, { stdio: 'ignore' })
65
+ return true
66
+ } catch {
67
+ return false
68
+ }
69
+ }
70
+
71
+ p.intro('storyboard setup')
72
+
73
+ // 1. Check for node_modules (quick sanity check — full install runs at the end)
74
+ if (!existsSync('node_modules')) {
75
+ p.log.info('node_modules not found — will install at end of setup')
76
+ } else {
77
+ p.log.success('Dependencies present')
78
+ }
79
+
80
+ // 2. Homebrew
81
+ let hasBrew = isInstalled('brew')
82
+ if (!hasBrew) {
83
+ const brewSpin = p.spinner()
84
+ brewSpin.start('Installing Homebrew...')
85
+ try {
86
+ run('/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"')
87
+ const brewPaths = ['/opt/homebrew/bin/brew', '/usr/local/bin/brew', '/home/linuxbrew/.linuxbrew/bin/brew']
88
+ for (const bp of brewPaths) {
89
+ if (existsSync(bp)) {
90
+ process.env.PATH = `${bp.replace(/\/brew$/, '')}:${process.env.PATH}`
91
+ break
92
+ }
93
+ }
94
+ hasBrew = true
95
+ brewSpin.stop('Homebrew installed')
96
+ } catch {
97
+ brewSpin.stop('Failed to install Homebrew')
98
+ p.log.warning('Install manually: https://brew.sh')
99
+ }
100
+ }
101
+
102
+ // 3. Caddy
103
+ if (hasBrew) {
104
+ if (isCaddyInstalled()) {
105
+ p.log.success('Caddy proxy installed')
106
+ } else {
107
+ const caddySpin = p.spinner()
108
+ caddySpin.start('Installing Caddy...')
109
+ try {
110
+ run('brew install caddy')
111
+ caddySpin.stop('Caddy proxy installed')
112
+ } catch {
113
+ caddySpin.stop('Failed to install Caddy')
114
+ p.log.warning('Install manually: brew install caddy')
115
+ }
116
+ }
117
+
118
+ // 4. GitHub CLI
119
+ if (isInstalled('gh')) {
120
+ p.log.success('GitHub CLI installed')
121
+ } else {
122
+ const ghSpin = p.spinner()
123
+ ghSpin.start('Installing GitHub CLI...')
124
+ try {
125
+ run('brew install gh')
126
+ ghSpin.stop('GitHub CLI installed')
127
+ } catch {
128
+ ghSpin.stop('Failed to install GitHub CLI')
129
+ p.log.warning('Install manually: brew install gh')
130
+ }
131
+ }
132
+ }
133
+
134
+ // 5. VS Code CLI
135
+ if (isInstalled('code')) {
136
+ p.log.success('VS Code CLI installed')
137
+ } else {
138
+ // Try to install the `code` CLI from VS Code's known locations
139
+ const codePaths = [
140
+ '/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code',
141
+ '/usr/local/bin/code',
142
+ ]
143
+ let installed = false
144
+ for (const codePath of codePaths) {
145
+ if (existsSync(codePath)) {
146
+ p.log.success('VS Code CLI available (symlink exists)')
147
+ installed = true
148
+ break
149
+ }
150
+ }
151
+ if (!installed) {
152
+ // Try the VS Code shell command installer
153
+ const vsCodeApp = '/Applications/Visual Studio Code.app'
154
+ if (existsSync(vsCodeApp)) {
155
+ const shellScript = `${vsCodeApp}/Contents/Resources/app/bin/code`
156
+ if (existsSync(shellScript)) {
157
+ try {
158
+ // Create symlink in /usr/local/bin
159
+ run(`ln -sf "${shellScript}" /usr/local/bin/code`)
160
+ p.log.success('VS Code CLI installed (symlinked to /usr/local/bin/code)')
161
+ installed = true
162
+ } catch {
163
+ // Fall through to manual instructions
164
+ }
165
+ }
166
+ }
167
+ if (!installed) {
168
+ p.log.warning('VS Code CLI not found. Open VS Code and run:')
169
+ p.log.info(' Cmd+Shift+P → "Shell Command: Install \'code\' command in PATH"')
170
+ }
171
+ }
172
+ }
173
+
174
+ // 6. Git hooks
175
+ {
176
+ // Create .githooks/ if it doesn't exist — copy from scaffold template
177
+ if (!existsSync('.githooks')) {
178
+ const scaffoldHooks = path.resolve(import.meta.dirname, '..', '..', 'scaffold', 'githooks')
179
+ if (existsSync(scaffoldHooks)) {
180
+ try {
181
+ run(`cp -r "${scaffoldHooks}" .githooks`)
182
+ run('chmod +x .githooks/*')
183
+ } catch { /* fall through */ }
184
+ }
185
+ }
186
+
187
+ if (existsSync('.githooks')) {
188
+ try {
189
+ run('git config core.hooksPath .githooks')
190
+ if (existsSync('.githooks/pre-push')) {
191
+ run('chmod +x .githooks/pre-push')
192
+ }
193
+ p.log.success('Git hooks activated (.githooks/)')
194
+ } catch {
195
+ p.log.warning('Failed to set git hooks path')
196
+ }
197
+ } else {
198
+ p.log.info(dim('No .githooks/ directory — run storyboard-scaffold first'))
199
+ }
200
+ }
201
+
202
+ // 7. Asset directories
203
+ {
204
+ const dirs = ['assets/canvas/images', 'assets/.storyboard-public/terminal-snapshots', '.storyboard', '.storyboard/terminals', '.storyboard/terminal-buffers', '.storyboard/logs']
205
+ for (const dir of dirs) {
206
+ if (!existsSync(dir)) {
207
+ try { mkdirSync(dir, { recursive: true }) } catch { /* ignore */ }
208
+ }
209
+ }
210
+
211
+ // Scaffold initial .selectedwidgets.json if missing
212
+ const selectedWidgetsPath = '.storyboard/.selectedwidgets.json'
213
+ if (!existsSync(selectedWidgetsPath)) {
214
+ try {
215
+ writeFileSync(selectedWidgetsPath, JSON.stringify({
216
+ canvasId: null,
217
+ canvasFile: null,
218
+ selectedWidgetIds: [],
219
+ widgets: [],
220
+ }, null, 2) + '\n', 'utf-8')
221
+ } catch { /* ignore */ }
222
+ }
223
+
224
+ p.log.success('Canvas asset directories ready')
225
+ }
226
+
227
+ // 7a. Scaffold .gitignore entries for private canvas images
228
+ {
229
+ const gitignorePath = '.gitignore'
230
+ const privatePatterns = [
231
+ 'src/canvas/images/~*',
232
+ 'assets/canvas/images/~*',
233
+ 'assets/canvas/snapshots/~*',
234
+ 'assets/.storyboard-public/terminal-snapshots/~*',
235
+ ]
236
+ if (existsSync(gitignorePath)) {
237
+ try {
238
+ let content = readFileSync(gitignorePath, 'utf-8')
239
+ const missing = privatePatterns.filter(p => !content.includes(p))
240
+ if (missing.length > 0) {
241
+ const block = '\n# Private canvas images (tilde prefix = not committed)\n' + missing.join('\n') + '\n'
242
+ content = content.trimEnd() + '\n' + block
243
+ writeFileSync(gitignorePath, content, 'utf-8')
244
+ p.log.success('Added private image patterns to .gitignore')
245
+ }
246
+ } catch { /* ignore */ }
247
+ }
248
+ }
249
+
250
+ // 7b. Copilot agents
251
+ {
252
+ const agentsDir = '.agents'
253
+ if (!existsSync(agentsDir)) {
254
+ try { mkdirSync(agentsDir, { recursive: true }) } catch { /* ignore */ }
255
+ }
256
+
257
+ const scaffoldAgents = path.resolve(import.meta.dirname, '..', '..', 'scaffold', 'agents')
258
+ if (existsSync(scaffoldAgents)) {
259
+ try {
260
+ const agentFiles = execSync(`ls "${scaffoldAgents}"`, { encoding: 'utf8' }).trim().split('\n').filter(Boolean)
261
+ for (const file of agentFiles) {
262
+ const dest = path.join(agentsDir, file)
263
+ if (!existsSync(dest)) {
264
+ run(`cp "${path.join(scaffoldAgents, file)}" "${dest}"`)
265
+ }
266
+ }
267
+ if (agentFiles.length > 0) {
268
+ p.log.success('Copilot agents scaffolded (.agents/)')
269
+ }
270
+ } catch { /* ignore */ }
271
+ }
272
+
273
+ // Create symlinks in each CLI's expected agent directory.
274
+ // Source of truth: .agents/*.agent.md
275
+ // Copilot CLI reads .github/agents/*.md
276
+ // Claude Code reads .claude/agents/*.md
277
+ // Codex CLI uses .codex/config.toml (no agent files needed)
278
+ try {
279
+ const agentFiles = readdirSync(agentsDir).filter(f => f.endsWith('.agent.md'))
280
+ const targets = [
281
+ { dir: path.join('.github', 'agents'), label: 'Copilot CLI' },
282
+ { dir: path.join('.claude', 'agents'), label: 'Claude Code' },
283
+ ]
284
+ for (const { dir, label } of targets) {
285
+ try { mkdirSync(dir, { recursive: true }) } catch { /* ignore */ }
286
+ let linked = 0
287
+ for (const file of agentFiles) {
288
+ const linkName = file.replace('.agent.md', '.md')
289
+ const linkPath = path.join(dir, linkName)
290
+ const target = path.relative(dir, path.join(agentsDir, file))
291
+ if (!existsSync(linkPath)) {
292
+ try { symlinkSync(target, linkPath); linked++ } catch { /* ignore */ }
293
+ }
294
+ }
295
+ if (linked > 0) {
296
+ p.log.success(`${label} agent symlinks created (${dir}/)`)
297
+ }
298
+ }
299
+ } catch { /* ignore */ }
300
+ }
301
+
302
+ // 8. Proxy
303
+ if (isCaddyInstalled()) {
304
+ const proxySpin = p.spinner()
305
+ const caddyfilePath = generateCaddyfile()
306
+ if (isCaddyRunning()) {
307
+ proxySpin.start('Reloading proxy...')
308
+ reloadCaddy(caddyfilePath)
309
+ proxySpin.stop('Proxy reloaded')
310
+ } else {
311
+ proxySpin.start('Starting proxy...')
312
+ startCaddy(caddyfilePath)
313
+ proxySpin.stop('Proxy started')
314
+ }
315
+ }
316
+
317
+ // 9. Install / sync dependencies
318
+ // Skip npm install if a dev server is running — it would trigger Vite's
319
+ // file watcher and restart the server on a different port.
320
+ {
321
+ const { SERVER_PORT } = await import('../server/index.js')
322
+ const { readDevDomain: readDomain } = await import('./proxy.js')
323
+ const http = await import('node:http')
324
+
325
+ const devRunning = await new Promise((resolve) => {
326
+ const req = http.get(`http://localhost:${SERVER_PORT}/health`, (res) => {
327
+ if (res.statusCode !== 200) { resolve(false); return }
328
+ let data = ''
329
+ res.on('data', (chunk) => { data += chunk })
330
+ res.on('end', () => {
331
+ try {
332
+ const json = JSON.parse(data)
333
+ const ourDomain = readDomain()
334
+ resolve(json.ok === true && json.devDomain === ourDomain)
335
+ } catch { resolve(false) }
336
+ })
337
+ })
338
+ req.on('error', () => resolve(false))
339
+ req.setTimeout(1000, () => { req.destroy(); resolve(false) })
340
+ })
341
+
342
+ if (devRunning) {
343
+ p.log.info(dim('Skipping npm install — dev server is running (would cause restart)'))
344
+ p.log.info(dim('Run `npm install` manually after stopping the dev server if needed'))
345
+ } else {
346
+ try {
347
+ await withSpin(
348
+ 'Installing dependencies...',
349
+ 'Dependencies installed',
350
+ () => { run('npm install', { stdio: 'ignore' }) }
351
+ )
352
+ } catch {
353
+ p.log.warning('npm install failed — run it manually to see details')
354
+ p.log.info(` ${dim('npm install')}`)
355
+ }
356
+ }
357
+ }
358
+
359
+ p.note(
360
+ [
361
+ ...gettingStartedLines(),
362
+ '',
363
+ ` ${dim('Run')} ${yellow('npx storyboard')} ${dim('to see all commands')}`,
364
+ ].join('\n'),
365
+ 'Getting started'
366
+ )
367
+
368
+ // Offer branch guide
369
+ {
370
+ // Non-interactive: --branch=<name> or --skip-branch
371
+ if (flags.branch) {
372
+ const { runBranchGuide } = await import('./branch.js')
373
+ await runBranchGuide(flags.branch)
374
+ } else if (flags['skip-branch']) {
375
+ console.log()
376
+ console.log(mascot())
377
+ p.outro('')
378
+ } else {
379
+ // Interactive: ask the user
380
+ let currentBranchName = 'main'
381
+ try {
382
+ currentBranchName = execSync('git branch --show-current', { encoding: 'utf8' }).trim() || 'main'
383
+ } catch { /* empty */ }
384
+
385
+ const wantBranch = await p.select({
386
+ message: 'Want to work from a different branch?',
387
+ options: [
388
+ { value: true, label: 'Yes (help me create it)' },
389
+ { value: false, label: `No (stay on current branch ${bold(currentBranchName)})` },
390
+ ],
391
+ initialValue: false,
392
+ })
393
+
394
+ if (!p.isCancel(wantBranch) && wantBranch) {
395
+ const { runBranchGuide } = await import('./branch.js')
396
+ await runBranchGuide()
397
+ } else {
398
+ console.log()
399
+ console.log(mascot())
400
+ p.log.info(`${dim('Non-interactive:')} ${green('npx sb setup --skip-branch')}`)
401
+ p.outro('')
402
+ }
403
+ }
404
+ }
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * storyboard terminal {close|open|remove} --id <name-or-id>
4
+ *
5
+ * Subcommands for managing terminal sessions by friendly name, tmux name, or widget ID.
6
+ */
7
+
8
+ import * as p from '@clack/prompts'
9
+ import { execSync } from 'node:child_process'
10
+ import { detectWorktreeName, resolveRunningPort } from '../worktree/port.js'
11
+ import { readDevDomain } from './proxy.js'
12
+ import { parseFlags } from './flags.js'
13
+ import { dim, cyan, bold, yellow } from './intro.js'
14
+
15
+ const flagSchema = {
16
+ id: { type: 'string', required: true, description: 'Session name, tmux name, or widget ID' },
17
+ }
18
+
19
+ const subcommand = process.argv[3]
20
+ const { flags, missing, errors } = parseFlags(process.argv.slice(4), flagSchema)
21
+
22
+ /** Resolve the dev server base URL (proxy or direct) */
23
+ function getBaseUrl(worktreeName, port) {
24
+ const domain = readDevDomain()
25
+ const isMain = worktreeName === 'main'
26
+ const proxyBase = isMain ? `http://${domain}/` : `http://${domain}/branch--${worktreeName}/`
27
+ const directBase = isMain ? `http://localhost:${port}/` : `http://localhost:${port}/branch--${worktreeName}/`
28
+ return { proxyBase, directBase }
29
+ }
30
+
31
+ /** Fetch all sessions from the dev server */
32
+ async function fetchSessions(worktreeName, port) {
33
+ const { proxyBase, directBase } = getBaseUrl(worktreeName, port)
34
+ for (const base of [proxyBase, directBase]) {
35
+ try {
36
+ const res = await fetch(`${base}_storyboard/terminal/sessions`, { signal: AbortSignal.timeout(3000) })
37
+ if (!res.ok) continue
38
+ const data = await res.json()
39
+ return { sessions: data.sessions || [], base }
40
+ } catch { continue }
41
+ }
42
+ return null
43
+ }
44
+
45
+ /** POST/DELETE helper that tries proxy then direct */
46
+ async function apiRequest(worktreeName, port, path, method = 'POST', body = null) {
47
+ const { proxyBase, directBase } = getBaseUrl(worktreeName, port)
48
+ for (const base of [proxyBase, directBase]) {
49
+ try {
50
+ const opts = { method, signal: AbortSignal.timeout(5000) }
51
+ if (body) {
52
+ opts.headers = { 'Content-Type': 'application/json' }
53
+ opts.body = JSON.stringify(body)
54
+ }
55
+ const res = await fetch(`${base}${path}`, opts)
56
+ if (res.ok) return { ok: true, data: await res.json().catch(() => ({})) }
57
+ } catch { continue }
58
+ }
59
+ return { ok: false }
60
+ }
61
+
62
+ /**
63
+ * Resolve --id to a session entry. Tries:
64
+ * 1. Friendly name match (e.g. "red-robin")
65
+ * 2. Exact tmux name match (e.g. "sb-abc123def456")
66
+ * 3. Widget ID match (e.g. "terminal-abc123")
67
+ */
68
+ function resolveSession(sessions, id) {
69
+ // Friendly name
70
+ const byName = sessions.find(s => s.name === id)
71
+ if (byName) return byName
72
+ // Tmux name
73
+ const byTmux = sessions.find(s => s.tmuxName === id)
74
+ if (byTmux) return byTmux
75
+ // Widget ID
76
+ const byWidget = sessions.find(s => s.widgetId === id)
77
+ if (byWidget) return byWidget
78
+ return null
79
+ }
80
+
81
+ /** Detect the current tmux session name (only if it's a storyboard session) */
82
+ function getCurrentTmuxSession() {
83
+ try {
84
+ const name = execSync('tmux display-message -p "#{session_name}" 2>/dev/null', {
85
+ encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'],
86
+ }).trim()
87
+ // Only return if this is a storyboard terminal session
88
+ return name.startsWith('sb-') ? name : null
89
+ } catch {
90
+ return null
91
+ }
92
+ }
93
+
94
+ /** Check if we're inside ANY tmux (storyboard or user's own) */
95
+ function _isInsideTmux() {
96
+ return Boolean(process.env.TMUX)
97
+ }
98
+ void _isInsideTmux
99
+
100
+ // ── Close (archive) ──
101
+
102
+ async function closeSession(session, worktreeName, port) {
103
+ p.intro(bold('Close session'))
104
+
105
+ const label = session.name || session.tmuxName
106
+
107
+ if (session.status === 'live') {
108
+ p.log.info(`Session ${cyan(label)} is live — detaching first...`)
109
+ const detachResult = await apiRequest(
110
+ worktreeName, port,
111
+ `_storyboard/terminal/sessions/${encodeURIComponent(session.tmuxName)}/detach`,
112
+ )
113
+ if (!detachResult.ok) {
114
+ p.log.warn('Could not detach via API, continuing with archive...')
115
+ }
116
+ }
117
+
118
+ // Orphan the session (archive with grace timer)
119
+ const orphanResult = await apiRequest(
120
+ worktreeName, port,
121
+ `_storyboard/terminal/sessions/${encodeURIComponent(session.tmuxName)}/orphan`,
122
+ )
123
+
124
+ if (orphanResult.ok) {
125
+ p.log.success(`Session ${cyan(label)} archived. Tmux session kept alive with grace timer.`)
126
+ } else {
127
+ p.log.error(`Failed to archive session ${cyan(label)}`)
128
+ process.exit(1)
129
+ }
130
+
131
+ p.outro('')
132
+ }
133
+
134
+ // ── Open (attach) ──
135
+
136
+ async function openSession(session, _worktreeName, _port) {
137
+ void _worktreeName
138
+ void _port
139
+ p.intro(bold('Open session'))
140
+
141
+ const label = session.name || session.tmuxName
142
+ const currentSb = getCurrentTmuxSession()
143
+
144
+ if (session.status === 'live' && session.tmuxName !== currentSb) {
145
+ p.log.warn(
146
+ `Session ${cyan(label)} is currently live on widget ${dim(session.widgetId)} ` +
147
+ `in canvas ${cyan(session.canvasId)}.`
148
+ )
149
+ const confirm = await p.confirm({
150
+ message: 'Attach anyway? This may cause conflicts with the live widget.',
151
+ })
152
+ if (p.isCancel(confirm) || !confirm) {
153
+ p.outro(dim('Cancelled'))
154
+ process.exit(0)
155
+ }
156
+ }
157
+
158
+ // If we're inside a storyboard tmux session, use switch-client.
159
+ // Otherwise always use attach-session (even if user has their own tmux).
160
+ if (currentSb) {
161
+ p.outro(`Switching to ${bold(label)}...`)
162
+ try {
163
+ execSync(`tmux switch-client -t "${session.tmuxName}"`, { stdio: 'inherit' })
164
+ } catch {
165
+ p.log.error('Failed to switch tmux client')
166
+ process.exit(1)
167
+ }
168
+ } else {
169
+ p.outro(`Attaching to ${bold(label)}...`)
170
+ try {
171
+ execSync(`tmux attach-session -t "${session.tmuxName}"`, { stdio: 'inherit' })
172
+ } catch {
173
+ p.log.error('Failed to attach to tmux session')
174
+ process.exit(1)
175
+ }
176
+ }
177
+ }
178
+
179
+ // ── Remove (destroy) ──
180
+
181
+ async function removeSession(session, worktreeName, port) {
182
+ p.intro(bold('Remove session'))
183
+
184
+ const label = session.name || session.tmuxName
185
+
186
+ const widgetNote = session.widgetId && session.widgetId !== 'unknown'
187
+ ? `\n This will also ${yellow('remove the terminal widget')} from canvas ${cyan(session.canvasId)}.`
188
+ : ''
189
+
190
+ const confirm = await p.confirm({
191
+ message: `Permanently destroy session ${bold(label)}?${widgetNote}\n The tmux session and all running processes inside it will be lost.`,
192
+ })
193
+
194
+ if (p.isCancel(confirm) || !confirm) {
195
+ p.outro(dim('Cancelled'))
196
+ process.exit(0)
197
+ }
198
+
199
+ // Kill via API
200
+ const killResult = await apiRequest(
201
+ worktreeName, port,
202
+ `_storyboard/terminal/sessions/${encodeURIComponent(session.tmuxName)}`,
203
+ 'DELETE',
204
+ )
205
+
206
+ if (killResult.ok) {
207
+ p.log.success(`Session ${cyan(label)} destroyed`)
208
+ } else {
209
+ p.log.warn('API call failed, killing tmux session directly...')
210
+ try {
211
+ execSync(`tmux kill-session -t "${session.tmuxName}" 2>/dev/null`, { stdio: 'ignore' })
212
+ p.log.success(`Session ${cyan(label)} killed via tmux`)
213
+ } catch {
214
+ p.log.error('Failed to kill session')
215
+ process.exit(1)
216
+ }
217
+ }
218
+
219
+ // Remove widget from canvas
220
+ if (session.widgetId && session.widgetId !== 'unknown' && session.canvasId && session.canvasId !== 'unknown') {
221
+ const removeResult = await apiRequest(
222
+ worktreeName, port,
223
+ '_storyboard/canvas/widget',
224
+ 'DELETE',
225
+ { name: session.canvasId, widgetId: session.widgetId },
226
+ )
227
+ if (removeResult.ok) {
228
+ p.log.success(`Widget ${dim(session.widgetId)} removed from canvas ${cyan(session.canvasId)}`)
229
+ }
230
+ }
231
+
232
+ p.outro('')
233
+ }
234
+
235
+ // ── Main ──
236
+
237
+ async function main() {
238
+ if (missing.length > 0 || errors.length > 0) {
239
+ p.intro(bold('Terminal'))
240
+ if (errors.length) errors.forEach(e => p.log.error(e))
241
+ if (missing.length) p.log.error(`Missing required flag: ${bold('--id')}`)
242
+ p.log.info(`Usage: ${cyan(`storyboard terminal ${subcommand} --id <name-or-id>`)}`)
243
+ p.outro('')
244
+ process.exit(1)
245
+ }
246
+
247
+ const worktreeName = detectWorktreeName()
248
+ const port = resolveRunningPort(worktreeName)
249
+ const result = await fetchSessions(worktreeName, port)
250
+
251
+ if (!result) {
252
+ p.intro(bold('Terminal'))
253
+ p.log.error('Could not connect to dev server. Is it running?')
254
+ p.outro('')
255
+ process.exit(1)
256
+ }
257
+
258
+ const session = resolveSession(result.sessions, flags.id)
259
+ if (!session) {
260
+ p.intro(bold('Terminal'))
261
+ p.log.error(`No session found matching ${bold(flags.id)}`)
262
+ p.log.info(`Try ${cyan('storyboard terminal')} to browse sessions.`)
263
+ p.outro('')
264
+ process.exit(1)
265
+ }
266
+
267
+ switch (subcommand) {
268
+ case 'close':
269
+ case 'archive':
270
+ await closeSession(session, worktreeName, port)
271
+ break
272
+ case 'open':
273
+ await openSession(session, worktreeName, port)
274
+ break
275
+ case 'remove':
276
+ await removeSession(session, worktreeName, port)
277
+ break
278
+ default:
279
+ p.log.error(`Unknown subcommand: ${subcommand}`)
280
+ process.exit(1)
281
+ }
282
+ }
283
+
284
+ main().catch((err) => {
285
+ p.log.error(err.message)
286
+ process.exit(1)
287
+ })