@dfosco/storyboard 0.7.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/dist/storyboard-ui.css +1 -1
  2. package/dist/storyboard-ui.js +14128 -10953
  3. package/dist/storyboard-ui.js.map +1 -1
  4. package/dist/tailwind.css +1 -1
  5. package/dist/tiny-canvas.css +1 -1
  6. package/package.json +138 -128
  7. package/scaffold/AGENTS.md +9 -7
  8. package/scaffold/README.md +18 -0
  9. package/scaffold/deploy.yml +21 -0
  10. package/scaffold/gitignore +8 -14
  11. package/scaffold/preview.yml +40 -0
  12. package/scaffold/{manifest.json → scaffold.config.json} +73 -3
  13. package/scaffold/scripts/aggregate-artifacts.mjs +257 -0
  14. package/scaffold/skills/canvas/SKILL.md +1 -1
  15. package/scaffold/skills/migrate-0.5.0/SKILL.md +1 -1
  16. package/scaffold/skills/ship/SKILL.md +2 -2
  17. package/scaffold/skills/worktree/SKILL.md +1 -1
  18. package/scaffold/src/_prototype.jsx +17 -0
  19. package/scaffold/src/_story.jsx +13 -0
  20. package/scaffold/src/library/README.md +34 -0
  21. package/scaffold/src/library/_app.jsx +41 -0
  22. package/scaffold/src/library/_app.module.css +2 -0
  23. package/scaffold/src/library/create.jsx +10 -0
  24. package/scaffold/src/library/index.jsx +14 -0
  25. package/scaffold/src/library/mount.jsx +51 -0
  26. package/scaffold/src/library/prototypes-entry.jsx +57 -0
  27. package/scaffold/src/library/routes.jsx +205 -0
  28. package/scaffold/src/library/viewfinder.jsx +13 -0
  29. package/scaffold/src/library/workspace.jsx +17 -0
  30. package/scripts/aggregate-artifacts.mjs +257 -0
  31. package/src/canvas/style.css +16 -16
  32. package/src/core/artifact/operations.js +54 -15
  33. package/src/core/artifact/schemas/canvas.schema.json +7 -1
  34. package/src/core/autosync/server.test.js +1 -1
  35. package/src/core/canvas/hot-pool.js +21 -33
  36. package/src/core/canvas/materializer.js +39 -13
  37. package/src/core/canvas/selectedWidgets.js +1 -1
  38. package/src/core/canvas/server.js +376 -100
  39. package/src/core/canvas/server.test.js +472 -2
  40. package/src/core/canvas/terminal-server.js +144 -57
  41. package/src/core/canvas/undoRedo.js +174 -0
  42. package/src/core/canvas/undoRedo.test.js +250 -0
  43. package/src/core/cli/artifact.js +2 -0
  44. package/src/core/cli/buildManifest.js +34 -0
  45. package/src/core/cli/canvasRedo.js +44 -0
  46. package/src/core/cli/canvasUndo.js +40 -0
  47. package/src/core/cli/create.js +151 -23
  48. package/src/core/cli/dev.js +23 -35
  49. package/src/core/cli/index.js +21 -0
  50. package/src/core/cli/schemas.js +8 -4
  51. package/src/core/cli/serverUrl.js +24 -0
  52. package/src/core/cli/setup.js +14 -25
  53. package/src/core/cli/terminal-welcome.js +12 -6
  54. package/src/core/cli/updateVersion.js +42 -2
  55. package/src/core/cli/workleaf.js +140 -0
  56. package/src/core/comments/api.js +1 -1
  57. package/src/core/comments/queries.js +3 -0
  58. package/src/core/comments/ui/CommentWindow.jsx +3 -2
  59. package/src/core/comments/ui/comment-layout.css +14 -14
  60. package/src/core/data/artifactIndex.js +235 -0
  61. package/src/core/data/artifactIndex.test.js +203 -0
  62. package/src/core/data/artifactManifest.js +290 -0
  63. package/src/core/data/artifactManifest.test.js +207 -0
  64. package/src/core/data/viewfinder.js +47 -7
  65. package/src/core/index.js +24 -1
  66. package/src/core/inspector/highlighter.js +1 -1
  67. package/src/core/inspector/mouseMode.js +2 -2
  68. package/src/core/lib/components/ui/avatar/avatar-fallback.jsx +2 -2
  69. package/src/core/lib/components/ui/avatar/avatar-image.jsx +2 -2
  70. package/src/core/lib/components/ui/avatar/avatar.jsx +2 -2
  71. package/src/core/lib/components/ui/checkbox/checkbox.jsx +12 -9
  72. package/src/core/lib/components/ui/dialog/dialog-content.jsx +45 -31
  73. package/src/core/lib/components/ui/dialog/dialog-trigger.jsx +9 -5
  74. package/src/core/lib/components/ui/dialog/dialog.jsx +12 -3
  75. package/src/core/lib/components/ui/dialog/dialog.module.css +118 -0
  76. package/src/core/lib/components/ui/dialog/index.js +4 -34
  77. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-group.jsx +3 -3
  78. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-checkbox-item.jsx +19 -9
  79. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-content.jsx +8 -8
  80. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group-heading.jsx +2 -2
  81. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-group.jsx +2 -2
  82. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-item.jsx +15 -4
  83. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-portal.jsx +2 -2
  84. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-group.jsx +2 -2
  85. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-radio-item.jsx +14 -7
  86. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-separator.jsx +2 -2
  87. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.jsx +8 -4
  88. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub-trigger.jsx +4 -4
  89. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-sub.jsx +2 -2
  90. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu-trigger.jsx +8 -2
  91. package/src/core/lib/components/ui/dropdown-menu/dropdown-menu.jsx +2 -2
  92. package/src/core/lib/components/ui/label/label.jsx +1 -2
  93. package/src/core/lib/components/ui/panel/panel-close.jsx +7 -4
  94. package/src/core/lib/components/ui/panel/panel-content.jsx +7 -9
  95. package/src/core/lib/components/ui/panel/panel-title.jsx +3 -3
  96. package/src/core/lib/components/ui/panel/panel.jsx +11 -2
  97. package/src/core/lib/components/ui/separator/separator.jsx +6 -4
  98. package/src/core/lib/components/ui/tooltip/tooltip-content.jsx +7 -6
  99. package/src/core/lib/components/ui/tooltip/tooltip-trigger.jsx +13 -6
  100. package/src/core/lib/components/ui/tooltip/tooltip.jsx +5 -5
  101. package/src/core/modes/modes.css +4 -4
  102. package/src/core/mountStoryboardCore.js +287 -83
  103. package/src/core/mountStoryboardCore.test.js +198 -0
  104. package/src/core/rename-watcher/config.json +1 -1
  105. package/src/core/rename-watcher/watcher.js +330 -37
  106. package/src/core/rename-watcher/watcher.test.js +253 -0
  107. package/src/core/scaffold/fragments.js +287 -0
  108. package/src/core/scaffold/fragments.test.js +401 -0
  109. package/src/core/scaffold/index.js +291 -0
  110. package/src/core/scaffold/migrateLegacyGitignore.js +157 -0
  111. package/src/core/scaffold/scaffold-integration.test.js +247 -0
  112. package/src/core/scaffold.js +4 -97
  113. package/src/core/stores/canvasInteractionStore.js +120 -0
  114. package/src/core/stores/canvasInteractionStore.test.js +126 -0
  115. package/src/core/stores/configSchema.js +64 -8
  116. package/src/core/stores/configSchema.test.js +3 -0
  117. package/src/core/stores/customerModeConfig.js +24 -3
  118. package/src/core/stores/customerModeConfig.test.js +46 -0
  119. package/src/core/stores/presentationStore.js +118 -0
  120. package/src/core/stores/presentationStore.test.js +114 -0
  121. package/src/core/stores/recentArtifacts.js +49 -2
  122. package/src/core/stores/recentArtifacts.test.js +59 -6
  123. package/src/core/stores/themeBootstrap.js +264 -0
  124. package/src/core/stores/themeBootstrap.test.js +211 -0
  125. package/src/core/stores/themeStore.test.js +154 -0
  126. package/src/core/stores/themeStore.ts +235 -220
  127. package/src/core/stores/uiConfig.js +10 -2
  128. package/src/core/styles/generateThemesCss.js +63 -0
  129. package/src/core/styles/tailwind.css +150 -94
  130. package/src/core/styles/workspace-chrome.css +13 -0
  131. package/src/core/tools/handlers/paletteTheme.js +11 -8
  132. package/src/core/ui/ActionMenuButton.jsx +2 -1
  133. package/src/core/ui/AutosyncMenuButton.css +7 -7
  134. package/src/core/ui/BranchSelect.module.css +5 -5
  135. package/src/core/ui/CanvasCreateMenu.jsx +1 -19
  136. package/src/core/ui/ChromeSlot.jsx +92 -0
  137. package/src/core/ui/ChromeSlot.test.jsx +140 -0
  138. package/src/core/ui/CoreUIBar.css +5 -5
  139. package/src/core/ui/CoreUIBar.jsx +170 -142
  140. package/src/core/ui/InspectorPanel.css +5 -5
  141. package/src/core/ui/InspectorPanel.jsx +9 -9
  142. package/src/core/ui/PwaInstallBanner.css +5 -5
  143. package/src/core/ui/PwaInstallBanner.jsx +5 -5
  144. package/src/core/ui/ThemeMenuButton.jsx +45 -30
  145. package/src/core/ui/core-ui-colors.css +28 -66
  146. package/src/core/ui/sidepanel.css +12 -12
  147. package/src/core/vite/server-plugin.js +13 -0
  148. package/src/core/workleaf/cleanup.js +102 -0
  149. package/src/core/workleaf/create.js +199 -0
  150. package/src/core/workleaf/delete.js +101 -0
  151. package/src/core/workleaf/list.js +57 -0
  152. package/src/core/workleaf/merge.js +238 -0
  153. package/src/core/workleaf/paths.js +146 -0
  154. package/src/core/workleaf/paths.test.js +230 -0
  155. package/src/core/workshop/features/createCanvas/CreateCanvasForm.jsx +130 -34
  156. package/src/core/workshop/features/createFlow/server.js +5 -1
  157. package/src/core/workshop/features/createPrototype/server.js +5 -1
  158. package/src/core/workshop/features/templateIndex.js +28 -1
  159. package/src/core/worktree/port.js +48 -5
  160. package/src/core/worktree/port.test.js +42 -0
  161. package/src/internals/ArtifactForm/ArtifactForm.jsx +173 -112
  162. package/src/internals/ArtifactForm/ArtifactForm.module.css +51 -69
  163. package/src/internals/ArtifactForm/artifactSchemas.js +40 -5
  164. package/src/internals/AuthModal/AuthModal.module.css +34 -34
  165. package/src/internals/BaseUiForm/BaseUiForm.jsx +293 -0
  166. package/src/internals/BaseUiForm/BaseUiForm.module.css +542 -0
  167. package/src/internals/BranchBar/BranchBar.module.css +4 -4
  168. package/src/internals/BranchesDropdown/BranchesDropdown.jsx +69 -0
  169. package/src/internals/BranchesDropdown/BranchesDropdown.module.css +84 -0
  170. package/src/internals/CommandPalette/CommandPalette.jsx +41 -29
  171. package/src/internals/CommandPalette/CreateArtifactForm.jsx +150 -0
  172. package/src/internals/CommandPalette/CreateDialog.jsx +20 -110
  173. package/src/internals/CommandPalette/WidgetArtifactDialog.css +16 -16
  174. package/src/internals/CommandPalette/WidgetArtifactDialog.jsx +1 -1
  175. package/src/internals/CommandPalette/command-palette.css +14 -14
  176. package/src/internals/CommandPalette/createHelpers.js +30 -0
  177. package/src/internals/CreatePage/CreatePage.jsx +57 -0
  178. package/src/internals/CreatePage/CreatePage.module.css +34 -0
  179. package/src/internals/Dialog.jsx +51 -0
  180. package/src/internals/FlowError.module.css +5 -5
  181. package/src/internals/PrototypeErrorBoundary.module.css +15 -15
  182. package/src/internals/SimpleWorkspace/SimpleWorkspace.jsx +534 -0
  183. package/src/internals/SimpleWorkspace/SimpleWorkspace.module.css +323 -0
  184. package/src/internals/Tooltip.jsx +39 -0
  185. package/src/internals/Viewfinder.jsx +229 -64
  186. package/src/internals/Viewfinder.module.css +253 -182
  187. package/src/internals/canvas/CanvasControls.module.css +11 -11
  188. package/src/internals/canvas/CanvasPage.bridge.test.jsx +11 -37
  189. package/src/internals/canvas/CanvasPage.jsx +358 -217
  190. package/src/internals/canvas/CanvasPage.module.css +20 -19
  191. package/src/internals/canvas/CanvasPage.multiselect.test.jsx +39 -26
  192. package/src/internals/canvas/CanvasToolbar.module.css +11 -11
  193. package/src/internals/canvas/ConnectorLayer.jsx +39 -2
  194. package/src/internals/canvas/ConnectorLayer.module.css +10 -10
  195. package/src/internals/canvas/PageSelector.jsx +15 -1
  196. package/src/internals/canvas/PageSelector.module.css +41 -27
  197. package/src/internals/canvas/canvasApi.js +40 -5
  198. package/src/internals/canvas/canvasTheme.js +22 -111
  199. package/src/internals/canvas/isolation/createIsolationMiddleware.js +68 -0
  200. package/src/internals/canvas/isolation/mountIsolationApp.jsx +88 -0
  201. package/src/internals/canvas/isolation/prototypeRoutes.jsx +140 -0
  202. package/src/internals/canvas/isolation/prototypesEntry.jsx +28 -0
  203. package/src/internals/canvas/isolation/storiesEntry.jsx +20 -0
  204. package/src/internals/canvas/isolation/storyRoutes.jsx +55 -0
  205. package/src/internals/canvas/useUndoRedo.js +75 -60
  206. package/src/internals/canvas/useUndoRedo.test.js +46 -178
  207. package/src/internals/canvas/widgets/CodePenEmbed.jsx +16 -36
  208. package/src/internals/canvas/widgets/CodePenEmbed.module.css +12 -12
  209. package/src/internals/canvas/widgets/CropOverlay.module.css +23 -23
  210. package/src/internals/canvas/widgets/ExpandedPane.module.css +8 -8
  211. package/src/internals/canvas/widgets/ExpandedPaneTopBar.jsx +1 -1
  212. package/src/internals/canvas/widgets/FigmaEmbed.jsx +15 -36
  213. package/src/internals/canvas/widgets/FigmaEmbed.module.css +15 -15
  214. package/src/internals/canvas/widgets/FrozenTerminalOverlay.jsx +30 -21
  215. package/src/internals/canvas/widgets/FrozenTerminalOverlay.module.css +5 -5
  216. package/src/internals/canvas/widgets/ImageWidget.jsx +7 -2
  217. package/src/internals/canvas/widgets/ImageWidget.module.css +3 -3
  218. package/src/internals/canvas/widgets/LinkPreview.module.css +79 -80
  219. package/src/internals/canvas/widgets/MarkdownBlock.module.css +31 -31
  220. package/src/internals/canvas/widgets/PromptWidget.jsx +2 -16
  221. package/src/internals/canvas/widgets/PromptWidget.module.css +33 -33
  222. package/src/internals/canvas/widgets/PrototypeEmbed.jsx +92 -17
  223. package/src/internals/canvas/widgets/PrototypeEmbed.module.css +58 -56
  224. package/src/internals/canvas/widgets/PrototypeEmbed.test.jsx +118 -4
  225. package/src/internals/canvas/widgets/ResizeHandle.module.css +4 -4
  226. package/src/internals/canvas/widgets/StorySetWidget.jsx +53 -25
  227. package/src/internals/canvas/widgets/StoryWidget.jsx +71 -56
  228. package/src/internals/canvas/widgets/StoryWidget.module.css +22 -20
  229. package/src/internals/canvas/widgets/TerminalReadWidget.jsx +13 -15
  230. package/src/internals/canvas/widgets/TerminalReadWidget.module.css +4 -4
  231. package/src/internals/canvas/widgets/TerminalWidget.jsx +56 -45
  232. package/src/internals/canvas/widgets/TerminalWidget.module.css +10 -10
  233. package/src/internals/canvas/widgets/TilesWidget.module.css +17 -17
  234. package/src/internals/canvas/widgets/WidgetChrome.jsx +1 -1
  235. package/src/internals/canvas/widgets/WidgetChrome.module.css +76 -61
  236. package/src/internals/canvas/widgets/WidgetWrapper.module.css +2 -2
  237. package/src/internals/canvas/widgets/embedOverlay.module.css +14 -4
  238. package/src/internals/canvas/widgets/embedTheme.js +12 -104
  239. package/src/internals/canvas/widgets/expandUtils.js +19 -19
  240. package/src/internals/canvas/widgets/normalizeLegacyEmbedSrc.js +74 -0
  241. package/src/internals/canvas/widgets/widgetConfig.js +5 -5
  242. package/src/internals/context.jsx +22 -1
  243. package/src/internals/context.test.jsx +47 -0
  244. package/src/internals/hooks/useArtifactIndex.js +31 -0
  245. package/src/internals/hooks/useArtifactIndex.test.js +45 -0
  246. package/src/internals/hooks/usePrototypeReloadGuard.js +14 -8
  247. package/src/internals/index.js +6 -0
  248. package/src/internals/loadConsumerWrapper.js +60 -0
  249. package/src/internals/story/ComponentSetPage.jsx +75 -34
  250. package/src/internals/story/ComponentSetPage.module.css +14 -14
  251. package/src/internals/story/StoryPage.jsx +16 -25
  252. package/src/internals/story/StoryPage.module.css +2 -2
  253. package/src/internals/vite/data-plugin.js +317 -149
  254. package/src/internals/vite/data-plugin.test.js +11 -11
  255. package/{scaffold/storyboard.config.json → storyboard.config.json} +25 -0
  256. package/terminal.config.json +1 -1
  257. package/src/core/lib/components/ui/card/card-action.jsx +0 -11
  258. package/src/core/lib/components/ui/card/card-content.jsx +0 -11
  259. package/src/core/lib/components/ui/card/card-description.jsx +0 -11
  260. package/src/core/lib/components/ui/card/card-footer.jsx +0 -11
  261. package/src/core/lib/components/ui/card/card-header.jsx +0 -19
  262. package/src/core/lib/components/ui/card/card-title.jsx +0 -11
  263. package/src/core/lib/components/ui/card/card.jsx +0 -17
  264. package/src/core/lib/components/ui/card/index.js +0 -25
  265. package/src/core/lib/components/ui/collapsible/collapsible-content.jsx +0 -7
  266. package/src/core/lib/components/ui/collapsible/collapsible-trigger.jsx +0 -7
  267. package/src/core/lib/components/ui/collapsible/collapsible.jsx +0 -7
  268. package/src/core/lib/components/ui/collapsible/index.js +0 -13
  269. package/src/core/lib/components/ui/dialog/dialog-close.jsx +0 -7
  270. package/src/core/lib/components/ui/dialog/dialog-description.jsx +0 -15
  271. package/src/core/lib/components/ui/dialog/dialog-footer.jsx +0 -23
  272. package/src/core/lib/components/ui/dialog/dialog-header.jsx +0 -11
  273. package/src/core/lib/components/ui/dialog/dialog-overlay.jsx +0 -15
  274. package/src/core/lib/components/ui/dialog/dialog-portal.jsx +0 -4
  275. package/src/core/lib/components/ui/dialog/dialog-title.jsx +0 -15
  276. package/src/core/lib/components/ui/popover/index.js +0 -28
  277. package/src/core/lib/components/ui/popover/popover-close.jsx +0 -7
  278. package/src/core/lib/components/ui/popover/popover-content.jsx +0 -22
  279. package/src/core/lib/components/ui/popover/popover-description.jsx +0 -11
  280. package/src/core/lib/components/ui/popover/popover-header.jsx +0 -11
  281. package/src/core/lib/components/ui/popover/popover-portal.jsx +0 -4
  282. package/src/core/lib/components/ui/popover/popover-title.jsx +0 -11
  283. package/src/core/lib/components/ui/popover/popover-trigger.jsx +0 -8
  284. package/src/core/lib/components/ui/popover/popover.jsx +0 -4
  285. package/src/core/lib/components/ui/select/index.js +0 -37
  286. package/src/core/lib/components/ui/select/select-content.jsx +0 -30
  287. package/src/core/lib/components/ui/select/select-group-heading.jsx +0 -17
  288. package/src/core/lib/components/ui/select/select-group.jsx +0 -15
  289. package/src/core/lib/components/ui/select/select-item.jsx +0 -26
  290. package/src/core/lib/components/ui/select/select-label.jsx +0 -11
  291. package/src/core/lib/components/ui/select/select-portal.jsx +0 -4
  292. package/src/core/lib/components/ui/select/select-scroll-down-button.jsx +0 -18
  293. package/src/core/lib/components/ui/select/select-scroll-up-button.jsx +0 -18
  294. package/src/core/lib/components/ui/select/select-separator.jsx +0 -15
  295. package/src/core/lib/components/ui/select/select-trigger.jsx +0 -25
  296. package/src/core/lib/components/ui/select/select.jsx +0 -4
  297. package/src/core/lib/components/ui/sheet/index.js +0 -34
  298. package/src/core/lib/components/ui/sheet/sheet-close.jsx +0 -7
  299. package/src/core/lib/components/ui/sheet/sheet-content.jsx +0 -35
  300. package/src/core/lib/components/ui/sheet/sheet-description.jsx +0 -15
  301. package/src/core/lib/components/ui/sheet/sheet-footer.jsx +0 -11
  302. package/src/core/lib/components/ui/sheet/sheet-header.jsx +0 -11
  303. package/src/core/lib/components/ui/sheet/sheet-overlay.jsx +0 -15
  304. package/src/core/lib/components/ui/sheet/sheet-portal.jsx +0 -4
  305. package/src/core/lib/components/ui/sheet/sheet-title.jsx +0 -15
  306. package/src/core/lib/components/ui/sheet/sheet-trigger.jsx +0 -7
  307. package/src/core/lib/components/ui/sheet/sheet.jsx +0 -4
  308. package/src/core/lib/components/ui/toggle/index.js +0 -8
  309. package/src/core/lib/components/ui/toggle/toggle.jsx +0 -36
  310. package/src/core/lib/components/ui/toggle-group/index.js +0 -10
  311. package/src/core/lib/components/ui/toggle-group/toggle-group-item.jsx +0 -29
  312. package/src/core/lib/components/ui/toggle-group/toggle-group.jsx +0 -43
  313. package/src/internals/canvas/StoryErrorBoundary.jsx +0 -47
  314. package/src/internals/canvas/WebGLContextPool.jsx +0 -291
  315. package/src/internals/canvas/WebGLContextPool.test.jsx +0 -165
  316. package/src/internals/canvas/componentIsolate.jsx +0 -144
  317. package/src/internals/canvas/componentSetIsolate.jsx +0 -300
  318. package/src/internals/canvas/widgets/ComponentSetWidget.module.css +0 -89
  319. package/src/internals/canvas/widgets/InlineStoryRenderer.jsx +0 -129
  320. package/src/internals/canvas/widgets/InlineStoryRenderer.test.jsx +0 -68
  321. package/src/internals/canvas/widgets/StorySetWidget.module.css +0 -89
  322. package/src/primer/ThemeSync.jsx +0 -73
  323. package/src/primer/index.js +0 -1
