@agent-native/core 0.92.10 → 0.92.11

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 (334) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +23 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +181 -15
  5. package/corpus/core/src/agent/run-manager.ts +31 -4
  6. package/corpus/core/src/agent/run-store.ts +80 -0
  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.ts +165 -1
  12. package/corpus/core/src/client/chat/index.ts +9 -1
  13. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
  14. package/corpus/core/src/client/index.ts +5 -0
  15. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  16. package/corpus/core/src/collab/awareness-store.ts +23 -12
  17. package/corpus/core/src/collab/client.ts +16 -5
  18. package/corpus/core/src/collab/index.ts +4 -0
  19. package/corpus/core/src/collab/recent-edits.ts +65 -2
  20. package/corpus/core/src/index.browser.ts +1 -0
  21. package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
  22. package/corpus/core/src/server/builder-design-systems.ts +30 -4
  23. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
  24. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
  25. package/corpus/templates/clips/AGENTS.md +4 -0
  26. package/corpus/templates/clips/actions/create-recording.ts +2 -0
  27. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
  28. package/corpus/templates/clips/actions/request-transcript.ts +102 -33
  29. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
  30. package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
  31. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +48 -4
  32. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  33. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  34. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  44. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  45. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  46. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  47. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  48. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  56. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  57. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  58. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  59. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  60. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  61. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  62. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  63. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  64. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  65. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  66. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  67. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  68. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  69. package/corpus/templates/content/AGENTS.md +7 -6
  70. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  71. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  72. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  73. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  74. package/corpus/templates/content/actions/pull-document.ts +5 -7
  75. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  76. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  77. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  78. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  79. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  80. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  81. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  82. package/corpus/templates/content/app/i18n-data.ts +18 -0
  83. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  84. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  85. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  86. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  87. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  88. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  89. package/corpus/templates/design/AGENTS.md +45 -10
  90. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  91. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  92. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  93. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  94. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  95. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  96. package/corpus/templates/design/actions/generate-design.ts +79 -12
  97. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  98. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  99. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  100. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  101. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  102. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  103. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  104. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  105. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  106. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  107. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  108. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  109. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  110. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  111. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  112. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  113. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  114. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  115. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  116. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  117. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  118. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  119. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  120. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  121. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  122. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  123. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  124. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  125. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  126. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  127. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  128. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  129. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  130. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  131. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  132. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  133. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  134. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  135. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  136. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  137. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  138. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  139. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  140. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  141. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  142. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  143. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  144. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  145. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  146. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  147. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  148. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  149. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  150. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  151. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  152. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  153. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  154. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  155. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  156. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  157. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  158. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  159. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  160. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  161. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  162. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  163. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  164. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  165. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  166. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  167. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  168. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  169. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  170. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  171. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  172. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  173. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  174. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  175. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  176. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  177. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  178. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  179. package/corpus/templates/design/app/components/design/types.ts +33 -0
  180. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  181. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  182. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  183. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  184. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  185. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  186. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  187. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  188. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  189. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  190. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  191. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  192. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  193. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  194. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  195. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  196. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  197. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  198. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  199. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  200. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  201. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  202. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  203. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  239. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  240. package/corpus/templates/design/e2e/helpers.ts +27 -6
  241. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  242. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  243. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  244. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  245. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  246. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  247. package/corpus/templates/design/server/plugins/db.ts +41 -0
  248. package/corpus/templates/design/server/source-workspace.ts +64 -0
  249. package/corpus/templates/design/shared/board-file.ts +13 -2
  250. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  251. package/corpus/templates/design/shared/code-layer.ts +122 -8
  252. package/corpus/templates/design/shared/component-library.ts +161 -0
  253. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  254. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  255. package/corpus/templates/design/shared/figma-url.ts +149 -0
  256. package/corpus/templates/design/shared/pen-path.ts +16 -7
  257. package/corpus/templates/design/shared/source-mode.ts +1 -0
  258. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  259. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  260. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  261. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  262. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  263. package/dist/agent/production-agent.d.ts +79 -6
  264. package/dist/agent/production-agent.d.ts.map +1 -1
  265. package/dist/agent/production-agent.js +161 -14
  266. package/dist/agent/production-agent.js.map +1 -1
  267. package/dist/agent/run-manager.d.ts.map +1 -1
  268. package/dist/agent/run-manager.js +29 -5
  269. package/dist/agent/run-manager.js.map +1 -1
  270. package/dist/agent/run-store.d.ts +46 -0
  271. package/dist/agent/run-store.d.ts.map +1 -1
  272. package/dist/agent/run-store.js +61 -0
  273. package/dist/agent/run-store.js.map +1 -1
  274. package/dist/cli/design-connect.d.ts +6 -0
  275. package/dist/cli/design-connect.d.ts.map +1 -1
  276. package/dist/cli/design-connect.js +336 -139
  277. package/dist/cli/design-connect.js.map +1 -1
  278. package/dist/cli/skills.d.ts.map +1 -1
  279. package/dist/cli/skills.js +14 -0
  280. package/dist/cli/skills.js.map +1 -1
  281. package/dist/client/AssistantChat.d.ts +2 -0
  282. package/dist/client/AssistantChat.d.ts.map +1 -1
  283. package/dist/client/AssistantChat.js +19 -3
  284. package/dist/client/AssistantChat.js.map +1 -1
  285. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  286. package/dist/client/MultiTabAssistantChat.js +38 -18
  287. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  288. package/dist/client/agent-chat.d.ts +60 -0
  289. package/dist/client/agent-chat.d.ts.map +1 -1
  290. package/dist/client/agent-chat.js +122 -1
  291. package/dist/client/agent-chat.js.map +1 -1
  292. package/dist/client/chat/index.d.ts +1 -1
  293. package/dist/client/chat/index.d.ts.map +1 -1
  294. package/dist/client/chat/index.js +1 -1
  295. package/dist/client/chat/index.js.map +1 -1
  296. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  297. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  298. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  299. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  300. package/dist/client/index.d.ts +1 -1
  301. package/dist/client/index.d.ts.map +1 -1
  302. package/dist/client/index.js +1 -1
  303. package/dist/client/index.js.map +1 -1
  304. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  305. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  306. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  307. package/dist/collab/awareness-store.d.ts +5 -0
  308. package/dist/collab/awareness-store.d.ts.map +1 -1
  309. package/dist/collab/awareness-store.js +19 -12
  310. package/dist/collab/awareness-store.js.map +1 -1
  311. package/dist/collab/client.d.ts.map +1 -1
  312. package/dist/collab/client.js +13 -5
  313. package/dist/collab/client.js.map +1 -1
  314. package/dist/collab/index.d.ts +1 -0
  315. package/dist/collab/index.d.ts.map +1 -1
  316. package/dist/collab/index.js +1 -0
  317. package/dist/collab/index.js.map +1 -1
  318. package/dist/collab/recent-edits.d.ts +3 -1
  319. package/dist/collab/recent-edits.d.ts.map +1 -1
  320. package/dist/collab/recent-edits.js +57 -2
  321. package/dist/collab/recent-edits.js.map +1 -1
  322. package/dist/index.browser.d.ts +1 -1
  323. package/dist/index.browser.d.ts.map +1 -1
  324. package/dist/index.browser.js +1 -1
  325. package/dist/index.browser.js.map +1 -1
  326. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  327. package/dist/server/agent-chat-plugin.js +87 -10
  328. package/dist/server/agent-chat-plugin.js.map +1 -1
  329. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  330. package/dist/server/builder-design-systems.d.ts +15 -0
  331. package/dist/server/builder-design-systems.d.ts.map +1 -1
  332. package/dist/server/builder-design-systems.js +9 -4
  333. package/dist/server/builder-design-systems.js.map +1 -1
  334. package/package.json +1 -1
