@agent-native/core 0.90.1 → 0.90.2

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 (295) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  5. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +11 -6
  6. package/corpus/core/src/client/use-action.ts +39 -2
  7. package/corpus/core/src/client/use-db-sync.ts +44 -18
  8. package/corpus/core/src/db/create-get-db.ts +32 -2
  9. package/corpus/core/src/server/framework-request-handler.ts +52 -4
  10. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
  11. package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
  12. package/corpus/templates/assets/AGENTS.md +23 -4
  13. package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
  14. package/corpus/templates/assets/actions/_helpers.ts +8 -1
  15. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
  16. package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
  17. package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
  18. package/corpus/templates/assets/actions/generate-image.ts +342 -25
  19. package/corpus/templates/assets/actions/get-library.ts +4 -3
  20. package/corpus/templates/assets/actions/navigate.ts +2 -1
  21. package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
  22. package/corpus/templates/assets/actions/update-asset.ts +1 -0
  23. package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
  24. package/corpus/templates/assets/actions/view-screen.ts +8 -1
  25. package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +9 -1
  26. package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
  27. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
  28. package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
  29. package/corpus/templates/assets/app/i18n-data.ts +650 -0
  30. package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -1
  31. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +44 -33
  32. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
  33. package/corpus/templates/assets/app/routes/library.tsx +43 -1
  34. package/corpus/templates/assets/server/lib/generation.ts +366 -44
  35. package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
  36. package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
  37. package/corpus/templates/assets/shared/api.ts +47 -0
  38. package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -2
  39. package/corpus/templates/clips/app/components/library/sort-menu.tsx +1 -1
  40. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +114 -33
  41. package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
  42. package/corpus/templates/clips/app/routes/record.tsx +157 -68
  43. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +5 -5
  44. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +20 -3
  45. package/corpus/templates/clips/shared/finalize-recovery.ts +23 -3
  46. package/corpus/templates/content/AGENTS.md +52 -52
  47. package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
  48. package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
  49. package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
  50. package/corpus/templates/content/actions/_database-utils.ts +44 -4
  51. package/corpus/templates/content/actions/_notion-action-utils.ts +6 -2
  52. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
  53. package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
  54. package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
  55. package/corpus/templates/content/actions/delete-content-database.ts +1 -0
  56. package/corpus/templates/content/actions/get-document.ts +11 -2
  57. package/corpus/templates/content/actions/list-content-databases.ts +57 -9
  58. package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
  59. package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
  60. package/corpus/templates/content/actions/restore-content-database.ts +1 -0
  61. package/corpus/templates/content/actions/update-document.ts +43 -0
  62. package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
  63. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +0 -1
  64. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -28
  65. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +69 -17
  66. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +4 -4
  67. package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
  68. package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
  69. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +9076 -2876
  70. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +41 -1
  71. package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +36 -11
  72. package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
  73. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -1
  74. package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +11 -8
  75. package/corpus/templates/content/app/global.css +41 -0
  76. package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
  77. package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
  78. package/corpus/templates/content/app/hooks/use-documents.ts +47 -0
  79. package/corpus/templates/content/app/hooks/use-notion.ts +12 -10
  80. package/corpus/templates/content/app/i18n/zh-TW.ts +35 -0
  81. package/corpus/templates/content/app/i18n-data.ts +355 -0
  82. package/corpus/templates/content/app/root.tsx +22 -0
  83. package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
  84. package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
  85. package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
  86. package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
  87. package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
  88. package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
  89. package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
  90. package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
  91. package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
  92. package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
  93. package/corpus/templates/content/shared/api.ts +13 -0
  94. package/corpus/templates/content/shared/builder-mdx.ts +16 -3
  95. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -13
  96. package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
  97. package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
  98. package/corpus/templates/design/AGENTS.md +9 -2
  99. package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
  100. package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
  101. package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
  102. package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
  103. package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
  104. package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
  105. package/corpus/templates/design/actions/create-component.ts +29 -60
  106. package/corpus/templates/design/actions/create-file.ts +9 -2
  107. package/corpus/templates/design/actions/duplicate-design.ts +31 -2
  108. package/corpus/templates/design/actions/edit-design.ts +36 -38
  109. package/corpus/templates/design/actions/generate-design-title.ts +87 -0
  110. package/corpus/templates/design/actions/generate-design.ts +53 -18
  111. package/corpus/templates/design/actions/generate-screens.ts +108 -4
  112. package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
  113. package/corpus/templates/design/actions/insert-asset.ts +34 -29
  114. package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
  115. package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
  116. package/corpus/templates/design/actions/present-design-variants.ts +7 -2
  117. package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
  118. package/corpus/templates/design/actions/update-file.ts +190 -75
  119. package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
  120. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +469 -222
  121. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
  122. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +83 -3
  123. package/corpus/templates/design/app/components/design/EditPanel.tsx +1091 -6948
  124. package/corpus/templates/design/app/components/design/LayersPanel.tsx +419 -85
  125. package/corpus/templates/design/app/components/design/MotionDock.tsx +1124 -103
  126. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
  127. package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
  128. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2182 -235
  129. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
  130. package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
  131. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
  132. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
  133. package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +1 -1
  134. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +1 -3
  135. package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
  136. package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
  137. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
  138. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
  139. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
  140. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
  141. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
  142. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
  143. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
  144. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
  145. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
  146. package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
  147. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
  148. package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
  149. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
  150. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
  151. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
  152. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
  153. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
  154. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
  155. package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
  156. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
  157. package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
  158. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
  159. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
  160. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +91 -40
  161. package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
  162. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +189 -87
  163. package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
  164. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
  165. package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
  166. package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
  167. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +99 -9
  168. package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
  169. package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
  170. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
  171. package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
  172. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
  173. package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
  174. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
  175. package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
  176. package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
  177. package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
  178. package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
  179. package/corpus/templates/design/app/components/design/types.ts +15 -0
  180. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +109 -5
  181. package/corpus/templates/design/app/global.css +8 -8
  182. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
  183. package/corpus/templates/design/app/i18n/zh-TW.ts +163 -9
  184. package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
  185. package/corpus/templates/design/app/i18n-data.ts +1664 -152
  186. package/corpus/templates/design/app/i18n-motion.ts +563 -0
  187. package/corpus/templates/design/app/lib/design-import.ts +73 -0
  188. package/corpus/templates/design/app/pages/Index.tsx +41 -22
  189. package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
  190. package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
  191. package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
  192. package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
  193. package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
  194. package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
  195. package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
  196. package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
  197. package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
  198. package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
  199. package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
  200. package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
  201. package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
  202. package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
  203. package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
  240. package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
  241. package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
  242. package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
  243. package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
  244. package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
  245. package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
  246. package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
  247. package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
  248. package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
  249. package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
  250. package/corpus/templates/design/server/source-workspace.ts +139 -59
  251. package/corpus/templates/design/shared/board-file.ts +11 -2
  252. package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
  253. package/corpus/templates/design/shared/canvas-math.ts +354 -35
  254. package/corpus/templates/design/shared/code-layer.ts +277 -8
  255. package/corpus/templates/design/shared/design-state.ts +11 -4
  256. package/corpus/templates/design/shared/interaction-states.ts +548 -0
  257. package/corpus/templates/design/shared/motion-compiler.ts +186 -23
  258. package/corpus/templates/design/shared/motion-easing.ts +294 -0
  259. package/corpus/templates/design/shared/motion-timeline.ts +400 -34
  260. package/corpus/templates/design/shared/prompt-title.ts +95 -0
  261. package/corpus/templates/design/shared/responsive-classes.ts +448 -10
  262. package/corpus/templates/design/shared/screen-annotation.ts +46 -0
  263. package/corpus/templates/design/shared/shader-fills.ts +1009 -0
  264. package/corpus/templates/design/shared/shader-presets.ts +852 -4
  265. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +15 -2
  266. package/corpus/templates/slides/app/components/editor/ImportButton.tsx +10 -1
  267. package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
  268. package/dist/client/AgentPanel.js +1 -1
  269. package/dist/client/AgentPanel.js.map +1 -1
  270. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  271. package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -6
  272. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  273. package/dist/client/use-action.d.ts +3 -0
  274. package/dist/client/use-action.d.ts.map +1 -1
  275. package/dist/client/use-action.js +34 -3
  276. package/dist/client/use-action.js.map +1 -1
  277. package/dist/client/use-db-sync.d.ts +4 -0
  278. package/dist/client/use-db-sync.d.ts.map +1 -1
  279. package/dist/client/use-db-sync.js +37 -18
  280. package/dist/client/use-db-sync.js.map +1 -1
  281. package/dist/collab/routes.d.ts +1 -1
  282. package/dist/collab/struct-routes.d.ts +1 -1
  283. package/dist/db/create-get-db.d.ts +25 -0
  284. package/dist/db/create-get-db.d.ts.map +1 -1
  285. package/dist/db/create-get-db.js +25 -2
  286. package/dist/db/create-get-db.js.map +1 -1
  287. package/dist/observability/routes.d.ts +3 -3
  288. package/dist/secrets/routes.d.ts +9 -9
  289. package/dist/server/framework-request-handler.d.ts.map +1 -1
  290. package/dist/server/framework-request-handler.js +37 -1
  291. package/dist/server/framework-request-handler.js.map +1 -1
  292. package/package.json +1 -1
  293. package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
  294. package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
  295. package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22924
