@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,86 +1,101 @@
1
1
  import { useRef, useState, useCallback } from 'react'
2
2
 
3
3
  const MAX_HISTORY = 100
4
- const COALESCE_MS = 2000
5
4
 
6
5
  /**
7
- * Snapshot-based undo/redo history for canvas widgets.
6
+ * Per-tab undo/redo history for canvas events.
8
7
  *
9
- * Tracks past/future stacks of widget array clones. The present state
10
- * is always the live `localWidgets` this hook only manages history.
8
+ * Maintains two stacks of event IDs:
9
+ * undoStack ids of user-initiated forward events, newest last.
10
+ * redoStack — ids of undo events (the inverse events appended by the
11
+ * server's POST /undo), newest last.
11
12
  *
12
- * Edit coalescing: continuous edits to the same widget within COALESCE_MS
13
- * are merged into one undo step (like Figma).
13
+ * Workflow:
14
+ * Every user-initiated mutation that succeeds calls `track(eventId)`
15
+ * pushes onto undoStack and clears redoStack.
16
+ * • `popUndo()` returns the next id to undo. Caller POSTs /undo and on
17
+ * success calls `pushRedo(inverseId)`.
18
+ * • `popRedo()` returns the next id to redo. Caller POSTs /redo and on
19
+ * success calls `pushUndo(inverseId)` (so the redo is itself undoable).
20
+ *
21
+ * This hook holds *no* widget data — undo state lives in the JSONL. The
22
+ * stacks are per-tab and ephemeral; refreshing the page resets them.
14
23
  */