@@ -1,12 +1,12 @@
1
1
  /* tiny-canvas styles */
2
2
 
3
3
  :root {
4
- --tc-border-radius: var(--borderRadius-large, 12px);
5
- --tc-shadow-rest: var(--shadow-resting-small, 0 1px 3px rgba(0, 0, 0, 0.12));
6
- --tc-shadow-float: var(--shadow-floating-small, 0 4px 12px rgba(0, 0, 0, 0.15));
7
- --tc-border-color: var(--borderColor-muted, rgba(0, 0, 0, 0.15));
8
- --tc-bg-muted: var(--bgColor-muted, #f6f8fa);
9
- --tc-dot-color: var(--overlay-backdrop-bgColor, rgba(0, 0, 0, 0.08));
4
+ --tc-border-radius: var(--radius, 12px);
5
+ --tc-shadow-rest: var(--shadow-resting, 0 1px 3px rgba(0, 0, 0, 0.12));
6
+ --tc-shadow-float: var(--shadow-floating, 0 4px 12px rgba(0, 0, 0, 0.15));
7
+ --tc-border-color: var(--color-border-muted, rgba(0, 0, 0, 0.15));
8
+ --tc-bg-muted: var(--color-background-muted, #f6f8fa);
9
+ --tc-dot-color: var(--color-overlay-backdrop, rgba(0, 0, 0, 0.08));
10
10
  --tc-grid-size: var(--golden-number, 36px);
11
11
  --tc-grid-offset: calc(var(--tc-grid-size) / -2);
12
12
  --tc-dot-radius: 2px;
@@ -46,22 +46,22 @@
46
46
 
47
47
  @media (prefers-color-scheme: dark) {
48
48
  .tc-canvas:not([data-color-mode="light"]) {
49
- --tc-bg-muted: var(--bgColor-muted, #161b22);
50
- --tc-dot-color: var(--overlay-backdrop-bgColor, rgba(255, 255, 255, 0.1));
51
- --tc-border-color: var(--borderColor-muted, rgba(255, 255, 255, 0.15));
52
- --tc-shadow-rest: var(--shadow-resting-small, 0 1px 3px rgba(0, 0, 0, 0.3));
53
- --tc-shadow-float: var(--shadow-floating-small, 0 4px 12px rgba(0, 0, 0, 0.4));
49
+ --tc-bg-muted: var(--color-background-muted, #161b22);
50
+ --tc-dot-color: var(--color-overlay-backdrop, rgba(255, 255, 255, 0.1));
51
+ --tc-border-color: var(--color-border-muted, rgba(255, 255, 255, 0.15));
52
+ --tc-shadow-rest: var(--shadow-resting, 0 1px 3px rgba(0, 0, 0, 0.3));
53
+ --tc-shadow-float: var(--shadow-floating, 0 4px 12px rgba(0, 0, 0, 0.4));
54
54
  }
55
55
  }
56
56
 
57
57
  /* Dark mode — explicit override */
58
58
 
59
59
  .tc-canvas[data-color-mode="dark"] {
60
- --tc-bg-muted: var(--bgColor-muted, #161b22);
61
- --tc-dot-color: var(--overlay-backdrop-bgColor, rgba(255, 255, 255, 0.1));
62
- --tc-border-color: var(--borderColor-muted, rgba(255, 255, 255, 0.15));
63
- --tc-shadow-rest: var(--shadow-resting-small, 0 1px 3px rgba(0, 0, 0, 0.3));
64
- --tc-shadow-float: var(--shadow-floating-small, 0 4px 12px rgba(0, 0, 0, 0.4));
60
+ --tc-bg-muted: var(--color-background-muted, #161b22);
61
+ --tc-dot-color: var(--color-overlay-backdrop, rgba(255, 255, 255, 0.1));
62
+ --tc-border-color: var(--color-border-muted, rgba(255, 255, 255, 0.15));
63
+ --tc-shadow-rest: var(--shadow-resting, 0 1px 3px rgba(0, 0, 0, 0.3));
64
+ --tc-shadow-float: var(--shadow-floating, 0 4px 12px rgba(0, 0, 0, 0.4));
65
65
  }
66
66
 
67
67
  /* Draggable */
@@ -128,8 +128,6 @@ function createPrototype(values, root) {
128
128
  ? path.join(prototypesDir, `${values.folder}.folder`, values.name)
129
129
  : path.join(prototypesDir, values.name)
130
130
 
131
- fs.mkdirSync(targetDir, { recursive: true })
132
-
133
131
  const files = []
134
132
  const relDir = path.relative(root, targetDir)
135
133
 
@@ -148,6 +146,22 @@ function createPrototype(values, root) {
148
146
  return { success: false, errors: [{ message: partialError }] }
149
147
  }
150
148
 
149
+ // Pre-resolve the partial's component file *before* writing anything.
150
+ // If the partial's template/recipe directory is missing or empty we'd
151
+ // otherwise leave an orphan .prototype.json on disk and Vite would
152
+ // then try (and fail) to lazy-load a non-existent index.jsx. Validate
153
+ // first, write second.
154
+ let componentFile = null
155
+ if (partialEntry && !values.url) {
156
+ const partialDir = path.join(root, 'src', partialEntry.baseDir, partialEntry.name)
157
+ componentFile = findComponentFile(partialDir)
158
+ if (!componentFile) {
159
+ return { success: false, errors: [{ message: `No .jsx or .tsx file found in src/${partialEntry.baseDir}/${partialEntry.name}/` }] }
160
+ }
161
+ }
162
+
163
+ fs.mkdirSync(targetDir, { recursive: true })
164
+
151
165
  // Write .prototype.json (with $global from partial when applicable)
152
166
  const protoFile = `${values.name}.prototype.json`
153
167
  const protoBody = JSON.parse(prototypeJson(values))
@@ -159,22 +173,14 @@ function createPrototype(values, root) {
159
173
 
160
174
  // Write index.jsx (only for non-external prototypes)
161
175
  if (!values.url) {
162
- let body
163
- if (partialEntry) {
164
- const partialDir = path.join(root, 'src', partialEntry.baseDir, partialEntry.name)
165
- const componentFile = findComponentFile(partialDir)
166
- if (!componentFile) {
167
- return { success: false, errors: [{ message: `No .jsx or .tsx file found in src/${partialEntry.baseDir}/${partialEntry.name}/` }] }
168
- }
169
- body = renderPartialIndexJsx({
176
+ const body = partialEntry
177
+ ? renderPartialIndexJsx({
170
178
  partialEntry,
171
179
  componentFile,
172
180
  componentName: toPascalCase(values.name),
173
181
  title: values.title || values.name,
174
182
  })
175
- } else {
176
- body = prototypeIndexJsx(values.name)
177
- }
183
+ : prototypeIndexJsx(values.name)
178
184
  fs.writeFileSync(path.join(targetDir, 'index.jsx'), body, 'utf-8')
179
185
  files.push(`${relDir}/index.jsx`)
180
186
  }
@@ -191,7 +197,30 @@ function createPrototype(values, root) {
191
197
 
192
198
  function createCanvas(values, root) {
193
199
  const canvasDir = path.join(root, 'src', 'canvas')
194
- const targetDir = values.folder ? path.join(canvasDir, values.folder) : canvasDir
200
+
201
+ // Folder resolution mirrors the canvas-server /create endpoint:
202
+ // - if folder is empty → write at root
203
+ // - if <folder>.folder/ exists → use it (workspace grouping)
204
+ // - if <folder>/ exists → use it (multi-page grouping)
205
+ // - otherwise create a new dir per `folderKind`
206
+ // ("workspace" → .folder/, default "pages" → plain dir)
207
+ let targetDir = canvasDir
208
+ let usedFolderKind = null
209
+ if (values.folder) {
210
+ const dotFolderDir = path.join(canvasDir, `${values.folder}.folder`)
211
+ const plainDir = path.join(canvasDir, values.folder)
212
+ if (fs.existsSync(dotFolderDir)) {
213
+ targetDir = dotFolderDir
214
+ usedFolderKind = 'workspace'
215
+ } else if (fs.existsSync(plainDir) && fs.statSync(plainDir).isDirectory()) {
216
+ targetDir = plainDir
217
+ usedFolderKind = 'pages'
218
+ } else {
219
+ const useWorkspace = values.folderKind === 'workspace'
220
+ targetDir = useWorkspace ? dotFolderDir : plainDir
221
+ usedFolderKind = useWorkspace ? 'workspace' : 'pages'
222
+ }
223
+ }
195
224
  fs.mkdirSync(targetDir, { recursive: true })
196
225
 
197
226
  const files = []
@@ -212,8 +241,18 @@ function createCanvas(values, root) {
212
241
  files.push(path.relative(root, path.join(targetDir, jsxFile)))
213
242
  }
214
243
 
244
+ // Route always strips `.folder/` (per the data plugin), so both kinds
245
+ // produce the same `/canvas/<folder>/<name>` URL shape.
215
246
  const canvasRoute = `/canvas/${values.folder ? `${values.folder}/` : ''}${values.name}`
216
- return { success: true, type: 'canvas', name: values.name, path: path.relative(root, targetDir), route: canvasRoute, files }
247
+ return {
248
+ success: true,
249
+ type: 'canvas',
250
+ name: values.name,
251
+ path: path.relative(root, targetDir),
252
+ route: canvasRoute,
253
+ folderKind: usedFolderKind,
254
+ files,
255
+ }
217
256
  }
218
257
 
219
258
  function createComponent(values, root) {
@@ -19,7 +19,13 @@
19
19
  },
20
20
  "folder": {
21
21
  "type": "string",
22
- "description": "Parent folder directory"
22
+ "description": "Parent folder directory (name only — no .folder suffix). If the folder already exists, its kind wins; otherwise `folderKind` decides whether to create a workspace folder (.folder/) or a multi-page canvas group (plain dir)."
23
+ },
24
+ "folderKind": {
25
+ "type": "string",
26
+ "enum": ["workspace", "pages"],
27
+ "default": "pages",
28
+ "description": "When `folder` is set and doesn't yet exist: `workspace` creates `<folder>.folder/` (each canvas appears under a workspace folder header), `pages` creates a plain `<folder>/` dir (canvases become pages of a shared multi-page group). Ignored when the folder already exists."
23
29
  },
24
30
  "grid": {
25
31
  "type": "boolean",
@@ -21,7 +21,7 @@ describe('autosync scope helpers', () => {
21
21
  expect(matchesAutosyncScope('canvas', 'src/canvas/notes.txt')).toBe(true)
22
22
  expect(matchesAutosyncScope('canvas', 'assets/canvas/images/photo.png')).toBe(true)
23
23
  expect(matchesAutosyncScope('canvas', 'assets/canvas/snapshots/snapshot-widget--latest.webp')).toBe(true)
24
- expect(matchesAutosyncScope('canvas', 'assets/.storyboard-public/terminal-snapshots/agent-abc.snapshot.json')).toBe(true)
24
+ expect(matchesAutosyncScope('canvas', 'assets/.storyboard-public/terminal-snapshots/agents.snapshot.json')).toBe(true)
25
25
  expect(matchesAutosyncScope('canvas', 'src/prototypes/demo/default.flow.json')).toBe(false)
26
26
  })
27
27
 
@@ -82,7 +82,6 @@ export class HotPool {
82
82
  #healthTimer = null
83
83
  #prereqsAvailable = null
84
84
  #wsSend = null
85
- #webglReadySlots = 0
86
85
 
87
86
  // Agent config (null for bare shell pools)
88
87
  #agentConfig = null
@@ -110,7 +109,6 @@ export class HotPool {
110
109
  this.#loadBalancer = config.load_balancer !== false
111
110
  this.#wsSend = wsSend
112
111
  this.#agentConfig = agentConfig
113
- this.#webglReadySlots = Math.max(0, config.webgl_ready_slots ?? 0)
114
112
  }
115
113
 
116
114
  get poolId() { return this.#poolId }
@@ -169,17 +167,16 @@ export class HotPool {
169
167
  }
170
168
 
171
169
  /**
172
- * Non-mutating probe: would the next acquire() return a session, and would it
173
- * be webgl-ready? Used by callers that want to render in webgl-ready mode
174
- * without claiming a slot (the actual claim happens later when the WS
175
- * connects). Returns { ready, webglReady }.
170
+ * Non-mutating probe: would the next acquire() return a session? Used by
171
+ * callers that want to know whether to render in hot mode without claiming
172
+ * a slot (the actual claim happens later when the WS connects).
173
+ * Returns { ready }.
176
174
  */
177
175
  peek() {
178
- if (!this.#enabled || this.#queue.length === 0) return { ready: false, webglReady: false }
176
+ if (!this.#enabled || this.#queue.length === 0) return { ready: false }
179
177
  const idx = this.#queue.findIndex(s => s.state === 'ready')
180
- if (idx === -1) return { ready: false, webglReady: false }
181
- const webglReady = this.#webglReadySlots > 0 && idx < this.#webglReadySlots
182
- return { ready: true, webglReady }
178
+ if (idx === -1) return { ready: false }
179
+ return { ready: true }
183
180
  }
184
181
 
185
182
  acquire() {
@@ -193,12 +190,8 @@ export class HotPool {
193
190
  return null
194
191
  }
195
192
 
196
- // Session is WebGL-ready if its queue position was within webgl_ready_slots
197
- const webglReady = this.#webglReadySlots > 0 && idx < this.#webglReadySlots
198
-
199
193
  const session = this.#queue.splice(idx, 1)[0]
200
194
  session.state = 'acquired'
201
- session.webglReady = webglReady
202
195
  this.#acquired.set(session.id, session)
203
196
  const age = ((Date.now() - session.createdAt) / 1000).toFixed(1)
204
197
  const readyCount = this.#queue.filter(s => s.state === 'ready').length
@@ -206,10 +199,10 @@ export class HotPool {
206
199
  // Scale-up: queue drained to 0 ready → enter pressure mode
207
200
  if (readyCount === 0 && !this.#pressured) {
208
201
  this.#pressured = true
209
- this.#log(`→ ACQUIRED ${session.id} tmux=${session.tmuxName} (age: ${age}s, webglReady: ${webglReady}) — ⚡ PRESSURE ON (scaling to max_pool_size=${this.#maxPoolSize})`)
202
+ this.#log(`→ ACQUIRED ${session.id} tmux=${session.tmuxName} (age: ${age}s) — ⚡ PRESSURE ON (scaling to max_pool_size=${this.#maxPoolSize})`)
210
203
  this.#resetCooldown()
211
204
  } else {
212
- this.#log(`→ ACQUIRED ${session.id} tmux=${session.tmuxName} (age: ${age}s, webglReady: ${webglReady}, queue: ${readyCount}/${this.#fillTarget})`)
205
+ this.#log(`→ ACQUIRED ${session.id} tmux=${session.tmuxName} (age: ${age}s, queue: ${readyCount}/${this.#fillTarget})`)
213
206
  this.#resetCooldown()
214
207
  }
215
208
 
@@ -273,7 +266,6 @@ export class HotPool {
273
266
  load_balancer: this.#loadBalancer,
274
267
  load_balancer_cooldown_mins: this.#cooldownMs / 60_000,
275
268
  verbose: this.#verbose,
276
- webgl_ready_slots: this.#webglReadySlots,
277
269
  },
278
270
  agentConfig: this.#agentConfig ? {
279
271
  startupCommand: this.#agentConfig.startupCommand,
@@ -294,7 +286,6 @@ export class HotPool {
294
286
  if (config.max_pool_size !== undefined) this.#maxPoolSize = Math.max(0, config.max_pool_size)
295
287
  if (config.load_balancer_cooldown_mins !== undefined) this.#cooldownMs = config.load_balancer_cooldown_mins * 60_000
296
288
  if (config.load_balancer !== undefined) this.#loadBalancer = !!config.load_balancer
297
- if (config.webgl_ready_slots !== undefined) this.#webglReadySlots = Math.max(0, config.webgl_ready_slots)
298
289
  const newSize = Math.min(Math.max(0, config.pool_size ?? this.#poolSize), this.#maxPoolSize)
299
290
  const newEnabled = config.enabled !== false
300
291
  if (config.verbose !== undefined) this.#verbose = !!config.verbose
@@ -551,8 +542,8 @@ export class HotPool {
551
542
  const paneContent = execSync(
552
543
  // H1: include scrollback so the readiness echo can still be
553
544
  // matched after the agent's TUI repaints over it.
554
- `tmux capture-pane -t "${tmuxName}" -p -S -200`,
555
- { encoding: 'utf8', timeout: 1000 }
545
+ `tmux capture-pane -t "${tmuxName}" -p -S -200 2>/dev/null`,
546
+ { encoding: 'utf8', timeout: 1000, stdio: ['ignore', 'pipe', 'ignore'] }
556
547
  )
557
548
  // Strip ANSI escape sequences — agent CLIs use heavy formatting
558
549
  // eslint-disable-next-line no-control-regex
@@ -580,14 +571,13 @@ export class HotPool {
580
571
  return this.#tmuxSessionExists(tmuxName)
581
572
  }
582
573
 
583
- // Send postStartup command (e.g. "/allow-all on")
574
+ // postStartup is NOT sent here intentionally. It's deferred to widget
575
+ // handoff (see terminal-server.js warm path) so it runs *after* the
576
+ // identity/role injection — mode-toggling slash commands like
577
+ // `/autopilot` must come last so canvas context lands in conversation
578
+ // history before the agent enters autopilot mode.
584
579
  if (postStartup) {
585
- try {
586
- await new Promise(r => setTimeout(r, 500))
587
- execSync(`tmux send-keys -t "${tmuxName}" -l ${JSON.stringify(postStartup)}`, { stdio: 'ignore' })
588
- execSync(`tmux send-keys -t "${tmuxName}" Enter`, { stdio: 'ignore' })
589
- this.#log(`⊕ AGENT ${sessionId} postStartup sent: ${postStartup}`)
590
- } catch { /* empty */ }
580
+ this.#log(`⊕ AGENT ${sessionId} postStartup "${postStartup}" deferred to widget handoff`)
591
581
  }
592
582
 
593
583
  this.#log(`⊕ AGENT ${sessionId} ready (${signalFilePath ? 'file' : 'signal'}: "${readinessSignal || 'file'}")`)
@@ -615,8 +605,8 @@ export class HotPool {
615
605
  if (this.#agentConfig?.startupCommand) {
616
606
  try {
617
607
  const cmd = execSync(
618
- `tmux display-message -t "${session.tmuxName}" -p "#{pane_current_command}"`,
619
- { encoding: 'utf8', timeout: 1000 }
608
+ `tmux display-message -t "${session.tmuxName}" -p "#{pane_current_command}" 2>/dev/null`,
609
+ { encoding: 'utf8', timeout: 1000, stdio: ['ignore', 'pipe', 'ignore'] }
620
610
  ).trim()
621
611
  // Agent exited if the pane is back to a shell
622
612
  const shells = ['zsh', 'bash', 'sh', 'fish']
@@ -707,7 +697,6 @@ export class HotPoolManager {
707
697
  load_balancer: config.load_balancer !== false,
708
698
  enabled: this.#enabled,
709
699
  verbose: !!config.verbose,
710
- webgl_ready_slots: poolsConfig[poolId]?.webgl_ready_slots ?? 0,
711
700
  })
712
701
 
713
702
  // Terminal pool (bare shells)
@@ -772,10 +761,10 @@ export class HotPoolManager {
772
761
  return pool.acquire()
773
762
  }
774
763
 
775
- /** Non-mutating probe: { ready, webglReady } for the specified pool. */
764
+ /** Non-mutating probe: { ready } for the specified pool. */
776
765
  peek(poolId) {
777
766
  const pool = this.#pools.get(poolId)
778
- if (!pool) return { ready: false, webglReady: false }
767
+ if (!pool) return { ready: false }
779
768
  return pool.peek()
780
769
  }
781
770
 
@@ -819,7 +808,6 @@ export class HotPoolManager {
819
808
  load_balancer: config.load_balancer,
820
809
  enabled: config.enabled,
821
810
  verbose: config.verbose,
822
- webgl_ready_slots: poolsConfig[id]?.webgl_ready_slots,
823
811
  }
824
812
  pool.reconfigure(poolConfig)
825
813
  }
@@ -7,17 +7,36 @@
7
7
  * Event types:
8
8
  * canvas_created — full initial state (first line)
9
9
  * widget_added — append a widget
10
- * widget_updated — patch widget props
11
- * widget_moved — update widget position
12
- * widget_removed — remove a widget by id
13
- * settings_updated patch canvas-level settings
14
- * source_updated replace the sources array
15
- * widgets_replaced — replace the entire widgets array (bulk update)
10
+ * widget_updated — patch widget props (may carry `prevProps` for undo)
11
+ * widget_moved — update widget position (may carry `prevPosition` for undo)
12
+ * widget_removed — remove a widget by id (carries `widget` for undo;
13
+ * legacy events only had `widgetId`)
14
+ * settings_updated patch canvas-level settings (may carry `prevSettings` for undo)
15
+ * source_updated — replace the sources array (may carry `prevSources` for undo)
16
+ * widgets_replaced — replace the entire widgets array (compaction only, not undoable)
16
17
  * connector_added — append a connector between two widgets
17
- * connector_removed — remove a connector by id
18
- * connectors_replaced replace the entire connectors array (bulk update, used by undo/redo)
19
- * connector_waypoints_set set/replace manual routing waypoints on a connector
20
- * connector_waypoints_cleared drop manual routing, revert to auto-routing
18
+ * connector_removed — remove a connector by id (carries `connector` for undo;
19
+ * legacy events only had `connectorId`)
20
+ * connectors_replaced — replace the entire connectors array (compaction only, not undoable)
21
+ * connector_waypoints_set set/replace manual routing waypoints (may carry `prevWaypoints`)
22
+ * connector_waypoints_cleared — drop manual routing (may carry `prevWaypoints`)
23
+ * connector_updated — patch connector anchors/meta (may carry `prevUpdates` for undo)
24
+ *
25
+ * Event metadata (all events):
26
+ * id — short random id stamped by the server, used by undo/redo
27
+ * to target a specific event. Legacy events without an id are
28
+ * non-undoable but materialize normally.
29
+ * meta.kind — present on undo/redo emissions ('undo' | 'redo'). The
30
+ * materializer ignores meta; it's informational for tools
31
+ * and a future timeline UI.
32
+ * meta.of — the id of the event this undo/redo targets.
33
+ *
34
+ * Undo model: the materializer never "skips" events. Undo is a normal
35
+ * forward event (a real `widget_added`/`widget_removed`/`widget_updated`)
36
+ * that happens to carry meta.kind === 'undo'. Folding is one pass, no
37
+ * special cases. Inverse payloads (`prevProps`, `prevPosition`, full
38
+ * `widget`/`connector` on removes, etc.) are read only by the server's
39
+ * POST /undo and POST /redo endpoints when computing the inverse event.
21
40
  */
22
41
 
23
42
  /**
@@ -134,13 +153,16 @@ export function materialize(events) {
134
153
  }
135
154
 
136
155
  case 'widget_removed': {
156
+ // Back-compat: legacy events only had `widgetId`; new events carry
157
+ // the full `widget` object so undo can re-add without history walk.
158
+ const removedId = evt.widget?.id ?? evt.widgetId
137
159
  state.widgets = (state.widgets || []).filter(
138
- (w) => w.id !== evt.widgetId,
160
+ (w) => w.id !== removedId,
139
161
  )
140
162
  // Cascade: remove connectors referencing the deleted widget
141
163
  if (state.connectors?.length) {
142
164
  state.connectors = state.connectors.filter(
143
- (c) => c.start.widgetId !== evt.widgetId && c.end.widgetId !== evt.widgetId,
165
+ (c) => c.start.widgetId !== removedId && c.end.widgetId !== removedId,
144
166
  )
145
167
  }
146
168
  break
@@ -179,8 +201,12 @@ export function materialize(events) {
179
201
  }
180
202
 
181
203
  case 'connector_removed': {
204
+ // Back-compat: legacy events only had `connectorId`; new events
205
+ // carry the full `connector` object so undo can re-add without
206
+ // history walk.
207
+ const removedId = evt.connector?.id ?? evt.connectorId
182
208
  state.connectors = (state.connectors || []).filter(
183
- (c) => c.id !== evt.connectorId,
209
+ (c) => c.id !== removedId,
184
210
  )
185
211
  break
186
212
  }
@@ -34,7 +34,7 @@ function createPathResolver(root) {
34
34
 
35
35
  function scanFiles() {
36
36
  const results = []
37
- const ignore = new Set(['node_modules', 'dist', '.git', '.worktrees'])
37
+ const ignore = new Set(['node_modules', 'dist', '.git', '.worktrees', 'worktrees', 'workleafs'])
38
38
  function walk(dir, rel) {
39
39
  let entries
40
40
  try { entries = fs.readdirSync(dir, { withFileTypes: true }) } catch { return }