@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,515 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * storyboard terminal-welcome — interactive welcome prompt for new terminal sessions.
4
+ *
5
+ * Runs inside tmux, presents a Clack select prompt, loops back after
6
+ * the chosen program exits.
7
+ *
8
+ * When called with --startup <cmd>, auto-launches that command on the first
9
+ * iteration, then falls back to the interactive menu on subsequent iterations
10
+ * (i.e. when the command exits). This makes the welcome screen the universal
11
+ * supervisor for all terminal widget sessions.
12
+ *
13
+ * Usage (called automatically by terminal-server for new sessions):
14
+ * storyboard terminal-welcome [--branch <name>] [--canvas <name>]
15
+ * storyboard terminal-welcome --startup "copilot --agent terminal-agent" [--branch <name>] [--canvas <name>]
16
+ */
17
+
18
+ import * as p from '@clack/prompts'
19
+ import { execSync, spawn } from 'node:child_process'
20
+ import { readFileSync, existsSync } from 'node:fs'
21
+ import { resolve, join } from 'node:path'
22
+ import { parseFlags } from './flags.js'
23
+ import { dim, bold } from './intro.js'
24
+ import { takePendingMessages } from '../canvas/terminal-config.js'
25
+
26
+ const blue = (s) => `\x1b[34m${s}\x1b[0m`
27
+ const yellow = (s) => `\x1b[33m${s}\x1b[0m`
28
+
29
+ /**
30
+ * Drain any pending bytes from stdin to prevent stale mouse escape sequences
31
+ * (or other buffered input) from being consumed by Clack prompts.
32
+ * This is critical after tmux mouse mode was on — mouse events from the
33
+ * browser widget are sent as escape sequences to stdin.
34
+ */
35
+ function drainStdin() {
36
+ if (!process.stdin.readable) return
37
+ const wasPaused = process.stdin.isPaused?.()
38
+ try {
39
+ process.stdin.setRawMode?.(true)
40
+ process.stdin.resume()
41
+ // Read and discard all buffered data
42
+ while (process.stdin.read() !== null) { /* discard */ }
43
+ } catch { /* best effort */ }
44
+ if (wasPaused) {
45
+ try { process.stdin.pause() } catch { /* empty */ }
46
+ }
47
+ }
48
+
49
+ // Prepend .storyboard/terminals/bin/ to PATH so `start`, `copilot`, etc.
50
+ // are available in child shells. Done once at startup; child shells inherit it.
51
+ const binDir = join(process.cwd(), '.storyboard', 'terminals', 'bin')
52
+ if (existsSync(binDir) && !process.env.PATH?.includes(binDir)) {
53
+ process.env.PATH = `${binDir}:${process.env.PATH || ''}`
54
+ }
55
+
56
+ /**
57
+ * Return a copy of process.env with the .storyboard/terminals/bin/ dir
58
+ * stripped from PATH. Used when spawning agent commands so the real binary
59
+ * is found instead of the wrapper scripts that route back through
60
+ * terminal-welcome (which would cause infinite recursion).
61
+ */
62
+ function agentEnv() {
63
+ const cleanPath = (process.env.PATH || '').split(':')
64
+ .filter(p => !p.endsWith('.storyboard/terminals/bin'))
65
+ .join(':')
66
+ return { ...process.env, PATH: cleanPath }
67
+ }
68
+
69
+ /**
70
+ * Read agents config from storyboard.config.json.
71
+ * Returns an array of { id, label, startupCommand, resumeCommand } entries.
72
+ */
73
+ function loadAgents() {
74
+ try {
75
+ const raw = readFileSync(resolve(process.cwd(), 'storyboard.config.json'), 'utf8')
76
+ const config = JSON.parse(raw)
77
+ const agents = config?.canvas?.agents
78
+ if (!agents || typeof agents !== 'object') return []
79
+ return Object.entries(agents).map(([id, cfg]) => ({
80
+ id,
81
+ label: cfg.label || id,
82
+ startupCommand: cfg.startupCommand || null,
83
+ resumeCommand: cfg.resumeCommand || null,
84
+ readinessSignal: cfg.readinessSignal || null,
85
+ postStartup: cfg.postStartup || null,
86
+ })).filter(a => a.startupCommand)
87
+ } catch { return [] }
88
+ }
89
+
90
+ const agents = loadAgents()
91
+
92
+ // Enable/disable tmux mouse — must be off during Clack prompts (mouse
93
+ // events crash Clack), on during shell/copilot sessions (for scrolling).
94
+ function setMouse(on) {
95
+ try { execSync(`tmux set-option mouse ${on ? 'on' : 'off'} 2>/dev/null`, { stdio: 'ignore' }) } catch { /* empty */ }
96
+ }
97
+
98
+ const flagSchema = {
99
+ branch: { type: 'string', description: 'Current branch name' },
100
+ canvas: { type: 'string', description: 'Current canvas name' },
101
+ name: { type: 'string', description: 'Terminal pretty name' },
102
+ startup: { type: 'string', description: 'Auto-launch this command on first iteration' },
103
+ }
104
+
105
+ const { flags } = parseFlags(process.argv.slice(3), flagSchema)
106
+ const branch = flags.branch || 'unknown'
107
+ const canvas = flags.canvas || 'unknown'
108
+ const prettyName = flags.name || null
109
+ const canvasShort = canvas === 'unknown' ? canvas : canvas.split('/').pop()
110
+ const startupCmd = flags.startup || null
111
+
112
+ /**
113
+ * Reset terminal state after a child process exits.
114
+ * Children (especially TUI apps) may leave the terminal in raw mode,
115
+ * alternate screen, or with the cursor hidden.
116
+ */
117
+ function resetTerminal() {
118
+ // Leave alternate screen, show cursor, reset attributes
119
+ process.stdout.write('\x1b[?1049l\x1b[?25h\x1b[0m')
120
+ try { execSync('stty sane 2>/dev/null', { stdio: 'ignore' }) } catch { /* empty */ }
121
+ }
122
+
123
+ /**
124
+ * Spawn an interactive shell with the storyboard bin dir on PATH.
125
+ * zsh re-initializes PATH from .zshrc/.zprofile, so we inject an
126
+ * `export PATH=...` via tmux send-keys after the shell initializes.
127
+ */
128
+ function spawnShell() {
129
+ const shell = process.env.SHELL || '/bin/zsh'
130
+ const child = spawn(shell, [], { stdio: 'inherit' })
131
+
132
+ // Inject PATH after shell init so `start`, `copilot`, etc. are available
133
+ if (existsSync(binDir)) {
134
+ setTimeout(() => {
135
+ try {
136
+ execSync(`tmux send-keys -l ${JSON.stringify(`export PATH="${binDir}:$PATH"`)}`, { stdio: 'ignore' })
137
+ execSync(`tmux send-keys Enter`, { stdio: 'ignore' })
138
+ setTimeout(() => {
139
+ try {
140
+ execSync(`tmux send-keys -l "clear"`, { stdio: 'ignore' })
141
+ execSync(`tmux send-keys Enter`, { stdio: 'ignore' })
142
+ } catch { /* empty */ }
143
+ }, 200)
144
+ } catch { /* empty */ }
145
+ }, 500)
146
+ }
147
+
148
+ return new Promise((resolve) => {
149
+ child.on('close', resolve)
150
+ child.on('error', resolve)
151
+ })
152
+ }
153
+
154
+ /**
155
+ * Get the current tmux session name (safe — returns null outside tmux).
156
+ */
157
+ function getTmuxName() {
158
+ try {
159
+ return execSync('tmux display-message -p "#{session_name}"', { encoding: 'utf8', timeout: 1000 }).trim() || null
160
+ } catch { return null }
161
+ }
162
+
163
+ /**
164
+ * Inject [System] identity message into the running agent via tmux send-keys.
165
+ * Mirrors the identity injection in terminal-server.js so agents launched from
166
+ * the welcome menu receive the same initial context.
167
+ */
168
+ function injectIdentityMessage(tmuxName) {
169
+ const widgetId = process.env.STORYBOARD_WIDGET_ID
170
+ if (!tmuxName || !widgetId) return
171
+
172
+ const canvasId = process.env.STORYBOARD_CANVAS_ID || canvas
173
+ const serverUrl = process.env.STORYBOARD_SERVER_URL || ''
174
+ const displayName = prettyName || widgetId
175
+ const configFile = `.storyboard/terminals/${widgetId}.json`
176
+
177
+ const msg = `[System] Your terminal identity has been set. widgetId=${widgetId} displayName=${displayName} canvasId=${canvasId} configFile=${configFile} serverUrl=${serverUrl} — this is a configuration step, no response needed.`
178
+ try {
179
+ execSync(`tmux send-keys -t "${tmuxName}" -l ${JSON.stringify(msg)}`, { stdio: 'ignore' })
180
+ execSync(`tmux send-keys -t "${tmuxName}" Enter`, { stdio: 'ignore' })
181
+ } catch { /* empty */ }
182
+ }
183
+
184
+ /**
185
+ * Deliver pending messages to the running agent via tmux send-keys.
186
+ * Uses takePendingMessages for atomic read+clear.
187
+ */
188
+ function deliverPendingMessages(tmuxName) {
189
+ const widgetId = process.env.STORYBOARD_WIDGET_ID
190
+ if (!tmuxName || !widgetId) return
191
+
192
+ const messages = takePendingMessages(widgetId)
193
+ messages.forEach((msg, i) => {
194
+ setTimeout(() => {
195
+ try {
196
+ const excerpt = msg.message.length > 200 ? msg.message.slice(0, 200) + '…' : msg.message
197
+ const formatted = `📩 [${msg.fromName || msg.from || 'unknown'} → you]\n\`\`\`\n${excerpt}\n\`\`\`${msg.from ? `\nFull context: cat .storyboard/terminals/${msg.from}.json | jq '.latestOutput.content'` : ''}`
198
+ execSync(`tmux send-keys -t "${tmuxName}" -l ${JSON.stringify(formatted)}`, { stdio: 'ignore' })
199
+ execSync(`tmux send-keys -t "${tmuxName}" Enter`, { stdio: 'ignore' })
200
+ } catch { /* empty */ }
201
+ }, i * 1500)
202
+ })
203
+ }
204
+
205
+ /**
206
+ * Launch an agent by spawning its startupCommand via the user's shell.
207
+ * After the agent reaches readiness, injects identity context and pending
208
+ * messages — same treatment as the terminal-server cold path.
209
+ *
210
+ * @param {Object} agent - Agent config with label, startupCommand, readinessSignal, postStartup
211
+ * @param {Object} [opts]
212
+ * @param {boolean} [opts.isInitialStartup=false] - Skip context injection on the first
213
+ * --startup iteration (terminal-server.js handles it independently).
214
+ */
215
+ async function launchAgent(agent, { isInitialStartup = false } = {}) {
216
+ // Show metadata after selection
217
+ const meta = [
218
+ prettyName ? `${dim('name:')} ${blue(prettyName)}` : null,
219
+ `${dim('branch:')} ${blue(branch)}`,
220
+ `${dim('canvas:')} ${blue(canvasShort)}`,
221
+ ].filter(Boolean).join(' ')
222
+ p.log.info(meta)
223
+ p.outro(dim(`Starting ${agent.label}...`))
224
+ setMouse(true)
225
+
226
+ let exitCode = null
227
+ const startTime = Date.now()
228
+
229
+ try {
230
+ const shell = process.env.SHELL || '/bin/zsh'
231
+ const child = spawn(shell, ['-lc', agent.startupCommand], {
232
+ stdio: 'inherit',
233
+ env: agentEnv(),
234
+ })
235
+
236
+ // Context injection — inject identity, postStartup, and pending messages
237
+ // after the agent reaches readiness. Skip on initial --startup since
238
+ // terminal-server.js handles that path independently.
239
+ let pollInterval = null
240
+ let readinessTimeout = null
241
+ const tmuxName = !isInitialStartup ? getTmuxName() : null
242
+ const widgetId = process.env.STORYBOARD_WIDGET_ID
243
+
244
+ if (tmuxName && widgetId) {
245
+ const readinessSignal = agent.readinessSignal
246
+ const firstWord = agent.startupCommand.trim().split(/\s+/)[0]
247
+
248
+ if (readinessSignal) {
249
+ let contextSent = false
250
+ pollInterval = setInterval(() => {
251
+ if (contextSent) { clearInterval(pollInterval); pollInterval = null; return }
252
+ try {
253
+ const paneContent = execSync(
254
+ `tmux capture-pane -t "${tmuxName}" -p`,
255
+ { encoding: 'utf8', timeout: 1000 }
256
+ )
257
+ // Check configured readiness signal + copilot prompt fallback
258
+ const isReady = paneContent.includes(readinessSignal) ||
259
+ (firstWord === 'copilot' && paneContent.match(/^[>❯]\s*$/m))
260
+ if (isReady) {
261
+ contextSent = true
262
+ clearInterval(pollInterval)
263
+ pollInterval = null
264
+ setTimeout(() => {
265
+ if (agent.postStartup) {
266
+ try {
267
+ execSync(`tmux send-keys -t "${tmuxName}" -l ${JSON.stringify(agent.postStartup)}`, { stdio: 'ignore' })
268
+ execSync(`tmux send-keys -t "${tmuxName}" Enter`, { stdio: 'ignore' })
269
+ } catch { /* empty */ }
270
+ }
271
+ injectIdentityMessage(tmuxName)
272
+ setTimeout(() => deliverPendingMessages(tmuxName), 2000)
273
+ }, 500)
274
+ }
275
+ } catch { /* empty */ }
276
+ }, 2000)
277
+ // Timeout after 30s — don't wait forever
278
+ readinessTimeout = setTimeout(() => {
279
+ if (!contextSent) {
280
+ contextSent = true
281
+ if (pollInterval) { clearInterval(pollInterval); pollInterval = null }
282
+ }
283
+ }, 30000)
284
+ } else {
285
+ // No readiness signal configured — inject after a delay
286
+ const delayTimer = setTimeout(() => {
287
+ injectIdentityMessage(tmuxName)
288
+ setTimeout(() => deliverPendingMessages(tmuxName), 2000)
289
+ }, 5000)
290
+ // Store reference for cleanup
291
+ pollInterval = { clear: () => clearTimeout(delayTimer) }
292
+ }
293
+ }
294
+
295
+ exitCode = await new Promise((resolve) => {
296
+ child.on('close', (code) => {
297
+ if (pollInterval) {
298
+ if (typeof pollInterval.clear === 'function') pollInterval.clear()
299
+ else clearInterval(pollInterval)
300
+ }
301
+ if (readinessTimeout) clearTimeout(readinessTimeout)
302
+ resolve(code)
303
+ })
304
+ child.on('error', () => {
305
+ if (pollInterval) {
306
+ if (typeof pollInterval.clear === 'function') pollInterval.clear()
307
+ else clearInterval(pollInterval)
308
+ }
309
+ if (readinessTimeout) clearTimeout(readinessTimeout)
310
+ resolve(1)
311
+ })
312
+ })
313
+ } catch {
314
+ p.log.error(`Failed to start ${agent.label}. Is it installed?`)
315
+ await new Promise(r => setTimeout(r, 2000))
316
+ exitCode = 1
317
+ } finally {
318
+ // Always disable mouse and drain stdin before returning to the welcome
319
+ // loop. Without this, tmux mouse escape sequences from the browser widget
320
+ // accumulate in stdin while the agent runs, and Clack's p.select() reads
321
+ // them as keystrokes — auto-selecting menu options in a tight loop.
322
+ setMouse(false)
323
+ await new Promise(r => setTimeout(r, 50))
324
+ drainStdin()
325
+ }
326
+
327
+ const durationMs = Date.now() - startTime
328
+ return { exitCode, durationMs }
329
+ }
330
+
331
+ async function welcomeLoop() {
332
+ let firstIteration = true
333
+ const MAX_STARTUP_RETRIES = 2
334
+
335
+ while (true) {
336
+ // On first iteration with --startup, auto-launch the command
337
+ if (firstIteration && startupCmd) {
338
+ firstIteration = false
339
+
340
+ if (startupCmd === 'shell') {
341
+ // Plain shell — spawn interactive shell, return to welcome on exit
342
+ setMouse(true)
343
+ try { await spawnShell() } catch { /* empty */ }
344
+ resetTerminal()
345
+ continue
346
+ }
347
+
348
+ // Try to match against a configured agent for label resolution
349
+ const matchedAgent = agents.find(a =>
350
+ startupCmd.startsWith(a.startupCommand?.split(' ')[0])
351
+ )
352
+ const agent = matchedAgent || { label: startupCmd.split(/\s+/)[0], startupCommand: startupCmd }
353
+
354
+ let succeeded = false
355
+ for (let attempt = 0; attempt < MAX_STARTUP_RETRIES; attempt++) {
356
+ const result = await launchAgent(agent, { isInitialStartup: true })
357
+ resetTerminal()
358
+
359
+ // Normal exit (user quit the agent) — proceed to welcome menu
360
+ if (result.exitCode === 0 || result.exitCode === null) { succeeded = true; break }
361
+
362
+ // Non-zero exit — agent crashed or failed to start
363
+ const isLastAttempt = attempt === MAX_STARTUP_RETRIES - 1
364
+ if (isLastAttempt) {
365
+ p.log.warn(yellow(`${agent.label} failed to start (exit code ${result.exitCode}).`))
366
+ p.log.info(dim('Falling back to the welcome menu. You can retry from there.'))
367
+ await new Promise(r => setTimeout(r, 2000))
368
+ } else {
369
+ p.log.warn(yellow(`${agent.label} exited unexpectedly (exit code ${result.exitCode}). Retrying...`))
370
+ await new Promise(r => setTimeout(r, 3000))
371
+ }
372
+ }
373
+
374
+ if (succeeded) continue
375
+ // Fall through to the interactive welcome menu
376
+ }
377
+ firstIteration = false
378
+
379
+ resetTerminal()
380
+ setMouse(false)
381
+ drainStdin()
382
+ console.clear()
383
+ p.intro(`${bold('storyboard terminal')}`)
384
+
385
+ // Build the first option based on number of configured agents
386
+ const agentOption = agents.length > 1
387
+ ? { value: 'agents', label: '✦ Start a new agent session' }
388
+ : { value: 'copilot', label: `✦ Start a new ${agents[0]?.label || 'Copilot'} session` }
389
+
390
+ drainStdin()
391
+ const action = await p.select({
392
+ message: 'How would you like to start?',
393
+ options: [
394
+ agentOption,
395
+ { value: 'shell', label: '▸ Start a new terminal session' },
396
+ { value: 'sessions', label: '⊞ Browse existing sessions' },
397
+ ],
398
+ })
399
+
400
+ if (p.isCancel(action)) {
401
+ // Don't exit to shell on cancel — loop back to welcome
402
+ continue
403
+ }
404
+
405
+ if (action === 'agents') {
406
+ // Multi-agent sub-select
407
+ drainStdin()
408
+ const agentChoice = await p.select({
409
+ message: 'Which agent?',
410
+ options: agents.map(a => ({
411
+ value: a.id,
412
+ label: `✦ Start a new ${a.label} session`,
413
+ })),
414
+ })
415
+
416
+ if (p.isCancel(agentChoice)) continue
417
+
418
+ const agent = agents.find(a => a.id === agentChoice)
419
+ if (agent) {
420
+ await launchAgent(agent)
421
+ }
422
+ continue
423
+ }
424
+
425
+ if (action === 'copilot') {
426
+ // Single agent — launch directly
427
+ const agent = agents[0] || { label: 'Copilot', startupCommand: 'copilot --agent terminal-agent' }
428
+ await launchAgent(agent)
429
+ continue
430
+ }
431
+
432
+ // Show metadata for non-agent actions (shell, sessions)
433
+ const meta = [
434
+ prettyName ? `${dim('name:')} ${blue(prettyName)}` : null,
435
+ `${dim('branch:')} ${blue(branch)}`,
436
+ `${dim('canvas:')} ${blue(canvasShort)}`,
437
+ ].filter(Boolean).join(' ')
438
+ p.log.info(meta)
439
+
440
+ if (action === 'shell') {
441
+ p.outro(dim('Opening shell... Enter any command below.'))
442
+ setMouse(true)
443
+ // Spawn an interactive shell; when it exits, loop back to welcome
444
+ try { await spawnShell() } catch { /* empty */ }
445
+ continue
446
+ }
447
+
448
+ if (action === 'sessions') {
449
+ // Sub-menu: pick which agent's sessions to browse, or terminal sessions
450
+ const resumableAgents = agents.filter(a => a.resumeCommand)
451
+
452
+ const sessionOptions = [
453
+ ...resumableAgents.map(a => ({
454
+ value: `agent:${a.id}`,
455
+ label: `✦ ${a.label} sessions`,
456
+ })),
457
+ { value: 'terminal', label: '⊞ Terminal sessions' },
458
+ ]
459
+
460
+ drainStdin()
461
+ const sessionChoice = await p.select({
462
+ message: 'Browse sessions',
463
+ options: sessionOptions,
464
+ })
465
+
466
+ if (p.isCancel(sessionChoice)) continue
467
+
468
+ if (sessionChoice === 'terminal') {
469
+ p.outro(dim('Loading terminal sessions...'))
470
+ try {
471
+ const child = spawn('storyboard', ['terminal'], { stdio: 'inherit' })
472
+ await new Promise((resolve) => {
473
+ child.on('close', resolve)
474
+ child.on('error', resolve)
475
+ })
476
+ } catch {
477
+ p.log.error('Failed to load sessions.')
478
+ await new Promise(r => setTimeout(r, 2000))
479
+ }
480
+ continue
481
+ }
482
+
483
+ // Agent resume — spawn the resume command interactively
484
+ if (sessionChoice.startsWith('agent:')) {
485
+ const agentId = sessionChoice.replace('agent:', '')
486
+ const agent = resumableAgents.find(a => a.id === agentId)
487
+ if (agent) {
488
+ p.outro(dim(`Loading ${agent.label} sessions...`))
489
+ setMouse(true)
490
+ try {
491
+ const shell = process.env.SHELL || '/bin/zsh'
492
+ const child = spawn(shell, ['-lc', agent.resumeCommand], {
493
+ stdio: 'inherit',
494
+ env: agentEnv(),
495
+ })
496
+ await new Promise((resolve) => {
497
+ child.on('close', resolve)
498
+ child.on('error', resolve)
499
+ })
500
+ } catch {
501
+ p.log.error(`Failed to load ${agent.label} sessions.`)
502
+ await new Promise(r => setTimeout(r, 2000))
503
+ }
504
+ }
505
+ continue
506
+ }
507
+
508
+ continue
509
+ }
510
+ }
511
+ }
512
+
513
+ welcomeLoop().catch(() => {
514
+ // On any error, just let the shell take over
515
+ })
@@ -0,0 +1,124 @@
1
+ /**
2
+ * storyboard update[:channel|:version] — Update all @dfosco/storyboard-* packages.
3
+ *
4
+ * Usage:
5
+ * storyboard update # update to latest stable
6
+ * storyboard update:version 4.0.0 # update to specific version
7
+ * storyboard update:4.0.0-beta.1 # update to specific version (shorthand)
8
+ * storyboard update:beta # update to latest beta
9
+ * storyboard update:alpha # update to latest alpha
10
+ */
11
+
12
+ import * as p from '@clack/prompts'
13
+ import { execSync } from 'child_process'
14
+ import { readFileSync } from 'fs'
15
+ import { resolve } from 'path'
16
+
17
+ const command = process.argv[2]
18
+ const channels = { 'update:beta': 'beta', 'update:alpha': 'alpha' }
19
+
20
+ let channel, targetVersion
21
+ if (channels[command]) {
22
+ channel = channels[command]
23
+ } else if (command === 'update:version') {
24
+ targetVersion = process.argv[3]
25
+ } else if (command && command.startsWith('update:')) {
26
+ // update:<version> shorthand, e.g. update:4.0.0-beta.1
27
+ targetVersion = command.slice('update:'.length)
28
+ }
29
+
30
+ const pkgPath = resolve(process.cwd(), 'package.json')
31
+ let pkg
32
+ try {
33
+ pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
34
+ } catch (err) {
35
+ p.log.error(`Failed to read package.json: ${err.message}`)
36
+ process.exit(1)
37
+ }
38
+
39
+ // Collect all @dfosco/storyboard-* packages from deps and devDeps
40
+ const storyboardPkgs = new Set()
41
+ for (const depField of ['dependencies', 'devDependencies']) {
42
+ if (!pkg[depField]) continue
43
+ for (const name of Object.keys(pkg[depField])) {
44
+ if (name.startsWith('@dfosco/storyboard-') || name === '@dfosco/tiny-canvas') {
45
+ storyboardPkgs.add(name)
46
+ }
47
+ }
48
+ }
49
+
50
+ if (storyboardPkgs.size === 0) {
51
+ p.log.warn('No @dfosco/storyboard-* packages found in package.json')
52
+ process.exit(0)
53
+ }
54
+
55
+ const tag = channel || (targetVersion ? undefined : 'latest')
56
+ const suffix = targetVersion ? `@${targetVersion}` : `@${tag}`
57
+ const packages = [...storyboardPkgs].map(name => `${name}${suffix}`).join(' ')
58
+
59
+ // Resolve actual version from the registry when using a tag (channel or latest)
60
+ let resolvedVersion
61
+ if (tag) {
62
+ try {
63
+ const probe = [...storyboardPkgs][0]
64
+ resolvedVersion = execSync(`npm view ${probe}@${tag} version`, { encoding: 'utf8' }).trim()
65
+ } catch { /* fall back to showing the tag */ }
66
+ }
67
+
68
+ const displayVersion = resolvedVersion || targetVersion || tag
69
+ const label = resolvedVersion && channel ? `to ${resolvedVersion} (${channel})` : resolvedVersion ? `to ${resolvedVersion}` : channel ? `to ${channel}` : targetVersion ? `to ${targetVersion}` : ''
70
+ p.intro(`storyboard ${command}`)
71
+ p.log.info(`Updating ${storyboardPkgs.size} package(s)${label ? ` ${label}` : ''}…`)
72
+ for (const name of storyboardPkgs) {
73
+ p.log.message(` ${name}@${displayVersion}`)
74
+ }
75
+
76
+ try {
77
+ execSync(`npm install ${packages}`, { stdio: 'inherit', cwd: process.cwd() })
78
+ p.log.success('All storyboard packages updated')
79
+ } catch {
80
+ p.log.error('Failed to update packages — see npm output above')
81
+ process.exit(1)
82
+ }
83
+
84
+ // Sync scaffold files (skills, scripts) from the updated package
85
+ p.log.info('Syncing scaffold files…')
86
+ try {
87
+ execSync('npx storyboard-scaffold', { stdio: 'inherit', cwd: process.cwd() })
88
+ } catch {
89
+ p.log.warn('Scaffold sync failed — run `npx storyboard-scaffold` manually')
90
+ }
91
+
92
+ // Auto-commit the version update (only if package.json or lock file changed)
93
+ try {
94
+ // Read the installed version from the core package
95
+ const corePkg = JSON.parse(readFileSync(resolve(process.cwd(), 'node_modules', '@dfosco', 'storyboard-core', 'package.json'), 'utf8'))
96
+ const installedVersion = corePkg.version || suffix.slice(1)
97
+ const commitMsg = `[storyboard-update] Update storyboard to ${installedVersion}`
98
+
99
+ // Only stage update-related files (package.json, lock files, scaffold outputs)
100
+ const filesToStage = [
101
+ 'package.json',
102
+ 'package-lock.json',
103
+ 'yarn.lock',
104
+ 'pnpm-lock.yaml',
105
+ '.github/skills',
106
+ 'scripts',
107
+ ]
108
+ for (const f of filesToStage) {
109
+ try { execSync(`git add ${f}`, { cwd: process.cwd(), stdio: 'pipe' }) } catch { /* empty */ }
110
+ }
111
+
112
+ // Only commit if there are staged changes
113
+ try {
114
+ execSync('git diff --cached --quiet', { cwd: process.cwd(), stdio: 'pipe' })
115
+ p.log.message('No changes to commit — already up to date')
116
+ } catch {
117
+ execSync(`git commit -m "${commitMsg}"`, { cwd: process.cwd(), stdio: 'pipe' })
118
+ p.log.success(`Committed: ${commitMsg}`)
119
+ }
120
+ } catch (err) {
121
+ p.log.warn(`Auto-commit failed: ${err.message}`)
122
+ }
123
+
124
+ p.outro('Done')