@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
@@ -14,14 +14,17 @@ import fs from 'node:fs'
14
14
  import path from 'node:path'
15
15
  import { parse as parseJsonc } from 'jsonc-parser'
16
16
  import { getConfig } from '../configSchema.js'
17
+ import { createDevLogger, setDevLogger } from '../logger/devLogger.js'
17
18
  import { serverFeatures as workshopFeatures } from '../workshop/features/registry-server.js'
18
19
  import { docsHandler, collectFiles } from './docs-handler.js'
19
20
  import { createCanvasHandler } from '../canvas/server.js'
20
21
  import { setupSelectedWidgets } from '../canvas/selectedWidgets.js'
22
+ import { HotPoolManager } from '../canvas/hot-pool.js'
21
23
  import { createAutosyncHandler } from '../autosync/server.js'
22
24
  import { setupTerminalServer } from '../canvas/terminal-server.js'
23
- import { listSessions, detachSession, killSession, orphanSession } from '../canvas/terminal-registry.js'
25
+ import { listSessions, detachSession, killSession, orphanSession, bulkCleanup, getSessionStats } from '../canvas/terminal-registry.js'
24
26
  import { execSync as cpExecSync } from 'node:child_process'
27
+ import { list as listRunningServers } from '../worktree/serverRegistry.js'
25
28
 
26
29
  const API_PREFIX = '/_storyboard/'
27
30
 
@@ -49,6 +52,27 @@ function sendJson(res, status, data) {
49
52
  res.end(JSON.stringify(data))
50
53
  }
51
54
 
55
+ /**
56
+ * Create a logging wrapper around sendJson.
57
+ * Reads per-request route context from res.__sbLogCtx (set by middleware).
58
+ */
59
+ function createLoggedSendJson(logger) {
60
+ return function sendJsonLogged(res, status, data) {
61
+ sendJson(res, status, data)
62
+ if (status >= 400 && logger) {
63
+ const ctx = res.__sbLogCtx || {}
64
+ logger.logResponse({
65
+ status,
66
+ method: ctx.method || 'UNKNOWN',
67
+ url: ctx.url || '',
68
+ route: ctx.route || null,
69
+ subRoute: ctx.subRoute || null,
70
+ error: data?.error || null,
71
+ })
72
+ }
73
+ }
74
+ }
75
+
52
76
  /**
53
77
  * Read storyboard.config.json from the project root and apply defaults.
54
78
  */
@@ -100,11 +124,17 @@ export default function storyboardServer() {
100
124
  },
101
125
 