15
24
  export default function useUndoRedo() {
16
- const pastRef = useRef([])
17
- const futureRef = useRef([])
18
- const lastActionRef = useRef({ type: null, widgetId: null, time: 0 })
19
- // State counter drives canUndo/canRedo re-renders without cloning the stacks
20
- const [counts, setCounts] = useState({ past: 0, future: 0 })
25
+ const undoStackRef = useRef([])
26
+ const redoStackRef = useRef([])
27
+ const [counts, setCounts] = useState({ undo: 0, redo: 0 })
28
+
29
+ const syncCounts = useCallback(() => {
30
+ setCounts({ undo: undoStackRef.current.length, redo: redoStackRef.current.length })
31
+ }, [])
21
32
 
22
- const snapshot = useCallback((currentWidgets, actionType, widgetId) => {
23
- const widgets = currentWidgets ?? []
33
+ const track = useCallback((eventId) => {
34
+ if (!eventId || typeof eventId !== 'string') return
35
+ undoStackRef.current.push(eventId)
36
+ if (undoStackRef.current.length > MAX_HISTORY) undoStackRef.current.shift()
37
+ // New mutation invalidates the redo chain
38
+ redoStackRef.current = []
39
+ syncCounts()
40
+ }, [syncCounts])
24
41
 
25
- // Edit coalescing skip snapshot if same edit target within timeout
26
- if (actionType === 'edit' && widgetId) {
27
- const last = lastActionRef.current
28
- const now = Date.now()
29
- if (
30
- last.type === 'edit' &&
31
- last.widgetId === widgetId &&
32
- now - last.time < COALESCE_MS
33
- ) {
34
- lastActionRef.current = { type: 'edit', widgetId, time: now }
35
- return
42
+ const trackMany = useCallback((eventIds) => {
43
+ if (!Array.isArray(eventIds)) return
44
+ for (const id of eventIds) {
45
+ if (id && typeof id === 'string') {
46
+ undoStackRef.current.push(id)
47
+ if (undoStackRef.current.length > MAX_HISTORY) undoStackRef.current.shift()
36
48
  }
37
49
  }
50
+ redoStackRef.current = []
51
+ syncCounts()
52
+ }, [syncCounts])
38
53
 
39
- pastRef.current.push(structuredClone(widgets))
40
- if (pastRef.current.length > MAX_HISTORY) pastRef.current.shift()
41
- futureRef.current = []
42
- lastActionRef.current = {
43
- type: actionType,
44
- widgetId: widgetId || null,
45
- time: Date.now(),
46
- }
47
- setCounts({ past: pastRef.current.length, future: 0 })
48
- }, [])
54
+ const popUndo = useCallback(() => {
55
+ const id = undoStackRef.current.pop()
56
+ syncCounts()
57
+ return id ?? null
58
+ }, [syncCounts])
49
59
 
50
- const undo = useCallback((currentWidgets) => {
51
- if (pastRef.current.length === 0) return null
52
- futureRef.current.push(structuredClone(currentWidgets))
53
- const previous = pastRef.current.pop()
54
- lastActionRef.current = { type: 'undo', widgetId: null, time: Date.now() }
55
- setCounts({ past: pastRef.current.length, future: futureRef.current.length })
56
- return previous
57
- }, [])
60
+ const popRedo = useCallback(() => {
61
+ const id = redoStackRef.current.pop()
62
+ syncCounts()
63
+ return id ?? null
64
+ }, [syncCounts])
58
65
 
59
- const redo = useCallback((currentWidgets) => {
60
- if (futureRef.current.length === 0) return null
61
- pastRef.current.push(structuredClone(currentWidgets))
62
- const next = futureRef.current.pop()
63
- lastActionRef.current = { type: 'redo', widgetId: null, time: Date.now() }
64
- setCounts({ past: pastRef.current.length, future: futureRef.current.length })
65
- return next
66
- }, [])
66
+ const pushRedo = useCallback((eventId) => {
67
+ if (!eventId || typeof eventId !== 'string') return
68
+ redoStackRef.current.push(eventId)
69
+ if (redoStackRef.current.length > MAX_HISTORY) redoStackRef.current.shift()
70
+ syncCounts()
71
+ }, [syncCounts])
72
+
73
+ const pushUndo = useCallback((eventId) => {
74
+ if (!eventId || typeof eventId !== 'string') return
75
+ undoStackRef.current.push(eventId)
76
+ if (undoStackRef.current.length > MAX_HISTORY) undoStackRef.current.shift()
77
+ syncCounts()
78
+ }, [syncCounts])
67
79
 
68
80
  const reset = useCallback(() => {
69
- pastRef.current = []
70
- futureRef.current = []
71
- lastActionRef.current = { type: null, widgetId: null, time: 0 }
81
+ undoStackRef.current = []
82
+ redoStackRef.current = []
72
83
  setCounts((prev) => {
73
- if (prev.past === 0 && prev.future === 0) return prev
74
- return { past: 0, future: 0 }
84
+ if (prev.undo === 0 && prev.redo === 0) return prev
85
+ return { undo: 0, redo: 0 }
75
86
  })
76
87
  }, [])
77
88
 
78
89
  return {
79
- snapshot,
80
- undo,
81
- redo,
90
+ track,
91
+ trackMany,
92
+ popUndo,
93
+ popRedo,
94
+ pushRedo,
95
+ pushUndo,
82
96
  reset,
83
- canUndo: counts.past > 0,
84
- canRedo: counts.future > 0,
97
+ canUndo: counts.undo > 0,
98
+ canRedo: counts.redo > 0,
85
99
  }
86
100
  }
101
+
@@ -1,231 +1,99 @@
1
1
  import { renderHook, act } from '@testing-library/react'
2
+ import { describe, it, expect } from 'vitest'
2
3
  import useUndoRedo from './useUndoRedo.js'
3
4
 
4
- describe('useUndoRedo', () => {
5
+ describe('useUndoRedo (canvas)', () => {
5
6
  it('starts with canUndo and canRedo as false', () => {
6
7
  const { result } = renderHook(() => useUndoRedo())
7
8
  expect(result.current.canUndo).toBe(false)
8
9
  expect(result.current.canRedo).toBe(false)
9
10
  })
10
11
 
11
- it('can undo after a snapshot', () => {
12
+ it('track pushes an event id and enables undo', () => {
12
13
  const { result } = renderHook(() => useUndoRedo())
13
- const widgets = [{ id: '1', type: 'sticky-note', props: { text: 'a' }, position: { x: 0, y: 0 } }]
14
-
15
- act(() => result.current.snapshot(widgets, 'add'))
16
-
14
+ act(() => result.current.track('evt_a'))
17
15
  expect(result.current.canUndo).toBe(true)
18
16
  expect(result.current.canRedo).toBe(false)
19
17
  })
20
18
 
21
- it('undo returns the previous state and enables redo', () => {
19
+ it('popUndo returns the most recent tracked id; pushRedo enables redo', () => {
22
20
  const { result } = renderHook(() => useUndoRedo())
23
- const before = [{ id: '1', props: { text: 'a' } }]
24
- const after = [{ id: '1', props: { text: 'b' } }]
21
+ act(() => result.current.track('evt_a'))
22
+ act(() => result.current.track('evt_b'))
25
23
 
26
- act(() => result.current.snapshot(before, 'edit', '1'))
24
+ let popped
25
+ act(() => { popped = result.current.popUndo() })
27
26
 
28
- let restored
29
- act(() => { restored = result.current.undo(after) })
27
+ expect(popped).toBe('evt_b')
28
+ expect(result.current.canUndo).toBe(true)
29
+ expect(result.current.canRedo).toBe(false)
30
30
 
31
- expect(restored).toEqual(before)
32
- expect(result.current.canUndo).toBe(false)
31
+ act(() => result.current.pushRedo('evt_b_inv'))
33
32
  expect(result.current.canRedo).toBe(true)
34
33
  })
35
34
 
36
- it('redo returns the next state', () => {
35
+ it('popRedo returns the most recent redo id; pushUndo restores undo availability', () => {
37
36
  const { result } = renderHook(() => useUndoRedo())
38
- const before = [{ id: '1', props: { text: 'a' } }]
39
- const after = [{ id: '1', props: { text: 'b' } }]
37
+ act(() => result.current.track('evt_a'))
38
+ act(() => { result.current.popUndo() })
39
+ act(() => result.current.pushRedo('evt_a_inv'))
40
40
 
41
- act(() => result.current.snapshot(before, 'edit', '1'))
42
- act(() => { result.current.undo(after) })
41
+ expect(result.current.canRedo).toBe(true)
43
42
 
44
- let redone
45
- act(() => { redone = result.current.redo(before) })
43
+ let popped
44
+ act(() => { popped = result.current.popRedo() })
45
+ expect(popped).toBe('evt_a_inv')
46
+ expect(result.current.canRedo).toBe(false)
46
47
 
47
- expect(redone).toEqual(after)
48
+ act(() => result.current.pushUndo('evt_a_inv_inv'))
48
49
  expect(result.current.canUndo).toBe(true)
49
- expect(result.current.canRedo).toBe(false)
50
50
  })
51
51
 
52
- it('new mutation after undo clears the redo chain', () => {
52
+ it('new track call after undo clears the redo stack', () => {
53
53
  const { result } = renderHook(() => useUndoRedo())
54
- const s0 = [{ id: '1' }]
55
- const s1 = [{ id: '1' }, { id: '2' }]
56
- const s2 = [{ id: '1' }, { id: '3' }] // eslint-disable-line no-unused-vars
57
-
58
- act(() => result.current.snapshot(s0, 'add'))
59
- act(() => result.current.undo(s1))
54
+ act(() => result.current.track('evt_a'))
55
+ act(() => { result.current.popUndo() })
56
+ act(() => result.current.pushRedo('evt_a_inv'))
60
57
  expect(result.current.canRedo).toBe(true)
61
58
 
62
- // New mutation breaks redo
63
- act(() => result.current.snapshot(s0, 'add'))
59
+ act(() => result.current.track('evt_b'))
64
60
  expect(result.current.canRedo).toBe(false)
65
61
  expect(result.current.canUndo).toBe(true)
66
62
  })
67
63
 
68
- it('supports multi-step undo-redo-undo chains', () => {
64
+ it('trackMany pushes multiple ids and clears redo', () => {
69
65
  const { result } = renderHook(() => useUndoRedo())
70
- const s0 = [{ id: '1' }]
71
- const s1 = [{ id: '1' }, { id: '2' }]
72
- const s2 = [{ id: '1' }, { id: '2' }, { id: '3' }]
73
- const s3 = [{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }]
74
-
75
- // Build history: s0 → s1 → s2 → s3
76
- act(() => result.current.snapshot(s0, 'add'))
77
- act(() => result.current.snapshot(s1, 'add'))
78
- act(() => result.current.snapshot(s2, 'add'))
79
-
80
- // present = s3, past = [s0, s1, s2]
81
- // Undo to s2
82
- let r
83
- act(() => { r = result.current.undo(s3) })
84
- expect(r).toEqual(s2)
85
-
86
- // Undo to s1
87
- act(() => { r = result.current.undo(s2) })
88
- expect(r).toEqual(s1)
89
-
90
- // Redo to s2
91
- act(() => { r = result.current.redo(s1) })
92
- expect(r).toEqual(s2)
93
-
94
- // Redo to s3
95
- act(() => { r = result.current.redo(s2) })
96
- expect(r).toEqual(s3)
97
-
98
- // Undo to s2 again
99
- act(() => { r = result.current.undo(s3) })
100
- expect(r).toEqual(s2)
101
-
102
- // Undo to s1
103
- act(() => { r = result.current.undo(s2) })
104
- expect(r).toEqual(s1)
105
-
106
- // Undo to s0
107
- act(() => { r = result.current.undo(s1) })
108
- expect(r).toEqual(s0)
109
-
110
- // Can't undo further
111
- expect(result.current.canUndo).toBe(false)
112
- act(() => { r = result.current.undo(s0) })
113
- expect(r).toBeNull()
114
- })
115
-
116
- it('coalesces edits to the same widget within timeout', () => {
117
- const { result } = renderHook(() => useUndoRedo())
118
- const s0 = [{ id: '1', props: { text: '' } }]
119
-
120
- // First edit — creates snapshot
121
- act(() => result.current.snapshot(s0, 'edit', '1'))
66
+ act(() => result.current.trackMany(['evt_a', 'evt_b', 'evt_c']))
122
67
  expect(result.current.canUndo).toBe(true)
123
-
124
- // Second edit to same widget within 2s — coalesced, no new snapshot
125
- act(() => result.current.snapshot(s0, 'edit', '1'))
126
- // Still only one entry in past
127
- let r
128
- act(() => { r = result.current.undo([{ id: '1', props: { text: 'abc' } }]) })
129
- expect(r).toEqual(s0)
130
- // No more undo after that
131
- expect(result.current.canUndo).toBe(false)
68
+ let popped
69
+ act(() => { popped = result.current.popUndo() })
70
+ expect(popped).toBe('evt_c')
132
71
  })
133
72
 
134
- it('does NOT coalesce edits to different widgets', () => {
73
+ it('ignores empty / non-string ids', () => {
135
74
  const { result } = renderHook(() => useUndoRedo())
136
- const s0 = [{ id: '1' }, { id: '2' }]
137
- const s1 = [{ id: '1', props: { text: 'a' } }, { id: '2' }]
138
-
139
- act(() => result.current.snapshot(s0, 'edit', '1'))
140
- act(() => result.current.snapshot(s1, 'edit', '2'))
141
-
142
- // Two entries in past
143
- expect(result.current.canUndo).toBe(true)
144
- act(() => { result.current.undo([]) })
145
- expect(result.current.canUndo).toBe(true)
146
- act(() => { result.current.undo([]) })
75
+ act(() => result.current.track(null))
76
+ act(() => result.current.track(undefined))
77
+ act(() => result.current.track(''))
78
+ act(() => result.current.track(42))
147
79
  expect(result.current.canUndo).toBe(false)
148
80
  })
149
81
 
150
- it('does NOT coalesce edit after a different action type', () => {
82
+ it('reset clears both stacks', () => {
151
83
  const { result } = renderHook(() => useUndoRedo())
152
- const s0 = [{ id: '1' }]
153
- const s1 = [{ id: '1' }, { id: '2' }]
154
-
155
- act(() => result.current.snapshot(s0, 'edit', '1'))
156
- act(() => result.current.snapshot(s1, 'add'))
157
- act(() => result.current.snapshot(s1, 'edit', '1'))
158
-
159
- // Three entries in past (edit + add + edit — not coalesced because add broke the sequence)
160
- act(() => { result.current.undo([]) })
161
- act(() => { result.current.undo([]) })
162
- act(() => { result.current.undo([]) })
163
- expect(result.current.canUndo).toBe(false)
164
- })
165
-
166
- it('reset clears all history', () => {
167
- const { result } = renderHook(() => useUndoRedo())
168
- act(() => result.current.snapshot([{ id: '1' }], 'add'))
169
- act(() => result.current.snapshot([{ id: '1' }, { id: '2' }], 'add'))
170
- expect(result.current.canUndo).toBe(true)
84
+ act(() => result.current.track('evt_a'))
85
+ act(() => { result.current.popUndo() })
86
+ act(() => result.current.pushRedo('evt_a_inv'))
171
87
 
172
88
  act(() => result.current.reset())
173
89
  expect(result.current.canUndo).toBe(false)
174
90
  expect(result.current.canRedo).toBe(false)
175
91
  })
176
92
 
177
- it('undo returns null when history is empty', () => {
178
- const { result } = renderHook(() => useUndoRedo())
179
- let r
180
- act(() => { r = result.current.undo([]) })
181
- expect(r).toBeNull()
182
- })
183
-
184
- it('redo returns null when future is empty', () => {
185
- const { result } = renderHook(() => useUndoRedo())
186
- let r
187
- act(() => { r = result.current.redo([]) })
188
- expect(r).toBeNull()
189
- })
190
-
191
- it('snapshots are deep clones (mutations do not leak)', () => {
192
- const { result } = renderHook(() => useUndoRedo())
193
- const widgets = [{ id: '1', props: { text: 'original' } }]
194
-
195
- act(() => result.current.snapshot(widgets, 'add'))
196
-
197
- // Mutate the original array
198
- widgets[0].props.text = 'mutated'
199
-
200
- let restored
201
- act(() => { restored = result.current.undo(widgets) })
202
- expect(restored[0].props.text).toBe('original')
203
- })
204
-
205
- it('caps history at 100 entries', () => {
93
+ it('popUndo on empty stack returns null', () => {
206
94
  const { result } = renderHook(() => useUndoRedo())
207
-
208
- for (let i = 0; i < 110; i++) {
209
- act(() => result.current.snapshot([{ id: String(i) }], 'add'))
210
- }
211
-
212
- // Should be capped at 100 — undo 100 times, then can't undo further
213
- let count = 0
214
- let r = true
215
- while (r !== null) {
216
- act(() => { r = result.current.undo([]) })
217
- if (r !== null) count++
218
- }
219
- expect(count).toBe(100)
220
- })
221
-
222
- it('snapshots null widgets as empty array (first widget on new canvas)', () => {
223
- const { result } = renderHook(() => useUndoRedo())
224
- act(() => result.current.snapshot(null, 'add'))
225
- expect(result.current.canUndo).toBe(true)
226
-
227
- let restored
228
- act(() => { restored = result.current.undo([{ id: '1' }]) })
229
- expect(restored).toEqual([])
95
+ let popped
96
+ act(() => { popped = result.current.popUndo() })
97
+ expect(popped).toBeNull()
230
98
  })
231
99
  })
@@ -58,7 +58,6 @@ export default forwardRef(function CodePenEmbed({ id, props, onUpdate, resizable
58
58
  const height = readProp(props, 'height', codepenEmbedSchema)
59
59
 
60
60
  const [interactive, setInteractive] = useState(false)
61
- const [showIframe, setShowIframe] = useState(true)
62
61
  const [expandedMode, setExpandedMode] = useExpandOverride('codepen', id)
63
62
  const expanded = expandedMode === '1'
64
63
  const setExpanded = useCallback((open) => setExpandedMode(open ? '1' : null), [setExpandedMode])
@@ -67,8 +66,6 @@ export default forwardRef(function CodePenEmbed({ id, props, onUpdate, resizable
67
66
  const embedRef = useRef(null)
68
67
  const inlineContainerRef = useRef(null)
69
68
  const modalContainerRef = useRef(null)
70
- const teardownTimerRef = useRef(null)
71
- const exitSessionRef = useRef(0)
72
69
 
73
70
  const isValid = useMemo(() => isCodePenUrl(url), [url])
74
71
  const embedUrl = useMemo(() => (isValid ? toCodePenEmbedUrl(url) : ''), [url, isValid])
@@ -91,37 +88,26 @@ export default forwardRef(function CodePenEmbed({ id, props, onUpdate, resizable
91
88
 
92
89
  useIframeDevLogs({
93
90
  widget: 'CodePenEmbed',
94
- loaded: showIframe && Boolean(embedUrl),
91
+ loaded: Boolean(embedUrl),
95
92
  src: embedUrl,
96
93
  })
97
94
 
98
95
  const enterInteractive = useCallback(() => {
99
- exitSessionRef.current++
100
- clearTimeout(teardownTimerRef.current)
101
- setShowIframe(true)
102
96
  setInteractive(true)
103
97
  }, [])
104
98
 
105
- // Exit interactive mode on click outside — keep iframe alive for 2 min
99
+ // Exit interactive mode on click outside
106
100
  useEffect(() => {
107
101
  if (!interactive || expanded) return
108
102
  function handlePointerDown(e) {
109
103
  if (embedRef.current && !embedRef.current.contains(e.target)) {
110
104
  setInteractive(false)
111
- const session = ++exitSessionRef.current
112
- clearTimeout(teardownTimerRef.current)
113
- teardownTimerRef.current = setTimeout(() => {
114
- if (exitSessionRef.current !== session) return
115
- setShowIframe(false)
116
- }, 2 * 60 * 1000)
117
105
  }
118
106
  }
119
107
  document.addEventListener('pointerdown', handlePointerDown)
120
108
  return () => document.removeEventListener('pointerdown', handlePointerDown)
121
109
  }, [interactive, expanded])
122
110
 
123
- useEffect(() => () => clearTimeout(teardownTimerRef.current), [])
124
-
125
111
  // Close expanded modal on Escape
126
112
  useEffect(() => {
127
113
  if (!expanded) return
@@ -172,7 +158,6 @@ export default forwardRef(function CodePenEmbed({ id, props, onUpdate, resizable
172
158
  if (actionId === 'open-external') {
173
159
  if (url) window.open(url, '_blank', 'noopener')
174
160
  } else if (actionId === 'expand') {
175
- setShowIframe(true)
176
161
  setExpanded(true)
177
162
  }
178
163
  },
@@ -188,25 +173,20 @@ export default forwardRef(function CodePenEmbed({ id, props, onUpdate, resizable
188
173
  </div>
189
174
  {embedUrl ? (
190
175
  <>
191
- {showIframe ? (
192
- <div
193
- ref={inlineContainerRef}
194
- className={styles.iframeContainer}
195
- style={expanded ? { visibility: 'hidden' } : undefined}
196
- >
197
- <iframe
198
- ref={iframeRef}
199
- src={embedUrl}
200
- className={styles.iframe}
201
- title={`CodePen: ${headerTitle}`}
202
- allowFullScreen
203
- loading="lazy"
204
- onLoad={(e) => e.target.blur()}
205
- />
206
- </div>
207
- ) : (
208
- <div className={styles.iframeContainer} />
209
- )}
176
+ <div
177
+ ref={inlineContainerRef}
178
+ className={styles.iframeContainer}
179
+ style={expanded ? { visibility: 'hidden' } : undefined}
180
+ >
181
+ <iframe
182
+ ref={iframeRef}
183
+ src={embedUrl}
184
+ className={styles.iframe}
185
+ title={`CodePen: ${headerTitle}`}
186
+ allowFullScreen
187
+ onLoad={(e) => e.target.blur()}
188
+ />
189
+ </div>
210
190
  {!interactive && !expanded && (
211
191
  <div
212
192
  className={overlayStyles.interactOverlay}
@@ -1,8 +1,8 @@
1
1
  .embed {
2
2
  position: relative;
3
3
  overflow: hidden;
4
- background: var(--bgColor-default, #ffffff);
5
- border: 3px solid var(--borderColor-default, #d0d7de);
4
+ background: var(--color-background, #ffffff);
5
+ border: 3px solid var(--color-border, #d0d7de);
6
6
  border-radius: 12px;
7
7
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
8
8
  }
@@ -14,9 +14,9 @@
14
14
  padding: 6px 10px;
15
15
  font-size: 12px;
16
16
  font-weight: 500;
17
- color: var(--fgColor-muted, #656d76);
18
- background: var(--bgColor-muted, #f6f8fa);
19
- border-bottom: 1px solid var(--borderColor-muted, #d8dee4);
17
+ color: var(--color-foreground-muted, #656d76);
18
+ background: var(--color-background-muted, #f6f8fa);
19
+ border-bottom: 1px solid var(--color-border-muted, #d8dee4);
20
20
  white-space: nowrap;
21
21
  overflow: hidden;
22
22
  text-overflow: ellipsis;
@@ -57,12 +57,12 @@
57
57
  background: linear-gradient(
58
58
  135deg,
59
59
  transparent 40%,
60
- var(--borderColor-muted, rgba(0, 0, 0, 0.15)) 40%,
61
- var(--borderColor-muted, rgba(0, 0, 0, 0.15)) 50%,
60
+ var(--color-border-muted, rgba(0, 0, 0, 0.15)) 40%,
61
+ var(--color-border-muted, rgba(0, 0, 0, 0.15)) 50%,
62
62
  transparent 50%,
63
63
  transparent 65%,
64
- var(--borderColor-muted, rgba(0, 0, 0, 0.15)) 65%,
65
- var(--borderColor-muted, rgba(0, 0, 0, 0.15)) 75%,
64
+ var(--color-border-muted, rgba(0, 0, 0, 0.15)) 65%,
65
+ var(--color-border-muted, rgba(0, 0, 0, 0.15)) 75%,
66
66
  transparent 75%
67
67
  );
68
68
  opacity: 0;
@@ -98,7 +98,7 @@
98
98
  position: relative;
99
99
  border-radius: 12px;
100
100
  overflow: hidden;
101
- background: var(--bgColor-default, #ffffff);
101
+ background: var(--color-background, #ffffff);
102
102
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.4);
103
103
  animation: expandScaleIn 0.2s ease;
104
104
  }
@@ -150,12 +150,12 @@
150
150
  }
151
151
 
152
152
  .emptyIcon {
153
- color: var(--fgColor-muted, #656d76);
153
+ color: var(--color-foreground-muted, #656d76);
154
154
  opacity: 0.5;
155
155
  }
156
156
 
157
157
  .emptyLabel {
158
- color: var(--fgColor-muted, #656d76);
158
+ color: var(--color-foreground-muted, #656d76);
159
159
  font-size: 13px;
160
160
  font-style: italic;
161
161
  }