@@ -93,6 +93,23 @@ const FRAME_FILL = "hsl(var(--primary) / 0.05)";
93
93
  /** Small radius matching Tailwind `rounded-sm` (2 px). */
94
94
  const RECT_RADIUS = "2px";
95
95
 
96
+ /**
97
+ * Canonical default stroke for vector primitives (line / arrow / pen path).
98
+ *
99
+ * Figma-parity: a freshly drawn line, arrow, or pen path defaults to solid
100
+ * black at 1px — not the editor's `--primary` accent color at 3px. These are
101
+ * exported so every call site that draws or commits one of these primitives
102
+ * (the board draft preview in MultiScreenCanvas.tsx, the persisted board file
103
+ * in shared/board-file.ts, and DesignEditor.tsx's
104
+ * `appendCanvasPrimitiveToHtml`) agrees on the same default, exactly like the
105
+ * div-based `canvasPrimitiveVisual` tokens above keep rect/ellipse/frame/text
106
+ * consistent.
107
+ */
108
+ export const DEFAULT_LINE_STROKE = "#000000";
109
+
110
+ /** Default stroke width, in pixels, for a freshly drawn line/arrow/pen path. */
111
+ export const DEFAULT_LINE_STROKE_WIDTH_PX = 1;
112
+
96
113
  // ---------------------------------------------------------------------------
