@agent-native/core 0.92.10 → 0.92.12

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 (354) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +214 -17
  5. package/corpus/core/src/agent/run-manager.ts +72 -14
  6. package/corpus/core/src/agent/run-store.ts +152 -27
  7. package/corpus/core/src/cli/design-connect.ts +433 -151
  8. package/corpus/core/src/cli/skills.ts +14 -0
  9. package/corpus/core/src/client/AssistantChat.tsx +25 -0
  10. package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
  11. package/corpus/core/src/client/agent-chat-adapter.ts +56 -12
  12. package/corpus/core/src/client/agent-chat.ts +165 -1
  13. package/corpus/core/src/client/chat/index.ts +9 -1
  14. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
  15. package/corpus/core/src/client/index.ts +5 -0
  16. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  17. package/corpus/core/src/collab/awareness-store.ts +23 -12
  18. package/corpus/core/src/collab/client.ts +16 -5
  19. package/corpus/core/src/collab/index.ts +4 -0
  20. package/corpus/core/src/collab/recent-edits.ts +65 -2
  21. package/corpus/core/src/index.browser.ts +1 -0
  22. package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
  23. package/corpus/core/src/server/builder-design-systems.ts +30 -4
  24. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
  25. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
  26. package/corpus/templates/clips/AGENTS.md +4 -0
  27. package/corpus/templates/clips/actions/create-recording.ts +2 -0
  28. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
  29. package/corpus/templates/clips/actions/request-transcript.ts +102 -33
  30. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
  31. package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
  32. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +75 -9
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  34. package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  44. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  45. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  46. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  47. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  48. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  56. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  57. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  58. package/corpus/templates/clips/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.md +6 -0
  59. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  60. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  61. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  62. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  63. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  64. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  65. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  66. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  67. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  68. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  69. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  70. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  71. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  72. package/corpus/templates/content/AGENTS.md +7 -6
  73. package/corpus/templates/content/actions/_builder-cms-read-client.ts +82 -7
  74. package/corpus/templates/content/actions/_database-source-utils.ts +149 -30
  75. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  76. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  77. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +4 -7
  78. package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
  79. package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
  80. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  81. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  82. package/corpus/templates/content/actions/pull-document.ts +5 -7
  83. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  84. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  85. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  86. package/corpus/templates/content/actions/suggest-source-join-key.ts +8 -1
  87. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  88. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  89. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  90. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  91. package/corpus/templates/content/app/i18n-data.ts +18 -0
  92. package/corpus/templates/content/changelog/2026-07-09-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -0
  93. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  94. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  95. package/corpus/templates/content/shared/api.ts +1 -0
  96. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  97. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  98. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  99. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  100. package/corpus/templates/design/AGENTS.md +45 -10
  101. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  102. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  103. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  104. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  105. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  106. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  107. package/corpus/templates/design/actions/generate-design.ts +79 -12
  108. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  109. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  110. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  111. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  112. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  113. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  114. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  115. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  116. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  117. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  118. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  119. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  120. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  121. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  122. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  123. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  124. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  125. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  126. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  127. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  128. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  129. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  130. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  131. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  132. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  133. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  134. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  135. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  136. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  137. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  138. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  139. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  140. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  141. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  142. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  143. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  144. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  145. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  146. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  147. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  148. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  149. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  150. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  151. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  152. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  153. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  154. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  155. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  156. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  157. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  158. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  159. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  160. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  161. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  162. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  163. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  164. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  165. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  166. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  167. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  168. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  169. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  170. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  171. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  172. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  173. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  174. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  175. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  176. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  177. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  178. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  179. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  180. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  181. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  182. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  183. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  184. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  185. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  186. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  187. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  188. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  189. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  190. package/corpus/templates/design/app/components/design/types.ts +33 -0
  191. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  192. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  193. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  194. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  195. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  196. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  197. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  198. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  199. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  200. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  201. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  202. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  203. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  204. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  205. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  206. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  207. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  208. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  209. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  210. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  211. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  212. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  213. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  240. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  241. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  242. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  243. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  244. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  245. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  246. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  247. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  248. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  249. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  250. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  251. package/corpus/templates/design/e2e/helpers.ts +27 -6
  252. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  253. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  254. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  255. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  256. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  257. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  258. package/corpus/templates/design/server/plugins/db.ts +41 -0
  259. package/corpus/templates/design/server/source-workspace.ts +64 -0
  260. package/corpus/templates/design/shared/board-file.ts +13 -2
  261. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  262. package/corpus/templates/design/shared/code-layer.ts +122 -8
  263. package/corpus/templates/design/shared/component-library.ts +161 -0
  264. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  265. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  266. package/corpus/templates/design/shared/figma-url.ts +149 -0
  267. package/corpus/templates/design/shared/pen-path.ts +16 -7
  268. package/corpus/templates/design/shared/source-mode.ts +1 -0
  269. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  270. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  271. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  272. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  273. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  274. package/dist/agent/production-agent.d.ts +82 -7
  275. package/dist/agent/production-agent.d.ts.map +1 -1
  276. package/dist/agent/production-agent.js +180 -17
  277. package/dist/agent/production-agent.js.map +1 -1
  278. package/dist/agent/run-manager.d.ts.map +1 -1
  279. package/dist/agent/run-manager.js +63 -14
  280. package/dist/agent/run-manager.js.map +1 -1
  281. package/dist/agent/run-store.d.ts +46 -0
  282. package/dist/agent/run-store.d.ts.map +1 -1
  283. package/dist/agent/run-store.js +129 -22
  284. package/dist/agent/run-store.js.map +1 -1
  285. package/dist/cli/design-connect.d.ts +6 -0
  286. package/dist/cli/design-connect.d.ts.map +1 -1
  287. package/dist/cli/design-connect.js +336 -139
  288. package/dist/cli/design-connect.js.map +1 -1
  289. package/dist/cli/skills.d.ts.map +1 -1
  290. package/dist/cli/skills.js +14 -0
  291. package/dist/cli/skills.js.map +1 -1
  292. package/dist/client/AssistantChat.d.ts +2 -0
  293. package/dist/client/AssistantChat.d.ts.map +1 -1
  294. package/dist/client/AssistantChat.js +19 -3
  295. package/dist/client/AssistantChat.js.map +1 -1
  296. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  297. package/dist/client/MultiTabAssistantChat.js +38 -18
  298. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  299. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  300. package/dist/client/agent-chat-adapter.js +48 -12
  301. package/dist/client/agent-chat-adapter.js.map +1 -1
  302. package/dist/client/agent-chat.d.ts +60 -0
  303. package/dist/client/agent-chat.d.ts.map +1 -1
  304. package/dist/client/agent-chat.js +122 -1
  305. package/dist/client/agent-chat.js.map +1 -1
  306. package/dist/client/chat/index.d.ts +1 -1
  307. package/dist/client/chat/index.d.ts.map +1 -1
  308. package/dist/client/chat/index.js +1 -1
  309. package/dist/client/chat/index.js.map +1 -1
  310. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  311. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  312. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  313. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  314. package/dist/client/index.d.ts +1 -1
  315. package/dist/client/index.d.ts.map +1 -1
  316. package/dist/client/index.js +1 -1
  317. package/dist/client/index.js.map +1 -1
  318. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  319. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  320. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  321. package/dist/collab/awareness-store.d.ts +5 -0
  322. package/dist/collab/awareness-store.d.ts.map +1 -1
  323. package/dist/collab/awareness-store.js +19 -12
  324. package/dist/collab/awareness-store.js.map +1 -1
  325. package/dist/collab/awareness.d.ts +2 -2
  326. package/dist/collab/awareness.d.ts.map +1 -1
  327. package/dist/collab/client.d.ts.map +1 -1
  328. package/dist/collab/client.js +13 -5
  329. package/dist/collab/client.js.map +1 -1
  330. package/dist/collab/index.d.ts +1 -0
  331. package/dist/collab/index.d.ts.map +1 -1
  332. package/dist/collab/index.js +1 -0
  333. package/dist/collab/index.js.map +1 -1
  334. package/dist/collab/recent-edits.d.ts +3 -1
  335. package/dist/collab/recent-edits.d.ts.map +1 -1
  336. package/dist/collab/recent-edits.js +57 -2
  337. package/dist/collab/recent-edits.js.map +1 -1
  338. package/dist/collab/struct-routes.d.ts +1 -1
  339. package/dist/index.browser.d.ts +1 -1
  340. package/dist/index.browser.d.ts.map +1 -1
  341. package/dist/index.browser.js +1 -1
  342. package/dist/index.browser.js.map +1 -1
  343. package/dist/observability/routes.d.ts +5 -5
  344. package/dist/progress/routes.d.ts +1 -1
  345. package/dist/resources/handlers.d.ts +3 -3
  346. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  347. package/dist/server/agent-chat-plugin.js +87 -10
  348. package/dist/server/agent-chat-plugin.js.map +1 -1
  349. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  350. package/dist/server/builder-design-systems.d.ts +15 -0
  351. package/dist/server/builder-design-systems.d.ts.map +1 -1
  352. package/dist/server/builder-design-systems.js +9 -4
  353. package/dist/server/builder-design-systems.js.map +1 -1
  354. package/package.json +1 -1