@@ -152,6 +152,27 @@ const LEAF_TAGS = new Set([
152
152
  "iframe",
153
153
  ]);
154
154
 
155
+ /**
156
+ * Explicit, unambiguous signals that an element IS a text object: a real
157
+ * text tag, an authoritative `primitiveKind === "text"` marker, or a
158
+ * `draft-text-*` tool-drawn id. Deliberately excludes `isTextElement()`'s
159
+ * last-resort fallback for payloads with no primitive marker at all (a
160
+ * childless div with its own text content) — that heuristic exists to catch
161
+ * genuine T-tool text primitives whose payload happens to be missing
162
+ * `primitiveKind`, but it also matches ordinary content divs with no
163
+ * relation to the T-tool at all (Tailwind pill/badge/button-label markup is
164
+ * routinely a childless `<div>` with its own text). Used by
165
+ * `isContainerElement()` below, which must not misclassify those ordinary
166
+ * divs as text just because they're childless and have text content.
167
+ */
168
+ function hasExplicitTextIdentity(element: ElementInfo): boolean {
169
+ const tag = (element.tagName || "").toLowerCase();
170
+ if (TEXT_TAGS.has(tag)) return true;
171
+ if (element.primitiveKind) return element.primitiveKind === "text";
172
+ const nodeId = element.sourceId || element.pendingNodeId || "";
173
+ return nodeId.startsWith("draft-text-");
174
+ }
175
+
155
176
  /**
156
177
  * Whether the element should expose the Auto layout section. True for anything
157
178
  * already laid out with flexbox, or any block-level container tag that isn't a
@@ -159,6 +180,26 @@ const LEAF_TAGS = new Set([
159
180
  * children show the full Auto layout section the same way does.
160
181
  */
