@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
@@ -19,11 +19,57 @@ import { initPlugins } from './plugins.js'
19
19
  import { initUIConfig } from './uiConfig.js'
20
20
  import { initCanvasConfig } from './canvasConfig.js'
21
21
  import { initCommandPaletteConfig } from './commandPaletteConfig.js'
22
- import { initToolbarConfig } from './toolbarConfigStore.js'
22
+ import { initToolbarConfig, consumeClientToolbarOverrides } from './toolbarConfigStore.js'
23
23
  import { initCustomerModeConfig } from './customerModeConfig.js'
24
+ import { getConfig } from './configStore.js'
24
25
 
25
26
  let _mounted = false
26
27
 
28
+ const CHROME_HIDDEN_KEY = 'sb-chrome-hidden'
29
+
30
+ /**
31
+ * Migrate localStorage keys renamed in 4.3.0.
32
+ * Runs once at startup, idempotent: only copies if new key doesn't exist yet.
33
+ */
34
+ function migrateLocalStorageKeys() {
35
+ if (typeof localStorage === 'undefined') return
36
+ const renames = [
37
+ ['sb-viewfinder-starred', 'sb-workspace-starred'],
38
+ ['sb-viewfinder-recent', 'sb-workspace-recent'],
39
+ ['sb-viewfinder-group-folders', 'sb-workspace-group-folders'],
40
+ ]
41
+ for (const [oldKey, newKey] of renames) {
42
+ if (localStorage.getItem(newKey) === null && localStorage.getItem(oldKey) !== null) {
43
+ localStorage.setItem(newKey, localStorage.getItem(oldKey))
44
+ }
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Restore the saved chrome-hidden state immediately, before React mounts.
50
+ * Prevents a flash of toolbars appearing then disappearing.
51
+ */
52
+ function applyEarlyChromeState() {
53
+ if (typeof document === 'undefined' || typeof localStorage === 'undefined') return
54
+ const hidden = localStorage.getItem(CHROME_HIDDEN_KEY) === '1'
55
+ if (hidden) {
56
+ document.documentElement.classList.add('storyboard-chrome-hidden')
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Watch for changes to the storyboard-chrome-hidden class and persist to
62
+ * localStorage. Works regardless of which code path toggles the class.
63
+ */
64
+ function installChromeStatePersistence() {
65
+ if (typeof document === 'undefined' || typeof localStorage === 'undefined') return
66
+ const observer = new MutationObserver(() => {
67
+ const hidden = document.documentElement.classList.contains('storyboard-chrome-hidden')
68
+ localStorage.setItem(CHROME_HIDDEN_KEY, hidden ? '1' : '0')
69
+ })
70
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
71
+ }
72
+
27
73
  /**
28
74
  * Apply the saved theme to Primer CSS attributes immediately, before
29
75
  * React or Svelte mount. This prevents a flash of wrong-theme content.
@@ -138,6 +184,12 @@ export async function mountStoryboardCore(config = {}, options = {}) {
138
184
  const basePath = options.basePath || '/'
139
185
  const customHandlers = options.handlers || {}
140
186
 
187
+ // Migrate renamed localStorage keys (4.3.0: viewfinder → workspace)
188
+ migrateLocalStorageKeys()
189
+
190
+ // Apply saved chrome-hidden state immediately — before React mount
191
+ applyEarlyChromeState()
192
+
141
193
  // Apply saved theme to DOM immediately — before Svelte/React mount
142
194
  applyEarlyTheme()
143
195
 
@@ -145,60 +197,104 @@ export async function mountStoryboardCore(config = {}, options = {}) {
145
197
  installHideParamListener()
146
198
  installHistorySync()
147
199
  installBodyClassSync()
200
+ installChromeStatePersistence()
148
201
 
149
- // Initialize config-driven systems
150
- if (config.featureFlags) {
202
+ // Initialize config-driven systems.
203
+ // The unified config store is already seeded by the virtual module's initConfig().
204
+ // Individual stores are initialized here for backward compatibility — consumers
205
+ // that import directly from these stores still work.
206
+ const uc = getConfig()
207
+
208
+ if (uc.featureFlags && Object.keys(uc.featureFlags).length > 0) {
209
+ initFeatureFlags(uc.featureFlags)
210
+ } else if (config.featureFlags) {
151
211
  initFeatureFlags(config.featureFlags)
152
212
  }
153
213
 
154
- if (config.plugins) {
214
+ if (uc.plugins && Object.keys(uc.plugins).length > 0) {
215
+ initPlugins(uc.plugins)
216
+ } else if (config.plugins) {
155
217
  initPlugins(config.plugins)
156
218
  }
157
219
 
158
- if (config.ui) {
220
+ if (uc.ui && Object.keys(uc.ui).length > 0) {
221
+ initUIConfig(uc.ui)
222
+ } else if (config.ui) {
159
223
  initUIConfig(config.ui)
160
224
  }
161
225
 
162
- if (config.canvas) {
226
+ if (uc.canvas && Object.keys(uc.canvas).length > 0) {
227
+ initCanvasConfig(uc.canvas)
228
+ } else if (config.canvas) {
163
229
  initCanvasConfig(config.canvas)
164
230
  }
165
231
 
166
232
  // Load and merge command palette config.
167
- // Core defaults come from commandpalette.config.json (bundled).
168
- // Client can provide overrides via config.commandPalette (legacy) or a commandpalette.config.json at repo root.
169
- const defaultCmdPaletteConfig = (await import('../commandpalette.config.json')).default
170
- if (config.commandPalette) {
171
- const { deepMerge: deepMergeCp } = await import('./loader.js')
172
- initCommandPaletteConfig(deepMergeCp(defaultCmdPaletteConfig, config.commandPalette))
233
+ // If the unified store has commandPalette data, use it directly.
234
+ // Otherwise fall back to legacy merging with bundled defaults.
235
+ const ucCmdPalette = uc.commandPalette
236
+ if (ucCmdPalette && Object.keys(ucCmdPalette).length > 0) {
237
+ initCommandPaletteConfig(ucCmdPalette)
173
238
  } else {
174
- initCommandPaletteConfig({ ...defaultCmdPaletteConfig })
239
+ const defaultCmdPaletteConfig = (await import('../commandpalette.config.json')).default
240
+ if (config.commandPalette) {
241
+ const merged = { ...defaultCmdPaletteConfig, ...config.commandPalette }
242
+ if (config.commandPalette.sections && defaultCmdPaletteConfig.sections) {
243
+ const clientIds = new Set(config.commandPalette.sections.map(s => s.id))
244
+ const preserved = defaultCmdPaletteConfig.sections.filter(s => !clientIds.has(s.id))
245
+ merged.sections = [...config.commandPalette.sections, ...preserved]
246
+ }
247
+ initCommandPaletteConfig(merged)
248
+ } else {
249
+ initCommandPaletteConfig({ ...defaultCmdPaletteConfig })
250
+ }
175
251
  }
176
252
 
177
253
  // Initialize customer mode config
178
- if (config.customerMode) {
254
+ if (uc.customerMode && Object.keys(uc.customerMode).length > 0) {
255
+ initCustomerModeConfig(uc.customerMode)
256
+ } else if (config.customerMode) {
179
257
  initCustomerModeConfig(config.customerMode)
180
258
  }
181
259
 
182
260
  // Initialize comments config (framework-agnostic)
183
- if (config.comments) {
184
- initCommentsConfig(config, { basePath })
261
+ const commentsConfig = uc.comments && Object.keys(uc.comments).length > 0 ? uc.comments : config.comments
262
+ if (commentsConfig) {
263
+ initCommentsConfig({ ...config, comments: commentsConfig }, { basePath })
185
264
  }
186
265
 
187
266
  // Inject compiled UI styles (await to prevent late restyle / FOUC)
188
267
  await injectUIStyles()
189
268
 
190
- // Load and merge toolbar config.
191
- // Core defaults come from toolbar.config.json (bundled).
192
- // Client can provide overrides via config.toolbar or a toolbar.config.json at repo root.
269
+ // Load toolbar config from the unified store.
270
+ // The unified store already has core defaults merged with client overrides.
271
+ // Fall back to legacy merging if unified store wasn't seeded.
193
272
  const { deepMerge } = await import('./loader.js')
194
- const defaultConfig = (await import('../toolbar.config.json')).default
195
- let toolbarConfig = config.toolbar
196
- ? deepMerge(defaultConfig, config.toolbar)
197
- : { ...defaultConfig }
198
-
199
- // Inject repository URL from storyboard.config.json into the toolbar config
200
- if (config.repository?.owner && config.repository?.name) {
201
- const repoUrl = `https://github.com/${config.repository.owner}/${config.repository.name}`
273
+ let toolbarConfig = uc.toolbar && Object.keys(uc.toolbar).length > 0
274
+ ? { ...uc.toolbar }
275
+ : null
276
+
277
+ if (!toolbarConfig) {
278
+ // Legacy path: unified store not seeded, merge manually
279
+ const defaultConfig = (await import('../toolbar.config.json')).default
280
+ const clientOverrides = consumeClientToolbarOverrides()
281
+ const explicitToolbar = config.toolbar
282
+
283
+ if (explicitToolbar && clientOverrides) {
284
+ toolbarConfig = deepMerge(deepMerge(defaultConfig, clientOverrides), explicitToolbar)
285
+ } else if (explicitToolbar) {
286
+ toolbarConfig = deepMerge(defaultConfig, explicitToolbar)
287
+ } else if (clientOverrides) {
288
+ toolbarConfig = deepMerge(defaultConfig, clientOverrides)
289
+ } else {
290
+ toolbarConfig = { ...defaultConfig }
291
+ }
292
+ }
293
+
294
+ // Inject repository URL into the toolbar config
295
+ const repo = uc.repository || config.repository
296
+ if (repo?.owner && repo?.name) {
297
+ const repoUrl = `https://github.com/${repo.owner}/${repo.name}`
202
298
 
203
299
  // New tools schema
204
300
  if (toolbarConfig.tools?.repository) {
@@ -55,6 +55,9 @@ export function buildCommandItems(mode, basePath) {
55
55
  if (state === 'disabled' || state === 'hidden') continue
56
56
  }
57
57
 
58
+ // Skip items hidden from search
59
+ if (action.hideFromCommandPaletteSearch) continue
60
+
58
61
  if (action.type === 'submenu') {
59
62
  // Flatten submenu children into individual items
60
63
  const children = getActionChildren(action.id)
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, vi } from 'vitest'
2
2
 
3
- // Mock loader and viewfinder before importing providers
3
+ // Mock loader and workspace before importing providers
4
4
  vi.mock('./loader.js', () => ({
5
5
  listStories: vi.fn(() => ['button', 'card']),
6
6
  getStoryData: vi.fn((name) => ({
@@ -34,7 +34,7 @@ vi.mock('./loader.js', () => ({
34
34
 
35
35
  vi.mock('./commandActions.js', () => ({
36
36
  getActionsForMode: vi.fn(() => [
37
- { id: 'core/viewfinder', label: 'Go to Viewfinder', type: 'link', url: '/', toolKey: 'viewfinder' },
37
+ { id: 'core/workspace', label: 'Go to Workspace', type: 'link', url: '/', toolKey: 'workspace' },
38
38
  { id: 'core/docs', label: 'Documentation', type: 'default', toolKey: 'docs' },
39
39
  { id: 'core/devtools', label: 'DevTools', type: 'submenu', toolKey: 'devtools' },
40
40
  { type: 'header', label: 'Command Menu' },
@@ -16,9 +16,11 @@ import http from 'node:http'
16
16
  import { spawn } from 'node:child_process'
17
17
  import { existsSync, readFileSync, mkdirSync } from 'node:fs'
18
18
  import { resolve, join, dirname } from 'node:path'
19
- import { getPort, portsFilePath, repoRoot, worktreeDir, listWorktrees } from '../worktree/port.js'
19
+ import { getPort, releasePort, repoRoot, worktreeDir, listWorktrees } from '../worktree/port.js'
20
20
  import { generateCaddyfile, generateRouteConfig, upsertCaddyRoute, isCaddyRunning, reloadCaddy, readDevDomain } from '../cli/proxy.js'
21
21
  import { compactAll } from '../canvas/compact.js'
22
+ import { register, unregister, generateId, list, findByWorktree } from '../worktree/serverRegistry.js'
23
+ import { createDevLogger } from '../logger/devLogger.js'
22
24
 
23
25
  const SERVER_PORT_BASE = 4100
24
26
 
@@ -46,6 +48,12 @@ const processes = new Map()
46
48
  /** Route handlers: prefix → handler function */
47
49
  const routeHandlers = new Map()
48
50
 
51
+ // ─── Dev Logger ───
52
+
53
+ let _currentBranch = null
54
+ try { _currentBranch = (await import('node:child_process')).execSync('git branch --show-current', { encoding: 'utf8', cwd: repoRoot() }).trim() } catch {}
55
+ const devLogger = createDevLogger({ root: repoRoot(), devDomain: DEV_DOMAIN.replace('.localhost', ''), branch: _currentBranch })
56
+
49
57
  // ─── JSON helpers ───
50
58
 
51
59
  function sendJson(res, status, data) {
@@ -54,6 +62,21 @@ function sendJson(res, status, data) {
54
62
  res.end(body)
55
63
  }
56
64
 
65
+ function sendJsonLogged(res, status, data) {
66
+ sendJson(res, status, data)
67
+ if (status >= 400) {
68
+ const ctx = res.__sbLogCtx || {}
69
+ devLogger.logResponse({
70
+ status,
71
+ method: ctx.method || 'UNKNOWN',
72
+ url: ctx.url || '',
73
+ route: ctx.route || null,
74
+ subRoute: ctx.subRoute || null,
75
+ error: data?.error || null,
76
+ })
77
+ }
78
+ }
79
+
57
80
  async function parseBody(req) {
58
81
  const chunks = []
59
82
  for await (const chunk of req) chunks.push(chunk)
@@ -117,15 +140,23 @@ function spawnVite(branch) {
117
140
  stdio: ['ignore', 'pipe', 'pipe'],
118
141
  })
119
142
 
120
- const entry = { child, port, status: 'starting', cwd, branch }
143
+ const entry = { child, port, status: 'starting', cwd, branch, serverId: generateId() }
121
144
  processes.set(branch, entry)
122
145
 
146
+ // Register in persistent server registry
147
+ register({ id: entry.serverId, worktree: branch, pid: child.pid, port, background: true })
148
+
123
149
  // Detect ready state from stdout
124
150
  child.stdout.on('data', (data) => {
125
151
  const text = data.toString()
126
152
  const portMatch = text.match(/localhost:(\d+)/)
127
153
  if (portMatch) {
128
- entry.port = Number(portMatch[1])
154
+ const actualPort = Number(portMatch[1])
155
+ if (actualPort !== entry.port) {
156
+ entry.port = actualPort
157
+ // Re-register with the actual port Vite bound to
158
+ register({ id: entry.serverId, worktree: branch, pid: child.pid, port: actualPort, background: true })
159
+ }
129
160
  }
130
161
  if (text.includes('ready in')) {
131
162
  entry.status = 'ready'
@@ -138,6 +169,8 @@ function spawnVite(branch) {
138
169
  child.on('exit', (code) => {
139
170
  entry.status = 'stopped'
140
171
  processes.delete(branch)
172
+ unregister(entry.serverId)
173
+ releasePort(branch)
141
174
  })
142
175
 
143
176
  return entry
@@ -167,13 +200,13 @@ function registerCaddyRoute(branch, port) {
167
200
  // POST /_storyboard/switch-branch
168
201
  routeHandlers.set('switch-branch', async (req, res, ctx) => {
169
202
  if (ctx.method !== 'POST') {
170
- sendJson(res, 405, { error: 'POST required' })
203
+ sendJsonLogged(res, 405, { error: 'POST required' })
171
204
  return
172
205
  }
173
206
 
174
207
  const { branch } = ctx.body
175
208
  if (!branch) {
176
- sendJson(res, 400, { error: 'branch is required' })
209
+ sendJsonLogged(res, 400, { error: 'branch is required' })
177
210
  return
178
211
  }
179
212
 
@@ -188,59 +221,76 @@ routeHandlers.set('switch-branch', async (req, res, ctx) => {
188
221
  if (existing && existing.status === 'ready') {
189
222
  const alive = await isPortReady(existing.port)
190
223
  if (alive) {
191
- sendJson(res, 200, { url: targetUrl, status: 'already_running' })
224
+ sendJsonLogged(res, 200, { url: targetUrl, status: 'already_running' })
192
225
  return
193
226
  }
194
227
  // Stale entry — remove it
195
228
  processes.delete(branch)
196
229
  }
197
230
 
198
- // Check if Vite is already running on the assigned port (started outside this server)
231
+ // Check if Vite is already running in this server's process map
199
232
  const port = getPort(branch)
200
- if (await isPortReady(port)) {
201
- registerCaddyRoute(branch, port)
202
- sendJson(res, 200, { url: targetUrl, status: 'already_running' })
203
- return
233
+ const existingInRegistry = findByWorktree(branch)
234
+ if (existingInRegistry.length > 0) {
235
+ const latest = existingInRegistry.reduce((a, b) =>
236
+ (a.startedAt || '') >= (b.startedAt || '') ? a : b
237
+ )
238
+ if (await isPortReady(latest.port)) {
239
+ registerCaddyRoute(branch, latest.port)
240
+ sendJsonLogged(res, 200, { url: targetUrl, status: 'already_running' })
241
+ return
242
+ }
204
243
  }
205
244
 
206
245
  // Check worktree exists
207
246
  const cwd = resolveWorktreeCwd(branch)
208
247
  if (!cwd) {
209
- sendJson(res, 404, { error: `Worktree not found: ${branch}. Create it with: npx storyboard dev ${branch}` })
248
+ sendJsonLogged(res, 404, { error: `Worktree not found: ${branch}. Create it with: npx storyboard dev ${branch}` })
210
249
  return
211
250
  }
212
251
 
213
252
  // Spawn Vite
214
253
  const entry = spawnVite(branch)
215
254
 
216
- // Wait for ready
217
- const ready = await waitForPort(entry.port, HEALTH_TIMEOUT)
255
+ // Wait for Vite to report ready via stdout (not TCP polling, which
256
+ // can false-positive on occupied ports from other processes)
257
+ const ready = await (async () => {
258
+ const start = Date.now()
259
+ while (Date.now() - start < HEALTH_TIMEOUT) {
260
+ if (entry.status === 'ready') return true
261
+ await new Promise(r => setTimeout(r, 300))
262
+ }
263
+ return false
264
+ })()
218
265
  if (!ready) {
219
266
  stopVite(branch)
220
- sendJson(res, 504, { error: `Vite server for ${branch} did not become ready in time` })
267
+ sendJsonLogged(res, 504, { error: `Vite server for ${branch} did not become ready in time` })
221
268
  return
222
269
  }
223
270
 
224
- sendJson(res, 200, { url: targetUrl, status: 'started' })
271
+ sendJsonLogged(res, 200, { url: targetUrl, status: 'started' })
225
272
  } catch (err) {
226
- sendJson(res, 500, { error: err.message })
273
+ sendJsonLogged(res, 500, { error: err.message })
227
274
  }
228
275
  })
229
276
 
230
277
  // GET /_storyboard/worktrees
231
278
  routeHandlers.set('worktrees', async (req, res) => {
232
279
  try {
233
- const pf = portsFilePath()
234
- if (!existsSync(pf)) { sendJson(res, 200, []); return }
235
- const ports = JSON.parse(readFileSync(pf, 'utf8'))
236
- const branches = Object.keys(ports).map(name => ({
237
- branch: name,
238
- folder: name === 'main' ? '' : `branch--${name}/`,
239
- port: ports[name],
240
- running: processes.has(name) ? processes.get(name).status : null,
280
+ // Use the server registry (live processes) instead of stale ports.json
281
+ const servers = list()
282
+ const branches = servers.map(srv => ({
283
+ branch: srv.worktree,
284
+ folder: srv.worktree === 'main' ? '' : `branch--${srv.worktree}/`,
285
+ port: srv.port,
286
+ running: processes.has(srv.worktree) ? processes.get(srv.worktree).status : 'background',
241
287
  }))
242
- sendJson(res, 200, branches)
243
- } catch { sendJson(res, 200, []) }
288
+ // Always include main even if no server is registered for it
289
+ if (!branches.some(b => b.branch === 'main')) {
290
+ branches.unshift({ branch: 'main', folder: '', port: 1234, running: null })
291
+ }
292
+ sendJsonLogged(res, 200, branches)
293
+ } catch { sendJsonLogged(res, 200, []) }
244
294
  })
245
295
 
246
296
  // GET /_storyboard/server/status
@@ -250,10 +300,10 @@ routeHandlers.set('server', async (req, res, ctx) => {
250
300
  for (const [branch, entry] of processes) {
251
301
  active.push({ branch, port: entry.port, status: entry.status })
252
302
  }
253
- sendJson(res, 200, { active, serverPort: SERVER_PORT })
303
+ sendJsonLogged(res, 200, { active, serverPort: SERVER_PORT })
254
304
  return
255
305
  }
256
- sendJson(res, 404, { error: 'Unknown server route' })
306
+ sendJsonLogged(res, 404, { error: 'Unknown server route' })
257
307
  })
258
308
 
259
309
  // ─── HTTP Server ───
@@ -280,6 +330,9 @@ const server = http.createServer(async (req, res) => {
280
330
  const prefix = slashIdx === -1 ? after : after.slice(0, slashIdx)
281
331
  const restPath = slashIdx === -1 ? '/' : after.slice(slashIdx)
282
332
 
333
+ // Attach route context for the logging sendJson wrapper
334
+ res.__sbLogCtx = { method: req.method, url: req.url, route: prefix, subRoute: restPath }
335
+
283
336
  const handler = routeHandlers.get(prefix)
284
337
  if (handler) {
285
338
  let body = {}
@@ -289,8 +342,7 @@ const server = http.createServer(async (req, res) => {
289
342
  try {
290
343
  await handler(req, res, { body, path: restPath, method: req.method })
291
344
  } catch (err) {
292
- console.error(`[storyboard-server] Error in ${prefix}:`, err)
293
- sendJson(res, 500, { error: err.message })
345
+ sendJsonLogged(res, 500, { error: err.message })
294
346
  }
295
347
  return
296
348
  }
@@ -298,18 +350,28 @@ const server = http.createServer(async (req, res) => {
298
350
 
299
351
  // Health check
300
352
  if (pathname === '/health') {
301
- sendJson(res, 200, { ok: true, devDomain: DEV_DOMAIN })
353
+ sendJsonLogged(res, 200, { ok: true, devDomain: DEV_DOMAIN })
302
354
  return
303
355
  }
304
356
 
305
- sendJson(res, 404, { error: 'Not found' })
357
+ // Set context for catch-all 404
358
+ res.__sbLogCtx = { method: req.method, url: req.url, route: null, subRoute: null }
359
+ sendJsonLogged(res, 404, { error: 'Not found' })
306
360
  })
307
361
 
308
362
  export function startServer(port = SERVER_PORT) {
309
- server.listen(port, () => {
310
- // port logged by CLI via onReady; suppress here to avoid duplicate output
363
+ return new Promise((resolve, reject) => {
364
+ server.on('error', (err) => {
365
+ if (err.code === 'EADDRINUSE') {
366
+ reject(Object.assign(new Error(`Port ${port} already in use`), { code: 'EADDRINUSE' }))
367
+ return
368
+ }
369
+ reject(err)
370
+ })
371
+ server.listen(port, () => {
372
+ resolve(server)
373
+ })
311
374
  })
312
- return server
313
375
  }
314
376
 
315
377
  /** Public API for spawning Vite from CLI (with stdout piping) */