@@ -285,11 +285,18 @@ export function parseGradientCss(
285
285
  interface AngleDialProps {
286
286
  angle: number;
287
287
  onChange: (angle: number) => void;
288
+ /** Fires once when a drag gesture ends (pointerup/pointercancel) — see GradientEditorProps.onCommit. */
289
+ onCommit?: () => void;
288
290
  disabled?: boolean;
289
291
  }
290
292
 
291
293
  /** design-editor circular dial for rotating gradient angle. */
292
- function AngleDial({ angle, onChange, disabled = false }: AngleDialProps) {
294
+ function AngleDial({
295
+ angle,
296
+ onChange,
297
+ onCommit,
298
+ disabled = false,
299
+ }: AngleDialProps) {
293
300
  const dialRef = useRef<HTMLDivElement>(null);
294
301
  const draggingRef = useRef(false);
295
302
 
@@ -319,10 +326,12 @@ function AngleDial({ angle, onChange, disabled = false }: AngleDialProps) {
319
326
  };
320
327
 
321
328
  const handlePointerUp = (e: ReactPointerEvent<HTMLDivElement>) => {
329
+ const wasDragging = draggingRef.current;
322
330
  draggingRef.current = false;
323
331
  if (e.currentTarget.hasPointerCapture(e.pointerId)) {
324
332
  e.currentTarget.releasePointerCapture(e.pointerId);
325
333
  }
334
+ if (wasDragging) onCommit?.();
326
335
  };
327
336
 
328
337
  const dotAngle = ((angle - 90) * Math.PI) / 180;
@@ -349,9 +358,11 @@ function AngleDial({ angle, onChange, disabled = false }: AngleDialProps) {
349
358
  if (e.key === "ArrowRight" || e.key === "ArrowUp") {
350
359
  e.preventDefault();
351
360
  onChange((angle + 1) % 360);
361
+ onCommit?.();
352
362
  } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
353
363
  e.preventDefault();
354
364
  onChange((angle - 1 + 360) % 360);
365
+ onCommit?.();
355
366
  }
356
367
  }}
357
368
  className={cn(
@@ -394,6 +405,18 @@ function AngleDial({ angle, onChange, disabled = false }: AngleDialProps) {
394
405
  export interface GradientEditorProps {
395
406
  value: GradientValue;
396
407
  onChange: (value: GradientValue) => void;
408
+ /**
409
+ * Fires once per discrete edit or drag gesture — mirrors the
410
+ * `onChangeComplete` convention used by `DesignColorPickerProps` (see the
411
+ * doc comment there): `onChange` alone fires on every stop-drag/angle-drag
412
+ * pointermove tick (cheap live preview), while `onCommit` fires exactly
413
+ * once (stop added, stop removed, stop-drag pointerup, angle-drag
414
+ * pointerup, or a position/angle field committed via blur/Enter) so a
415
+ * caller that persists through history only records one entry per gesture
416
+ * instead of one per tick. Optional so existing callers that only wired
417
+ * `onChange` keep their current every-tick-is-final behavior.
418
+ */
419
+ onCommit?: () => void;
397
420
  selectedStopId: string;
398
421
  onSelectStop: (id: string) => void;
399
422
  disabled?: boolean;
@@ -412,6 +435,7 @@ const WRAPPER_HEIGHT = BAR_HEIGHT + HANDLE_AREA; // total component height
412
435
  export function GradientEditor({
413
436
  value,
414
437
  onChange,
438
+ onCommit,
415
439
  selectedStopId,
416
440
  onSelectStop,
417
441
  disabled = false,
@@ -419,6 +443,10 @@ export function GradientEditor({
419
443
  }: GradientEditorProps) {
420
444
  const barRef = useRef<HTMLDivElement>(null);
421
445
  const draggingStopRef = useRef<string | null>(null);
446
+ // Set once a stop-drag actually moves the pointer (vs a plain click that
447
+ // only selects the stop) — endStopDrag uses this to skip firing onCommit
448
+ // for a no-op "select" click where nothing actually changed.
449
+ const stopDragMovedRef = useRef(false);
422
450
  // Track whether a pointerdown on the bar started a drag (vs click-to-add).
423
451
  const barClickRef = useRef<{ moved: boolean; startX: number } | null>(null);
424
452
 
@@ -502,6 +530,7 @@ export function GradientEditor({
502
530
  };
503
531
  onChange({ ...value, stops: [...value.stops, newStop] });
504
532
  onSelectStop(newStop.id);
533
+ onCommit?.();
505
534
  };
506
535
 
507
536
  const startStopDrag = (
@@ -513,6 +542,7 @@ export function GradientEditor({
513
542
  event.preventDefault();
514
543
  onSelectStop(id);
515
544
  draggingStopRef.current = id;
545
+ stopDragMovedRef.current = false;
516
546
  event.currentTarget.setPointerCapture(event.pointerId);
517
547
  };
518
548
 
@@ -520,6 +550,7 @@ export function GradientEditor({
520
550
  event: ReactPointerEvent<HTMLButtonElement>,
521
551
  ) => {
522
552
  if (!draggingStopRef.current || disabled) return;
553
+ stopDragMovedRef.current = true;
523
554
  updateStopPosition(
524
555
  draggingStopRef.current,
525
556
  positionFromPointer(event.clientX),
@@ -527,10 +558,14 @@ export function GradientEditor({
527
558
  };
528
559
 
529
560
  const endStopDrag = (event: ReactPointerEvent<HTMLButtonElement>) => {
561
+ const wasDragging =
562
+ draggingStopRef.current !== null && stopDragMovedRef.current;
530
563
  draggingStopRef.current = null;
564
+ stopDragMovedRef.current = false;
531
565
  if (event.currentTarget.hasPointerCapture(event.pointerId)) {
532
566
  event.currentTarget.releasePointerCapture(event.pointerId);
533
567
  }
568
+ if (wasDragging) onCommit?.();
534
569
  };
535
570
 
536
571
  const removeStop = (id: string) => {
@@ -541,6 +576,7 @@ export function GradientEditor({
541
576
  if (selectedStopId === id) {
542
577
  onSelectStop(nearestStopId(nextStops, removed?.position) ?? "");
543
578
  }
579
+ onCommit?.();
544
580
  };
545
581
 
546
582
  const setAngle = (angle: number) => {
@@ -576,6 +612,7 @@ export function GradientEditor({
576
612
  return;
577
613
  }
578
614
  updateStopPosition(selectedStopId, parsed);
615
+ onCommit?.();
579
616
  };
580
617
 
581
618
  const revertPositionDraft = () => {
@@ -651,6 +688,28 @@ export function GradientEditor({
651
688
  e.stopPropagation();
652
689
  removeStop(stop.id);
653
690
  }}
691
+ onKeyDown={(e) => {
692
+ if (disabled) return;
693
+ // Figma parity: Delete/Backspace removes the focused stop,
694
+ // and Left/Right nudges its position (Shift for a bigger
695
+ // step) — mirrors the numeric position field's arrow-key
696
+ // behavior but works directly on the handle too.
697
+ if (e.key === "Delete" || e.key === "Backspace") {
698
+ e.preventDefault();
699
+ removeStop(stop.id);
700
+ return;
701
+ }
702
+ if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
703
+ e.preventDefault();
704
+ const step = e.shiftKey ? 10 : 1;
705
+ const delta = e.key === "ArrowRight" ? step : -step;
706
+ updateStopPosition(
707
+ stop.id,
708
+ clamp(stop.position + delta, 0, 100),
709
+ );
710
+ onCommit?.();
711
+ }
712
+ }}
654
713
  className={cn(
655
714
  // Base: round handle with white border ring + outer accent ring
656
715
  "absolute cursor-grab active:cursor-grabbing",
@@ -724,6 +783,7 @@ export function GradientEditor({
724
783
  <AngleDial
725
784
  angle={value.angle}
726
785
  onChange={setAngle}
786
+ onCommit={onCommit}
727
787
  disabled={disabled}
728
788
  />
729
789
  <div className="flex h-6 w-14 items-center overflow-hidden rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)]">
@@ -744,7 +804,17 @@ export function GradientEditor({
744
804
  setAngleInput(e.target.value);
745
805
  }
746
806
  }}
747
- onBlur={() => setAngleInput(null)}
807
+ onBlur={() => {
808
+ // `angleInput` is only non-null once the user has actually
809
+ // typed into this field (see onChange below) — mirrors the
810
+ // sibling stop-position field's commitPositionDraft, which
811
+ // only commits on an actual edit rather than unconditionally
812
+ // on every blur (tabbing through/refocusing without typing
813
+ // must not re-fire the commit).
814
+ const changed = angleInput !== null;
815
+ setAngleInput(null);
816
+ if (changed) onCommit?.();
817
+ }}
748
818
  className="h-full min-w-0 flex-1 bg-transparent px-1.5 !text-[11px] tabular-nums focus-visible:outline-none"
749
819
  />
750
820
  <span className="flex w-4 shrink-0 items-center justify-center text-[10px] text-muted-foreground">
@@ -78,6 +78,26 @@ export interface ScrubInputProps extends ScrubExpressionOptions {
78
78
  tooltipLabel?: string;
79
79
  }
80
80
 
81
+ export interface PendingScrubCommit {
82
+ value: number;
83
+ /** Incoming prop value at commit time. While this exact value remains, the
84
+ * host has not acknowledged the write yet and the optimistic draft should
85
+ * stay visible. A different incoming value is authoritative host
86
+ * normalization/rejection and must supersede the optimistic draft. */
87
+ baseline: number;
88
+ }
89
+
90
+ export function resolvePendingScrubCommit(
91
+ pending: PendingScrubCommit | null,
92
+ incomingValue: number,
93
+ options: ScrubExpressionOptions,
94
+ ): "none" | "hold" | "confirmed" | "superseded" {
95
+ if (pending === null) return "none";
96
+ const incoming = normalizeScrubNumber(incomingValue, options);
97
+ if (incoming === pending.value) return "confirmed";
98
+ return incoming === pending.baseline ? "hold" : "superseded";
99
+ }
100
+
81
101
  export function ScrubInput({
82
102
  label,
83
103
  value,
@@ -128,18 +148,27 @@ export function ScrubInput({
128
148
  // value back to the old one the instant focus leaves the input, which
129
149
  // reads as "Enter resets to the old value". Cleared as soon as a fresh
130
150
  // `value` prop confirms (or supersedes) the pending commit.
131
- const pendingCommitRef = useRef<number | null>(null);
151
+ const pendingCommitRef = useRef<PendingScrubCommit | null>(null);
132
152
  const options = { unit, min, max, precision };
133
153
 
134
154
  useEffect(() => {
135
- if (pendingCommitRef.current !== null) {
136
- if (normalizeScrubNumber(value, options) === pendingCommitRef.current) {
137
- // The host echoed back exactly what we committed — resolved, safe to
138
- // resume syncing from the prop again.
155
+ if (mixed) pendingCommitRef.current = null;
156
+ const resolution = resolvePendingScrubCommit(
157
+ pendingCommitRef.current,
158
+ value,
159
+ options,
160
+ );
161
+ if (resolution !== "none") {
162
+ if (resolution === "confirmed" || resolution === "superseded") {
163
+ // The host either echoed exactly what we committed or returned a new,
164
+ // authoritative normalized/rejected value. In both cases resume prop
165
+ // synchronization. The old equality-only logic held forever on the
166
+ // second path, leaving the field permanently stuck on a value the
167
+ // canvas never accepted.
139
168
  pendingCommitRef.current = null;
140
169
  } else {
141
- // Still waiting for the round-trip (or the host is reporting a
142
- // different in-flight value) don't stomp the optimistic draft.
170
+ // Still seeing the exact pre-commit prop don't stomp the optimistic
171
+ // draft while the source-write round trip is pending.
143
172
  return;
144
173
  }
145
174
  }
@@ -151,7 +180,7 @@ export function ScrubInput({
151
180
  setDraft(formatted);
152
181
  }
153
182
  // eslint-disable-next-line react-hooks/exhaustive-deps -- `options` is a fresh object every render; the individual fields it's built from (unit/min/max/precision) are already listed below.
154
- }, [focused, mixed, precision, unit, value]);
183
+ }, [focused, max, min, mixed, precision, unit, value]);
155
184
 
156
185
  const resolvedTooltipLabel = tooltipLabel ?? ariaLabel ?? label;
157
186
 
@@ -162,7 +191,12 @@ export function ScrubInput({
162
191
  // prop before the host's round-trip lands (see pendingCommitRef above).
163
192
  // Preview ticks don't need this: they're expected to be superseded by
164
193
  // the next tick or the gesture's own final commit almost immediately.
165
- if (meta.phase === "commit") pendingCommitRef.current = normalized;
194
+ if (meta.phase === "commit") {
195
+ pendingCommitRef.current = {
196
+ value: normalized,
197
+ baseline: normalizeScrubNumber(value, options),
198
+ };
199
+ }
166
200
  onChange(normalized, meta);
167
201
  const formatted = formatScrubValue(normalized, options);
168
202
  draftRef.current = formatted;
@@ -192,7 +226,10 @@ export function ScrubInput({
192
226
  if (parsed.value !== value || mixed) {
193
227
  // See setNextValue's pendingCommitRef comment — this text-commit path
194
228
  // (Enter/blur) bypasses setNextValue, so mark it pending here too.
195
- pendingCommitRef.current = parsed.value;
229
+ pendingCommitRef.current = {
230
+ value: parsed.value,
231
+ baseline: normalizeScrubNumber(value, options),
232
+ };
196
233
  onChange(parsed.value, {
197
234
  source: "commit",
198
235
  expression: currentDraft,
@@ -348,7 +385,10 @@ export function ScrubInput({
348
385
  // authoritative value as pending confirmation so releasing the drag
349
386
  // can't be clobbered back to the pre-drag value by a slow host
350
387
  // round-trip (same class of bug as the Enter/blur text-commit case).
351
- pendingCommitRef.current = lastScrubValueRef.current;
388
+ pendingCommitRef.current = {
389
+ value: lastScrubValueRef.current,
390
+ baseline: normalizeScrubNumber(value, options),
391
+ };
352
392
  onChange(lastScrubValueRef.current, {
353
393
  source: "scrub",
354
394
  phase: "commit",
@@ -23,7 +23,7 @@ import {
23
23
  IconSearch,
24
24
  IconX,
25
25
  } from "@tabler/icons-react";
26
- import { Component, useMemo, useState, type ReactNode } from "react";
26
+ import { Component, useMemo, useRef, useState, type ReactNode } from "react";
27
27
 
28
28
  import { Input } from "@/components/ui/input";
29
29
  import {
@@ -169,8 +169,27 @@ function ShaderThumbnail({
169
169
  export interface ShaderFillsPanelProps {
170
170
  /** Currently-applied shader descriptor, if the fill is already a shader. */
171
171
  descriptor?: ShaderDescriptor;
172
- /** Emits when the user applies/tunes a shader. value = CSS fallback fill. */
172
+ /**
173
+ * Cheap live preview — fires on every ShaderControls tuning tick (typing
174
+ * or dragging a uniform) as well as once for a discrete preset/create-new
175
+ * pick. Should only update the visual CSS fallback fill; never trigger
176
+ * expensive persistence work.
177
+ */
173
178
  onApply: (descriptor: ShaderDescriptor, css: string) => void;
179
+ /**
180
+ * Fires once per gesture/discrete action with the same final
181
+ * descriptor+css already reported via `onApply` — mirrors GradientEditor's
182
+ * `onCommit` convention (see GradientEditor.tsx): `onApply` alone fires on
183
+ * every tuning tick for live preview, `onCommit` fires exactly once when a
184
+ * drag/type gesture ends (detected via pointerup/blur bubbling out of the
185
+ * tuning area below — `ShaderControls` doesn't surface its own ScrubInput
186
+ * gesture phase upward) or immediately for a discrete preset/create-new
187
+ * pick, so a caller that persists through undo history and the
188
+ * `apply-shader` codegen mutation only does so once per edit instead of
189
+ * once per tick. Optional so an existing caller that only wires `onApply`
190
+ * keeps that prop as the single source of truth.
191
+ */
192
+ onCommit?: (descriptor: ShaderDescriptor, css: string) => void;
174
193
  /** Close the shader panel and return to the color picker. */
175
194
  onBack: () => void;
176
195
  /** Optional design context forwarded to the apply-shader action. */
@@ -186,6 +205,7 @@ export interface ShaderFillsPanelProps {
186
205
  export function ShaderFillsPanel({
187
206
  descriptor,
188
207
  onApply,
208
+ onCommit,
189
209
  onBack,
190
210
  applyContext,
191
211
  disabled = false,
@@ -195,6 +215,18 @@ export function ShaderFillsPanel({
195
215
  descriptor ?? null,
196
216
  );
197
217
  const applyShader = useActionMutation("apply-shader");
218
+ // Last descriptor reported to `onApply`, so a bubbled pointerup/blur that
219
+ // ends a tuning gesture can re-commit that exact value once, without
220
+ // needing ShaderControls to surface its own gesture-end signal.
221
+ const lastAppliedRef = useRef<ShaderDescriptor | null>(descriptor ?? null);
222
+ // Whether `preview()` has applied a new descriptor since the last
223
+ // `commitNow()`. Any pointerup/blur that bubbles out of the tuning
224
+ // container — opening a Select, clicking a checkbox, tabbing between
225
+ // fields — would otherwise re-fire the real apply-shader mutation on an
226
+ // unchanged descriptor just because `lastAppliedRef` is seeded on mount.
227
+ // Gate `commitLastPreview` on this flag instead: only a real preview tick
228
+ // sets it, and every `commitNow` clears it.
229
+ const dirtyRef = useRef(false);
198
230
 
199
231
  const filtered = useMemo(() => {
200
232
  const query = search.trim().toLowerCase();
@@ -206,13 +238,26 @@ export function ShaderFillsPanel({
206
238
  );
207
239
  }, [search]);
208
240
 
209
- /** Validate + surface the descriptor for the agent, then emit the CSS fill. */
210
- const commit = (next: ShaderDescriptor) => {
241
+ /**
242
+ * Cheap live preview: updates the thumbnail/detail state and the caller's
243
+ * CSS fallback fill. Called on every ShaderControls tick — must never do
244
+ * the expensive apply-shader codegen mutation (see commitNow below).
245
+ */
246
+ const preview = (next: ShaderDescriptor) => {
211
247
  setActive(next);
248
+ lastAppliedRef.current = next;
249
+ dirtyRef.current = true;
250
+ onApply(next, shaderDescriptorToCss(next));
251
+ };
252
+
253
+ /** Validate + surface the descriptor for the agent exactly once. */
254
+ const commitNow = (next: ShaderDescriptor) => {
255
+ dirtyRef.current = false;
212
256
  const css = shaderDescriptorToCss(next);
213
- onApply(next, css);
257
+ onCommit?.(next, css);
214
258
  // Fire-and-forget validation/codegen so the agent can write real shader
215
- // code. The picker fill is applied immediately above regardless of result.
259
+ // code. The picker fill is already applied via `preview` above,
260
+ // regardless of this mutation's result.
216
261
  applyShader.mutate(
217
262
  {
218
263
  surface: SHADER_PRESET_MAP[next.preset]?.isEffect ? "effect" : "fill",
@@ -250,6 +295,25 @@ export function ShaderFillsPanel({
250
295
  );
251
296
  };
252
297
 
298
+ /** Discrete, one-shot pick (preset thumbnail / create-new tile): preview + commit in the same tick, same as before this change. */
299
+ const pick = (next: ShaderDescriptor) => {
300
+ preview(next);
301
+ commitNow(next);
302
+ };
303
+
304
+ /**
305
+ * Ends a ShaderControls tuning gesture: pointerup/blur bubbling out of the
306
+ * tuning area (see the wrapping div below). Gated on `dirtyRef` so any
307
+ * pointerup/blur that bubbles out without an intervening `preview()` tick —
308
+ * opening a Select, clicking a checkbox, tabbing between fields — is a
309
+ * no-op instead of re-committing the unchanged descriptor.
310
+ */
311
+ const commitLastPreview = () => {
312
+ if (dirtyRef.current && lastAppliedRef.current) {
313
+ commitNow(lastAppliedRef.current);
314
+ }
315
+ };
316
+
253
317
  // ── Detail view: a preset is selected → tune it with ShaderControls ───────
254
318
  if (active) {
255
319
  const preset = SHADER_PRESET_MAP[active.preset];
@@ -277,10 +341,19 @@ export function ShaderFillsPanel({
277
341
  <IconX className="size-3" />
278
342
  </button>
279
343
  </div>
280
- <div className="border-t border-border/70 p-2">
344
+ {/* onPointerUp/onBlur here catch the bubbled event that ends a
345
+ ShaderControls drag/type gesture (pointer capture redirects the
346
+ event's target but it still bubbles through this ancestor), so
347
+ the expensive apply-shader mutation commits exactly once per
348
+ gesture instead of once per preview tick. */}
349
+ <div
350
+ className="border-t border-border/70 p-2"
351
+ onPointerUp={commitLastPreview}
352
+ onBlur={commitLastPreview}
353
+ >
281
354
  <ShaderControls
282
355
  descriptor={active}
283
- onChange={(next) => commit(next)}
356
+ onChange={(next) => preview(next)}
284
357
  />
285
358
  </div>
286
359
  </div>
@@ -301,7 +374,7 @@ export function ShaderFillsPanel({
301
374
  type="button"
302
375
  aria-label={"Create new shader" /* i18n-ignore */}
303
376
  disabled={disabled}
304
- onClick={() => commit(descriptorFromPreset(SHADER_PRESETS[0]))}
377
+ onClick={() => pick(descriptorFromPreset(SHADER_PRESETS[0]))}
305
378
  className="flex size-5 items-center justify-center rounded text-muted-foreground hover:bg-[var(--design-editor-control-bg)] hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40"
306
379
  >
307
380
  <IconPlus className="size-3.5" />
@@ -358,7 +431,7 @@ export function ShaderFillsPanel({
358
431
  <button
359
432
  type="button"
360
433
  disabled={disabled}
361
- onClick={() => commit(descriptorFromPreset(SHADER_PRESETS[0]))}
434
+ onClick={() => pick(descriptorFromPreset(SHADER_PRESETS[0]))}
362
435
  className={cn(
363
436
  "group relative flex aspect-[4/3] w-full flex-col items-center justify-center gap-1 rounded-md border border-dashed border-[var(--design-editor-control-border)] text-muted-foreground transition-colors",
364
437
  "hover:border-foreground/40 hover:text-foreground",
@@ -399,7 +472,7 @@ export function ShaderFillsPanel({
399
472
  type="button"
400
473
  disabled={disabled}
401
474
  aria-label={preset.label}
402
- onClick={() => commit(descriptorFromPreset(preset))}
475
+ onClick={() => pick(descriptorFromPreset(preset))}
403
476
  className={cn(
404
477
  "group flex flex-col gap-1 text-left focus-visible:outline-none",
405
478
  disabled && "pointer-events-none opacity-40",
@@ -14,6 +14,9 @@ export {
14
14
  export {
15
15
  AutoLayoutMatrix,
16
16
  type AutoLayoutDirection,
17
+ type AutoLayoutFlow,
18
+ type AutoLayoutGridTrackSizing,
19
+ type AutoLayoutGridValue,
17
20
  type AutoLayoutMatrixLabels,
18
21
  type AutoLayoutMatrixProps,
19
22
  type AutoLayoutMatrixValue,