@dfosco/storyboard-core 4.2.0-beta.2 → 4.2.0-beta.21

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 (414) hide show
  1. package/commandpalette.config.json +109 -24
  2. package/dist/storyboard-ui.css +1 -1
  3. package/dist/storyboard-ui.js +17379 -28568
  4. package/dist/storyboard-ui.js.map +1 -1
  5. package/dist/tailwind.css +1 -1
  6. package/package.json +5 -2
  7. package/scaffold/agents/prompt-agent.agent.md +181 -0
  8. package/scaffold/agents/terminal-agent.agent.md +351 -0
  9. package/scaffold/codex/config.toml +246 -0
  10. package/scaffold/manifest.json +5 -0
  11. package/scaffold/skills/canvas/SKILL.md +5 -4
  12. package/scaffold/skills/ship/SKILL.md +1 -1
  13. package/scaffold/storyboard.config.json +14 -1
  14. package/scaffold/toolbar.config.json +1 -1
  15. package/src/ActionMenuButton.jsx +100 -0
  16. package/src/AutosyncMenuButton.css +67 -0
  17. package/src/AutosyncMenuButton.jsx +241 -0
  18. package/src/BranchSelect.jsx +29 -0
  19. package/src/BranchSelect.module.css +30 -0
  20. package/src/CanvasAgentsMenu.jsx +87 -0
  21. package/src/CanvasCreateMenu.jsx +609 -0
  22. package/src/CanvasSnap.css +27 -0
  23. package/src/CanvasSnap.jsx +51 -0
  24. package/src/CanvasUndoRedo.css +36 -0
  25. package/src/CanvasUndoRedo.jsx +62 -0
  26. package/src/CanvasZoomControl.css +53 -0
  27. package/src/CanvasZoomControl.jsx +49 -0
  28. package/src/CanvasZoomToFit.css +18 -0
  29. package/src/CanvasZoomToFit.jsx +26 -0
  30. package/src/CommandMenu.css +8 -0
  31. package/src/CommandMenu.jsx +286 -0
  32. package/src/CommandPalette.jsx +35 -0
  33. package/src/CommandPaletteTrigger.jsx +25 -0
  34. package/src/CommentsMenuButton.jsx +38 -0
  35. package/src/CoreUIBar.css +47 -0
  36. package/src/CoreUIBar.jsx +855 -0
  37. package/src/CreateMenuButton.jsx +116 -0
  38. package/src/HideChromeTrigger.jsx +40 -0
  39. package/src/InspectorPanel.css +109 -0
  40. package/src/InspectorPanel.jsx +629 -0
  41. package/src/PwaInstallBanner.css +42 -0
  42. package/src/PwaInstallBanner.jsx +124 -0
  43. package/src/SidePanel.jsx +260 -0
  44. package/src/ThemeMenuButton.jsx +136 -0
  45. package/src/autosync/server.js +202 -5
  46. package/src/autosync/server.test.js +112 -0
  47. package/src/canvas/__tests__/agent-integration.test.js +593 -0
  48. package/src/canvas/__tests__/helpers/browser.js +95 -0
  49. package/src/canvas/__tests__/helpers/canvas-api.js +129 -0
  50. package/src/canvas/__tests__/helpers/perf.js +118 -0
  51. package/src/canvas/__tests__/helpers/setup.js +176 -0
  52. package/src/canvas/__tests__/helpers/tmux.js +130 -0
  53. package/src/canvas/__tests__/helpers/transcript.js +129 -0
  54. package/src/canvas/__tests__/terminal-integration.test.js +175 -0
  55. package/src/canvas/hot-pool.js +757 -0
  56. package/src/canvas/materializer.js +31 -0
  57. package/src/canvas/materializer.test.js +56 -0
  58. package/src/canvas/selectedWidgets.js +65 -7
  59. package/src/canvas/server.js +1801 -22
  60. package/src/canvas/server.test.js +239 -0
  61. package/src/canvas/terminal-config.js +331 -0
  62. package/src/canvas/terminal-registry.js +38 -0
  63. package/src/canvas/terminal-server.js +1037 -29
  64. package/src/canvas/writeGuard.js +51 -3
  65. package/src/canvasConfig.js +67 -1
  66. package/src/canvasConfig.test.js +79 -1
  67. package/src/cli/agent.js +85 -0
  68. package/src/cli/branch.js +232 -0
  69. package/src/cli/canvasAdd.js +59 -12
  70. package/src/cli/canvasBatch.js +98 -0
  71. package/src/cli/canvasBounds.js +1 -1
  72. package/src/cli/canvasRead.js +1 -1
  73. package/src/cli/canvasUpdate.js +179 -0
  74. package/src/cli/create.js +38 -14
  75. package/src/cli/dev.js +157 -83
  76. package/src/cli/exit.js +23 -24
  77. package/src/cli/index.js +55 -2
  78. package/src/cli/proxy.js +96 -37
  79. package/src/cli/schemas.js +22 -4
  80. package/src/cli/server.js +148 -25
  81. package/src/cli/serverUrl.js +8 -3
  82. package/src/cli/sessions.js +131 -5
  83. package/src/cli/setup.js +109 -11
  84. package/src/cli/terminal-commands.js +16 -8
  85. package/src/cli/terminal-messaging.js +231 -0
  86. package/src/cli/terminal-welcome.js +365 -33
  87. package/src/commandActions.js +1 -0
  88. package/src/commandPaletteConfig.js +9 -0
  89. package/src/comments/auth.js +2 -1
  90. package/src/comments/ui/AuthModal.jsx +114 -0
  91. package/src/comments/ui/CommentWindow.jsx +329 -0
  92. package/src/comments/ui/CommentsDrawer.jsx +102 -0
  93. package/src/comments/ui/Composer.jsx +64 -0
  94. package/src/comments/ui/authModal.test.js +1 -1
  95. package/src/comments/ui/commentWindow.js +16 -17
  96. package/src/comments/ui/commentsDrawer.js +25 -26
  97. package/src/comments/ui/composer.js +23 -24
  98. package/src/comments/ui/index.js +2 -3
  99. package/src/configSchema.js +59 -1
  100. package/src/configStore.js +161 -0
  101. package/src/core-ui-colors.css +12 -0
  102. package/src/devtools.js +17 -19
  103. package/src/devtools.test.js +18 -9
  104. package/src/featureFlags.js +12 -5
  105. package/src/fuzzySearch.test.js +10 -0
  106. package/src/index.js +14 -2
  107. package/src/lib/components/ui/alert/alert-action.jsx +11 -0
  108. package/src/lib/components/ui/alert/alert-description.jsx +11 -0
  109. package/src/lib/components/ui/alert/alert-title.jsx +11 -0
  110. package/src/lib/components/ui/alert/alert.jsx +25 -0
  111. package/src/lib/components/ui/alert/index.js +15 -15
  112. package/src/lib/components/ui/avatar/avatar-badge.jsx +22 -0
  113. package/src/lib/components/ui/avatar/avatar-fallback.jsx +18 -0
  114. package/src/lib/components/ui/avatar/avatar-group-count.jsx +19 -0
  115. package/src/lib/components/ui/avatar/avatar-group.jsx +19 -0
  116. package/src/lib/components/ui/avatar/avatar-image.jsx +15 -0
  117. package/src/lib/components/ui/avatar/avatar.jsx +19 -0
  118. package/src/lib/components/ui/avatar/index.js +20 -20
  119. package/src/lib/components/ui/badge/badge.jsx +31 -0
  120. package/src/lib/components/ui/badge/index.js +2 -2
  121. package/src/lib/components/ui/button/button.jsx +100 -0
  122. package/src/lib/components/ui/button/index.js +9 -9
  123. package/src/lib/components/ui/card/card-action.jsx +11 -0
  124. package/src/lib/components/ui/card/card-content.jsx +11 -0
  125. package/src/lib/components/ui/card/card-description.jsx +11 -0
  126. package/src/lib/components/ui/card/card-footer.jsx +11 -0
  127. package/src/lib/components/ui/card/card-header.jsx +19 -0
  128. package/src/lib/components/ui/card/card-title.jsx +11 -0
  129. package/src/lib/components/ui/card/card.jsx +17 -0
  130. package/src/lib/components/ui/card/index.js +23 -23
  131. package/src/lib/components/ui/checkbox/checkbox.jsx +29 -0
  132. package/src/lib/components/ui/checkbox/index.js +5 -5
  133. package/src/lib/components/ui/collapsible/collapsible-content.jsx +7 -0
  134. package/src/lib/components/ui/collapsible/collapsible-trigger.jsx +7 -0
  135. package/src/lib/components/ui/collapsible/collapsible.jsx +7 -0
  136. package/src/lib/components/ui/collapsible/index.js +11 -11
  137. package/src/lib/components/ui/dialog/dialog-close.jsx +7 -0
  138. package/src/lib/components/ui/dialog/dialog-content.jsx +34 -0
  139. package/src/lib/components/ui/dialog/dialog-description.jsx +15 -0
  140. package/src/lib/components/ui/dialog/dialog-footer.jsx +23 -0
  141. package/src/lib/components/ui/dialog/dialog-header.jsx +11 -0
  142. package/src/lib/components/ui/dialog/dialog-overlay.jsx +15 -0
  143. package/src/lib/components/ui/dialog/dialog-portal.jsx +4 -0
  144. package/src/lib/components/ui/dialog/dialog-title.jsx +15 -0
  145. package/src/lib/components/ui/dialog/dialog-trigger.jsx +7 -0
  146. package/src/lib/components/ui/dialog/dialog.jsx +4 -0
  147. package/src/lib/components/ui/dialog/index.js +32 -32
  148. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +8 -0
  149. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +30 -0
  150. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +22 -0
  151. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +16 -0
  152. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +7 -0
  153. package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +20 -0
  154. package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.jsx +17 -0
  155. package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +4 -0
  156. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +7 -0
  157. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +29 -0
  158. package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +15 -0
  159. package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.jsx +16 -0
  160. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +15 -0
  161. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +23 -0
  162. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +4 -0
  163. package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +7 -0
  164. package/src/lib/components/ui/dropdown-menu/dropdown-menu.jsx +4 -0
  165. package/src/lib/components/ui/dropdown-menu/index.js +52 -52
  166. package/src/lib/components/ui/input/index.js +5 -5
  167. package/src/lib/components/ui/input/input.jsx +19 -0
  168. package/src/lib/components/ui/label/index.js +5 -5
  169. package/src/lib/components/ui/label/label.jsx +19 -0
  170. package/src/lib/components/ui/panel/index.js +21 -21
  171. package/src/lib/components/ui/panel/panel-body.jsx +11 -0
  172. package/src/lib/components/ui/panel/panel-close.jsx +16 -0
  173. package/src/lib/components/ui/panel/panel-content.jsx +29 -0
  174. package/src/lib/components/ui/panel/panel-footer.jsx +11 -0
  175. package/src/lib/components/ui/panel/panel-header.jsx +11 -0
  176. package/src/lib/components/ui/panel/panel-title.jsx +12 -0
  177. package/src/lib/components/ui/panel/panel.jsx +4 -0
  178. package/src/lib/components/ui/popover/index.js +26 -26
  179. package/src/lib/components/ui/popover/popover-close.jsx +7 -0
  180. package/src/lib/components/ui/popover/popover-content.jsx +22 -0
  181. package/src/lib/components/ui/popover/popover-description.jsx +11 -0
  182. package/src/lib/components/ui/popover/popover-header.jsx +11 -0
  183. package/src/lib/components/ui/popover/popover-portal.jsx +4 -0
  184. package/src/lib/components/ui/popover/popover-title.jsx +11 -0
  185. package/src/lib/components/ui/popover/popover-trigger.jsx +8 -0
  186. package/src/lib/components/ui/popover/popover.jsx +4 -0
  187. package/src/lib/components/ui/searchable-list.jsx +159 -0
  188. package/src/lib/components/ui/select/index.js +35 -35
  189. package/src/lib/components/ui/select/select-content.jsx +30 -0
  190. package/src/lib/components/ui/select/select-group-heading.jsx +17 -0
  191. package/src/lib/components/ui/select/select-group.jsx +15 -0
  192. package/src/lib/components/ui/select/select-item.jsx +26 -0
  193. package/src/lib/components/ui/select/select-label.jsx +11 -0
  194. package/src/lib/components/ui/select/select-portal.jsx +4 -0
  195. package/src/lib/components/ui/select/select-scroll-down-button.jsx +18 -0
  196. package/src/lib/components/ui/select/select-scroll-up-button.jsx +18 -0
  197. package/src/lib/components/ui/select/select-separator.jsx +15 -0
  198. package/src/lib/components/ui/select/select-trigger.jsx +25 -0
  199. package/src/lib/components/ui/select/select.jsx +4 -0
  200. package/src/lib/components/ui/separator/index.js +5 -5
  201. package/src/lib/components/ui/separator/separator.jsx +22 -0
  202. package/src/lib/components/ui/sheet/index.js +32 -32
  203. package/src/lib/components/ui/sheet/sheet-close.jsx +7 -0
  204. package/src/lib/components/ui/sheet/sheet-content.jsx +35 -0
  205. package/src/lib/components/ui/sheet/sheet-description.jsx +15 -0
  206. package/src/lib/components/ui/sheet/sheet-footer.jsx +11 -0
  207. package/src/lib/components/ui/sheet/sheet-header.jsx +11 -0
  208. package/src/lib/components/ui/sheet/sheet-overlay.jsx +15 -0
  209. package/src/lib/components/ui/sheet/sheet-portal.jsx +4 -0
  210. package/src/lib/components/ui/sheet/sheet-title.jsx +15 -0
  211. package/src/lib/components/ui/sheet/sheet-trigger.jsx +7 -0
  212. package/src/lib/components/ui/sheet/sheet.jsx +4 -0
  213. package/src/lib/components/ui/textarea/index.js +5 -5
  214. package/src/lib/components/ui/textarea/textarea.jsx +18 -0
  215. package/src/lib/components/ui/toggle/index.js +6 -9
  216. package/src/lib/components/ui/toggle/toggle.jsx +36 -0
  217. package/src/lib/components/ui/toggle-group/index.js +8 -8
  218. package/src/lib/components/ui/toggle-group/toggle-group-item.jsx +29 -0
  219. package/src/lib/components/ui/toggle-group/toggle-group.jsx +43 -0
  220. package/src/lib/components/ui/tooltip/index.js +3 -3
  221. package/src/lib/components/ui/tooltip/tooltip-content.jsx +21 -0
  222. package/src/lib/components/ui/tooltip/tooltip-trigger.jsx +23 -0
  223. package/src/lib/components/ui/tooltip/tooltip.jsx +11 -0
  224. package/src/lib/components/ui/trigger-button/index.js +3 -3
  225. package/src/lib/components/ui/trigger-button/trigger-button.css +38 -0
  226. package/src/lib/components/ui/trigger-button/trigger-button.jsx +63 -0
  227. package/src/logger/devLogger.js +238 -0
  228. package/src/logger/devLogger.test.js +193 -0
  229. package/src/modes.test.js +4 -4
  230. package/src/mountStoryboardCore.js +123 -27
  231. package/src/paletteProviders.js +3 -0
  232. package/src/paletteProviders.test.js +2 -2
  233. package/src/server/index.js +98 -36
  234. package/src/sidepanel.css +214 -0
  235. package/src/styles/tailwind.css +1 -1
  236. package/src/svelte-plugin-ui/__tests__/ModeSwitch.test.ts +8 -8
  237. package/src/svelte-plugin-ui/__tests__/ToolbarShell.test.ts +11 -10
  238. package/src/svelte-plugin-ui/components/Icon.css +11 -0
  239. package/src/svelte-plugin-ui/components/Icon.jsx +281 -0
  240. package/src/svelte-plugin-ui/components/ModeSwitch.css +90 -0
  241. package/src/svelte-plugin-ui/components/ModeSwitch.jsx +47 -0
  242. package/src/svelte-plugin-ui/components/ToolbarShell.css +80 -0
  243. package/src/svelte-plugin-ui/components/ToolbarShell.jsx +84 -0
  244. package/src/svelte-plugin-ui/components/Viewfinder.css +412 -0
  245. package/src/svelte-plugin-ui/components/Viewfinder.jsx +512 -0
  246. package/src/svelte-plugin-ui/mount.ts +12 -16
  247. package/src/toolRegistry.js +4 -4
  248. package/src/toolbarConfigStore.js +30 -0
  249. package/src/tools/handlers/autosync.js +1 -1
  250. package/src/tools/handlers/canvasAddWidget.js +1 -1
  251. package/src/tools/handlers/canvasAgents.js +19 -0
  252. package/src/tools/handlers/canvasToolbar.js +8 -8
  253. package/src/tools/handlers/commandPalette.js +9 -0
  254. package/src/tools/handlers/comments.js +1 -1
  255. package/src/tools/handlers/create.js +1 -1
  256. package/src/tools/handlers/devtools.js +16 -0
  257. package/src/tools/handlers/devtools.test.js +38 -0
  258. package/src/tools/handlers/flows.js +1 -1
  259. package/src/tools/handlers/hideChrome.js +9 -0
  260. package/src/tools/handlers/paletteTheme.js +35 -0
  261. package/src/tools/handlers/theme.js +1 -1
  262. package/src/tools/registry.js +4 -1
  263. package/src/tools/surfaces/commandList.js +3 -3
  264. package/src/tools/surfaces/mainToolbar.js +3 -3
  265. package/src/tools/surfaces/registry.js +4 -4
  266. package/src/ui/design-modes.ts +2 -2
  267. package/src/ui/viewfinder.ts +1 -1
  268. package/src/vite/server-plugin.js +242 -60
  269. package/src/workshop/features/createCanvas/CreateCanvasForm.jsx +260 -0
  270. package/src/workshop/features/createCanvas/index.js +1 -1
  271. package/src/workshop/features/createFlow/CreateFlowForm.jsx +334 -0
  272. package/src/workshop/features/createFlow/index.js +1 -1
  273. package/src/workshop/features/createPage/CreatePageForm.jsx +304 -0
  274. package/src/workshop/features/createPage/index.js +1 -1
  275. package/src/workshop/features/createPrototype/CreatePrototypeForm.jsx +289 -0
  276. package/src/workshop/features/createPrototype/index.js +1 -1
  277. package/src/workshop/features/createPrototype/server.js +98 -0
  278. package/src/workshop/features/createStory/CreateStoryForm.jsx +208 -0
  279. package/src/workshop/features/createStory/index.js +1 -1
  280. package/src/workshop/ui/WorkshopPanel.jsx +98 -0
  281. package/src/workshop/ui/mount.ts +1 -1
  282. package/src/worktree/port.js +48 -0
  283. package/src/worktree/serverRegistry.js +120 -0
  284. package/toolbar.config.json +93 -42
  285. package/widgets.config.json +580 -12
  286. package/src/ActionMenuButton.svelte +0 -119
  287. package/src/AutosyncMenuButton.svelte +0 -397
  288. package/src/CanvasCreateMenu.svelte +0 -295
  289. package/src/CanvasSnap.svelte +0 -87
  290. package/src/CanvasUndoRedo.svelte +0 -108
  291. package/src/CanvasZoomControl.svelte +0 -111
  292. package/src/CanvasZoomToFit.svelte +0 -52
  293. package/src/CommandMenu.svelte +0 -249
  294. package/src/CommandPalette.svelte +0 -33
  295. package/src/CommentsMenuButton.svelte +0 -53
  296. package/src/CoreUIBar.svelte +0 -847
  297. package/src/CreateMenuButton.svelte +0 -133
  298. package/src/DocPanel.svelte +0 -299
  299. package/src/InspectorPanel.svelte +0 -745
  300. package/src/PwaInstallBanner.svelte +0 -124
  301. package/src/SidePanel.svelte +0 -480
  302. package/src/ThemeMenuButton.svelte +0 -132
  303. package/src/comments/ui/AuthModal.svelte +0 -108
  304. package/src/comments/ui/CommentWindow.svelte +0 -333
  305. package/src/comments/ui/CommentsDrawer.svelte +0 -96
  306. package/src/comments/ui/Composer.svelte +0 -65
  307. package/src/lib/components/ui/alert/alert-action.svelte +0 -19
  308. package/src/lib/components/ui/alert/alert-description.svelte +0 -22
  309. package/src/lib/components/ui/alert/alert-title.svelte +0 -22
  310. package/src/lib/components/ui/alert/alert.svelte +0 -38
  311. package/src/lib/components/ui/avatar/avatar-badge.svelte +0 -25
  312. package/src/lib/components/ui/avatar/avatar-fallback.svelte +0 -20
  313. package/src/lib/components/ui/avatar/avatar-group-count.svelte +0 -22
  314. package/src/lib/components/ui/avatar/avatar-group.svelte +0 -22
  315. package/src/lib/components/ui/avatar/avatar-image.svelte +0 -17
  316. package/src/lib/components/ui/avatar/avatar.svelte +0 -24
  317. package/src/lib/components/ui/badge/badge.svelte +0 -44
  318. package/src/lib/components/ui/button/button.svelte +0 -108
  319. package/src/lib/components/ui/card/card-action.svelte +0 -21
  320. package/src/lib/components/ui/card/card-content.svelte +0 -19
  321. package/src/lib/components/ui/card/card-description.svelte +0 -19
  322. package/src/lib/components/ui/card/card-footer.svelte +0 -18
  323. package/src/lib/components/ui/card/card-header.svelte +0 -21
  324. package/src/lib/components/ui/card/card-title.svelte +0 -14
  325. package/src/lib/components/ui/card/card.svelte +0 -21
  326. package/src/lib/components/ui/checkbox/checkbox.svelte +0 -39
  327. package/src/lib/components/ui/collapsible/collapsible-content.svelte +0 -7
  328. package/src/lib/components/ui/collapsible/collapsible-trigger.svelte +0 -7
  329. package/src/lib/components/ui/collapsible/collapsible.svelte +0 -11
  330. package/src/lib/components/ui/dialog/dialog-close.svelte +0 -11
  331. package/src/lib/components/ui/dialog/dialog-content.svelte +0 -42
  332. package/src/lib/components/ui/dialog/dialog-description.svelte +0 -17
  333. package/src/lib/components/ui/dialog/dialog-footer.svelte +0 -29
  334. package/src/lib/components/ui/dialog/dialog-header.svelte +0 -19
  335. package/src/lib/components/ui/dialog/dialog-overlay.svelte +0 -17
  336. package/src/lib/components/ui/dialog/dialog-portal.svelte +0 -7
  337. package/src/lib/components/ui/dialog/dialog-title.svelte +0 -17
  338. package/src/lib/components/ui/dialog/dialog-trigger.svelte +0 -11
  339. package/src/lib/components/ui/dialog/dialog.svelte +0 -7
  340. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +0 -16
  341. package/src/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -40
  342. package/src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte +0 -27
  343. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +0 -18
  344. package/src/lib/components/ui/dropdown-menu/dropdown-menu-group.svelte +0 -7
  345. package/src/lib/components/ui/dropdown-menu/dropdown-menu-item.svelte +0 -24
  346. package/src/lib/components/ui/dropdown-menu/dropdown-menu-label.svelte +0 -20
  347. package/src/lib/components/ui/dropdown-menu/dropdown-menu-portal.svelte +0 -7
  348. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +0 -16
  349. package/src/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +0 -34
  350. package/src/lib/components/ui/dropdown-menu/dropdown-menu-separator.svelte +0 -17
  351. package/src/lib/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +0 -19
  352. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +0 -17
  353. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +0 -27
  354. package/src/lib/components/ui/dropdown-menu/dropdown-menu-sub.svelte +0 -7
  355. package/src/lib/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +0 -7
  356. package/src/lib/components/ui/dropdown-menu/dropdown-menu.svelte +0 -7
  357. package/src/lib/components/ui/input/input.svelte +0 -40
  358. package/src/lib/components/ui/label/label.svelte +0 -20
  359. package/src/lib/components/ui/panel/panel-body.svelte +0 -13
  360. package/src/lib/components/ui/panel/panel-close.svelte +0 -16
  361. package/src/lib/components/ui/panel/panel-content.svelte +0 -33
  362. package/src/lib/components/ui/panel/panel-footer.svelte +0 -13
  363. package/src/lib/components/ui/panel/panel-header.svelte +0 -16
  364. package/src/lib/components/ui/panel/panel-title.svelte +0 -14
  365. package/src/lib/components/ui/panel/panel.svelte +0 -15
  366. package/src/lib/components/ui/popover/popover-close.svelte +0 -7
  367. package/src/lib/components/ui/popover/popover-content.svelte +0 -27
  368. package/src/lib/components/ui/popover/popover-description.svelte +0 -19
  369. package/src/lib/components/ui/popover/popover-header.svelte +0 -19
  370. package/src/lib/components/ui/popover/popover-portal.svelte +0 -7
  371. package/src/lib/components/ui/popover/popover-title.svelte +0 -19
  372. package/src/lib/components/ui/popover/popover-trigger.svelte +0 -17
  373. package/src/lib/components/ui/popover/popover.svelte +0 -7
  374. package/src/lib/components/ui/select/select-content.svelte +0 -40
  375. package/src/lib/components/ui/select/select-group-heading.svelte +0 -19
  376. package/src/lib/components/ui/select/select-group.svelte +0 -17
  377. package/src/lib/components/ui/select/select-item.svelte +0 -38
  378. package/src/lib/components/ui/select/select-label.svelte +0 -18
  379. package/src/lib/components/ui/select/select-portal.svelte +0 -7
  380. package/src/lib/components/ui/select/select-scroll-down-button.svelte +0 -20
  381. package/src/lib/components/ui/select/select-scroll-up-button.svelte +0 -20
  382. package/src/lib/components/ui/select/select-separator.svelte +0 -17
  383. package/src/lib/components/ui/select/select-trigger.svelte +0 -27
  384. package/src/lib/components/ui/select/select.svelte +0 -11
  385. package/src/lib/components/ui/separator/separator.svelte +0 -23
  386. package/src/lib/components/ui/sheet/sheet-close.svelte +0 -7
  387. package/src/lib/components/ui/sheet/sheet-content.svelte +0 -43
  388. package/src/lib/components/ui/sheet/sheet-description.svelte +0 -17
  389. package/src/lib/components/ui/sheet/sheet-footer.svelte +0 -18
  390. package/src/lib/components/ui/sheet/sheet-header.svelte +0 -19
  391. package/src/lib/components/ui/sheet/sheet-overlay.svelte +0 -17
  392. package/src/lib/components/ui/sheet/sheet-portal.svelte +0 -7
  393. package/src/lib/components/ui/sheet/sheet-title.svelte +0 -17
  394. package/src/lib/components/ui/sheet/sheet-trigger.svelte +0 -7
  395. package/src/lib/components/ui/sheet/sheet.svelte +0 -7
  396. package/src/lib/components/ui/textarea/textarea.svelte +0 -21
  397. package/src/lib/components/ui/toggle/toggle.svelte +0 -45
  398. package/src/lib/components/ui/toggle-group/toggle-group-item.svelte +0 -35
  399. package/src/lib/components/ui/toggle-group/toggle-group.svelte +0 -63
  400. package/src/lib/components/ui/tooltip/tooltip-content.svelte +0 -24
  401. package/src/lib/components/ui/tooltip/tooltip-trigger.svelte +0 -27
  402. package/src/lib/components/ui/tooltip/tooltip.svelte +0 -9
  403. package/src/lib/components/ui/trigger-button/trigger-button.svelte +0 -106
  404. package/src/svelte-plugin-ui/components/Icon.svelte +0 -181
  405. package/src/svelte-plugin-ui/components/ModeSwitch.svelte +0 -121
  406. package/src/svelte-plugin-ui/components/ToolbarShell.svelte +0 -150
  407. package/src/svelte-plugin-ui/components/Viewfinder.svelte +0 -1001
  408. package/src/tools/handlers/docs.js +0 -11
  409. package/src/workshop/features/createCanvas/CreateCanvasForm.svelte +0 -139
  410. package/src/workshop/features/createFlow/CreateFlowForm.svelte +0 -314
  411. package/src/workshop/features/createPage/CreatePageForm.svelte +0 -249
  412. package/src/workshop/features/createPrototype/CreatePrototypeForm.svelte +0 -287
  413. package/src/workshop/features/createStory/CreateStoryForm.svelte +0 -161
  414. package/src/workshop/ui/WorkshopPanel.svelte +0 -97