102
126
  configureServer(server) {
103
- // --- Canvas reload guard ---------------------------------------------------
104
- // Suppress full-reloads and HMR updates for clients on canvas routes.
105
- // Canvas pages send heartbeats via import.meta.hot; the guard auto-expires
106
- // 5s after the last heartbeat so closed tabs never leave it stuck.
107
- // Opt out with ?canvas-hmr in the URL when developing canvas UI code.
127
+ // --- Reload guard ----------------------------------------------------------
128
+ // Suppress full-reloads and HMR updates for guarded clients.
129
+ //
130
+ // Two guard channels:
131
+ // 1. Canvas guard canvas pages send heartbeats via storyboard:canvas-hmr-guard.
132
+ // Controlled by the "canvas-auto-reload" feature flag (default: false = guard ON).
133
+ // 2. Prototype guard — all pages send heartbeats via storyboard:prototype-reload-guard.
134
+ // Controlled by the "prototype-auto-reload" feature flag (default: true = guard OFF).
135
+ //
136
+ // Both guards auto-expire 5s after the last heartbeat so closed tabs never
137
+ // leave them stuck. Custom storyboard events always pass through.
108
138
  {
109
139
  let recentCanvasMutationAt = 0
110
140
  const CANVAS_WINDOW_MS = 1500
@@ -119,29 +149,46 @@ export default function storyboardServer() {
119
149
  server.watcher.on('add', markCanvasMutation)
120
150
  server.watcher.on('unlink', markCanvasMutation)
121
151
 
122
- const guardedClients = new Map()
152
+ const canvasGuardedClients = new Map()
153
+ const prototypeGuardedClients = new Map()
123
154
 
124
155
  server.hot.on('storyboard:canvas-hmr-guard', (data, client) => {
125
- if (data.active && !data.hmrEnabled) {
126
- guardedClients.set(client, Date.now() + GUARD_TTL_MS)
156
+ if (data.active) {
157
+ canvasGuardedClients.set(client, Date.now() + GUARD_TTL_MS)
158
+ } else {
159
+ canvasGuardedClients.delete(client)
160
+ }
161
+ })
162
+
163
+ server.hot.on('storyboard:prototype-reload-guard', (data, client) => {
164
+ if (data.active) {
165
+ prototypeGuardedClients.set(client, Date.now() + GUARD_TTL_MS)
127
166
  } else {
128
- guardedClients.delete(client)
167
+ prototypeGuardedClients.delete(client)
129
168
  }
130
169
  })
131
170
 
132
171
  const cleanup = setInterval(() => {
133
172
  const now = Date.now()
134
- for (const [client, until] of guardedClients) {
173
+ for (const [client, until] of canvasGuardedClients) {
135
174
  if (now > until || !server.ws.clients.has(client)) {
136
- guardedClients.delete(client)
175
+ canvasGuardedClients.delete(client)
176
+ }
177
+ }
178
+ for (const [client, until] of prototypeGuardedClients) {
179
+ if (now > until || !server.ws.clients.has(client)) {
180
+ prototypeGuardedClients.delete(client)
137
181
  }
138
182
  }
139
183
  }, 10000)
140
184
  server.httpServer?.on('close', () => clearInterval(cleanup))
141
185
 
142
186
  function isClientGuarded(client) {
143
- const until = guardedClients.get(client)
144
- return until != null && Date.now() < until
187
+ const cu = canvasGuardedClients.get(client)
188
+ if (cu != null && Date.now() < cu) return true
189
+ const pu = prototypeGuardedClients.get(client)
190
+ if (pu != null && Date.now() < pu) return true
191
+ return false
145
192
  }
146
193
 
147
194
  const originalSend = server.ws.send.bind(server.ws)
@@ -156,7 +203,7 @@ export default function storyboardServer() {
156
203
  }
157
204
 
158
205
  // No guarded clients → broadcast normally
159
- if (guardedClients.size === 0) {
206
+ if (canvasGuardedClients.size === 0 && prototypeGuardedClients.size === 0) {
160
207
  return originalSend(payload, ...rest)
161
208
  }
162
209
 
@@ -174,7 +221,28 @@ export default function storyboardServer() {
174
221
  return originalSend(payload, ...rest)
175
222
  }
176
223
  }
177
- // --- End canvas reload guard -----------------------------------------------
224
+ // --- End reload guard ------------------------------------------------------
225
+
226
+ // Initialize dev logger for structured o11y logging
227
+ const devDomain = config.devDomain || null
228
+ let currentBranch = null
229
+ try { currentBranch = cpExecSync('git branch --show-current', { encoding: 'utf8', cwd: root }).trim() } catch {}
230
+ const logVerbose = config.featureFlags?.['dev-logs'] || false
231
+ const devLogger = createDevLogger({ root, devDomain, branch: currentBranch, verbose: logVerbose })
232
+ setDevLogger(devLogger) // make available to all server-side modules via devLog()
233
+ const sendJsonLogged = createLoggedSendJson(devLogger)
234
+
235
+ // Listen for browser-side console errors forwarded via HMR
236
+ server.hot.on('storyboard:client-error', (data) => {
237
+ devLogger.logEvent(data.level || 'error', data.message || 'Unknown browser error', {
238
+ source: 'browser',
239
+ url: data.url || null,
240
+ line: data.line || null,
241
+ col: data.col || null,
242
+ stack: data.stack || null,
243
+ route: data.route || null,
244
+ })
245
+ })
178
246
 
179
247
  const workshopConfig = config.workshop || {}
180
248
  const enabledFeatures = workshopConfig.features || {}
@@ -184,7 +252,7 @@ export default function storyboardServer() {
184
252
  for (const [featureName, featureModule] of Object.entries(workshopFeatures)) {
185
253
  if (enabledFeatures[featureName] === false) continue
186
254
  if (featureModule.serverSetup) {
187
- workshopHandlers.push(featureModule.serverSetup({ root, sendJson, workshopConfig }))
255
+ workshopHandlers.push(featureModule.serverSetup({ root, sendJson: sendJsonLogged, workshopConfig }))
188
256
  }
189
257
  }
190
258
  if (workshopHandlers.length > 0) {
@@ -193,15 +261,24 @@ export default function storyboardServer() {
193
261
  await handler(req, res, ctx)
194
262
  if (res.writableEnded) return
195
263
  }
196
- sendJson(res, 404, { error: `Unknown workshop route: ${ctx.method} ${ctx.path}` })
264
+ sendJsonLogged(res, 404, { error: `Unknown workshop route: ${ctx.method} ${ctx.path}` })
197
265
  })
198
266
  }
199
267
 
200
268
  // Wire docs API routes (always enabled — serves README + source files)
201
- routeHandlers.set('docs', docsHandler({ root, sendJson }))
269
+ routeHandlers.set('docs', docsHandler({ root, sendJson: sendJsonLogged }))
270
+
271
+ // Create shared hot pool manager (per-type pre-warmed sessions)
272
+ const hotPoolConfig = config.hotPool || {}
273
+ const agentsConfig = config.canvas?.agents || {}
274
+ const wsSend = server.ws.send.bind(server.ws)
275
+ const hotPool = new HotPoolManager({ root, config: hotPoolConfig, agentsConfig, wsSend })
276
+ hotPool.start().catch((err) => {
277
+ devLogger.logEvent('error', 'Hot pool failed to start', { error: err.message })
278
+ })
202
279
 
203
280
  // Wire canvas API routes (always enabled — CRUD for .canvas.jsonl files)
204
- routeHandlers.set('canvas', createCanvasHandler({ root, sendJson }))
281
+ routeHandlers.set('canvas', createCanvasHandler({ root, sendJson: sendJsonLogged, hotPool }))
205
282
 
206
283
  // Selected widgets bridge — writes .selectedwidgets.json for Copilot context
207
284
  setupSelectedWidgets(server, root)
@@ -212,15 +289,16 @@ export default function storyboardServer() {
212
289
  try {
213
290
  branch = cpExecSync('git branch --show-current', { encoding: 'utf8', cwd: root }).trim()
214
291
  } catch {}
215
- setupTerminalServer(server.httpServer, base, branch)
292
+ setupTerminalServer(server.httpServer, base, branch, hotPool)
216
293
  }
217
294
 
218
295
  // Ignore assets/canvas/ so image/snapshot writes don't trigger reloads
219
296
  server.watcher.unwatch(path.join(root, 'assets', 'canvas', 'images'))
220
297
  server.watcher.unwatch(path.join(root, 'assets', 'canvas', 'snapshots'))
298
+ server.watcher.unwatch(path.join(root, 'assets', '.storyboard-public', 'terminal-snapshots'))
221
299
 
222
300
  // Wire autosync API routes (always enabled — git automation for dev)
223
- routeHandlers.set('autosync', createAutosyncHandler({ root, sendJson }))
301
+ routeHandlers.set('autosync', createAutosyncHandler({ root, sendJson: sendJsonLogged }))
224
302
 
225
303
  // Terminal sessions API — list, detach, kill sessions
226
304
  routeHandlers.set('terminal', async (req, res, ctx) => {
@@ -232,7 +310,37 @@ export default function storyboardServer() {
232
310
  if (ctx.method === 'GET' && (subpath === 'sessions' || subpath === 'sessions/')) {
233
311
  const url = new URL(req.url, 'http://localhost')
234
312
  const filterBranch = url.searchParams.get('branch') || null
235
- sendJson(res, 200, { sessions: listSessions(filterBranch) })
313
+ sendJsonLogged(res, 200, { sessions: listSessions(filterBranch) })
314
+ return
315
+ }
316
+
317
+ // GET /sessions/stats — quick session counts by status
318
+ if (ctx.method === 'GET' && subpath === 'sessions/stats') {
319
+ sendJsonLogged(res, 200, getSessionStats())
320
+ return
321
+ }
322
+
323
+ // POST /sessions/cleanup — bulk remove sessions by status
324
+ if (ctx.method === 'POST' && subpath === 'sessions/cleanup') {
325
+ let body = ''
326
+ for await (const chunk of req) body += chunk
327
+ try {
328
+ const { statuses } = JSON.parse(body)
329
+ const allowed = new Set(['archived', 'background'])
330
+ if (!Array.isArray(statuses) || statuses.length === 0) {
331
+ sendJsonLogged(res, 400, { error: 'statuses must be a non-empty array' })
332
+ return
333
+ }
334
+ const invalid = statuses.filter(s => !allowed.has(s))
335
+ if (invalid.length > 0) {
336
+ sendJsonLogged(res, 400, { error: `Invalid statuses: ${invalid.join(', ')}. Allowed: archived, background` })
337
+ return
338
+ }
339
+ const result = bulkCleanup({ statuses })
340
+ sendJsonLogged(res, 200, { success: true, ...result })
341
+ } catch {
342
+ sendJsonLogged(res, 400, { error: 'Invalid JSON body' })
343
+ }
236
344
  return
237
345
  }
238
346
 
@@ -242,10 +350,10 @@ export default function storyboardServer() {
242
350
  const tmuxName = decodeURIComponent(detachMatch[1])
243
351
  const entry = detachSession(tmuxName)
244
352
  if (!entry) {
245
- sendJson(res, 404, { error: 'Session not found' })
353
+ sendJsonLogged(res, 404, { error: 'Session not found' })
246
354
  return
247
355
  }
248
- sendJson(res, 200, { success: true, session: entry })
356
+ sendJsonLogged(res, 200, { success: true, session: entry })
249
357
  return
250
358
  }
251
359
 
@@ -254,7 +362,7 @@ export default function storyboardServer() {
254
362
  if (ctx.method === 'POST' && orphanMatch) {
255
363
  const tmuxName = decodeURIComponent(orphanMatch[1])
256
364
  orphanSession(tmuxName)
257
- sendJson(res, 200, { success: true })
365
+ sendJsonLogged(res, 200, { success: true })
258
366
  return
259
367
  }
260
368
 
@@ -263,44 +371,76 @@ export default function storyboardServer() {
263
371
  if (ctx.method === 'DELETE' && deleteMatch) {
264
372
  const tmuxName = decodeURIComponent(deleteMatch[1])
265
373
  killSession(tmuxName)
266
- sendJson(res, 200, { success: true })
374
+ sendJsonLogged(res, 200, { success: true })
267
375
  return
268
376
  }
269
377
 
270
- sendJson(res, 404, { error: 'Not found' })
378
+ // ── Hot Pool routes (/terminal/hot-pool/*) ──────────────
379
+
380
+ // GET /hot-pool — pool status
381
+ if (ctx.method === 'GET' && subpath === 'hot-pool') {
382
+ sendJsonLogged(res, 200, hotPool.status())
383
+ return
384
+ }
385
+
386
+ // PUT /hot-pool — reconfigure pool
387
+ if (ctx.method === 'PUT' && subpath === 'hot-pool') {
388
+ hotPool.reconfigure(ctx.body || {})
389
+ sendJsonLogged(res, 200, hotPool.status())
390
+ return
391
+ }
392
+
393
+ // POST /hot-pool/acquire — acquire a warm session from a specific pool
394
+ if (ctx.method === 'POST' && subpath === 'hot-pool/acquire') {
395
+ const poolId = ctx.body?.poolId || 'terminal'
396
+ const session = hotPool.acquire(poolId)
397
+ if (!session) {
398
+ sendJsonLogged(res, 200, { acquired: false, poolId, session: null })
399
+ return
400
+ }
401
+ sendJsonLogged(res, 200, { acquired: true, poolId, session: { id: session.id, tmuxName: session.tmuxName, poolId: session.poolId } })
402
+ return
403
+ }
404
+
405
+ sendJsonLogged(res, 404, { error: 'Not found' })
271
406
  })
272
407
 
273
- // Worktrees API — lists available worktrees/branches from ports.json
408
+ // Worktrees API — lists running worktrees/branches from server registry
274
409
  routeHandlers.set('worktrees', async (req, res) => {
275
410
  try {
276
- // Walk up from root to find the repo root's .worktrees/ports.json
277
- let dir = root
278
- let portsPath = null
279
- for (let i = 0; i < 10; i++) {
280
- const candidate = path.join(dir, '.worktrees', 'ports.json')
281
- if (fs.existsSync(candidate)) { portsPath = candidate; break }
282
- // Check if ports.json is a sibling (we're inside a worktree)
283
- const parentCandidate = path.join(path.dirname(dir), 'ports.json')
284
- if (fs.existsSync(parentCandidate)) { portsPath = parentCandidate; break }
285
- const parent = path.dirname(dir)
286
- if (parent === dir) break
287
- dir = parent
288
- }
289
- if (!portsPath) { sendJson(res, 200, []); return }
290
- const ports = JSON.parse(fs.readFileSync(portsPath, 'utf8'))
291
- const branches = Object.keys(ports).map(name => ({
292
- branch: name,
293
- folder: name === 'main' ? '' : `branch--${name}/`,
411
+ const servers = listRunningServers()
412
+ const branches = servers.map(srv => ({
413
+ branch: srv.worktree,
414
+ folder: srv.worktree === 'main' ? '' : `branch--${srv.worktree}/`,
294
415
  }))
295
- sendJson(res, 200, branches)
296
- } catch { sendJson(res, 200, []) }
416
+ // Always include main
417
+ if (!branches.some(b => b.branch === 'main')) {
418
+ branches.unshift({ branch: 'main', folder: '' })
419
+ }
420
+ sendJsonLogged(res, 200, branches)
421
+ } catch { sendJsonLogged(res, 200, []) }
422
+ })
423
+
424
+ // Git user — return git config user name and GitHub login (via gh CLI)
425
+ routeHandlers.set('git-user', async (req, res) => {
426
+ try {
427
+ const { execSync } = await import('node:child_process')
428
+ const name = execSync('git config user.name', { cwd: root, encoding: 'utf8' }).trim()
429
+ let login = null
430
+ try {
431
+ const status = execSync('gh auth status 2>&1', { cwd: root, encoding: 'utf8' })
432
+ const m = status.match(/Logged in to github\.com account (\S+)/) || status.match(/Logged in to github\.com as (\S+)/)
433
+ if (m) login = m[1]
434
+ } catch { /* gh not installed or not logged in */ }
435
+ sendJsonLogged(res, 200, { name, login })
436
+ } catch { sendJsonLogged(res, 200, { name: null, login: null }) }
297
437
  })
298
438
 
299
439
  // Switch branch — proxy to storyboard server which manages worktree
300
440
  // dev servers. The server port is derived from the devDomain.
301
441
  routeHandlers.set('switch-branch', async (req, res, ctx) => {
302
442
  if (ctx.method !== 'POST') {
303
- sendJson(res, 405, { error: 'POST required' })
443
+ sendJsonLogged(res, 405, { error: 'POST required' })
304
444
  return
305
445
  }
306
446
  try {
@@ -319,16 +459,16 @@ export default function storyboardServer() {
319
459
  body: JSON.stringify(ctx.body),
320
460
  })
321
461
  const data = await proxyRes.json()
322
- sendJson(res, proxyRes.status, data)
462
+ sendJsonLogged(res, proxyRes.status, data)
323
463
  } catch {
324
- sendJson(res, 502, {
464
+ sendJsonLogged(res, 502, {
325
465
  error: 'Storyboard server not running. Start it with: npx storyboard server',
326
466
  })
327
467
  }
328
468
  })
329
469
 
330
470
  // Watch toolbar.config.json for changes — trigger full reload so
331
- // CoreUIBar.svelte picks up menu/mode config changes during dev
471
+ // CoreUIBar.jsx picks up menu/mode config changes during dev
332
472
  const toolbarConfigPath = path.resolve(
333
473
  path.dirname(new URL(import.meta.url).pathname),
334
474
  '../../toolbar.config.json'
@@ -377,6 +517,9 @@ export default function storyboardServer() {
377
517
  const prefix = slashIndex === -1 ? pathAfterPrefix : pathAfterPrefix.slice(0, slashIndex)
378
518
  const restPath = slashIndex === -1 ? '/' : pathAfterPrefix.slice(slashIndex)
379
519
 
520
+ // Attach route context for the logging sendJson wrapper
521
+ res.__sbLogCtx = { method: req.method, url, route: prefix, subRoute: restPath }
522
+
380
523
  const handler = routeHandlers.get(prefix)
381
524
  if (!handler) {
382
525
  // Proxy to standalone storyboard server for unhandled prefixes
@@ -388,7 +531,7 @@ export default function storyboardServer() {
388
531
  proxyRes.pipe(res)
389
532
  })
390
533
  proxy.on('error', () => {
391
- sendJson(res, 502, { error: `Storyboard server not running. Start it with: npx storyboard server` })
534
+ sendJsonLogged(res, 502, { error: `Storyboard server not running. Start it with: npx storyboard server` })
392
535
  })
393
536
  if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') {
394
537
  req.pipe(proxy)
@@ -396,7 +539,7 @@ export default function storyboardServer() {
396
539
  proxy.end()
397
540
  }
398
541
  } catch {
399
- sendJson(res, 502, { error: 'Storyboard server not running' })
542
+ sendJsonLogged(res, 502, { error: 'Storyboard server not running' })
400
543
  }
401
544
  return
402
545
  }
@@ -406,10 +549,9 @@ export default function storyboardServer() {
406
549
  if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH' || req.method === 'DELETE') {
407
550
  body = await parseJsonBody(req)
408
551
  }
409
- await handler(req, res, { body, path: restPath, method: req.method })
552
+ await handler(req, res, { body, path: restPath, method: req.method, __viteWs: server.ws })
410
553
  } catch (err) {
411
- console.error(`[storyboard-server] Error in ${prefix}:`, err)
412
- sendJson(res, 500, { error: err.message || 'Internal server error' })
554
+ sendJsonLogged(res, 500, { error: err.message || 'Internal server error' })
413
555
  }
414
556
  })
415
557
  },
@@ -424,6 +566,46 @@ export default function storyboardServer() {
424
566
  children: 'window.__SB_LOCAL_DEV__=true',
425
567
  injectTo: 'head',
426
568
  })
569
+
570
+ // Browser error bridge — forwards console.error/warn and uncaught
571
+ // exceptions to the dev server via HMR for structured o11y logging
572
+ tags.push({
573
+ tag: 'script',
574
+ attrs: { type: 'module' },
575
+ children: `
576
+ (function() {
577
+ if (!import.meta.hot) return;
578
+ var MAX_LEN = 2000;
579
+ function trunc(s) { return typeof s === 'string' && s.length > MAX_LEN ? s.slice(0, MAX_LEN) + '…' : s; }
580
+ function route() { return location.pathname + location.hash; }
581
+ function send(level, msg, extra) {
582
+ try { import.meta.hot.send('storyboard:client-error', Object.assign({ level: level, message: trunc(msg), route: route() }, extra || {})); } catch {}
583
+ }
584
+ // Patch console.error and console.warn
585
+ ['error', 'warn'].forEach(function(level) {
586
+ var orig = console[level];
587
+ console[level] = function() {
588
+ orig.apply(console, arguments);
589
+ var parts = [];
590
+ for (var i = 0; i < arguments.length; i++) {
591
+ try { parts.push(typeof arguments[i] === 'string' ? arguments[i] : JSON.stringify(arguments[i])); } catch { parts.push(String(arguments[i])); }
592
+ }
593
+ send(level, parts.join(' '));
594
+ };
595
+ });
596
+ // Uncaught errors
597
+ window.addEventListener('error', function(e) {
598
+ send('error', e.message || 'Uncaught error', { url: e.filename, line: e.lineno, col: e.colno, stack: trunc(e.error && e.error.stack) });
599
+ });
600
+ // Unhandled promise rejections
601
+ window.addEventListener('unhandledrejection', function(e) {
602
+ var msg = e.reason ? (e.reason.message || String(e.reason)) : 'Unhandled rejection';
603
+ send('error', msg, { stack: trunc(e.reason && e.reason.stack) });
604
+ });
605
+ })();
606
+ `.trim(),
607
+ injectTo: 'head',
608
+ })
427
609
  }
428
610
 
429
611
  // Inject base path so the inspector UI can resolve static assets
@@ -549,7 +731,7 @@ export default function storyboardServer() {
549
731
 
550
732
  // Emit canvas images so they're available in deployed (static) builds.
551
733
  // Dev server serves these dynamically; production needs the static files.
552
- // Private images (prefixed with _) are excluded from the build.
734
+ // Private images (prefixed with ~) are excluded from the build.
553
735
  for (const dir of [
554
736
  path.join(root, 'assets', 'canvas', 'images'),
555
737
  path.join(root, 'assets', 'canvas', 'snapshots'),
@@ -558,7 +740,7 @@ export default function storyboardServer() {
558
740
  const imageFiles = await fs.promises.readdir(dir)
559
741
  const subdir = dir.endsWith('snapshots') ? 'snapshots' : 'images'
560
742
  for (const file of imageFiles) {
561
- if (file.startsWith('_') || file.startsWith('.')) continue
743
+ if (file.startsWith('~') || file.startsWith('.')) continue
562
744
  try {
563
745
  const data = await fs.promises.readFile(path.join(dir, file))
564
746
  this.emitFile({