@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
@@ -37,7 +37,7 @@ import {
37
37
  type GlslShaderPanelContext,
38
38
  } from "../inspector/GlslShaderPanel";
39
39
  import type { ElementInfo } from "../types";
40
- import { elementIdentityKey } from "./element-identity";
40
+ import { elementStableKey } from "./element-identity";
41
41
  import { FieldTrailer } from "./field-primitives";
42
42
  import { splitCssLayers } from "./fill-gradient-helpers";
43
43
  import {
@@ -50,8 +50,10 @@ import {
50
50
  colorHasVisibleAlpha,
51
51
  compactCssValue,
52
52
  cssColorOrFallback,
53
+ roundToOneDecimal,
53
54
  swatchStyle,
54
55
  } from "./position-helpers";
56
+ import { isMixedValue } from "./selection-helpers";
55
57
  import type {
56
58
  MotionKeyframeFieldContext,
57
59
  StyleChangeHandler,
@@ -59,7 +61,7 @@ import type {
59
61
  StylesChangeHandler,
60
62
  } from "./style-change-types";
61
63
 
62
- interface ShadowLayer {
64
+ export interface ShadowLayer {
63
65
  id: string;
64
66
  x: number;
65
67
  y: number;
@@ -81,7 +83,7 @@ function defaultDropShadowLayer(index: number): ShadowLayer {
81
83
  };
82
84
  }
83
85
 
84
- function parseShadowLayers(value: string | undefined): ShadowLayer[] {
86
+ export function parseShadowLayers(value: string | undefined): ShadowLayer[] {
85
87
  return splitCssLayers(value || "")
86
88
  .filter((layer) => layer && layer !== "none")
87
89
  .map((layer, index) => parseShadowLayer(layer, index));
@@ -133,18 +135,18 @@ function splitCssTokens(value: string): string[] {
133
135
  return tokens;
134
136
  }
135
137
 
136
- function serializeShadowLayers(layers: ShadowLayer[]) {
138
+ export function serializeShadowLayers(layers: ShadowLayer[]) {
137
139
  if (!layers.length) return "none";
138
140
  return layers
139
141
  .map((layer) =>
140
142
  [
141
143
  layer.inset ? "inset" : "",
142
- `${Math.round(layer.x)}px`,
143
- `${Math.round(layer.y)}px`,
144
- `${Math.max(0, Math.round(layer.blur))}px`,
144
+ `${roundToOneDecimal(layer.x)}px`,
145
+ `${roundToOneDecimal(layer.y)}px`,
146
+ `${Math.max(0, roundToOneDecimal(layer.blur))}px`,
145
147
  // Spread radius may legitimately be negative for either inset or
146
148
  // drop shadows — only blur-radius is clamped to >= 0 in CSS.
147
- `${Math.round(layer.spread)}px`,
149
+ `${roundToOneDecimal(layer.spread)}px`,
148
150
  layer.color,
149
151
  ]
150
152
  .filter(Boolean)
@@ -153,8 +155,8 @@ function serializeShadowLayers(layers: ShadowLayer[]) {
153
155
  .join(", ");
154
156
  }
155
157
 
156
- function readBlurFilter(value: string | undefined): number {
157
- const match = value?.match(/blur\((-?\d+(?:\.\d+)?)px\)/);
158
+ export function readBlurFilter(value: string | undefined): number {
159
+ const match = value?.match(/blur\((-?(?:\d+(?:\.\d+)?|\.\d+))px\)/);
158
160
  return match ? Math.max(0, Number(match[1])) : 0;
159
161
  }
160
162
 
@@ -162,12 +164,62 @@ function hasBlurFilter(value: string | undefined): boolean {
162
164
  return /blur\(/.test(value || "");
163
165
  }
164
166
 
165
- function setBlurFilterValue(value: string | undefined, blur: number): string {
166
- const blurFn = `blur(${Math.max(0, Math.round(blur))}px)`;
167
+ export function setBlurFilterValue(
168
+ value: string | undefined,
169
+ blur: number,
170
+ ): string {
171
+ const blurFn = `blur(${Math.max(0, roundToOneDecimal(blur))}px)`;
167
172
  const existing = compactCssValue(value, "");
168
173
  return existing.includes("blur(")
169
174
  ? existing.replace(/blur\([^)]*\)/, blurFn)
170
- : blurFn;
175
+ : existing && existing !== "none"
176
+ ? `${existing} ${blurFn}`
177
+ : blurFn;
178
+ }
179
+
180
+ /** Remove only the layer/background blur function, preserving every sibling
181
+ * CSS filter (brightness, contrast, drop-shadow, etc.). Figma models layer
182
+ * blur as one effect row; deleting that row must not delete the other effects
183
+ * that happen to share CSS's `filter`/`backdrop-filter` declaration. */
184
+ export function removeBlurFilterValue(value: string | undefined): string {
185
+ const existing = compactCssValue(value, "");
186
+ if (!existing || existing === "none") return "none";
187
+ const remaining = existing
188
+ .replace(/blur\([^)]*\)/g, " ")
189
+ .replace(/\s+/g, " ")
190
+ .trim();
191
+ return remaining || "none";
192
+ }
193
+
194
+ /** Keep the transient original-opacity stash attached to the same shadow when
195
+ * positional CSS layers are reordered or removed. Parsed shadow ids are
196
+ * necessarily index-based (`shadow-0`, `shadow-1`, …); after a reorder the
197
+ * next computed-style read regenerates those ids in the new order. Without
198
+ * remapping, a hidden shadow's saved alpha stays at its old index and the eye
199
+ * button restores the wrong layer (or falls back to 25%). */
200
+ export function remapIndexedShadowStash(
201
+ stash: Record<string, string>,
202
+ elementKey: string,
203
+ nextLayers: readonly Pick<ShadowLayer, "id">[],
204
+ ): Record<string, string> {
205
+ const prefix = `${elementKey}:shadow:`;
206
+ const moved = nextLayers.flatMap((layer, index) => {
207
+ const value = stash[`${prefix}${layer.id}`];
208
+ return value === undefined ? [] : [[`${prefix}shadow-${index}`, value]];
209
+ });
210
+ if (!Object.keys(stash).some((key) => key.startsWith(prefix))) return stash;
211
+ const next = Object.fromEntries(
212
+ Object.entries(stash).filter(([key]) => !key.startsWith(prefix)),
213
+ );
214
+ for (const [key, value] of moved) next[key] = value;
215
+ const stashEntries = Object.entries(stash);
216
+ if (
217
+ stashEntries.length === Object.keys(next).length &&
218
+ stashEntries.every(([key, value]) => next[key] === value)
219
+ ) {
220
+ return stash;
221
+ }
222
+ return next;
171
223
  }
172
224
 
173
225
  function shadowColorWithOpacity(color: string, opacity: number): string {
@@ -179,6 +231,33 @@ function shadowColorWithOpacity(color: string, opacity: number): string {
179
231
  : color;
180
232
  }
181
233
 
234
+ /**
235
+ * True when the current multi-selection has differing box-shadow, filter,
236
+ * or backdrop-filter values (the synthetic mixed-selection ElementInfo
237
+ * reports the `MIXED_VALUE`/"Mixed" sentinel for any style property that
238
+ * disagrees across the selection — see selection-helpers.ts). Effects had no
239
+ * mixed-selection handling at all: `parseShadowLayers("Mixed")` would parse
240
+ * the literal sentinel string as a bogus single shadow layer (color:
241
+ * "Mixed", the rest defaulted), and editing any of its fields would commit
242
+ * an invalid `box-shadow: ... Mixed` to every selected element. Fill and
243
+ * Stroke both already gate their sections on an equivalent mixed check and
244
+ * show a "Click + to replace" hint instead of rendering broken per-field
245
+ * controls; Effects needs the same gate.
246
+ */
247
+ export function effectsSelectionIsMixed(styles: {
248
+ boxShadow?: string;
249
+ filter?: string;
250
+ backdropFilter?: string;
251
+ webkitBackdropFilter?: string;
252
+ }): boolean {
253
+ return [
254
+ styles.boxShadow,
255
+ styles.filter,
256
+ styles.backdropFilter,
257
+ styles.webkitBackdropFilter,
258
+ ].some(isMixedValue);
259
+ }
260
+
182
261
  function ShadowEffectRow({
183
262
  layer,
184
263
  index,
@@ -376,22 +455,37 @@ export function EffectsProperties({
376
455
  const t = useT();
377
456
  const [shaderPickerOpen, setShaderPickerOpen] = useState(false);
378
457
  const styles = element.computedStyles;
379
- const blurValue = readBlurFilter(styles.filter);
380
- const filterHasBlur = hasBlurFilter(styles.filter);
381
458
  // M5 · Background (backdrop) blur is a distinct design effect type, backed by
382
459
  // CSS `backdrop-filter: blur()` (vs layer blur's `filter: blur()`).
383
460
  const backdropFilterValue =
384
461
  styles.backdropFilter || styles.webkitBackdropFilter;
385
- const backdropFilterHasBlur = hasBlurFilter(backdropFilterValue);
462
+ // Differing box-shadow/filter/backdrop-filter across a multi-selection —
463
+ // gates the whole section to a "Click + to replace" hint below, same as
464
+ // Fill/Stroke, instead of parsing the "Mixed" sentinel as real CSS.
465
+ const effectsAreMixed = effectsSelectionIsMixed({
466
+ boxShadow: styles.boxShadow,
467
+ filter: styles.filter,
468
+ backdropFilter: styles.backdropFilter,
469
+ webkitBackdropFilter: styles.webkitBackdropFilter,
470
+ });
471
+ const blurValue = readBlurFilter(styles.filter);
472
+ const filterHasBlur = !effectsAreMixed && hasBlurFilter(styles.filter);
473
+ const backdropFilterHasBlur =
474
+ !effectsAreMixed && hasBlurFilter(backdropFilterValue);
386
475
  const backdropBlurValue = readBlurFilter(backdropFilterValue);
387
476
  const [hiddenEffectStash, setHiddenEffectStash] = useState<
388
477
  Record<string, string>
389
478
  >({});
390
- const effectStashKey = elementIdentityKey(element);
479
+ const effectStashKey = elementStableKey(element);
391
480
  const layerBlurStashKey = `${effectStashKey}:filter:blur`;
392
481
  const backdropBlurStashKey = `${effectStashKey}:backdrop-filter:blur`;
393
- const shadowLayers = parseShadowLayers(styles.boxShadow);
482
+ const shadowLayers = effectsAreMixed
483
+ ? []
484
+ : parseShadowLayers(styles.boxShadow);
394
485
  const setShadowLayers = (layers: ShadowLayer[], meta?: StyleChangeMeta) => {
486
+ setHiddenEffectStash((stash) =>
487
+ remapIndexedShadowStash(stash, effectStashKey, layers),
488
+ );
395
489
  const boxShadow = serializeShadowLayers(layers);
396
490
  if (onStylesChange) onStylesChange({ boxShadow }, meta);
397
491
  else onStyleChange("boxShadow", boxShadow, meta);
@@ -401,8 +495,10 @@ export function EffectsProperties({
401
495
  ...shadowLayers,
402
496
  defaultDropShadowLayer(shadowLayers.length),
403
497
  ]);
404
- const addLayerBlur = () => onStyleChange("filter", "blur(4px)");
405
- const addBackgroundBlur = () => onStyleChange("backdropFilter", "blur(8px)");
498
+ const addLayerBlur = () =>
499
+ onStyleChange("filter", setBlurFilterValue(styles.filter, 4));
500
+ const addBackgroundBlur = () =>
501
+ onStyleChange("backdropFilter", setBlurFilterValue(backdropFilterValue, 8));
406
502
  const reorderShadowLayers = (from: number, to: number) => {
407
503
  const next = [...shadowLayers];
408
504
  const [moved] = next.splice(from, 1);
@@ -443,6 +539,7 @@ export function EffectsProperties({
443
539
  // `!effectMount && !pickerOpen` early-return so opening the picker doesn't
444
540
  // get swallowed by this outer gate before it can render itself.
445
541
  const hasEffectsContent =
542
+ effectsAreMixed ||
446
543
  shadowLayers.length > 0 ||
447
544
  filterHasBlur ||
448
545
  backdropFilterHasBlur ||
@@ -506,262 +603,291 @@ export function EffectsProperties({
506
603
  >
507
604
  {hasEffectsContent ? (
508
605
  <>
509
- {shadowLayers.length ? (
510
- <div className="space-y-1.5">
511
- {shadowLayers.map((layer, index) => (
512
- <ShadowEffectRow
513
- key={layer.id}
514
- layer={layer}
515
- index={index}
516
- dragHandleLabel={t("editPanel.labels.reorderLayer")}
517
- dropIndicator={
518
- shadowDrag.dragIndex != null &&
519
- shadowDrag.overIndex === index
520
- ? shadowDrag.overIndex > shadowDrag.dragIndex
521
- ? "after"
522
- : "before"
523
- : null
524
- }
525
- rowProps={shadowDrag.getRowProps(index)}
526
- handleProps={shadowDrag.getHandleProps(index)}
527
- onChange={(patch, meta) => {
528
- const next = shadowLayers.map((candidate) =>
529
- candidate.id === layer.id
530
- ? { ...candidate, ...patch }
531
- : candidate,
532
- );
533
- setShadowLayers(next, meta);
534
- }}
535
- onToggleVisibility={() => {
536
- const visible = colorHasVisibleAlpha(layer.color);
537
- const shadowStashKey = `${effectStashKey}:shadow:${layer.id}`;
538
- if (visible) {
539
- setHiddenEffectStash((prev) => ({
540
- ...prev,
541
- [shadowStashKey]: layer.color,
542
- }));
543
- const next = shadowLayers.map((candidate) =>
544
- candidate.id === layer.id
545
- ? {
546
- ...candidate,
547
- color: shadowColorWithOpacity(candidate.color, 0),
548
- }
549
- : candidate,
550
- );
551
- setShadowLayers(next);
552
- return;
553
- }
606
+ {effectsAreMixed ? (
607
+ <p className="px-1.5 py-2 !text-[11px] text-muted-foreground">
608
+ {
609
+ "Click + to replace mixed content" /* i18n-ignore figma mixed effects hint */
610
+ }
611
+ </p>
612
+ ) : (
613
+ <>
614
+ {shadowLayers.length ? (
615
+ <div className="space-y-1.5">
616
+ {shadowLayers.map((layer, index) => (
617
+ <ShadowEffectRow
618
+ key={layer.id}
619
+ layer={layer}
620
+ index={index}
621
+ dragHandleLabel={t("editPanel.labels.reorderLayer")}
622
+ dropIndicator={
623
+ shadowDrag.dragIndex != null &&
624
+ shadowDrag.overIndex === index
625
+ ? shadowDrag.overIndex > shadowDrag.dragIndex
626
+ ? "after"
627
+ : "before"
628
+ : null
629
+ }
630
+ rowProps={shadowDrag.getRowProps(index)}
631
+ handleProps={shadowDrag.getHandleProps(index)}
632
+ onChange={(patch, meta) => {
633
+ const next = shadowLayers.map((candidate) =>
634
+ candidate.id === layer.id
635
+ ? { ...candidate, ...patch }
636
+ : candidate,
637
+ );
638
+ setShadowLayers(next, meta);
639
+ }}
640
+ onToggleVisibility={() => {
641
+ const visible = colorHasVisibleAlpha(layer.color);
642
+ const shadowStashKey = `${effectStashKey}:shadow:${layer.id}`;
643
+ if (visible) {
644
+ setHiddenEffectStash((prev) => ({
645
+ ...prev,
646
+ [shadowStashKey]: layer.color,
647
+ }));
648
+ const next = shadowLayers.map((candidate) =>
649
+ candidate.id === layer.id
650
+ ? {
651
+ ...candidate,
652
+ color: shadowColorWithOpacity(
653
+ candidate.color,
654
+ 0,
655
+ ),
656
+ }
657
+ : candidate,
658
+ );
659
+ setShadowLayers(next);
660
+ return;
661
+ }
554
662
 
555
- const restored =
556
- hiddenEffectStash[shadowStashKey] ??
557
- shadowColorWithOpacity(layer.color, 25);
558
- setHiddenEffectStash((prev) => {
559
- const next = { ...prev };
560
- delete next[shadowStashKey];
561
- return next;
562
- });
563
- const next = shadowLayers.map((candidate) =>
564
- candidate.id === layer.id
565
- ? { ...candidate, color: restored }
566
- : candidate,
567
- );
568
- setShadowLayers(next);
569
- }}
570
- onRemove={() =>
571
- setShadowLayers(
572
- shadowLayers.filter(
573
- (candidate) => candidate.id !== layer.id,
574
- ),
575
- )
576
- }
577
- element={element}
578
- motionKeyframeContext={motionKeyframeContext}
579
- />
580
- ))}
581
- </div>
582
- ) : null}
583
- {filterHasBlur ? (
584
- /* design effect row for layer blur: flat row matching shadow rows */
585
- <Popover>
586
- <div className="group flex items-center gap-1.5">
587
- <PopoverTrigger asChild>
588
- <button
589
- type="button"
590
- className="flex h-6 min-w-0 flex-1 items-center gap-1.5 rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 text-left !text-[11px] hover:bg-[var(--design-editor-panel-raised-bg)]"
591
- >
592
- <span className="min-w-0 flex-1 truncate font-medium text-foreground">
593
- {t("editPanel.labels.layerBlur")}
594
- </span>
595
- <span className="shrink-0 tabular-nums text-muted-foreground">
596
- {Math.round(blurValue)}px
597
- </span>
598
- </button>
599
- </PopoverTrigger>
600
- <SectionIconButton
601
- label={
602
- blurValue > 0
603
- ? t("editPanel.labels.hideLayer")
604
- : t("editPanel.labels.showLayer")
605
- }
606
- onClick={() => {
607
- if (blurValue > 0) {
608
- setHiddenEffectStash((prev) => ({
609
- ...prev,
610
- [layerBlurStashKey]: String(blurValue),
611
- }));
612
- onStyleChange(
613
- "filter",
614
- setBlurFilterValue(styles.filter, 0),
615
- );
616
- return;
617
- }
663
+ const restored =
664
+ hiddenEffectStash[shadowStashKey] ??
665
+ shadowColorWithOpacity(layer.color, 25);
666
+ setHiddenEffectStash((prev) => {
667
+ const next = { ...prev };
668
+ delete next[shadowStashKey];
669
+ return next;
670
+ });
671
+ const next = shadowLayers.map((candidate) =>
672
+ candidate.id === layer.id
673
+ ? { ...candidate, color: restored }
674
+ : candidate,
675
+ );
676
+ setShadowLayers(next);
677
+ }}
678
+ onRemove={() =>
679
+ setShadowLayers(
680
+ shadowLayers.filter(
681
+ (candidate) => candidate.id !== layer.id,
682
+ ),
683
+ )
684
+ }
685
+ element={element}
686
+ motionKeyframeContext={motionKeyframeContext}
687
+ />
688
+ ))}
689
+ </div>
690
+ ) : null}
691
+ {filterHasBlur ? (
692
+ /* design effect row for layer blur: flat row matching shadow rows */
693
+ <Popover>
694
+ <div className="group flex items-center gap-1.5">
695
+ <PopoverTrigger asChild>
696
+ <button
697
+ type="button"
698
+ className="flex h-6 min-w-0 flex-1 items-center gap-1.5 rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 text-left !text-[11px] hover:bg-[var(--design-editor-panel-raised-bg)]"
699
+ >
700
+ <span className="min-w-0 flex-1 truncate font-medium text-foreground">
701
+ {t("editPanel.labels.layerBlur")}
702
+ </span>
703
+ <span className="shrink-0 tabular-nums text-muted-foreground">
704
+ {roundToOneDecimal(blurValue)}px
705
+ </span>
706
+ </button>
707
+ </PopoverTrigger>
708
+ <SectionIconButton
709
+ label={
710
+ blurValue > 0
711
+ ? t("editPanel.labels.hideLayer")
712
+ : t("editPanel.labels.showLayer")
713
+ }
714
+ onClick={() => {
715
+ if (blurValue > 0) {
716
+ setHiddenEffectStash((prev) => ({
717
+ ...prev,
718
+ [layerBlurStashKey]: String(blurValue),
719
+ }));
720
+ onStyleChange(
721
+ "filter",
722
+ setBlurFilterValue(styles.filter, 0),
723
+ );
724
+ return;
725
+ }
618
726
 
619
- const restored = Number(
620
- hiddenEffectStash[layerBlurStashKey],
621
- );
622
- const nextBlur =
623
- Number.isFinite(restored) && restored > 0 ? restored : 4;
624
- setHiddenEffectStash((prev) => {
625
- const next = { ...prev };
626
- delete next[layerBlurStashKey];
627
- return next;
628
- });
629
- onStyleChange(
630
- "filter",
631
- setBlurFilterValue(styles.filter, nextBlur),
632
- );
633
- }}
634
- >
635
- {blurValue > 0 ? (
636
- <IconEye className="size-3.5" />
637
- ) : (
638
- <IconEyeOff className="size-3.5" />
639
- )}
640
- </SectionIconButton>
641
- <SectionIconButton
642
- label={t("editPanel.labels.removeLayer")}
643
- onClick={() => onStyleChange("filter", "none")}
644
- disabled={!filterHasBlur}
645
- >
646
- <IconMinus className="size-3.5" />
647
- </SectionIconButton>
648
- </div>
649
- <PopoverContent
650
- side="left"
651
- align="start"
652
- sideOffset={8}
653
- className="w-56 p-3"
654
- >
655
- <ScrubInput
656
- label={t("editPanel.labels.blur")}
657
- value={blurValue}
658
- onChange={(value, meta) =>
659
- onStyleChange(
660
- "filter",
661
- setBlurFilterValue(styles.filter, value),
662
- meta,
663
- )
664
- }
665
- unit="px"
666
- min={0}
667
- precision={1}
668
- labelClassName="w-16"
669
- inputClassName="h-6"
670
- />
671
- </PopoverContent>
672
- </Popover>
673
- ) : null}
674
- {backdropFilterHasBlur ? (
675
- /* M5 · Background (backdrop) blur effect row — mirrors the layer-blur row */
676
- <Popover>
677
- <div className="group flex items-center gap-1.5">
678
- <PopoverTrigger asChild>
679
- <button
680
- type="button"
681
- className="flex h-6 min-w-0 flex-1 items-center gap-1.5 rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 text-left !text-[11px] hover:bg-[var(--design-editor-panel-raised-bg)]"
727
+ const restored = Number(
728
+ hiddenEffectStash[layerBlurStashKey],
729
+ );
730
+ const nextBlur =
731
+ Number.isFinite(restored) && restored > 0
732
+ ? restored
733
+ : 4;
734
+ setHiddenEffectStash((prev) => {
735
+ const next = { ...prev };
736
+ delete next[layerBlurStashKey];
737
+ return next;
738
+ });
739
+ onStyleChange(
740
+ "filter",
741
+ setBlurFilterValue(styles.filter, nextBlur),
742
+ );
743
+ }}
744
+ >
745
+ {blurValue > 0 ? (
746
+ <IconEye className="size-3.5" />
747
+ ) : (
748
+ <IconEyeOff className="size-3.5" />
749
+ )}
750
+ </SectionIconButton>
751
+ <SectionIconButton
752
+ label={t("editPanel.labels.removeLayer")}
753
+ onClick={() =>
754
+ onStyleChange(
755
+ "filter",
756
+ removeBlurFilterValue(styles.filter),
757
+ )
758
+ }
759
+ disabled={!filterHasBlur}
760
+ >
761
+ <IconMinus className="size-3.5" />
762
+ </SectionIconButton>
763
+ </div>
764
+ <PopoverContent
765
+ side="left"
766
+ align="start"
767
+ sideOffset={8}
768
+ className="w-56 p-3"
682
769
  >
683
- <span className="min-w-0 flex-1 truncate font-medium text-foreground">
684
- {"Background blur" /* i18n-ignore design effect type */}
685
- </span>
686
- <span className="shrink-0 tabular-nums text-muted-foreground">
687
- {Math.round(backdropBlurValue)}px
688
- </span>
689
- </button>
690
- </PopoverTrigger>
691
- <SectionIconButton
692
- label={
693
- backdropBlurValue > 0
694
- ? t("editPanel.labels.hideLayer")
695
- : t("editPanel.labels.showLayer")
696
- }
697
- onClick={() => {
698
- if (backdropBlurValue > 0) {
699
- setHiddenEffectStash((prev) => ({
700
- ...prev,
701
- [backdropBlurStashKey]: String(backdropBlurValue),
702
- }));
703
- onStyleChange(
704
- "backdropFilter",
705
- setBlurFilterValue(backdropFilterValue, 0),
706
- );
707
- return;
708
- }
770
+ <ScrubInput
771
+ label={t("editPanel.labels.blur")}
772
+ value={blurValue}
773
+ onChange={(value, meta) =>
774
+ onStyleChange(
775
+ "filter",
776
+ setBlurFilterValue(styles.filter, value),
777
+ meta,
778
+ )
779
+ }
780
+ unit="px"
781
+ min={0}
782
+ precision={1}
783
+ labelClassName="w-16"
784
+ inputClassName="h-6"
785
+ />
786
+ </PopoverContent>
787
+ </Popover>
788
+ ) : null}
789
+ {backdropFilterHasBlur ? (
790
+ /* M5 · Background (backdrop) blur effect row — mirrors the layer-blur row */
791
+ <Popover>
792
+ <div className="group flex items-center gap-1.5">
793
+ <PopoverTrigger asChild>
794
+ <button
795
+ type="button"
796
+ className="flex h-6 min-w-0 flex-1 items-center gap-1.5 rounded-md border border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] px-1.5 text-left !text-[11px] hover:bg-[var(--design-editor-panel-raised-bg)]"
797
+ >
798
+ <span className="min-w-0 flex-1 truncate font-medium text-foreground">
799
+ {
800
+ "Background blur" /* i18n-ignore design effect type */
801
+ }
802
+ </span>
803
+ <span className="shrink-0 tabular-nums text-muted-foreground">
804
+ {roundToOneDecimal(backdropBlurValue)}px
805
+ </span>
806
+ </button>
807
+ </PopoverTrigger>
808
+ <SectionIconButton
809
+ label={
810
+ backdropBlurValue > 0
811
+ ? t("editPanel.labels.hideLayer")
812
+ : t("editPanel.labels.showLayer")
813
+ }
814
+ onClick={() => {
815
+ if (backdropBlurValue > 0) {
816
+ setHiddenEffectStash((prev) => ({
817
+ ...prev,
818
+ [backdropBlurStashKey]: String(backdropBlurValue),
819
+ }));
820
+ onStyleChange(
821
+ "backdropFilter",
822
+ setBlurFilterValue(backdropFilterValue, 0),
823
+ );
824
+ return;
825
+ }
709
826
 
710
- const restored = Number(
711
- hiddenEffectStash[backdropBlurStashKey],
712
- );
713
- const nextBlur =
714
- Number.isFinite(restored) && restored > 0 ? restored : 8;
715
- setHiddenEffectStash((prev) => {
716
- const next = { ...prev };
717
- delete next[backdropBlurStashKey];
718
- return next;
719
- });
720
- onStyleChange(
721
- "backdropFilter",
722
- setBlurFilterValue(backdropFilterValue, nextBlur),
723
- );
724
- }}
725
- >
726
- {backdropBlurValue > 0 ? (
727
- <IconEye className="size-3.5" />
728
- ) : (
729
- <IconEyeOff className="size-3.5" />
730
- )}
731
- </SectionIconButton>
732
- <SectionIconButton
733
- label={t("editPanel.labels.removeLayer")}
734
- onClick={() => onStyleChange("backdropFilter", "none")}
735
- disabled={!backdropFilterHasBlur}
736
- >
737
- <IconMinus className="size-3.5" />
738
- </SectionIconButton>
739
- </div>
740
- <PopoverContent
741
- side="left"
742
- align="start"
743
- sideOffset={8}
744
- className="w-56 p-3"
745
- >
746
- <ScrubInput
747
- label={t("editPanel.labels.blur")}
748
- value={backdropBlurValue}
749
- onChange={(value, meta) =>
750
- onStyleChange(
751
- "backdropFilter",
752
- setBlurFilterValue(backdropFilterValue, value),
753
- meta,
754
- )
755
- }
756
- unit="px"
757
- min={0}
758
- precision={1}
759
- labelClassName="w-16"
760
- inputClassName="h-6"
761
- />
762
- </PopoverContent>
763
- </Popover>
764
- ) : null}
827
+ const restored = Number(
828
+ hiddenEffectStash[backdropBlurStashKey],
829
+ );
830
+ const nextBlur =
831
+ Number.isFinite(restored) && restored > 0
832
+ ? restored
833
+ : 8;
834
+ setHiddenEffectStash((prev) => {
835
+ const next = { ...prev };
836
+ delete next[backdropBlurStashKey];
837
+ return next;
838
+ });
839
+ onStyleChange(
840
+ "backdropFilter",
841
+ setBlurFilterValue(backdropFilterValue, nextBlur),
842
+ );
843
+ }}
844
+ >
845
+ {backdropBlurValue > 0 ? (
846
+ <IconEye className="size-3.5" />
847
+ ) : (
848
+ <IconEyeOff className="size-3.5" />
849
+ )}
850
+ </SectionIconButton>
851
+ <SectionIconButton
852
+ label={t("editPanel.labels.removeLayer")}
853
+ onClick={() =>
854
+ onStyleChange(
855
+ "backdropFilter",
856
+ removeBlurFilterValue(backdropFilterValue),
857
+ )
858
+ }
859
+ disabled={!backdropFilterHasBlur}
860
+ >
861
+ <IconMinus className="size-3.5" />
862
+ </SectionIconButton>
863
+ </div>
864
+ <PopoverContent
865
+ side="left"
866
+ align="start"
867
+ sideOffset={8}
868
+ className="w-56 p-3"
869
+ >
870
+ <ScrubInput
871
+ label={t("editPanel.labels.blur")}
872
+ value={backdropBlurValue}
873
+ onChange={(value, meta) =>
874
+ onStyleChange(
875
+ "backdropFilter",
876
+ setBlurFilterValue(backdropFilterValue, value),
877
+ meta,
878
+ )
879
+ }
880
+ unit="px"
881
+ min={0}
882
+ precision={1}
883
+ labelClassName="w-16"
884
+ inputClassName="h-6"
885
+ />
886
+ </PopoverContent>
887
+ </Popover>
888
+ ) : null}
889
+ </>
890
+ )}
765
891
  {glslShaderContext?.nodeId ? (
766
892
  /* Code-backed GLSL shader effect — overlay canvas above the
767
893
  element's content, persisted as editable GLSL in the screen HTML