97
114
  // canvasPrimitiveVisual
98
115
  // ---------------------------------------------------------------------------
@@ -63,7 +63,7 @@ export function SideBar({
63
63
  <div
64
64
  role="tablist"
65
65
  aria-label="Code sidebar views" /* i18n-ignore */
66
- className="flex h-10 shrink-0 items-center gap-1 border-b border-[var(--workbench-border)] px-2"
66
+ className="flex h-10 shrink-0 items-center gap-1 px-2"
67
67
  >
68
68
  {VIEW_TABS.map((item) => {
69
69
  const Icon = item.icon;
@@ -351,10 +351,8 @@ export function FileTree({
351
351
  className={cn(
352
352
  "group flex h-6 cursor-pointer items-center gap-1 rounded-[5px] pr-1 text-[12px] outline-none",
353
353
  isActive
354
- ? "bg-[var(--workbench-list-active-bg,var(--workbench-active-bg))] text-[var(--workbench-active-fg)]"
354
+ ? "bg-[var(--workbench-list-active-bg,var(--workbench-active-bg))] text-[var(--workbench-fg)]"
355
355
  : "text-[var(--workbench-fg)] hover:bg-[var(--workbench-hover-bg)]",
356
- isFocused &&
357
- "ring-1 ring-inset ring-[var(--workbench-accent)]",
358
356
  )}
359
357
  style={{ paddingLeft: 4 + row.depth * 12 }}
360
358
  draggable={false}
@@ -501,7 +501,7 @@ function QuickInputRow({
501
501
  className={cn(
502
502
  "mx-1 flex h-7 cursor-pointer items-center gap-2 rounded-[5px] px-2 text-[12px]",
503
503
  active
504
- ? "bg-[var(--workbench-list-active-bg,var(--workbench-active-bg))] text-[var(--workbench-active-fg)]"
504
+ ? "bg-[var(--workbench-list-active-bg,var(--workbench-active-bg))] text-[var(--workbench-fg)]"
505
505
  : "text-[var(--workbench-fg)] hover:bg-[var(--workbench-hover-bg)]",
506
506
  )}
507
507
  >
@@ -0,0 +1,518 @@
1
+ import { useT } from "@agent-native/core/client";
2
+ import {
3
+ IconBorderCorners,
4
+ IconBorderRadius,
5
+ IconCheck,
6
+ IconDroplet,
7
+ IconEye,
8
+ IconEyeOff,
9
+ IconGridDots,
10
+ IconRadiusBottomLeft,
11
+ IconRadiusBottomRight,
12
+ IconRadiusTopLeft,
13
+ IconRadiusTopRight,
14
+ } from "@tabler/icons-react";
15
+ import { useEffect, useState, type ReactNode } from "react";
16
+
17
+ import { Button } from "@/components/ui/button";
18
+ import {
19
+ DropdownMenu,
20
+ DropdownMenuContent,
21
+ DropdownMenuItem,
22
+ DropdownMenuSeparator,
23
+ DropdownMenuTrigger,
24
+ } from "@/components/ui/dropdown-menu";
25
+ import {
26
+ Tooltip,
27
+ TooltipContent,
28
+ TooltipTrigger,
29
+ } from "@/components/ui/tooltip";
30
+ import { cn } from "@/lib/utils";
31
+
32
+ import { ScrubInput, type ScrubInputChangeMeta } from "../inspector";
33
+ import type { ElementInfo } from "../types";
34
+ import { elementIdentityKey } from "./element-identity";
35
+ import { FieldTrailer } from "./field-primitives";
36
+ import { SectionIconToggle } from "./inspector-controls";
37
+ import { PanelSection } from "./panel-primitives";
38
+ import { cssLengthNumber, fourValuesEqual } from "./position-helpers";
39
+ import { isMixedValue, MIXED_VALUE } from "./selection-helpers";
40
+ import type {
41
+ BreakpointOverrideFieldContext,
42
+ MotionKeyframeFieldContext,
43
+ StyleChangeHandler,
44
+ } from "./style-change-types";
45
+ import {
46
+ BLEND_MODE_OPTIONS,
47
+ optionValue,
48
+ parseNumericValue,
49
+ } from "./style-options";
50
+
51
+ export function CornerRadiusControl({
52
+ styles,
53
+ onStyleChange,
54
+ element,
55
+ motionKeyframeContext,
56
+ breakpointOverrideContext,
57
+ }: {
58
+ styles: Record<string, string>;
59
+ onStyleChange: StyleChangeHandler;
60
+ /**
61
+ * Optional — only needed to render the keyframe diamond / breakpoint
62
+ * override indicator next to the uniform radius field. Omit for callers
63
+ * that don't wire those features (both affordances stay hidden).
64
+ */
65
+ element?: ElementInfo;
66
+ motionKeyframeContext?: MotionKeyframeFieldContext;
67
+ breakpointOverrideContext?: BreakpointOverrideFieldContext;
68
+ }) {
69
+ const t = useT();
70
+ const independentCornersLabel = t("editPanel.labels.independentCorners");
71
+ const cornerSources = {
72
+ topLeft: styles.borderTopLeftRadius || styles.borderRadius,
73
+ topRight: styles.borderTopRightRadius || styles.borderRadius,
74
+ bottomRight: styles.borderBottomRightRadius || styles.borderRadius,
75
+ bottomLeft: styles.borderBottomLeftRadius || styles.borderRadius,
76
+ };
77
+ const cornerMixed = {
78
+ topLeft: isMixedValue(cornerSources.topLeft),
79
+ topRight: isMixedValue(cornerSources.topRight),
80
+ bottomRight: isMixedValue(cornerSources.bottomRight),
81
+ bottomLeft: isMixedValue(cornerSources.bottomLeft),
82
+ };
83
+ // Guard cssLengthNumber against the Mixed sentinel — parseFloat("Mixed")
84
+ // would silently coerce it to 0 and render a concrete value.
85
+ const corners = {
86
+ topLeft: cornerMixed.topLeft ? 0 : cssLengthNumber(cornerSources.topLeft),
87
+ topRight: cornerMixed.topRight
88
+ ? 0
89
+ : cssLengthNumber(cornerSources.topRight),
90
+ bottomRight: cornerMixed.bottomRight
91
+ ? 0
92
+ : cssLengthNumber(cornerSources.bottomRight),
93
+ bottomLeft: cornerMixed.bottomLeft
94
+ ? 0
95
+ : cssLengthNumber(cornerSources.bottomLeft),
96
+ };
97
+ const anyCornerMixed =
98
+ cornerMixed.topLeft ||
99
+ cornerMixed.topRight ||
100
+ cornerMixed.bottomRight ||
101
+ cornerMixed.bottomLeft;
102
+ const allCornersMixed =
103
+ cornerMixed.topLeft &&
104
+ cornerMixed.topRight &&
105
+ cornerMixed.bottomRight &&
106
+ cornerMixed.bottomLeft;
107
+ // With mixed sentinels the parsed numbers are placeholders, so compare
108
+ // mixed-ness instead: all-mixed reads as uniform (each element may still be
109
+ // uniform), partially-mixed means at least one element has differing corners.
110
+ const cornersDiffer = anyCornerMixed
111
+ ? !allCornersMixed
112
+ : !fourValuesEqual([
113
+ corners.topLeft,
114
+ corners.topRight,
115
+ corners.bottomRight,
116
+ corners.bottomLeft,
117
+ ]);
118
+ // Seeds the toggle once per selection (this component is remounted per
119
+ // element via `key={elementIdentityKey(element)}` at its call site) and is
120
+ // otherwise a pure user-controlled toggle (see toggleIndependentCorners
121
+ // below). Do NOT add back a useEffect that re-derives this from
122
+ // `cornersDiffer` on every render: commitRadius below applies the 4 corner
123
+ // longhands + shorthand as separate onStyleChange calls, so a scrub
124
+ // gesture that re-invokes commitRadius on every drag tick can hit an
125
+ // intermediate render where one longhand has updated and another hasn't —
126
+ // `cornersDiffer` spikes true for that frame and a reactive effect would
127
+ // force-expand the per-corner view mid-drag, same class of bug as the
128
+ // padding auto-unlink fix above (STEVE TEST BATCH 4 #4 audit).
129
+ const [showIndependentCorners, setShowIndependentCorners] =
130
+ useState(cornersDiffer);
131
+ const radiusMixed =
132
+ anyCornerMixed || (!cornersDiffer && isMixedValue(styles.borderRadius));
133
+ const radius = radiusMixed
134
+ ? 0
135
+ : cornersDiffer
136
+ ? corners.topLeft
137
+ : cssLengthNumber(styles.borderRadius || String(corners.topLeft));
138
+ const commitRadius = (value: number, meta?: ScrubInputChangeMeta) => {
139
+ const next = `${Math.max(0, Math.round(value))}px`;
140
+ // Always write the longhands along with the shorthand: stale inline
141
+ // longhand declarations serialize after the shorthand and would override
142
+ // it, turning uniform-radius commits into silent no-ops.
143
+ onStyleChange("borderRadius", next, meta);
144
+ onStyleChange("borderTopLeftRadius", next, meta);
145
+ onStyleChange("borderTopRightRadius", next, meta);
146
+ onStyleChange("borderBottomRightRadius", next, meta);
147
+ onStyleChange("borderBottomLeftRadius", next, meta);
148
+ };
149
+ const toggleIndependentCorners = () => {
150
+ // Collapsing while corners differ flattens them to the displayed uniform
151
+ // value; otherwise the stale longhands would keep overriding the shorthand
152
+ // and the single field would silently no-op. Mixed selections collapse the
153
+ // UI only — committing would stamp the placeholder 0 onto every object.
154
+ if (showIndependentCorners && cornersDiffer && !radiusMixed) {
155
+ commitRadius(radius);
156
+ }
157
+ setShowIndependentCorners(!showIndependentCorners);
158
+ };
159
+
160
+ return (
161
+ <>
162
+ <div className="group/field relative min-w-0">
163
+ <AppearanceScrubField
164
+ label={t("editPanel.labels.cornerRadius")}
165
+ icon={IconBorderRadius}
166
+ value={radius}
167
+ onChange={commitRadius}
168
+ mixed={radiusMixed}
169
+ min={0}
170
+ precision={0}
171
+ />
172
+ {element ? (
173
+ <FieldTrailer
174
+ element={element}
175
+ motionCssProperty="border-radius"
176
+ motionKeyframeContext={motionKeyframeContext}
177
+ breakpointOverrideContext={breakpointOverrideContext}
178
+ className="absolute -top-3.5 right-0"
179
+ hoverRevealClassName="opacity-0 group-hover/field:opacity-100"
180
+ />
181
+ ) : null}
182
+ </div>
183
+ <Tooltip>
184
+ <TooltipTrigger asChild>
185
+ <Button
186
+ type="button"
187
+ variant="ghost"
188
+ size="icon"
189
+ className={cn(
190
+ "size-6 rounded-md text-muted-foreground hover:bg-[var(--design-editor-control-bg)] hover:text-foreground",
191
+ showIndependentCorners &&
192
+ "bg-[var(--design-editor-accent-color)]/20 text-[var(--design-editor-accent-color)] hover:bg-[var(--design-editor-accent-color)]/20 hover:text-[var(--design-editor-accent-color)]",
193
+ )}
194
+ aria-label={independentCornersLabel}
195
+ aria-pressed={showIndependentCorners}
196
+ onClick={toggleIndependentCorners}
197
+ >
198
+ <IconBorderCorners className="size-3.5" />
199
+ </Button>
200
+ </TooltipTrigger>
201
+ <TooltipContent>{independentCornersLabel}</TooltipContent>
202
+ </Tooltip>
203
+ {showIndependentCorners ? (
204
+ <>
205
+ <AppearanceScrubField
206
+ label={t("editPanel.labels.topLeft")}
207
+ ariaLabel="Top left"
208
+ icon={IconRadiusTopLeft}
209
+ value={corners.topLeft}
210
+ onChange={(value, meta) =>
211
+ onStyleChange(
212
+ "borderTopLeftRadius",
213
+ `${Math.max(0, Math.round(value))}px`,
214
+ meta,
215
+ )
216
+ }
217
+ mixed={cornerMixed.topLeft}
218
+ min={0}
219
+ precision={1}
220
+ />
221
+ <AppearanceScrubField
222
+ label={t("editPanel.labels.topRight")}
223
+ ariaLabel="Top right"
224
+ icon={IconRadiusTopRight}
225
+ value={corners.topRight}
226
+ onChange={(value, meta) =>
227
+ onStyleChange(
228
+ "borderTopRightRadius",
229
+ `${Math.max(0, Math.round(value))}px`,
230
+ meta,
231
+ )
232
+ }
233
+ mixed={cornerMixed.topRight}
234
+ min={0}
235
+ precision={1}
236
+ />
237
+ <span aria-hidden="true" />
238
+ <AppearanceScrubField
239
+ label={t("editPanel.labels.bottomLeft")}
240
+ ariaLabel="Bottom left"
241
+ icon={IconRadiusBottomLeft}
242
+ value={corners.bottomLeft}
243
+ onChange={(value, meta) =>
244
+ onStyleChange(
245
+ "borderBottomLeftRadius",
246
+ `${Math.max(0, Math.round(value))}px`,
247
+ meta,
248
+ )
249
+ }
250
+ mixed={cornerMixed.bottomLeft}
251
+ min={0}
252
+ precision={1}
253
+ />
254
+ <AppearanceScrubField
255
+ label={t("editPanel.labels.bottomRight")}
256
+ ariaLabel="Bottom right"
257
+ icon={IconRadiusBottomRight}
258
+ value={corners.bottomRight}
259
+ onChange={(value, meta) =>
260
+ onStyleChange(
261
+ "borderBottomRightRadius",
262
+ `${Math.max(0, Math.round(value))}px`,
263
+ meta,
264
+ )
265
+ }
266
+ mixed={cornerMixed.bottomRight}
267
+ min={0}
268
+ precision={1}
269
+ />
270
+ <span aria-hidden="true" />
271
+ </>
272
+ ) : null}
273
+ </>
274
+ );
275
+ }
276
+
277
+ export function AppearanceScrubField({
278
+ label,
279
+ ariaLabel,
280
+ icon,
281
+ value,
282
+ onChange,
283
+ mixed = false,
284
+ min,
285
+ max,
286
+ step,
287
+ unit,
288
+ precision,
289
+ disabled = false,
290
+ }: {
291
+ label: string;
292
+ ariaLabel?: string;
293
+ icon: (props: { className?: string }) => ReactNode;
294
+ value: number;
295
+ onChange: (value: number, meta?: ScrubInputChangeMeta) => void;
296
+ mixed?: boolean;
297
+ min?: number;
298
+ max?: number;
299
+ step?: number;
300
+ unit?: string;
301
+ precision?: number;
302
+ disabled?: boolean;
303
+ }) {
304
+ return (
305
+ <ScrubInput
306
+ label={label}
307
+ ariaLabel={ariaLabel ?? label}
308
+ icon={icon}
309
+ value={value}
310
+ onChange={onChange}
311
+ mixed={mixed}
312
+ min={min}
313
+ max={max}
314
+ step={step}
315
+ unit={unit}
316
+ precision={precision}
317
+ disabled={disabled}
318
+ className="min-w-0 gap-0"
319
+ labelClassName="h-6 w-7 justify-center gap-0 rounded-l-md rounded-r-none border border-r-0 border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] text-muted-foreground [&>span]:sr-only"
320
+ inputClassName="h-6 min-w-0 rounded-l-none rounded-r-md border-[var(--design-editor-control-border)] border-l-0 bg-[var(--design-editor-control-bg)] px-0 text-left shadow-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]"
321
+ />
322
+ );
323
+ }
324
+
325
+ export function BlendModeMenu({
326
+ styles,
327
+ onStyleChange,
328
+ }: {
329
+ styles: Record<string, string>;
330
+ onStyleChange: StyleChangeHandler;
331
+ }) {
332
+ const [open, setOpen] = useState(false);
333
+ const blendMode = optionValue(
334
+ BLEND_MODE_OPTIONS,
335
+ styles.mixBlendMode || "normal",
336
+ "normal",
337
+ );
338
+ // Recognize the Mixed sentinel BEFORE optionValue's fallback maps it to
339
+ // "normal" — a mixed selection must not check a wrong concrete mode.
340
+ // Isolation only disambiguates pass-through vs normal, so it only makes the
341
+ // state mixed when the blend mode itself resolves to normal.
342
+ const blendModeMixed =
343
+ isMixedValue(styles.mixBlendMode) ||
344
+ (blendMode === "normal" && isMixedValue(styles.isolation));
345
+ const selectedBlendMode = blendModeMixed
346
+ ? MIXED_VALUE
347
+ : blendMode === "normal" && styles.isolation !== "isolate"
348
+ ? "pass-through"
349
+ : blendMode;
350
+ const options = [
351
+ {
352
+ value: "pass-through",
353
+ label: "Pass through", // i18n-ignore design blend mode label
354
+ },
355
+ ...BLEND_MODE_OPTIONS,
356
+ ] as const;
357
+ const selectBlendMode = (value: (typeof options)[number]["value"]) => {
358
+ if (value === "pass-through") {
359
+ onStyleChange("mixBlendMode", "normal");
360
+ onStyleChange("isolation", "auto");
361
+ return;
362
+ }
363
+ onStyleChange("mixBlendMode", value);
364
+ if (value === "normal") onStyleChange("isolation", "isolate");
365
+ };
366
+
367
+ return (
368
+ <DropdownMenu open={open} onOpenChange={setOpen}>
369
+ <DropdownMenuTrigger asChild>
370
+ <Button
371
+ type="button"
372
+ variant="ghost"
373
+ size="icon"
374
+ aria-label={"Blend mode" /* i18n-ignore design inspector action */}
375
+ aria-pressed={open}
376
+ className={cn(
377
+ "size-6 cursor-pointer rounded-md text-muted-foreground hover:text-foreground",
378
+ open &&
379
+ "bg-[var(--design-editor-accent-color)]/20 text-[var(--design-editor-accent-color)] hover:text-[var(--design-editor-accent-color)]",
380
+ )}
381
+ >
382
+ <IconDroplet className="size-3.5" />
383
+ </Button>
384
+ </DropdownMenuTrigger>
385
+ <DropdownMenuContent
386
+ side="left"
387
+ align="start"
388
+ sideOffset={8}
389
+ className="z-[100010] w-48 rounded-xl border-[var(--design-editor-control-border)] bg-[var(--design-editor-panel-bg)] p-1 text-[13px] text-foreground shadow-2xl"
390
+ >
391
+ {blendModeMixed ? (
392
+ <>
393
+ {/* Placeholder state for a mixed selection: the check sits next to
394
+ "Mixed" instead of a wrong concrete mode. Picking any option
395
+ below applies it to every selected object. */}
396
+ <div className="flex h-9 items-center gap-3 rounded-md px-3 text-[13px] text-muted-foreground">
397
+ <span className="flex size-4 shrink-0 items-center justify-center">
398
+ <IconCheck className="size-4" />
399
+ </span>
400
+ <span>{MIXED_VALUE}</span>
401
+ </div>
402
+ <DropdownMenuSeparator />
403
+ </>
404
+ ) : null}
405
+ {options.map((option) => (
406
+ <DropdownMenuItem
407
+ key={option.value}
408
+ className="flex h-9 cursor-pointer items-center gap-3 rounded-md px-3 text-[13px] focus:bg-[var(--design-editor-control-bg)]"
409
+ onSelect={() => selectBlendMode(option.value)}
410
+ >
411
+ <span className="flex size-4 shrink-0 items-center justify-center">
412
+ {selectedBlendMode === option.value ? (
413
+ <IconCheck className="size-4" />
414
+ ) : null}
415
+ </span>
416
+ <span>{option.label}</span>
417
+ </DropdownMenuItem>
418
+ ))}
419
+ </DropdownMenuContent>
420
+ </DropdownMenu>
421
+ );
422
+ }
423
+
424
+ export function AppearanceProperties({
425
+ element,
426
+ onStyleChange,
427
+ motionKeyframeContext,
428
+ breakpointOverrideContext,
429
+ }: {
430
+ element: ElementInfo;
431
+ onStyleChange: StyleChangeHandler;
432
+ motionKeyframeContext?: MotionKeyframeFieldContext;
433
+ breakpointOverrideContext?: BreakpointOverrideFieldContext;
434
+ }) {
435
+ const t = useT();
436
+ const styles = element.computedStyles;
437
+ const hidden =
438
+ styles.visibility === "hidden" ||
439
+ styles.display === "none" ||
440
+ parseNumericValue(styles.opacity || "1") === 0;
441
+ return (
442
+ <PanelSection
443
+ title={t("root.commandAppearance")}
444
+ actions={
445
+ <>
446
+ <SectionIconToggle
447
+ label={
448
+ hidden
449
+ ? "Show" /* i18n-ignore design inspector action */
450
+ : "Hide" /* i18n-ignore design inspector action */
451
+ }
452
+ active={hidden}
453
+ onClick={() =>
454
+ onStyleChange("visibility", hidden ? "visible" : "hidden")
455
+ }
456
+ >
457
+ {hidden ? (
458
+ <IconEyeOff className="size-3.5" />
459
+ ) : (
460
+ <IconEye className="size-3.5" />
461
+ )}
462
+ </SectionIconToggle>
463
+ <BlendModeMenu styles={styles} onStyleChange={onStyleChange} />
464
+ </>
465
+ }
466
+ >
467
+ <div className="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto] items-center gap-x-2 gap-y-1.5">
468
+ <p className="min-w-0 truncate !text-[11px] font-medium text-muted-foreground">
469
+ {t("editPanel.labels.opacity")}
470
+ </p>
471
+ <p className="min-w-0 truncate !text-[11px] font-medium text-muted-foreground">
472
+ {t("editPanel.labels.cornerRadius")}
473
+ </p>
474
+ <span aria-hidden="true" />
475
+ <div className="group/field relative min-w-0">
476
+ <AppearanceScrubField
477
+ label={t("editPanel.labels.opacity")}
478
+ icon={IconGridDots}
479
+ value={
480
+ isMixedValue(styles.opacity)
481
+ ? 0
482
+ : parseNumericValue(styles.opacity || "1") * 100
483
+ }
484
+ onChange={(v, meta) =>
485
+ onStyleChange("opacity", String(v / 100), meta)
486
+ }
487
+ mixed={isMixedValue(styles.opacity)}
488
+ min={0}
489
+ max={100}
490
+ step={1}
491
+ unit="%"
492
+ precision={1}
493
+ />
494
+ <FieldTrailer
495
+ element={element}
496
+ motionCssProperty="opacity"
497
+ motionKeyframeContext={motionKeyframeContext}
498
+ breakpointOverrideContext={breakpointOverrideContext}
499
+ className="absolute -top-3.5 right-0"
500
+ hoverRevealClassName="opacity-0 group-hover/field:opacity-100"
501
+ />
502
+ </div>
503
+ {/* Selection-stable key so per-selection UI state (the independent-
504
+ corners toggle, which ratchets open while corners differ) resets on
505
+ selection change instead of leaking to the next element — same
506
+ pattern as ExportSettingsPanel. */}
507
+ <CornerRadiusControl
508
+ key={elementIdentityKey(element)}
509
+ styles={styles}
510
+ onStyleChange={onStyleChange}
511
+ element={element}
512
+ motionKeyframeContext={motionKeyframeContext}
513
+ breakpointOverrideContext={breakpointOverrideContext}
514
+ />
515
+ </div>
516
+ </PanelSection>
517
+ );
518
+ }