@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,596 @@
1
+ /**
2
+ * Agent Widget Integration Tests (Groups 2–4)
3
+ *
4
+ * For each configured agent (Copilot, Claude, etc.), runs:
5
+ * - Group 2: Startup & basic interaction
6
+ * - Group 3: Context awareness
7
+ * - Group 4: Connected widget CRUD
8
+ *
9
+ * Agent chains run in parallel — each agent gets its own widget, tmux session,
10
+ * and browser session. This mirrors real-world canvas usage where multiple
11
+ * agents run simultaneously.
12
+ *
13
+ * Prerequisites: dev server running, tmux installed, agent-browser installed.
14
+ * Run with: npm run test:integration
15
+ */
16
+
17
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest'
18
+ import { readFileSync, readdirSync } from 'node:fs'
19
+ import { join } from 'node:path'
20
+ import * as canvasApi from './helpers/canvas-api.js'
21
+ import * as tmux from './helpers/tmux.js'
22
+ import * as browser from './helpers/browser.js'
23
+ import * as transcript from './helpers/transcript.js'
24
+ import * as perf from './helpers/perf.js'
25
+ import {
26
+ preflight,
27
+ createTestCanvas,
28
+ deleteTestCanvas,
29
+ loadConfiguredAgents,
30
+ checkAgentAvailability,
31
+ writeResults,
32
+ } from './helpers/setup.js'
33
+
34
+ const CANVAS_NAME = '__test__-agent'
35
+ const ROOT = process.cwd()
36
+
37
+ let serverUrl
38
+ let agents = []
39
+
40
+ // Track created resources for cleanup
41
+ const createdWidgetIds = []
42
+ const tmuxSessions = []
43
+ const browserSessions = []
44
+
45
+ beforeAll(async () => {
46
+ const { url } = await preflight()
47
+ serverUrl = url
48
+ createTestCanvas(CANVAS_NAME)
49
+
50
+ // Load agents and filter to available ones
51
+ const configured = loadConfiguredAgents()
52
+ agents = configured.filter((a) => {
53
+ const available = checkAgentAvailability(a.id)
54
+ if (!available) {
55
+ console.log(`[skip] Agent "${a.label}" (${a.id}) — binary not found, skipping`)
56
+ }
57
+ return available
58
+ })
59
+
60
+ if (agents.length === 0) {
61
+ console.warn('[warn] No agents available — agent tests will be skipped')
62
+ }
63
+
64
+ // Give the dev server a moment to pick up the new canvas file
65
+ await new Promise((r) => setTimeout(r, 2000))
66
+ })
67
+
68
+ afterAll(() => {
69
+ // Flush all transcripts
70
+ const paths = transcript.flushAll()
71
+
72
+ // Write results
73
+ writeResults(perf.toJSON(), paths)
74
+ perf.report()
75
+
76
+ // Cleanup: kill tmux sessions
77
+ for (const s of tmuxSessions) {
78
+ tmux.killSession(s)
79
+ }
80
+
81
+ // Cleanup: close browser sessions
82
+ for (const s of browserSessions) {
83
+ browser.close(s)
84
+ }
85
+
86
+ // Cleanup: delete test canvas
87
+ deleteTestCanvas(CANVAS_NAME)
88
+ })
89
+
90
+ /**
91
+ * Read a terminal config for a widget from .storyboard/terminals/.
92
+ * Tries both the widgetId symlink and falls back to scanning files.
93
+ */
94
+ function readTerminalConfig(widgetId) {
95
+ const terminalsDir = join(ROOT, '.storyboard', 'terminals')
96
+ try {
97
+ // Try widgetId symlink first
98
+ const symPath = join(terminalsDir, `${widgetId}.json`)
99
+ return JSON.parse(readFileSync(symPath, 'utf8'))
100
+ } catch { /* fallback */ }
101
+
102
+ // Scan for config containing this widgetId
103
+ try {
104
+ const files = readdirSync(terminalsDir).filter((f) => f.endsWith('.json'))
105
+ for (const f of files) {
106
+ try {
107
+ const config = JSON.parse(readFileSync(join(terminalsDir, f), 'utf8'))
108
+ if (config.widgetId === widgetId) return config
109
+ } catch { /* skip */ }
110
+ }
111
+ } catch { /* dir may not exist */ }
112
+
113
+ return null
114
+ }
115
+
116
+ /**
117
+ * Review helper — logs full context on soft assertion failure.
118
+ */
119
+ function reviewLog(testId, expected, actual, extra = {}) {
120
+ const lines = [
121
+ `\n[REVIEW] ${testId}`,
122
+ ` Expected pattern: ${expected}`,
123
+ ` Actual output (last 30 lines):`,
124
+ ...String(actual).split('\n').slice(-30).map((l) => ` ${l}`),
125
+ ]
126
+ if (extra.widgetId) lines.push(` Widget ID: ${extra.widgetId}`)
127
+ if (extra.elapsed) lines.push(` Elapsed: ${extra.elapsed}`)
128
+ console.log(lines.join('\n'))
129
+ }
130
+
131
+ // Generate test suites for each agent — they run in parallel
132
+ for (const agent of loadConfiguredAgents()) {
133
+ const agentDescribe = checkAgentAvailability(agent.id) ? describe : describe.skip
134
+
135
+ agentDescribe(`Agent: ${agent.label} (${agent.id})`, () => {
136
+ let agentWidgetId
137
+ let agentTmuxSession
138
+ const browserSession = `test-${agent.id}`
139
+ let stickyNoteId
140
+ let connectorId
141
+ let markdownId
142
+ let _markdownConnectorId
143
+
144
+ browserSessions.push(browserSession)
145
+
146
+ // ─── Group 2: Startup & Basic Interaction ────────────────────────
147
+
148
+ describe('Group 2: Startup & Interaction', () => {
149
+ it(`T2.1 — Create ${agent.label} agent widget`, async () => {
150
+ const result = await canvasApi.createWidget(
151
+ CANVAS_NAME,
152
+ 'agent',
153
+ { startupCommand: agent.startupCommand },
154
+ { x: 100, y: 100 + agents.indexOf(agent) * 500 },
155
+ )
156
+
157
+ expect(result.success).toBe(true)
158
+ expect(result.widget).toBeDefined()
159
+ expect(result.widget.type).toBe('agent')
160
+ agentWidgetId = result.widget.id
161
+ createdWidgetIds.push(agentWidgetId)
162
+ })
163
+
164
+ it(`T2.2 — ${agent.label} starts and loads startup sequence`, async () => {
165
+ if (!agentWidgetId) return
166
+
167
+ // Open canvas in browser and click the agent widget
168
+ const canvasUrl = `${serverUrl}/storyboard/canvas/${CANVAS_NAME}`
169
+ browser.open(browserSession, canvasUrl)
170
+ browser.waitForLoad(browserSession, 'networkidle')
171
+
172
+ try {
173
+ browser.click(browserSession, `[data-widget-id="${agentWidgetId}"]`)
174
+ } catch { /* try alternative interaction */ }
175
+
176
+ // Wait for tmux session
177
+ const chainTimer = perf.start(`agent.${agent.id}.full_chain`)
178
+ const startupTimer = perf.start(`agent.${agent.id}.startup`)
179
+
180
+ try {
181
+ agentTmuxSession = await tmux.waitForSession(/^sb-/, 30000)
182
+ } catch {
183
+ agentTmuxSession = await tmux.waitForSession(/^sb-/, 60000)
184
+ }
185
+
186
+ tmuxSessions.push(agentTmuxSession)
187
+ transcript.createTranscript(agentTmuxSession, {
188
+ widgetId: agentWidgetId,
189
+ canvasName: CANVAS_NAME,
190
+ agentId: agent.id,
191
+ })
192
+ transcript.logSection(agentTmuxSession, 'T2.2')
193
+
194
+ expect(agentTmuxSession).toBeTruthy()
195
+
196
+ // Wait for agent to be ready — look for readiness indicators
197
+ try {
198
+ await tmux.waitForOutput(
199
+ agentTmuxSession,
200
+ /ready|copilot|claude|>|\$/i,
201
+ 90000,
202
+ 2000,
203
+ )
204
+ } catch (err) {
205
+ // Log but don't fail — startup might be slow
206
+ console.warn(`[warn] Agent ${agent.id} startup may be slow: ${err.message}`)
207
+ }
208
+
209
+ startupTimer.end()
210
+
211
+ // Validate startup output is clean — check for error indicators
212
+ const startupOutput = tmux.capturePane(agentTmuxSession)
213
+ const errorPatterns = /error:|stack trace|ENOENT|EACCES|segfault|panic|unhandled/i
214
+ if (errorPatterns.test(startupOutput)) {
215
+ console.warn(`[REVIEW] T2.2 — ${agent.label} startup contains error-like output:\n${startupOutput}`)
216
+ }
217
+
218
+ // Store chainTimer for later
219
+ agentDescribe.__chainTimer = chainTimer
220
+ }, 120_000)
221
+
222
+ it(`T2.3 — ${agent.label} terminal output flows correctly`, async () => {
223
+ if (!agentTmuxSession) return
224
+ transcript.logSection(agentTmuxSession, 'T2.3')
225
+
226
+ // Verify the pane has content (not empty/blank)
227
+ const output = tmux.capturePane(agentTmuxSession)
228
+ expect(output.trim().length).toBeGreaterThan(0)
229
+
230
+ // Check that there are no broken escape sequences (raw \x1b[ without completion)
231
+ // eslint-disable-next-line no-control-regex
232
+ const brokenEscapes = output.match(/\x1b\[[^a-zA-Z]*$/m)
233
+ expect.soft(brokenEscapes).toBeNull()
234
+
235
+ if (brokenEscapes) {
236
+ reviewLog(`T2.3 — ${agent.label} broken escapes`, 'no broken escape sequences', output, { widgetId: agentWidgetId })
237
+ }
238
+ })
239
+
240
+ it(`T2.5 — ${agent.label} answers a simple question`, async () => {
241
+ if (!agentTmuxSession) return
242
+ transcript.logSection(agentTmuxSession, 'T2.5')
243
+
244
+ const questionTimer = perf.start(`agent.${agent.id}.response`)
245
+
246
+ // Send the question
247
+ tmux.sendText(agentTmuxSession, 'What color is the sky during the day? Answer in one word.')
248
+ tmux.sendKeys(agentTmuxSession, 'Enter')
249
+
250
+ // Wait for a response — generous timeout for agent processing
251
+ let responseOutput = ''
252
+ try {
253
+ responseOutput = await tmux.waitForOutput(
254
+ agentTmuxSession,
255
+ /blue|azure|cerulean|cyan|sky\s*blue/i,
256
+ 90000,
257
+ 3000,
258
+ )
259
+ questionTimer.end({ matched: true })
260
+ } catch (err) {
261
+ questionTimer.end({ matched: false })
262
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
263
+
264
+ // Soft assertion — don't block subsequent tests
265
+ reviewLog(`T2.5 — ${agent.label} answer`, '/blue|azure|cerulean/i', responseOutput, {
266
+ widgetId: agentWidgetId,
267
+ elapsed: `${(questionTimer.end?.duration || 0 / 1000).toFixed(1)}s`,
268
+ })
269
+ }
270
+
271
+ // Soft assert — record failure but continue
272
+ expect.soft(responseOutput).toMatch(/blue|azure|cerulean|cyan|sky\s*blue/i)
273
+
274
+ // Verify in browser
275
+ try {
276
+ const _snap = browser.snapshot(browserSession)
277
+ void _snap
278
+ // Best-effort: terminal content may not be in accessibility tree
279
+ } catch { /* browser check is best-effort */ }
280
+ }, 120_000)
281
+ })
282
+
283
+ // ─── Group 3: Agent Context Awareness ────────────────────────────
284
+
285
+ describe('Group 3: Context Awareness', () => {
286
+ it(`T3.1 — ${agent.label} terminal config exists and is valid`, async () => {
287
+ if (!agentWidgetId) return
288
+
289
+ // Wait a moment for config to be written
290
+ await new Promise((r) => setTimeout(r, 2000))
291
+
292
+ const config = readTerminalConfig(agentWidgetId)
293
+
294
+ expect(config).toBeDefined()
295
+ if (config) {
296
+ expect(config.widgetId).toBe(agentWidgetId)
297
+ expect(config.canvasId).toBeTruthy()
298
+ expect(config.branch).toBeTruthy()
299
+ expect(config.serverUrl).toBeTruthy()
300
+ // displayName may be set from prettyName
301
+ expect(config.displayName || config.widgetProps?.prettyName).toBeTruthy()
302
+ }
303
+ })
304
+
305
+ it(`T3.2 — ${agent.label} can identify itself`, async () => {
306
+ if (!agentTmuxSession || !agentWidgetId) return
307
+ transcript.logSection(agentTmuxSession, 'T3.2')
308
+
309
+ const config = readTerminalConfig(agentWidgetId)
310
+ if (!config) return
311
+
312
+ // Ask the agent to read its config
313
+ tmux.sendText(
314
+ agentTmuxSession,
315
+ 'Read your terminal config and tell me your widget ID, canvas ID, and display name. Reply with just those three values.',
316
+ )
317
+ tmux.sendKeys(agentTmuxSession, 'Enter')
318
+
319
+ // Wait for response containing the widget ID
320
+ let responseOutput = ''
321
+ try {
322
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, new RegExp(agentWidgetId.slice(0, 12)), 90000, 3000)
323
+ } catch (err) {
324
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
325
+ }
326
+
327
+ // Soft assertions — agent response is non-deterministic
328
+ expect.soft(responseOutput).toMatch(new RegExp(agentWidgetId.slice(0, 12), 'i'))
329
+
330
+ if (!new RegExp(agentWidgetId.slice(0, 12), 'i').test(responseOutput)) {
331
+ reviewLog(`T3.2 — ${agent.label} self-identification`, agentWidgetId, responseOutput, { widgetId: agentWidgetId })
332
+ }
333
+ }, 120_000)
334
+
335
+ it(`T3.3 — ${agent.label} environment variables are injected`, async () => {
336
+ if (!agentTmuxSession) return
337
+ transcript.logSection(agentTmuxSession, 'T3.3')
338
+
339
+ // Check STORYBOARD_WIDGET_ID
340
+ tmux.sendText(agentTmuxSession, 'echo WIDGET_ID_CHECK:$STORYBOARD_WIDGET_ID')
341
+ tmux.sendKeys(agentTmuxSession, 'Enter')
342
+
343
+ try {
344
+ const output = await tmux.waitForOutput(agentTmuxSession, /WIDGET_ID_CHECK:/, 15000)
345
+ // The env var should contain the widget ID
346
+ if (agentWidgetId) {
347
+ expect.soft(output).toContain(agentWidgetId)
348
+ }
349
+ } catch {
350
+ // Agent may not support direct shell commands — that's OK
351
+ console.log(`[info] ${agent.label} may not support direct echo — skipping env var check`)
352
+ }
353
+
354
+ // Check STORYBOARD_CANVAS_ID
355
+ tmux.sendText(agentTmuxSession, 'echo CANVAS_ID_CHECK:$STORYBOARD_CANVAS_ID')
356
+ tmux.sendKeys(agentTmuxSession, 'Enter')
357
+
358
+ try {
359
+ const output = await tmux.waitForOutput(agentTmuxSession, /CANVAS_ID_CHECK:/, 15000)
360
+ expect.soft(output).toMatch(/CANVAS_ID_CHECK:.+/)
361
+ } catch { /* best-effort */ }
362
+ }, 60_000)
363
+ })
364
+
365
+ // ─── Group 4: Connected Widget CRUD ──────────────────────────────
366
+
367
+ describe('Group 4: Connected Widget CRUD', () => {
368
+ it(`T4.1 — Create sticky note and connect to ${agent.label}`, async () => {
369
+ if (!agentWidgetId) return
370
+
371
+ // Create a sticky note
372
+ const stickyResult = await canvasApi.createWidget(
373
+ CANVAS_NAME,
374
+ 'sticky-note',
375
+ { text: 'banana', color: 'yellow' },
376
+ { x: 400, y: 100 + agents.indexOf(agent) * 500 },
377
+ )
378
+
379
+ expect(stickyResult.success).toBe(true)
380
+ stickyNoteId = stickyResult.widget.id
381
+ createdWidgetIds.push(stickyNoteId)
382
+
383
+ // Connect sticky note to agent widget
384
+ const connResult = await canvasApi.addConnector(
385
+ CANVAS_NAME,
386
+ stickyNoteId,
387
+ 'right',
388
+ agentWidgetId,
389
+ 'left',
390
+ )
391
+
392
+ expect(connResult.success).toBe(true)
393
+ connectorId = connResult.connector?.id
394
+
395
+ // Verify connector exists in canvas
396
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
397
+ const conn = canvas.connectors?.find((c) => c.id === connectorId)
398
+ expect(conn).toBeDefined()
399
+
400
+ // Wait for terminal config to update with connected widgets
401
+ await new Promise((r) => setTimeout(r, 3000))
402
+
403
+ const config = readTerminalConfig(agentWidgetId)
404
+ if (config) {
405
+ // connectedWidgets should include the sticky note ID
406
+ const connected = config.connectedWidgets || config.connectedWidgetIds || []
407
+ const hasSticky = Array.isArray(connected)
408
+ ? connected.some((w) => (typeof w === 'string' ? w === stickyNoteId : w.id === stickyNoteId))
409
+ : false
410
+ expect.soft(hasSticky).toBe(true)
411
+ }
412
+ })
413
+
414
+ it(`T4.2 — ${agent.label} sees connected widget`, async () => {
415
+ if (!agentTmuxSession || !stickyNoteId) return
416
+ transcript.logSection(agentTmuxSession, 'T4.2')
417
+
418
+ tmux.sendText(
419
+ agentTmuxSession,
420
+ 'What widgets are connected to you? Tell me the widget type and its text content.',
421
+ )
422
+ tmux.sendKeys(agentTmuxSession, 'Enter')
423
+
424
+ let responseOutput = ''
425
+ try {
426
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, /banana|sticky/i, 90000, 3000)
427
+ } catch (err) {
428
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
429
+ }
430
+
431
+ expect.soft(responseOutput).toMatch(/banana/i)
432
+
433
+ if (!/banana/i.test(responseOutput)) {
434
+ reviewLog(`T4.2 — ${agent.label} connected widget`, '/banana/i', responseOutput, { widgetId: agentWidgetId })
435
+ }
436
+ }, 120_000)
437
+
438
+ it(`T4.3 — Edit connected widget text (banana → apple)`, async () => {
439
+ if (!stickyNoteId) return
440
+
441
+ // Update sticky note text
442
+ await canvasApi.updateWidget(CANVAS_NAME, stickyNoteId, {
443
+ props: { text: 'apple', color: 'yellow' },
444
+ })
445
+
446
+ // Verify update in canvas
447
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
448
+ const sticky = canvas.widgets?.find((w) => w.id === stickyNoteId)
449
+ expect(sticky?.props?.text).toBe('apple')
450
+
451
+ // Ask agent about the update
452
+ if (!agentTmuxSession) return
453
+ transcript.logSection(agentTmuxSession, 'T4.3')
454
+
455
+ tmux.sendText(agentTmuxSession, 'What does your connected sticky note say now? Just tell me the text.')
456
+ tmux.sendKeys(agentTmuxSession, 'Enter')
457
+
458
+ let responseOutput = ''
459
+ try {
460
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, /apple/i, 90000, 3000)
461
+ } catch (err) {
462
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
463
+ }
464
+
465
+ expect.soft(responseOutput).toMatch(/apple/i)
466
+
467
+ if (!/apple/i.test(responseOutput)) {
468
+ reviewLog(`T4.3 — ${agent.label} edited widget`, '/apple/i', responseOutput, { widgetId: agentWidgetId })
469
+ }
470
+ }, 120_000)
471
+
472
+ it(`T4.4 — Edit connected widget color (yellow → red)`, async () => {
473
+ if (!stickyNoteId) return
474
+
475
+ await canvasApi.updateWidget(CANVAS_NAME, stickyNoteId, {
476
+ props: { text: 'apple', color: 'red' },
477
+ })
478
+
479
+ // Verify in canvas
480
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
481
+ const sticky = canvas.widgets?.find((w) => w.id === stickyNoteId)
482
+ expect(sticky?.props?.color).toBe('red')
483
+
484
+ // Verify in browser
485
+ try {
486
+ browser.open(browserSession, `${serverUrl}/storyboard/canvas/${CANVAS_NAME}`)
487
+ browser.waitForLoad(browserSession, 'networkidle')
488
+ // Best-effort visual check
489
+ const _snap = browser.snapshot(browserSession)
490
+ void _snap
491
+ } catch { /* best-effort */ }
492
+ })
493
+
494
+ it(`T4.5 — Delete connected sticky note`, async () => {
495
+ if (!stickyNoteId) return
496
+
497
+ await canvasApi.deleteWidget(CANVAS_NAME, stickyNoteId)
498
+
499
+ // Verify widget is gone
500
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
501
+ const sticky = canvas.widgets?.find((w) => w.id === stickyNoteId)
502
+ expect(sticky).toBeUndefined()
503
+
504
+ // Connector should be orphaned (widget it points to is gone)
505
+ // The connector may still exist in the data but one end is invalid
506
+ })
507
+
508
+ it(`T4.6 — Connected markdown block (variant)`, async () => {
509
+ if (!agentWidgetId) return
510
+
511
+ // Create markdown widget
512
+ const mdResult = await canvasApi.createWidget(
513
+ CANVAS_NAME,
514
+ 'markdown',
515
+ { content: '# Hello World' },
516
+ { x: 400, y: 250 + agents.indexOf(agent) * 500 },
517
+ )
518
+
519
+ expect(mdResult.success).toBe(true)
520
+ markdownId = mdResult.widget.id
521
+ createdWidgetIds.push(markdownId)
522
+
523
+ // Connect to agent
524
+ const connResult = await canvasApi.addConnector(
525
+ CANVAS_NAME,
526
+ markdownId,
527
+ 'right',
528
+ agentWidgetId,
529
+ 'left',
530
+ )
531
+ expect(connResult.success).toBe(true)
532
+ _markdownConnectorId = connResult.connector?.id
533
+ void _markdownConnectorId
534
+
535
+ // Wait for config update
536
+ await new Promise((r) => setTimeout(r, 3000))
537
+
538
+ // Ask agent about connected widget
539
+ if (agentTmuxSession) {
540
+ transcript.logSection(agentTmuxSession, 'T4.6')
541
+
542
+ tmux.sendText(agentTmuxSession, 'What markdown content is connected to you? Just tell me the heading text.')
543
+ tmux.sendKeys(agentTmuxSession, 'Enter')
544
+
545
+ let responseOutput = ''
546
+ try {
547
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, /hello\s*world/i, 90000, 3000)
548
+ } catch (err) {
549
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
550
+ }
551
+
552
+ expect.soft(responseOutput).toMatch(/hello\s*world/i)
553
+
554
+ if (!/hello\s*world/i.test(responseOutput)) {
555
+ reviewLog(`T4.6 — ${agent.label} markdown widget`, '/hello world/i', responseOutput, { widgetId: agentWidgetId })
556
+ }
557
+ }
558
+
559
+ // Update markdown content
560
+ await canvasApi.updateWidget(CANVAS_NAME, markdownId, {
561
+ props: { content: '# Goodbye World' },
562
+ })
563
+
564
+ await new Promise((r) => setTimeout(r, 2000))
565
+
566
+ if (agentTmuxSession) {
567
+ tmux.sendText(agentTmuxSession, 'What does your connected markdown say now?')
568
+ tmux.sendKeys(agentTmuxSession, 'Enter')
569
+
570
+ let responseOutput = ''
571
+ try {
572
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, /goodbye\s*world/i, 90000, 3000)
573
+ } catch (err) {
574
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
575
+ }
576
+
577
+ expect.soft(responseOutput).toMatch(/goodbye\s*world/i)
578
+ }
579
+
580
+ // Delete markdown
581
+ await canvasApi.deleteWidget(CANVAS_NAME, markdownId)
582
+
583
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
584
+ const md = canvas.widgets?.find((w) => w.id === markdownId)
585
+ expect(md).toBeUndefined()
586
+ }, 180_000)
587
+ })
588
+
589
+ // End the chain timer in the last afterAll
590
+ afterAll(() => {
591
+ if (agentDescribe.__chainTimer) {
592
+ agentDescribe.__chainTimer.end()
593
+ }
594
+ })
595
+ })
596
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * agent-browser helpers for integration tests.
3
+ *
4
+ * Wraps the agent-browser CLI for headless browser interaction.
5
+ * Each agent chain should use its own session name.
6
+ */
7
+
8
+ import { execSync } from 'node:child_process'
9
+ import * as perf from './perf.js'
10
+
11
+ const DEFAULT_TIMEOUT = 30000
12
+
13
+ function run(args, timeout = DEFAULT_TIMEOUT) {
14
+ try {
15
+ return execSync(`agent-browser ${args}`, { encoding: 'utf8', timeout })
16
+ } catch (err) {
17
+ const msg = err.stderr || err.stdout || err.message
18
+ throw new Error(`[agent-browser] ${args} failed: ${msg}`)
19
+ }
20
+ }
21
+
22
+ /** Open a URL in a browser session. */
23
+ export function open(sessionName, url) {
24
+ const timer = perf.start('browser.open', { session: sessionName, url })
25
+ const out = run(`--session ${sessionName} open "${url}"`)
26
+ timer.end()
27
+ return out
28
+ }
29
+
30
+ /** Take an accessibility snapshot. */
31
+ export function snapshot(sessionName, options = '') {
32
+ const timer = perf.start('browser.snapshot', { session: sessionName })
33
+ const out = run(`--session ${sessionName} snapshot ${options}`)
34
+ timer.end()
35
+ return out
36
+ }
37
+
38
+ /** Take a screenshot. */
39
+ export function screenshot(sessionName, path, options = '') {
40
+ const timer = perf.start('browser.screenshot', { session: sessionName })
41
+ const out = run(`--session ${sessionName} screenshot ${options} ${path}`)
42
+ timer.end()
43
+ return out
44
+ }
45
+
46
+ /** Check if an element is visible. */
47
+ export function isVisible(sessionName, selector) {
48
+ try {
49
+ const out = run(`--session ${sessionName} is visible "${selector}"`)
50
+ return out.toLowerCase().includes('true') || out.toLowerCase().includes('visible')
51
+ } catch {
52
+ return false
53
+ }
54
+ }
55
+
56
+ /** Get text content of an element. */
57
+ export function getText(sessionName, selectorOrRef) {
58
+ return run(`--session ${sessionName} get text "${selectorOrRef}"`)
59
+ }
60
+
61
+ /** Click an element. */
62
+ export function click(sessionName, selectorOrRef) {
63
+ return run(`--session ${sessionName} click "${selectorOrRef}"`)
64
+ }
65
+
66
+ /** Fill an input with text. */
67
+ export function fill(sessionName, selectorOrRef, text) {
68
+ return run(`--session ${sessionName} fill "${selectorOrRef}" "${text}"`)
69
+ }
70
+
71
+ /** Wait for a specific condition. */
72
+ export function waitForLoad(sessionName, condition = 'networkidle') {
73
+ const timer = perf.start('browser.wait', { session: sessionName, condition })
74
+ const out = run(`--session ${sessionName} wait --load ${condition}`, 60000)
75
+ timer.end()
76
+ return out
77
+ }
78
+
79
+ /** Close a browser session. */
80
+ export function close(sessionName) {
81
+ try {
82
+ run(`--session ${sessionName} close`)
83
+ } catch {
84
+ // Session may already be closed
85
+ }
86
+ }
87
+
88
+ /** Close all browser sessions. */
89
+ export function closeAll() {
90
+ try {
91
+ run('close --all')
92
+ } catch {
93
+ // Nothing to close
94
+ }
95
+ }