@@ -0,0 +1,593 @@
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
+ resolveServerUrl,
30
+ loadConfiguredAgents,
31
+ checkAgentAvailability,
32
+ writeResults,
33
+ } from './helpers/setup.js'
34
+
35
+ const CANVAS_NAME = '__test__-agent'
36
+ const ROOT = process.cwd()
37
+
38
+ let serverUrl
39
+ let agents = []
40
+
41
+ // Track created resources for cleanup
42
+ const createdWidgetIds = []
43
+ const tmuxSessions = []
44
+ const browserSessions = []
45
+
46
+ beforeAll(async () => {
47
+ const { url } = await preflight()
48
+ serverUrl = url
49
+ createTestCanvas(CANVAS_NAME)
50
+
51
+ // Load agents and filter to available ones
52
+ const configured = loadConfiguredAgents()
53
+ agents = configured.filter((a) => {
54
+ const available = checkAgentAvailability(a.id)
55
+ if (!available) {
56
+ console.log(`[skip] Agent "${a.label}" (${a.id}) — binary not found, skipping`)
57
+ }
58
+ return available
59
+ })
60
+
61
+ if (agents.length === 0) {
62
+ console.warn('[warn] No agents available — agent tests will be skipped')
63
+ }
64
+
65
+ // Give the dev server a moment to pick up the new canvas file
66
+ await new Promise((r) => setTimeout(r, 2000))
67
+ })
68
+
69
+ afterAll(() => {
70
+ // Flush all transcripts
71
+ const paths = transcript.flushAll()
72
+
73
+ // Write results
74
+ writeResults(perf.toJSON(), paths)
75
+ perf.report()
76
+
77
+ // Cleanup: kill tmux sessions
78
+ for (const s of tmuxSessions) {
79
+ tmux.killSession(s)
80
+ }
81
+
82
+ // Cleanup: close browser sessions
83
+ for (const s of browserSessions) {
84
+ browser.close(s)
85
+ }
86
+
87
+ // Cleanup: delete test canvas
88
+ deleteTestCanvas(CANVAS_NAME)
89
+ })
90
+
91
+ /**
92
+ * Read a terminal config for a widget from .storyboard/terminals/.
93
+ * Tries both the widgetId symlink and falls back to scanning files.
94
+ */
95
+ function readTerminalConfig(widgetId) {
96
+ const terminalsDir = join(ROOT, '.storyboard', 'terminals')
97
+ try {
98
+ // Try widgetId symlink first
99
+ const symPath = join(terminalsDir, `${widgetId}.json`)
100
+ return JSON.parse(readFileSync(symPath, 'utf8'))
101
+ } catch { /* fallback */ }
102
+
103
+ // Scan for config containing this widgetId
104
+ try {
105
+ const files = readdirSync(terminalsDir).filter((f) => f.endsWith('.json'))
106
+ for (const f of files) {
107
+ try {
108
+ const config = JSON.parse(readFileSync(join(terminalsDir, f), 'utf8'))
109
+ if (config.widgetId === widgetId) return config
110
+ } catch { /* skip */ }
111
+ }
112
+ } catch { /* dir may not exist */ }
113
+
114
+ return null
115
+ }
116
+
117
+ /**
118
+ * Review helper — logs full context on soft assertion failure.
119
+ */
120
+ function reviewLog(testId, expected, actual, extra = {}) {
121
+ const lines = [
122
+ `\n[REVIEW] ${testId}`,
123
+ ` Expected pattern: ${expected}`,
124
+ ` Actual output (last 30 lines):`,
125
+ ...String(actual).split('\n').slice(-30).map((l) => ` ${l}`),
126
+ ]
127
+ if (extra.widgetId) lines.push(` Widget ID: ${extra.widgetId}`)
128
+ if (extra.elapsed) lines.push(` Elapsed: ${extra.elapsed}`)
129
+ console.log(lines.join('\n'))
130
+ }
131
+
132
+ // Generate test suites for each agent — they run in parallel
133
+ for (const agent of loadConfiguredAgents()) {
134
+ const agentDescribe = checkAgentAvailability(agent.id) ? describe : describe.skip
135
+
136
+ agentDescribe(`Agent: ${agent.label} (${agent.id})`, () => {
137
+ let agentWidgetId
138
+ let agentTmuxSession
139
+ const browserSession = `test-${agent.id}`
140
+ let stickyNoteId
141
+ let connectorId
142
+ let markdownId
143
+ let markdownConnectorId
144
+
145
+ browserSessions.push(browserSession)
146
+
147
+ // ─── Group 2: Startup & Basic Interaction ────────────────────────
148
+
149
+ describe('Group 2: Startup & Interaction', () => {
150
+ it(`T2.1 — Create ${agent.label} agent widget`, async () => {
151
+ const result = await canvasApi.createWidget(
152
+ CANVAS_NAME,
153
+ 'agent',
154
+ { startupCommand: agent.startupCommand },
155
+ { x: 100, y: 100 + agents.indexOf(agent) * 500 },
156
+ )
157
+
158
+ expect(result.success).toBe(true)
159
+ expect(result.widget).toBeDefined()
160
+ expect(result.widget.type).toBe('agent')
161
+ agentWidgetId = result.widget.id
162
+ createdWidgetIds.push(agentWidgetId)
163
+ })
164
+
165
+ it(`T2.2 — ${agent.label} starts and loads startup sequence`, async () => {
166
+ if (!agentWidgetId) return
167
+
168
+ // Open canvas in browser and click the agent widget
169
+ const canvasUrl = `${serverUrl}/storyboard/canvas/${CANVAS_NAME}`
170
+ browser.open(browserSession, canvasUrl)
171
+ browser.waitForLoad(browserSession, 'networkidle')
172
+
173
+ try {
174
+ browser.click(browserSession, `[data-widget-id="${agentWidgetId}"]`)
175
+ } catch { /* try alternative interaction */ }
176
+
177
+ // Wait for tmux session
178
+ const chainTimer = perf.start(`agent.${agent.id}.full_chain`)
179
+ const startupTimer = perf.start(`agent.${agent.id}.startup`)
180
+
181
+ try {
182
+ agentTmuxSession = await tmux.waitForSession(/^sb-/, 30000)
183
+ } catch {
184
+ agentTmuxSession = await tmux.waitForSession(/^sb-/, 60000)
185
+ }
186
+
187
+ tmuxSessions.push(agentTmuxSession)
188
+ transcript.createTranscript(agentTmuxSession, {
189
+ widgetId: agentWidgetId,
190
+ canvasName: CANVAS_NAME,
191
+ agentId: agent.id,
192
+ })
193
+ transcript.logSection(agentTmuxSession, 'T2.2')
194
+
195
+ expect(agentTmuxSession).toBeTruthy()
196
+
197
+ // Wait for agent to be ready — look for readiness indicators
198
+ try {
199
+ await tmux.waitForOutput(
200
+ agentTmuxSession,
201
+ /ready|copilot|claude|>|\$/i,
202
+ 90000,
203
+ 2000,
204
+ )
205
+ } catch (err) {
206
+ // Log but don't fail — startup might be slow
207
+ console.warn(`[warn] Agent ${agent.id} startup may be slow: ${err.message}`)
208
+ }
209
+
210
+ startupTimer.end()
211
+
212
+ // Validate startup output is clean — check for error indicators
213
+ const startupOutput = tmux.capturePane(agentTmuxSession)
214
+ const errorPatterns = /error:|stack trace|ENOENT|EACCES|segfault|panic|unhandled/i
215
+ if (errorPatterns.test(startupOutput)) {
216
+ console.warn(`[REVIEW] T2.2 — ${agent.label} startup contains error-like output:\n${startupOutput}`)
217
+ }
218
+
219
+ // Store chainTimer for later
220
+ agentDescribe.__chainTimer = chainTimer
221
+ }, 120_000)
222
+
223
+ it(`T2.3 — ${agent.label} terminal output flows correctly`, async () => {
224
+ if (!agentTmuxSession) return
225
+ transcript.logSection(agentTmuxSession, 'T2.3')
226
+
227
+ // Verify the pane has content (not empty/blank)
228
+ const output = tmux.capturePane(agentTmuxSession)
229
+ expect(output.trim().length).toBeGreaterThan(0)
230
+
231
+ // Check that there are no broken escape sequences (raw \x1b[ without completion)
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
+ // Best-effort: terminal content may not be in accessibility tree
278
+ } catch { /* browser check is best-effort */ }
279
+ }, 120_000)
280
+ })
281
+
282
+ // ─── Group 3: Agent Context Awareness ────────────────────────────
283
+
284
+ describe('Group 3: Context Awareness', () => {
285
+ it(`T3.1 — ${agent.label} terminal config exists and is valid`, async () => {
286
+ if (!agentWidgetId) return
287
+
288
+ // Wait a moment for config to be written
289
+ await new Promise((r) => setTimeout(r, 2000))
290
+
291
+ const config = readTerminalConfig(agentWidgetId)
292
+
293
+ expect(config).toBeDefined()
294
+ if (config) {
295
+ expect(config.widgetId).toBe(agentWidgetId)
296
+ expect(config.canvasId).toBeTruthy()
297
+ expect(config.branch).toBeTruthy()
298
+ expect(config.serverUrl).toBeTruthy()
299
+ // displayName may be set from prettyName
300
+ expect(config.displayName || config.widgetProps?.prettyName).toBeTruthy()
301
+ }
302
+ })
303
+
304
+ it(`T3.2 — ${agent.label} can identify itself`, async () => {
305
+ if (!agentTmuxSession || !agentWidgetId) return
306
+ transcript.logSection(agentTmuxSession, 'T3.2')
307
+
308
+ const config = readTerminalConfig(agentWidgetId)
309
+ if (!config) return
310
+
311
+ // Ask the agent to read its config
312
+ tmux.sendText(
313
+ agentTmuxSession,
314
+ 'Read your terminal config and tell me your widget ID, canvas ID, and display name. Reply with just those three values.',
315
+ )
316
+ tmux.sendKeys(agentTmuxSession, 'Enter')
317
+
318
+ // Wait for response containing the widget ID
319
+ let responseOutput = ''
320
+ try {
321
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, new RegExp(agentWidgetId.slice(0, 12)), 90000, 3000)
322
+ } catch (err) {
323
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
324
+ }
325
+
326
+ // Soft assertions — agent response is non-deterministic
327
+ expect.soft(responseOutput).toMatch(new RegExp(agentWidgetId.slice(0, 12), 'i'))
328
+
329
+ if (!new RegExp(agentWidgetId.slice(0, 12), 'i').test(responseOutput)) {
330
+ reviewLog(`T3.2 — ${agent.label} self-identification`, agentWidgetId, responseOutput, { widgetId: agentWidgetId })
331
+ }
332
+ }, 120_000)
333
+
334
+ it(`T3.3 — ${agent.label} environment variables are injected`, async () => {
335
+ if (!agentTmuxSession) return
336
+ transcript.logSection(agentTmuxSession, 'T3.3')
337
+
338
+ // Check STORYBOARD_WIDGET_ID
339
+ tmux.sendText(agentTmuxSession, 'echo WIDGET_ID_CHECK:$STORYBOARD_WIDGET_ID')
340
+ tmux.sendKeys(agentTmuxSession, 'Enter')
341
+
342
+ try {
343
+ const output = await tmux.waitForOutput(agentTmuxSession, /WIDGET_ID_CHECK:/, 15000)
344
+ // The env var should contain the widget ID
345
+ if (agentWidgetId) {
346
+ expect.soft(output).toContain(agentWidgetId)
347
+ }
348
+ } catch {
349
+ // Agent may not support direct shell commands — that's OK
350
+ console.log(`[info] ${agent.label} may not support direct echo — skipping env var check`)
351
+ }
352
+
353
+ // Check STORYBOARD_CANVAS_ID
354
+ tmux.sendText(agentTmuxSession, 'echo CANVAS_ID_CHECK:$STORYBOARD_CANVAS_ID')
355
+ tmux.sendKeys(agentTmuxSession, 'Enter')
356
+
357
+ try {
358
+ const output = await tmux.waitForOutput(agentTmuxSession, /CANVAS_ID_CHECK:/, 15000)
359
+ expect.soft(output).toMatch(/CANVAS_ID_CHECK:.+/)
360
+ } catch { /* best-effort */ }
361
+ }, 60_000)
362
+ })
363
+
364
+ // ─── Group 4: Connected Widget CRUD ──────────────────────────────
365
+
366
+ describe('Group 4: Connected Widget CRUD', () => {
367
+ it(`T4.1 — Create sticky note and connect to ${agent.label}`, async () => {
368
+ if (!agentWidgetId) return
369
+
370
+ // Create a sticky note
371
+ const stickyResult = await canvasApi.createWidget(
372
+ CANVAS_NAME,
373
+ 'sticky-note',
374
+ { text: 'banana', color: 'yellow' },
375
+ { x: 400, y: 100 + agents.indexOf(agent) * 500 },
376
+ )
377
+
378
+ expect(stickyResult.success).toBe(true)
379
+ stickyNoteId = stickyResult.widget.id
380
+ createdWidgetIds.push(stickyNoteId)
381
+
382
+ // Connect sticky note to agent widget
383
+ const connResult = await canvasApi.addConnector(
384
+ CANVAS_NAME,
385
+ stickyNoteId,
386
+ 'right',
387
+ agentWidgetId,
388
+ 'left',
389
+ )
390
+
391
+ expect(connResult.success).toBe(true)
392
+ connectorId = connResult.connector?.id
393
+
394
+ // Verify connector exists in canvas
395
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
396
+ const conn = canvas.connectors?.find((c) => c.id === connectorId)
397
+ expect(conn).toBeDefined()
398
+
399
+ // Wait for terminal config to update with connected widgets
400
+ await new Promise((r) => setTimeout(r, 3000))
401
+
402
+ const config = readTerminalConfig(agentWidgetId)
403
+ if (config) {
404
+ // connectedWidgets should include the sticky note ID
405
+ const connected = config.connectedWidgets || config.connectedWidgetIds || []
406
+ const hasSticky = Array.isArray(connected)
407
+ ? connected.some((w) => (typeof w === 'string' ? w === stickyNoteId : w.id === stickyNoteId))
408
+ : false
409
+ expect.soft(hasSticky).toBe(true)
410
+ }
411
+ })
412
+
413
+ it(`T4.2 — ${agent.label} sees connected widget`, async () => {
414
+ if (!agentTmuxSession || !stickyNoteId) return
415
+ transcript.logSection(agentTmuxSession, 'T4.2')
416
+
417
+ tmux.sendText(
418
+ agentTmuxSession,
419
+ 'What widgets are connected to you? Tell me the widget type and its text content.',
420
+ )
421
+ tmux.sendKeys(agentTmuxSession, 'Enter')
422
+
423
+ let responseOutput = ''
424
+ try {
425
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, /banana|sticky/i, 90000, 3000)
426
+ } catch (err) {
427
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
428
+ }
429
+
430
+ expect.soft(responseOutput).toMatch(/banana/i)
431
+
432
+ if (!/banana/i.test(responseOutput)) {
433
+ reviewLog(`T4.2 — ${agent.label} connected widget`, '/banana/i', responseOutput, { widgetId: agentWidgetId })
434
+ }
435
+ }, 120_000)
436
+
437
+ it(`T4.3 — Edit connected widget text (banana → apple)`, async () => {
438
+ if (!stickyNoteId) return
439
+
440
+ // Update sticky note text
441
+ await canvasApi.updateWidget(CANVAS_NAME, stickyNoteId, {
442
+ props: { text: 'apple', color: 'yellow' },
443
+ })
444
+
445
+ // Verify update in canvas
446
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
447
+ const sticky = canvas.widgets?.find((w) => w.id === stickyNoteId)
448
+ expect(sticky?.props?.text).toBe('apple')
449
+
450
+ // Ask agent about the update
451
+ if (!agentTmuxSession) return
452
+ transcript.logSection(agentTmuxSession, 'T4.3')
453
+
454
+ tmux.sendText(agentTmuxSession, 'What does your connected sticky note say now? Just tell me the text.')
455
+ tmux.sendKeys(agentTmuxSession, 'Enter')
456
+
457
+ let responseOutput = ''
458
+ try {
459
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, /apple/i, 90000, 3000)
460
+ } catch (err) {
461
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
462
+ }
463
+
464
+ expect.soft(responseOutput).toMatch(/apple/i)
465
+
466
+ if (!/apple/i.test(responseOutput)) {
467
+ reviewLog(`T4.3 — ${agent.label} edited widget`, '/apple/i', responseOutput, { widgetId: agentWidgetId })
468
+ }
469
+ }, 120_000)
470
+
471
+ it(`T4.4 — Edit connected widget color (yellow → red)`, async () => {
472
+ if (!stickyNoteId) return
473
+
474
+ await canvasApi.updateWidget(CANVAS_NAME, stickyNoteId, {
475
+ props: { text: 'apple', color: 'red' },
476
+ })
477
+
478
+ // Verify in canvas
479
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
480
+ const sticky = canvas.widgets?.find((w) => w.id === stickyNoteId)
481
+ expect(sticky?.props?.color).toBe('red')
482
+
483
+ // Verify in browser
484
+ try {
485
+ browser.open(browserSession, `${serverUrl}/storyboard/canvas/${CANVAS_NAME}`)
486
+ browser.waitForLoad(browserSession, 'networkidle')
487
+ // Best-effort visual check
488
+ const snap = browser.snapshot(browserSession)
489
+ } catch { /* best-effort */ }
490
+ })
491
+
492
+ it(`T4.5 — Delete connected sticky note`, async () => {
493
+ if (!stickyNoteId) return
494
+
495
+ await canvasApi.deleteWidget(CANVAS_NAME, stickyNoteId)
496
+
497
+ // Verify widget is gone
498
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
499
+ const sticky = canvas.widgets?.find((w) => w.id === stickyNoteId)
500
+ expect(sticky).toBeUndefined()
501
+
502
+ // Connector should be orphaned (widget it points to is gone)
503
+ // The connector may still exist in the data but one end is invalid
504
+ })
505
+
506
+ it(`T4.6 — Connected markdown block (variant)`, async () => {
507
+ if (!agentWidgetId) return
508
+
509
+ // Create markdown widget
510
+ const mdResult = await canvasApi.createWidget(
511
+ CANVAS_NAME,
512
+ 'markdown',
513
+ { content: '# Hello World' },
514
+ { x: 400, y: 250 + agents.indexOf(agent) * 500 },
515
+ )
516
+
517
+ expect(mdResult.success).toBe(true)
518
+ markdownId = mdResult.widget.id
519
+ createdWidgetIds.push(markdownId)
520
+
521
+ // Connect to agent
522
+ const connResult = await canvasApi.addConnector(
523
+ CANVAS_NAME,
524
+ markdownId,
525
+ 'right',
526
+ agentWidgetId,
527
+ 'left',
528
+ )
529
+ expect(connResult.success).toBe(true)
530
+ markdownConnectorId = connResult.connector?.id
531
+
532
+ // Wait for config update
533
+ await new Promise((r) => setTimeout(r, 3000))
534
+
535
+ // Ask agent about connected widget
536
+ if (agentTmuxSession) {
537
+ transcript.logSection(agentTmuxSession, 'T4.6')
538
+
539
+ tmux.sendText(agentTmuxSession, 'What markdown content is connected to you? Just tell me the heading text.')
540
+ tmux.sendKeys(agentTmuxSession, 'Enter')
541
+
542
+ let responseOutput = ''
543
+ try {
544
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, /hello\s*world/i, 90000, 3000)
545
+ } catch (err) {
546
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
547
+ }
548
+
549
+ expect.soft(responseOutput).toMatch(/hello\s*world/i)
550
+
551
+ if (!/hello\s*world/i.test(responseOutput)) {
552
+ reviewLog(`T4.6 — ${agent.label} markdown widget`, '/hello world/i', responseOutput, { widgetId: agentWidgetId })
553
+ }
554
+ }
555
+
556
+ // Update markdown content
557
+ await canvasApi.updateWidget(CANVAS_NAME, markdownId, {
558
+ props: { content: '# Goodbye World' },
559
+ })
560
+
561
+ await new Promise((r) => setTimeout(r, 2000))
562
+
563
+ if (agentTmuxSession) {
564
+ tmux.sendText(agentTmuxSession, 'What does your connected markdown say now?')
565
+ tmux.sendKeys(agentTmuxSession, 'Enter')
566
+
567
+ let responseOutput = ''
568
+ try {
569
+ responseOutput = await tmux.waitForOutput(agentTmuxSession, /goodbye\s*world/i, 90000, 3000)
570
+ } catch (err) {
571
+ responseOutput = err.lastCapture || tmux.capturePane(agentTmuxSession)
572
+ }
573
+
574
+ expect.soft(responseOutput).toMatch(/goodbye\s*world/i)
575
+ }
576
+
577
+ // Delete markdown
578
+ await canvasApi.deleteWidget(CANVAS_NAME, markdownId)
579
+
580
+ const canvas = await canvasApi.readCanvas(CANVAS_NAME)
581
+ const md = canvas.widgets?.find((w) => w.id === markdownId)
582
+ expect(md).toBeUndefined()
583
+ }, 180_000)
584
+ })
585
+
586
+ // End the chain timer in the last afterAll
587
+ afterAll(() => {
588
+ if (agentDescribe.__chainTimer) {
589
+ agentDescribe.__chainTimer.end()
590
+ }
591
+ })
592
+ })
593
+ }
@@ -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
+ }