161
182
  export function isContainerElement(element: ElementInfo): boolean {
183
+ // T-tool text primitives are divs and use `display:flex` for vertical text
184
+ // alignment, but they are still leaf text layers rather than auto-layout
185
+ // containers, so check text identity before the flex/container shortcuts
186
+ // (fixing empty frame controls must not expose Flow/Padding on text
187
+ // layers). This short-circuit must only fire on an EXPLICIT text signal
188
+ // (see `hasExplicitTextIdentity`) rather than `isTextElement()`'s generic
189
+ // childless-div-with-text fallback: that fallback alone would also match
190
+ // ordinary text-only divs with no primitive/tag/id marker at all —
191
+ // ubiquitous in generated markup (Tailwind pills/badges/button-label
192
+ // divs) — stripping the Flow/Padding/Auto-layout sections from every one
193
+ // of them even though they're legitimate flex/block containers.
194
+ if (hasExplicitTextIdentity(element)) return false;
195
+ // Canvas primitive markers are authoritative. This code-backed editor lets
196
+ // rectangles act as lightweight containers (nest-on-drop promotes them to
197
+ // auto layout), and frames are containers by definition. Other drawn
198
+ // shapes remain leaves even though several are represented by a plain div.
199
+ const primitiveKind = element.primitiveKind?.trim().toLowerCase();
200
+ if (primitiveKind) {
201
+ return ["frame", "rectangle", "rect"].includes(primitiveKind);
202
+ }
162
203
  if (element.isFlexContainer || element.isGridContainer) return true;
163
204
  const tag = (element.tagName || "").toLowerCase();
164
205
  if (TEXT_TAGS.has(tag) || LEAF_TAGS.has(tag)) return false;
@@ -176,13 +217,6 @@ export function isParentGrid(element: ElementInfo): boolean {
176
217
  return Boolean(element.parentDisplay?.toLowerCase().includes("grid"));
177
218
  }
178
219
 
179
- export function elementHasLayoutChildren(element: ElementInfo): boolean {
180
- if (typeof element.childElementCount === "number") {
181
- return element.childElementCount > 0;
182
- }
183
- return Boolean(element.htmlContent?.match(/<\s*[a-zA-Z][^>]*>/));
184
- }
185
-
186
220
  export function parentFlexDirection(
187
221
  element: ElementInfo,
188
222
  ): AutoLayoutSizingAxis {
@@ -5,7 +5,7 @@ import { roundToOneDecimal } from "./position-helpers";
5
5
 
6
6
  export function elementIdentityKey(element: ElementInfo): string {
7
7
  return [
8
- element.sourceId ?? element.id ?? element.selector ?? element.tagName,
8
+ elementStableKey(element),
9
9
  Math.round(element.boundingRect.x),
10
10
  Math.round(element.boundingRect.y),
11
11
  Math.round(element.boundingRect.width),
@@ -18,9 +18,22 @@ export function elementIdentityKey(element: ElementInfo): string {
18
18
  * resizing (unlike elementIdentityKey, which folds in the bounding rect and
19
19
  * therefore changes on every resize — exactly what an aspect-ratio lock needs
20
20
  * to persist across). Falls back through the same id chain.
21
+ *
22
+ * Uses `||`, not `??`, between the fallback candidates: the bridge
23
+ * (editor-chrome.bridge.ts getElementInfo) reports `sourceId` as the empty
24
+ * string `""` — not `undefined` — for any element that isn't source-backed
25
+ * (e.g. a runtime-only DOM node in a connected localhost/fusion screen). `??`
26
+ * only skips a nullish left-hand side, so an empty-string `sourceId` short-
27
+ * circuited the whole chain and every non-source-backed element collapsed to
28
+ * the SAME key ("" here, and just the rounded bounding rect in
29
+ * elementIdentityKey). For this hook that meant locking the aspect ratio on
30
+ * one such element silently applied its captured ratio to every other
31
+ * non-source-backed element too. `||` falls through empty strings the same
32
+ * way the bridge itself already does (`getSourceId(el) || getSelector(el)`),
33
+ * reaching the real per-element `selector` instead. Exported for tests.
21
34
  */
22
- function elementStableKey(element: ElementInfo): string {
23
- return element.sourceId ?? element.id ?? element.selector ?? element.tagName;
35
+ export function elementStableKey(element: ElementInfo): string {
36
+ return element.sourceId || element.id || element.selector || element.tagName;
24
37
  }
25
38
 
26
39
  /**
@@ -20,16 +20,29 @@ import {
20
20
  type ScrubInputChangeMeta,
21
21
  } from "../inspector";
22
22
  import type { ElementInfo } from "../types";
23
+ import { roundToOneDecimal } from "./position-helpers";
23
24
  import { isMixedValue } from "./selection-helpers";
24
25
  import {
25
26
  resolveBreakpointOverride,
26
27
  type BreakpointOverrideFieldContext,
27
28
  type MotionKeyframeFieldContext,
29
+ type StyleChangeMeta,
28
30
  type StyleChangeHandler,
29
31
  type StylesChangeHandler,
30
32
  } from "./style-change-types";
31
33
  import { parseNumericValue, sidesAreLinked } from "./style-options";
32
34
 
35
+ /**
36
+ * The CSS length DesignSpacingControl commits for one side's scrub/typed
37
+ * value. Rounds to one decimal place (not a whole pixel) to match the
38
+ * `precision={1}` the four per-side ScrubInput fields below advertise — see
39
+ * `roundToOneDecimal`'s docstring on why 0.5-unit values must survive the
40
+ * round trip. Exported so the precision contract is unit-testable directly.
41
+ */
42
+ export function resolveSpacingSideValue(value: number): string {
43
+ return `${roundToOneDecimal(value)}px`;
44
+ }
45
+
33
46
  export function DesignSpacingControl({
34
47
  label,
35
48
  values,
@@ -47,14 +60,19 @@ export function DesignSpacingControl({
47
60
  bottom: parseNumericValue(values.bottom || "0"),
48
61
  left: parseNumericValue(values.left || "0"),
49
62
  };
50
- const linkedValue = Math.round(
63
+ const linkedValue = roundToOneDecimal(
51
64
  (numeric.top + numeric.right + numeric.bottom + numeric.left) / 4,
52
65
  );
53
66
  const setSide = (
54
67
  side: "Top" | "Right" | "Bottom" | "Left",
55
68
  value: number,
56
69
  ) => {
57
- onChange(side, `${Math.round(value)}px`);
70
+ // Was `Math.round(value)`, which silently floored every typed/scrubbed
71
+ // 0.5px value to a whole pixel — contradicting the `precision={1}` these
72
+ // fields advertise (below) and diverging from every other ScrubInput
73
+ // commit site in this panel (position X/Y, stroke weight, font size all
74
+ // use roundToOneDecimal).
75
+ onChange(side, resolveSpacingSideValue(value));
58
76
  };
59
77
  const setAll = (value: number) => {
60
78
  (["Top", "Right", "Bottom", "Left"] as const).forEach((side) =>
@@ -98,6 +116,7 @@ export function DesignSpacingControl({
98
116
  onChange={setAll}
99
117
  unit="px"
100
118
  min={0}
119
+ precision={1}
101
120
  labelClassName="w-16"
102
121
  inputClassName="h-6"
103
122
  />
@@ -304,12 +323,13 @@ export function commitStylePatch(
304
323
  styles: Record<string, string>,
305
324
  onStyleChange: StyleChangeHandler,
306
325
  onStylesChange?: StylesChangeHandler,
326
+ meta?: StyleChangeMeta,
307
327
  ) {
308
328
  if (onStylesChange) {
309
- onStylesChange(styles);
329
+ onStylesChange(styles, meta);
310
330
  return;
311
331
  }
312
332
  Object.entries(styles).forEach(([property, value]) => {
313
- onStyleChange(property, value);
333
+ onStyleChange(property, value, meta);
314
334
  });
315
335
  }
@@ -11,7 +11,7 @@ import {
11
11
  type DesignGradientType,
12
12
  type ExportSettingsValue,
13
13
  } from "../inspector";
14
- import { cssColorOrFallback } from "./position-helpers";
14
+ import { colorHasVisibleAlpha, cssColorOrFallback } from "./position-helpers";
15
15
 
16
16
  export const SOLID_FILL_ID = "solid";
17
17
  export const FILL_LAYER_PREFIX = "layer:";
@@ -113,24 +113,45 @@ export function isLayerHiddenBySize(sizeEntry: string | undefined): boolean {
113
113
  * Rewrites the background-size list so `index` is hidden/shown via the
114
114
  * zero-size marker, padding shorter lists with "auto" (the CSS default) so
115
115
  * every other layer keeps rendering at its current/default size.
116
+ *
117
+ * `restoreValue` is the size to bring back when un-hiding (`hidden: false`).
118
+ * Without it, re-showing always reset the layer to "auto", permanently
119
+ * discarding whatever custom cover/contain/percentage size the layer had
120
+ * before it was hidden — callers should capture the layer's own size entry
121
+ * before hiding it (it's about to be overwritten with the marker) and pass
122
+ * it back in here on the show path. Defaults to "auto" so existing callers
123
+ * that don't have a stashed value keep today's behavior.
116
124
  */
117
125
  export function withLayerSizeMarker(
118
126
  sizeLayers: string[],
119
127
  layerCount: number,
120
128
  index: number,
121
129
  hidden: boolean,
130
+ restoreValue?: string,
122
131
  ): string {
123
132
  const next = Array.from(
124
133
  { length: layerCount },
125
134
  (_, i) => sizeLayers[i] || "auto",
126
135
  );
127
- next[index] = hidden ? HIDDEN_LAYER_SIZE_MARKER : "auto";
136
+ next[index] = hidden ? HIDDEN_LAYER_SIZE_MARKER : restoreValue || "auto";
128
137
  return joinCssLayers(next);
129
138
  }
130
139
 
131
140
  export function splitCssLayers(value: string): string[] {
132
141
  const trimmed = value.trim();
133
- if (!trimmed || trimmed === "none") return [];
142
+ // CSS-wide keywords represent the property's default/inherited value as a
143
+ // whole; they are not real comma-stack layers and cannot legally be
144
+ // preserved beside a new gradient/image (`url(...), initial` invalidates
145
+ // the entire declaration). Shorthand-authored page backgrounds commonly
146
+ // surface as `initial` through CSSStyleDeclaration, so normalize these to
147
+ // an empty editable layer stack before adding a real fill.
148
+ if (
149
+ !trimmed ||
150
+ trimmed === "none" ||
151
+ /^(?:initial|inherit|unset|revert|revert-layer)$/i.test(trimmed)
152
+ ) {
153
+ return [];
154
+ }
134
155
  const layers: string[] = [];
135
156
  let depth = 0;
136
157
  let start = 0;
@@ -197,6 +218,189 @@ export function removeFillLayerAtIndex(
197
218
  };
198
219
  }
199
220
 
221
+ /** One image-fill layer's four index-aligned CSS values. */
222
+ export interface ImageFillLayerStyles {
223
+ backgroundImage: string;
224
+ backgroundSize: string;
225
+ backgroundRepeat: string;
226
+ backgroundPosition: string;
227
+ }
228
+
229
+ /**
230
+ * Sets one image-fill layer's four index-aligned CSS values at `index`,
231
+ * preserving every sibling layer's own image/size/repeat/position — the
232
+ * image-fill analog of `removeFillLayerAtIndex`/`reorderFillLayers`. `index`
233
+ * may be one past the current layer count to append a new layer. Shorter
234
+ * parallel arrays are padded with each property's CSS default (mirrors
235
+ * `addFillLayerPatch`'s defaults) so sibling layers keep rendering unchanged.
236
+ */
237
+ export function setImageFillLayerPatch(
238
+ layers: FillLayerArrays,
239
+ index: number,
240
+ imageStyles: ImageFillLayerStyles,
241
+ ): Record<
242
+ | "backgroundImage"
243
+ | "backgroundSize"
244
+ | "backgroundRepeat"
245
+ | "backgroundPosition",
246
+ string
247
+ > {
248
+ const layerCount = Math.max(layers.backgroundImage.length, index + 1);
249
+ const buildLayer = (existing: string[], fallback: string, override: string) =>
250
+ joinCssLayers(
251
+ Array.from({ length: layerCount }, (_, i) =>
252
+ i === index ? override : existing[i] || fallback,
253
+ ),
254
+ );
255
+ return {
256
+ backgroundImage: buildLayer(
257
+ layers.backgroundImage,
258
+ "none",
259
+ imageStyles.backgroundImage,
260
+ ),
261
+ backgroundSize: buildLayer(
262
+ layers.backgroundSize,
263
+ "auto",
264
+ imageStyles.backgroundSize,
265
+ ),
266
+ backgroundRepeat: buildLayer(
267
+ layers.backgroundRepeat,
268
+ "no-repeat",
269
+ imageStyles.backgroundRepeat,
270
+ ),
271
+ backgroundPosition: buildLayer(
272
+ layers.backgroundPosition,
273
+ "0% 0%",
274
+ imageStyles.backgroundPosition,
275
+ ),
276
+ };
277
+ }
278
+
279
+ /**
280
+ * Full patch-building logic behind `ColorInput`'s image-fill handler.
281
+ *
282
+ * Previously, editing the base fill row's image (via `ImageFillControls` ->
283
+ * `DesignColorPicker.onImageFillChange`) always replaced the *whole*
284
+ * `backgroundImage`/`backgroundSize`/`backgroundRepeat`/`backgroundPosition`
285
+ * properties with a single-layer patch (see `imageFillToBackgroundStyles`),
286
+ * silently discarding any other gradient/image layers already stacked below
287
+ * it — there was no layer-index-aware merge at all.
288
+ *
289
+ * When `layerIndex` is a real index (editing an existing layer's own image),
290
+ * only that layer's four values are overwritten, preserving every sibling —
291
+ * see `setImageFillLayerPatch`. When `layerIndex` is `null` (the base
292
+ * solid/text row switching its paint type to Image, with no layer selected
293
+ * yet), the image becomes a new layer PREPENDED above the existing layer
294
+ * stack — mirroring `solidToGradientPatch`'s solid -> gradient conversion —
295
+ * instead of clobbering the whole background stack.
296
+ */
297
+ export function imageFillChangePatch(
298
+ layers: FillLayerArrays,
299
+ layerIndex: number | null,
300
+ imageStyles: ImageFillLayerStyles,
301
+ ): Record<
302
+ | "backgroundImage"
303
+ | "backgroundSize"
304
+ | "backgroundRepeat"
305
+ | "backgroundPosition",
306
+ string
307
+ > {
308
+ if (layerIndex !== null) {
309
+ return setImageFillLayerPatch(layers, layerIndex, imageStyles);
310
+ }
311
+ return {
312
+ backgroundImage: joinCssLayers([
313
+ imageStyles.backgroundImage,
314
+ ...layers.backgroundImage,
315
+ ]),
316
+ backgroundSize: joinCssLayers([
317
+ imageStyles.backgroundSize,
318
+ ...layers.backgroundSize,
319
+ ]),
320
+ backgroundRepeat: joinCssLayers([
321
+ imageStyles.backgroundRepeat,
322
+ ...layers.backgroundRepeat,
323
+ ]),
324
+ backgroundPosition: joinCssLayers([
325
+ imageStyles.backgroundPosition,
326
+ ...layers.backgroundPosition,
327
+ ]),
328
+ };
329
+ }
330
+
331
+ /**
332
+ * Patch for the Fill panel's "+" (add fill) action.
333
+ *
334
+ * Figma parity: clicking "+" always adds a new fill on top of whatever is
335
+ * already there. The only exception is a genuinely empty fill state (no
336
+ * visible base solid AND no existing background layers) — there "+" just
337
+ * reveals the hidden base solid instead of stacking an empty default
338
+ * gradient on top of nothing.
339
+ *
340
+ * Previously the caller only checked whether the base solid had visible
341
+ * alpha, so an element with an existing gradient/image layer stack but a
342
+ * *hidden* base solid (e.g. right after `solidToGradientPatch` converts
343
+ * solid -> gradient and clears backgroundColor to "transparent") had "+"
344
+ * silently un-hide the base solid instead of adding a new layer — the
345
+ * opposite of what "+" is supposed to do, and it reintroduced the exact
346
+ * phantom-second-fill problem `solidToGradientPatch` exists to avoid.
347
+ */
348
+ export function addFillLayerPatch(params: {
349
+ backgroundColor: string | undefined;
350
+ backgroundLayers: string[];
351
+ backgroundSizeLayers: string[];
352
+ backgroundRepeatLayers: string[];
353
+ backgroundPositionLayers: string[];
354
+ }): Record<string, string> {
355
+ const {
356
+ backgroundColor,
357
+ backgroundLayers,
358
+ backgroundSizeLayers,
359
+ backgroundRepeatLayers,
360
+ backgroundPositionLayers,
361
+ } = params;
362
+
363
+ if (!colorHasVisibleAlpha(backgroundColor) && backgroundLayers.length === 0) {
364
+ return { backgroundColor: cssColorOrFallback(backgroundColor, "#ffffff") };
365
+ }
366
+
367
+ const nextLayer = defaultGradientLayer(
368
+ "linear",
369
+ backgroundColor || "#ffffff",
370
+ );
371
+ if (backgroundLayers.length === 0) {
372
+ return { backgroundImage: nextLayer };
373
+ }
374
+
375
+ // Prepending a layer without also prepending matching entries to the
376
+ // other three index-aligned parallel arrays (size/repeat/position) would
377
+ // shift every existing layer's index by one, silently re-pairing each of
378
+ // them with the *previous* layer's size/repeat/position (same class of
379
+ // bug `removeFillLayerAtIndex` above fixes for removal).
380
+ return {
381
+ backgroundImage: joinCssLayers([nextLayer, ...backgroundLayers]),
382
+ backgroundSize: joinCssLayers(["auto", ...backgroundSizeLayers]),
383
+ backgroundRepeat: joinCssLayers(["no-repeat", ...backgroundRepeatLayers]),
384
+ backgroundPosition: joinCssLayers(["0% 0%", ...backgroundPositionLayers]),
385
+ };
386
+ }
387
+
388
+ /**
389
+ * Patch for removing just the base solid/text fill row — the row shown when
390
+ * the base color has visible alpha, or always for a text fill. Must only
391
+ * clear that one property, never `backgroundImage`, so any other stacked
392
+ * gradient/image layers (each rendered as its own row with its own
393
+ * independent remove button — see `removeFillLayerAtIndex`) are left
394
+ * untouched. This used to also zero `backgroundImage` whenever the caller
395
+ * had `onStylesChange` available, silently deleting every other fill layer
396
+ * any time the base row's own remove button was clicked.
397
+ */
398
+ export function removeBaseFillPatch(
399
+ fillProperty: "color" | "backgroundColor",
400
+ ): Record<string, string> {
401
+ return { [fillProperty]: "transparent" };
402
+ }
403
+
200
404
  export function parseGradientLayer(layer: string): ParsedGradientLayer | null {
201
405
  const match = layer.trim().match(/^(linear|radial|conic)-gradient\((.*)\)$/i);
202
406
  if (!match) return null;
@@ -247,7 +451,21 @@ function readLeadingColor(part: string): { raw: string; value: string } | null {
247
451
  return { raw: transparent[0], value: "rgba(0, 0, 0, 0)" };
248
452
  }
249
453
  const functionName = trimmed.match(/^[a-z][a-z0-9-]*\(/i);
250
- if (!functionName) return null;
454
+ if (!functionName) {
455
+ // Bare CSS color keyword — e.g. `linear-gradient(red, blue)` — not just
456
+ // hex/rgb/hsl/function colors. Gradients written by hand or generated
457
+ // from named-color CSS are common, and without this branch the leading
458
+ // stop was silently misread as a gradient *prefix* (the first stop's
459
+ // color was dropped, corrupting an otherwise-valid 2-stop gradient into
460
+ // a broken 1-stop one with a garbage prefix). Require the matched word to
461
+ // actually parse as a color so direction/shape prefix keywords ("to",
462
+ // "circle", "closest-side", "from", "at", …) are never misclassified.
463
+ const word = trimmed.match(/^[a-z]+\b/i);
464
+ if (word && parseCssColor(word[0])) {
465
+ return { raw: word[0], value: word[0] };
466
+ }
467
+ return null;
468
+ }
251
469
  let depth = 0;
252
470
  for (let index = 0; index < trimmed.length; index += 1) {
253
471
  const char = trimmed[index];