@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +14 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AgentPanel.tsx +1 -1
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +11 -6
- package/corpus/core/src/client/use-action.ts +39 -2
- package/corpus/core/src/client/use-db-sync.ts +44 -18
- package/corpus/core/src/db/create-get-db.ts +32 -2
- package/corpus/core/src/server/framework-request-handler.ts +52 -4
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
- package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
- package/corpus/templates/assets/AGENTS.md +23 -4
- package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
- package/corpus/templates/assets/actions/_helpers.ts +8 -1
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
- package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
- package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
- package/corpus/templates/assets/actions/generate-image.ts +342 -25
- package/corpus/templates/assets/actions/get-library.ts +4 -3
- package/corpus/templates/assets/actions/navigate.ts +2 -1
- package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
- package/corpus/templates/assets/actions/update-asset.ts +1 -0
- package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
- package/corpus/templates/assets/actions/view-screen.ts +8 -1
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +9 -1
- package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
- package/corpus/templates/assets/app/i18n-data.ts +650 -0
- package/corpus/templates/assets/app/routes/asset.$id.tsx +2 -1
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +44 -33
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
- package/corpus/templates/assets/app/routes/library.tsx +43 -1
- package/corpus/templates/assets/server/lib/generation.ts +366 -44
- package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
- package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
- package/corpus/templates/assets/shared/api.ts +47 -0
- package/corpus/templates/clips/app/components/library/library-layout.tsx +7 -2
- package/corpus/templates/clips/app/components/library/sort-menu.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +114 -33
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
- package/corpus/templates/clips/app/routes/record.tsx +157 -68
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +5 -5
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +20 -3
- package/corpus/templates/clips/shared/finalize-recovery.ts +23 -3
- package/corpus/templates/content/AGENTS.md +52 -52
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
- package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
- package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
- package/corpus/templates/content/actions/_database-utils.ts +44 -4
- package/corpus/templates/content/actions/_notion-action-utils.ts +6 -2
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
- package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
- package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
- package/corpus/templates/content/actions/delete-content-database.ts +1 -0
- package/corpus/templates/content/actions/get-document.ts +11 -2
- package/corpus/templates/content/actions/list-content-databases.ts +57 -9
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
- package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
- package/corpus/templates/content/actions/restore-content-database.ts +1 -0
- package/corpus/templates/content/actions/update-document.ts +43 -0
- package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +0 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +70 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +69 -17
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +4 -4
- package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
- package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +9076 -2876
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +41 -1
- package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +36 -11
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +11 -1
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +11 -8
- package/corpus/templates/content/app/global.css +41 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
- package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +47 -0
- package/corpus/templates/content/app/hooks/use-notion.ts +12 -10
- package/corpus/templates/content/app/i18n/zh-TW.ts +35 -0
- package/corpus/templates/content/app/i18n-data.ts +355 -0
- package/corpus/templates/content/app/root.tsx +22 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
- package/corpus/templates/content/shared/api.ts +13 -0
- package/corpus/templates/content/shared/builder-mdx.ts +16 -3
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +23 -13
- package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
- package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
- package/corpus/templates/design/AGENTS.md +9 -2
- package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
- package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
- package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
- package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
- package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
- package/corpus/templates/design/actions/create-component.ts +29 -60
- package/corpus/templates/design/actions/create-file.ts +9 -2
- package/corpus/templates/design/actions/duplicate-design.ts +31 -2
- package/corpus/templates/design/actions/edit-design.ts +36 -38
- package/corpus/templates/design/actions/generate-design-title.ts +87 -0
- package/corpus/templates/design/actions/generate-design.ts +53 -18
- package/corpus/templates/design/actions/generate-screens.ts +108 -4
- package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
- package/corpus/templates/design/actions/insert-asset.ts +34 -29
- package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
- package/corpus/templates/design/actions/present-design-variants.ts +7 -2
- package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
- package/corpus/templates/design/actions/update-file.ts +190 -75
- package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +469 -222
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +83 -3
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1091 -6948
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +419 -85
- package/corpus/templates/design/app/components/design/MotionDock.tsx +1124 -103
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
- package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2182 -235
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
- package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
- package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +1 -1
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +1 -3
- package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
- package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
- package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
- package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +91 -40
- package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +189 -87
- package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
- package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
- package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +99 -9
- package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
- package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
- package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
- package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
- package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
- package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
- package/corpus/templates/design/app/components/design/types.ts +15 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +109 -5
- package/corpus/templates/design/app/global.css +8 -8
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +163 -9
- package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
- package/corpus/templates/design/app/i18n-data.ts +1664 -152
- package/corpus/templates/design/app/i18n-motion.ts +563 -0
- package/corpus/templates/design/app/lib/design-import.ts +73 -0
- package/corpus/templates/design/app/pages/Index.tsx +41 -22
- package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
- package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
- package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
- package/corpus/templates/design/server/source-workspace.ts +139 -59
- package/corpus/templates/design/shared/board-file.ts +11 -2
- package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
- package/corpus/templates/design/shared/canvas-math.ts +354 -35
- package/corpus/templates/design/shared/code-layer.ts +277 -8
- package/corpus/templates/design/shared/design-state.ts +11 -4
- package/corpus/templates/design/shared/interaction-states.ts +548 -0
- package/corpus/templates/design/shared/motion-compiler.ts +186 -23
- package/corpus/templates/design/shared/motion-easing.ts +294 -0
- package/corpus/templates/design/shared/motion-timeline.ts +400 -34
- package/corpus/templates/design/shared/prompt-title.ts +95 -0
- package/corpus/templates/design/shared/responsive-classes.ts +448 -10
- package/corpus/templates/design/shared/screen-annotation.ts +46 -0
- package/corpus/templates/design/shared/shader-fills.ts +1009 -0
- package/corpus/templates/design/shared/shader-presets.ts +852 -4
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +15 -2
- package/corpus/templates/slides/app/components/editor/ImportButton.tsx +10 -1
- package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/client/use-action.d.ts +3 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +34 -3
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +4 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +37 -18
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/db/create-get-db.d.ts +25 -0
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +25 -2
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +37 -1
- package/dist/server/framework-request-handler.js.map +1 -1
- package/package.json +1 -1
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
- package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22924
package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx
ADDED
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client";
|
|
2
|
+
import {
|
|
3
|
+
composeTransform3D,
|
|
4
|
+
isTransform3DActive,
|
|
5
|
+
parseTransform3DParts,
|
|
6
|
+
type Transform3DParts,
|
|
7
|
+
} from "@shared/canvas-math";
|
|
8
|
+
import {
|
|
9
|
+
IconAngle,
|
|
10
|
+
IconAxisX,
|
|
11
|
+
IconAxisY,
|
|
12
|
+
IconFlipHorizontal,
|
|
13
|
+
IconFlipVertical,
|
|
14
|
+
IconLayoutAlignBottom,
|
|
15
|
+
IconLayoutAlignCenter,
|
|
16
|
+
IconLayoutAlignLeft,
|
|
17
|
+
IconLayoutAlignMiddle,
|
|
18
|
+
IconLayoutAlignRight,
|
|
19
|
+
IconLayoutAlignTop,
|
|
20
|
+
IconLayoutDistributeHorizontal,
|
|
21
|
+
IconPerspective,
|
|
22
|
+
IconRotate3d,
|
|
23
|
+
} from "@tabler/icons-react";
|
|
24
|
+
import { useCallback, useState } from "react";
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
Tooltip,
|
|
28
|
+
TooltipContent,
|
|
29
|
+
TooltipTrigger,
|
|
30
|
+
} from "@/components/ui/tooltip";
|
|
31
|
+
import { cn } from "@/lib/utils";
|
|
32
|
+
|
|
33
|
+
import {
|
|
34
|
+
ConstraintsPreview,
|
|
35
|
+
ConstraintsWidget,
|
|
36
|
+
ScrubInput,
|
|
37
|
+
type AlignmentMatrixValue,
|
|
38
|
+
type ConstraintsValue,
|
|
39
|
+
type ScrubInputChangeMeta,
|
|
40
|
+
} from "../inspector";
|
|
41
|
+
import type { ElementInfo } from "../types";
|
|
42
|
+
import {
|
|
43
|
+
AppearanceScrubField,
|
|
44
|
+
CornerRadiusControl,
|
|
45
|
+
} from "./appearance-properties";
|
|
46
|
+
import { FieldTrailer, ScrubStyleInput } from "./field-primitives";
|
|
47
|
+
import {
|
|
48
|
+
InspectorIconButton,
|
|
49
|
+
InspectorSegment,
|
|
50
|
+
SectionIconToggle,
|
|
51
|
+
} from "./inspector-controls";
|
|
52
|
+
import { authoredStyleValue } from "./interaction-state-helpers";
|
|
53
|
+
import { PanelSection, SubsectionLabel } from "./panel-primitives";
|
|
54
|
+
import { roundToOneDecimal } from "./position-helpers";
|
|
55
|
+
import { isMixedValue, MIXED_VALUE } from "./selection-helpers";
|
|
56
|
+
import type {
|
|
57
|
+
BreakpointOverrideFieldContext,
|
|
58
|
+
MotionKeyframeFieldContext,
|
|
59
|
+
StyleChangeHandler,
|
|
60
|
+
} from "./style-change-types";
|
|
61
|
+
import {
|
|
62
|
+
mergeRotationValue,
|
|
63
|
+
mergeTranslateFunction,
|
|
64
|
+
parseRotationValue,
|
|
65
|
+
parseScaleValue,
|
|
66
|
+
} from "./transform-helpers";
|
|
67
|
+
|
|
68
|
+
/** Position, size, and spacing properties */
|
|
69
|
+
export function PositionLayoutProperties({
|
|
70
|
+
element,
|
|
71
|
+
onStyleChange,
|
|
72
|
+
onAlignSelection,
|
|
73
|
+
motionKeyframeContext,
|
|
74
|
+
breakpointOverrideContext,
|
|
75
|
+
}: {
|
|
76
|
+
element: ElementInfo;
|
|
77
|
+
onStyleChange: StyleChangeHandler;
|
|
78
|
+
/**
|
|
79
|
+
* Moves the selection itself (Figma's real "Alignment" row semantics):
|
|
80
|
+
* aligns to the combined selection bounding box for a 2+ multi-selection,
|
|
81
|
+
* or to the parent for a single selected object. When provided, the
|
|
82
|
+
* alignment row's six buttons call this instead of writing flex-alignment
|
|
83
|
+
* properties on the selected element. See the `onAlignSelection` contract
|
|
84
|
+
* note above `PositionLayoutProperties` usage in this file for the exact
|
|
85
|
+
* edge semantics the caller (DesignEditor) must implement.
|
|
86
|
+
*/
|
|
87
|
+
onAlignSelection?: (
|
|
88
|
+
edge: "left" | "center-h" | "right" | "top" | "center-v" | "bottom",
|
|
89
|
+
) => void;
|
|
90
|
+
motionKeyframeContext?: MotionKeyframeFieldContext;
|
|
91
|
+
breakpointOverrideContext?: BreakpointOverrideFieldContext;
|
|
92
|
+
}) {
|
|
93
|
+
const t = useT();
|
|
94
|
+
const styles = element.computedStyles;
|
|
95
|
+
const constrainedPosition =
|
|
96
|
+
styles.position === "absolute" || styles.position === "fixed";
|
|
97
|
+
// NOTE: this row used to also write flex alignment (justifyContent/
|
|
98
|
+
// alignItems) on the selected element when it was a flex container —
|
|
99
|
+
// i.e. it aligned the element's own children. That duplicated exactly
|
|
100
|
+
// what FlexContainerControls' AutoLayoutMatrix already offers via its
|
|
101
|
+
// CompactAlignmentMatrix (onAlignmentChange, wired a few hundred lines
|
|
102
|
+
// up in this file) and was never real Figma behavior: Figma's Alignment
|
|
103
|
+
// row in the Position section always moves the selected object(s), not
|
|
104
|
+
// their children. That fallback has been removed — flex child alignment
|
|
105
|
+
// now lives exclusively in the auto-layout section's alignment matrix.
|
|
106
|
+
const handlePositionAlignH = (value: AlignmentMatrixValue["horizontal"]) => {
|
|
107
|
+
onAlignSelection?.(
|
|
108
|
+
value === "left" ? "left" : value === "right" ? "right" : "center-h",
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
const handlePositionAlignV = (value: AlignmentMatrixValue["vertical"]) => {
|
|
112
|
+
onAlignSelection?.(
|
|
113
|
+
value === "top" ? "top" : value === "bottom" ? "bottom" : "center-v",
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
// Authored (not computed) offsets: when inlineStyles is present, "auto"/absent
|
|
117
|
+
// is treated as truly unset instead of the computed px fallback, so a plain
|
|
118
|
+
// top-left-anchored element reads as "left" rather than always "left-right".
|
|
119
|
+
const authoredLeft = authoredStyleValue(element, "left");
|
|
120
|
+
const authoredRight = authoredStyleValue(element, "right");
|
|
121
|
+
const authoredTop = authoredStyleValue(element, "top");
|
|
122
|
+
const authoredBottom = authoredStyleValue(element, "bottom");
|
|
123
|
+
const authoredWidth = authoredStyleValue(element, "width");
|
|
124
|
+
const authoredHeight = authoredStyleValue(element, "height");
|
|
125
|
+
const authoredTransform = authoredStyleValue(element, "transform");
|
|
126
|
+
const constraintsValue: ConstraintsValue = {
|
|
127
|
+
horizontal:
|
|
128
|
+
// Check scale before left+right: "scale" writes width:100% and clears
|
|
129
|
+
// left/right to auto, but legacy data may have 0px values that are truthy.
|
|
130
|
+
authoredWidth === "100%"
|
|
131
|
+
? "scale"
|
|
132
|
+
: authoredLeft && authoredRight
|
|
133
|
+
? "left-right"
|
|
134
|
+
: authoredRight
|
|
135
|
+
? "right"
|
|
136
|
+
: authoredTransform?.includes("translateX(-50%)")
|
|
137
|
+
? "center"
|
|
138
|
+
: "left",
|
|
139
|
+
vertical:
|
|
140
|
+
authoredHeight === "100%"
|
|
141
|
+
? "scale"
|
|
142
|
+
: authoredTop && authoredBottom
|
|
143
|
+
? "top-bottom"
|
|
144
|
+
: authoredBottom
|
|
145
|
+
? "bottom"
|
|
146
|
+
: authoredTransform?.includes("translateY(-50%)")
|
|
147
|
+
? "center"
|
|
148
|
+
: "top",
|
|
149
|
+
};
|
|
150
|
+
const [constraintsExpanded, setConstraintsExpanded] = useState(false);
|
|
151
|
+
// 3D rotation/perspective progressive-disclosure expander — mirrors
|
|
152
|
+
// CornerRadiusControl's showIndependentCorners pattern. Default-expanded
|
|
153
|
+
// when the authored transform already has non-zero X/Y rotation or
|
|
154
|
+
// perspective, so an element edited elsewhere (e.g. by the agent) doesn't
|
|
155
|
+
// hide its active 3D state behind a collapsed control.
|
|
156
|
+
const initialTransform3DParts = parseTransform3DParts(
|
|
157
|
+
isMixedValue(authoredTransform) ? undefined : authoredTransform,
|
|
158
|
+
);
|
|
159
|
+
const [rotation3DExpanded, setRotation3DExpanded] = useState(
|
|
160
|
+
() =>
|
|
161
|
+
initialTransform3DParts !== null &&
|
|
162
|
+
isTransform3DActive(initialTransform3DParts),
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const handleConstraintsChange = useCallback(
|
|
166
|
+
(value: ConstraintsValue) => {
|
|
167
|
+
onStyleChange("position", "absolute");
|
|
168
|
+
|
|
169
|
+
// Compute the desired translateX/Y for each axis independently, then
|
|
170
|
+
// compose both into a single transform write so the two axes don't
|
|
171
|
+
// overwrite each other when both change simultaneously.
|
|
172
|
+
const txValue = value.horizontal === "center" ? "-50%" : null;
|
|
173
|
+
const tyValue = value.vertical === "center" ? "-50%" : null;
|
|
174
|
+
// Start from the current transform, apply X, then apply Y on top.
|
|
175
|
+
const transformAfterX = mergeTranslateFunction(
|
|
176
|
+
authoredTransform,
|
|
177
|
+
"X",
|
|
178
|
+
txValue,
|
|
179
|
+
);
|
|
180
|
+
const transformAfterXY = mergeTranslateFunction(
|
|
181
|
+
transformAfterX,
|
|
182
|
+
"Y",
|
|
183
|
+
tyValue,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
if (value.horizontal === "left") {
|
|
187
|
+
onStyleChange(
|
|
188
|
+
"left",
|
|
189
|
+
authoredLeft || `${Math.round(element.boundingRect.x)}px`,
|
|
190
|
+
);
|
|
191
|
+
onStyleChange("right", "auto");
|
|
192
|
+
} else if (value.horizontal === "right") {
|
|
193
|
+
onStyleChange("right", "0px");
|
|
194
|
+
onStyleChange("left", "auto");
|
|
195
|
+
} else if (value.horizontal === "left-right") {
|
|
196
|
+
onStyleChange(
|
|
197
|
+
"left",
|
|
198
|
+
authoredLeft || `${Math.round(element.boundingRect.x)}px`,
|
|
199
|
+
);
|
|
200
|
+
onStyleChange("right", "0px");
|
|
201
|
+
} else if (value.horizontal === "center") {
|
|
202
|
+
onStyleChange("left", "50%");
|
|
203
|
+
onStyleChange("right", "auto");
|
|
204
|
+
} else {
|
|
205
|
+
// scale: use auto (not 0px) so the left && right truthiness check
|
|
206
|
+
// in the reader does not misidentify this as "left-right".
|
|
207
|
+
onStyleChange("left", "auto");
|
|
208
|
+
onStyleChange("right", "auto");
|
|
209
|
+
onStyleChange("width", "100%");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (value.vertical === "top") {
|
|
213
|
+
onStyleChange(
|
|
214
|
+
"top",
|
|
215
|
+
authoredTop || `${Math.round(element.boundingRect.y)}px`,
|
|
216
|
+
);
|
|
217
|
+
onStyleChange("bottom", "auto");
|
|
218
|
+
} else if (value.vertical === "bottom") {
|
|
219
|
+
onStyleChange("bottom", "0px");
|
|
220
|
+
onStyleChange("top", "auto");
|
|
221
|
+
} else if (value.vertical === "top-bottom") {
|
|
222
|
+
onStyleChange(
|
|
223
|
+
"top",
|
|
224
|
+
authoredTop || `${Math.round(element.boundingRect.y)}px`,
|
|
225
|
+
);
|
|
226
|
+
onStyleChange("bottom", "0px");
|
|
227
|
+
} else if (value.vertical === "center") {
|
|
228
|
+
onStyleChange("top", "50%");
|
|
229
|
+
onStyleChange("bottom", "auto");
|
|
230
|
+
} else {
|
|
231
|
+
// scale
|
|
232
|
+
onStyleChange("top", "auto");
|
|
233
|
+
onStyleChange("bottom", "auto");
|
|
234
|
+
onStyleChange("height", "100%");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Write the composed transform once, after both axes are resolved.
|
|
238
|
+
onStyleChange("transform", transformAfterXY);
|
|
239
|
+
},
|
|
240
|
+
[
|
|
241
|
+
element.boundingRect.x,
|
|
242
|
+
element.boundingRect.y,
|
|
243
|
+
onStyleChange,
|
|
244
|
+
authoredLeft,
|
|
245
|
+
authoredTop,
|
|
246
|
+
authoredTransform,
|
|
247
|
+
],
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
return (
|
|
251
|
+
<PanelSection
|
|
252
|
+
title={t("editPanel.sections.positionLayout")}
|
|
253
|
+
actions={
|
|
254
|
+
<SectionIconToggle
|
|
255
|
+
label={"Absolute position" /* i18n-ignore design inspector action */}
|
|
256
|
+
active={constrainedPosition}
|
|
257
|
+
onClick={() =>
|
|
258
|
+
onStyleChange(
|
|
259
|
+
"position",
|
|
260
|
+
constrainedPosition ? "relative" : "absolute",
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
>
|
|
264
|
+
<IconLayoutDistributeHorizontal className="size-3.5" />
|
|
265
|
+
</SectionIconToggle>
|
|
266
|
+
}
|
|
267
|
+
>
|
|
268
|
+
<div className="space-y-1.5">
|
|
269
|
+
<SubsectionLabel>
|
|
270
|
+
{"Alignment" /* i18n-ignore design inspector label */}
|
|
271
|
+
</SubsectionLabel>
|
|
272
|
+
<div className="flex items-center gap-3">
|
|
273
|
+
<InspectorSegment>
|
|
274
|
+
<InspectorIconButton
|
|
275
|
+
label={t("editPanel.textAligns.left")}
|
|
276
|
+
shortcut="⌥A"
|
|
277
|
+
onClick={() => handlePositionAlignH("left")}
|
|
278
|
+
>
|
|
279
|
+
<IconLayoutAlignLeft className="size-3.5" />
|
|
280
|
+
</InspectorIconButton>
|
|
281
|
+
<InspectorIconButton
|
|
282
|
+
label={t("editPanel.textAligns.center")}
|
|
283
|
+
shortcut="⌥H"
|
|
284
|
+
onClick={() => handlePositionAlignH("center")}
|
|
285
|
+
>
|
|
286
|
+
<IconLayoutAlignCenter className="size-3.5" />
|
|
287
|
+
</InspectorIconButton>
|
|
288
|
+
<InspectorIconButton
|
|
289
|
+
label={t("editPanel.textAligns.right")}
|
|
290
|
+
shortcut="⌥D"
|
|
291
|
+
onClick={() => handlePositionAlignH("right")}
|
|
292
|
+
>
|
|
293
|
+
<IconLayoutAlignRight className="size-3.5" />
|
|
294
|
+
</InspectorIconButton>
|
|
295
|
+
</InspectorSegment>
|
|
296
|
+
<InspectorSegment>
|
|
297
|
+
<InspectorIconButton
|
|
298
|
+
label={t("editPanel.alignSelfOptions.start")}
|
|
299
|
+
shortcut="⌥W"
|
|
300
|
+
onClick={() => handlePositionAlignV("top")}
|
|
301
|
+
>
|
|
302
|
+
<IconLayoutAlignTop className="size-3.5" />
|
|
303
|
+
</InspectorIconButton>
|
|
304
|
+
<InspectorIconButton
|
|
305
|
+
label={t("editPanel.alignSelfOptions.center")}
|
|
306
|
+
shortcut="⌥V"
|
|
307
|
+
onClick={() => handlePositionAlignV("middle")}
|
|
308
|
+
>
|
|
309
|
+
<IconLayoutAlignMiddle className="size-3.5" />
|
|
310
|
+
</InspectorIconButton>
|
|
311
|
+
<InspectorIconButton
|
|
312
|
+
label={t("editPanel.alignSelfOptions.end")}
|
|
313
|
+
shortcut="⌥S"
|
|
314
|
+
onClick={() => handlePositionAlignV("bottom")}
|
|
315
|
+
>
|
|
316
|
+
<IconLayoutAlignBottom className="size-3.5" />
|
|
317
|
+
</InspectorIconButton>
|
|
318
|
+
</InspectorSegment>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
<div className="space-y-1.5">
|
|
323
|
+
<SubsectionLabel>{t("editPanel.labels.position")}</SubsectionLabel>
|
|
324
|
+
<div className="grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_1.75rem] gap-2">
|
|
325
|
+
<div className="group/field relative min-w-0">
|
|
326
|
+
<ScrubStyleInput
|
|
327
|
+
label="X"
|
|
328
|
+
ariaLabel="X-position"
|
|
329
|
+
tooltipLabel="X-position"
|
|
330
|
+
value={
|
|
331
|
+
isMixedValue(authoredLeft) ? MIXED_VALUE : authoredLeft || ""
|
|
332
|
+
}
|
|
333
|
+
placeholder={element.boundingRect.x}
|
|
334
|
+
inputClassName="h-6"
|
|
335
|
+
onChange={(v, meta) => {
|
|
336
|
+
// Typing X/Y on a static (non-positioned) element is a no-op on
|
|
337
|
+
// canvas unless we first give it a position to offset from —
|
|
338
|
+
// mirror handleConstraintsChange, which always sets
|
|
339
|
+
// position:absolute (the convention canvas drag/resize and
|
|
340
|
+
// primitive creation both use) before writing left/top.
|
|
341
|
+
if (!constrainedPosition) onStyleChange("position", "absolute");
|
|
342
|
+
onStyleChange("left", `${roundToOneDecimal(v)}px`, meta);
|
|
343
|
+
}}
|
|
344
|
+
/>
|
|
345
|
+
<FieldTrailer
|
|
346
|
+
element={element}
|
|
347
|
+
motionCssProperty="translate"
|
|
348
|
+
overrideProperty="left"
|
|
349
|
+
motionKeyframeContext={motionKeyframeContext}
|
|
350
|
+
breakpointOverrideContext={breakpointOverrideContext}
|
|
351
|
+
className="absolute -top-3.5 right-0"
|
|
352
|
+
hoverRevealClassName="opacity-0 group-hover/field:opacity-100"
|
|
353
|
+
/>
|
|
354
|
+
</div>
|
|
355
|
+
<div className="group/field relative min-w-0">
|
|
356
|
+
<ScrubStyleInput
|
|
357
|
+
label="Y"
|
|
358
|
+
ariaLabel="Y-position"
|
|
359
|
+
tooltipLabel="Y-position"
|
|
360
|
+
value={
|
|
361
|
+
isMixedValue(authoredTop) ? MIXED_VALUE : authoredTop || ""
|
|
362
|
+
}
|
|
363
|
+
placeholder={element.boundingRect.y}
|
|
364
|
+
inputClassName="h-6"
|
|
365
|
+
onChange={(v, meta) => {
|
|
366
|
+
if (!constrainedPosition) onStyleChange("position", "absolute");
|
|
367
|
+
onStyleChange("top", `${roundToOneDecimal(v)}px`, meta);
|
|
368
|
+
}}
|
|
369
|
+
/>
|
|
370
|
+
<FieldTrailer
|
|
371
|
+
element={element}
|
|
372
|
+
motionCssProperty="translate"
|
|
373
|
+
overrideProperty="top"
|
|
374
|
+
motionKeyframeContext={motionKeyframeContext}
|
|
375
|
+
breakpointOverrideContext={breakpointOverrideContext}
|
|
376
|
+
className="absolute -top-3.5 right-0"
|
|
377
|
+
hoverRevealClassName="opacity-0 group-hover/field:opacity-100"
|
|
378
|
+
/>
|
|
379
|
+
</div>
|
|
380
|
+
<Tooltip>
|
|
381
|
+
<TooltipTrigger asChild>
|
|
382
|
+
<button
|
|
383
|
+
type="button"
|
|
384
|
+
aria-label={
|
|
385
|
+
"Constraints" /* i18n-ignore design inspector action */
|
|
386
|
+
}
|
|
387
|
+
aria-pressed={constraintsExpanded}
|
|
388
|
+
onClick={() => setConstraintsExpanded((expanded) => !expanded)}
|
|
389
|
+
className={cn(
|
|
390
|
+
"flex size-7 items-center justify-center rounded-md transition-colors",
|
|
391
|
+
"hover:bg-[var(--design-editor-control-bg)] hover:text-foreground",
|
|
392
|
+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
|
|
393
|
+
constraintsExpanded
|
|
394
|
+
? "bg-[var(--design-editor-selection-color)] text-[var(--design-editor-accent-color)] hover:text-[var(--design-editor-accent-color)]"
|
|
395
|
+
: "text-muted-foreground",
|
|
396
|
+
)}
|
|
397
|
+
>
|
|
398
|
+
<ConstraintsPreview value={constraintsValue} />
|
|
399
|
+
</button>
|
|
400
|
+
</TooltipTrigger>
|
|
401
|
+
<TooltipContent>
|
|
402
|
+
{"Constraints" /* i18n-ignore design inspector tooltip */}
|
|
403
|
+
</TooltipContent>
|
|
404
|
+
</Tooltip>
|
|
405
|
+
</div>
|
|
406
|
+
{constraintsExpanded ? (
|
|
407
|
+
<ConstraintsWidget
|
|
408
|
+
value={constraintsValue}
|
|
409
|
+
onChange={handleConstraintsChange}
|
|
410
|
+
className="pt-1"
|
|
411
|
+
/>
|
|
412
|
+
) : null}
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
<div className="space-y-1.5">
|
|
416
|
+
<SubsectionLabel>{t("editPanel.labels.rotation")}</SubsectionLabel>
|
|
417
|
+
<div className="group flex items-center gap-2">
|
|
418
|
+
<div className="min-w-0 flex-1">
|
|
419
|
+
<ScrubStyleInput
|
|
420
|
+
label="Rotation"
|
|
421
|
+
ariaLabel={t("editPanel.labels.rotation")}
|
|
422
|
+
tooltipLabel={t("editPanel.labels.rotation")}
|
|
423
|
+
hideIcon={false}
|
|
424
|
+
icon={IconAngle}
|
|
425
|
+
labelClassName="[&>span]:sr-only"
|
|
426
|
+
// Detect the Mixed sentinel BEFORE parsing: parseRotationValue
|
|
427
|
+
// would silently turn "Mixed" into 0 and render "0deg" instead
|
|
428
|
+
// of the mixed state (mirrors the opacity field's guard).
|
|
429
|
+
value={
|
|
430
|
+
isMixedValue(styles.transform)
|
|
431
|
+
? MIXED_VALUE
|
|
432
|
+
: `${parseRotationValue(styles.transform)}deg`
|
|
433
|
+
}
|
|
434
|
+
unit="deg"
|
|
435
|
+
inputClassName="h-6"
|
|
436
|
+
onChange={(v, meta) =>
|
|
437
|
+
onStyleChange(
|
|
438
|
+
"transform",
|
|
439
|
+
// From a mixed selection the sentinel is not a transform —
|
|
440
|
+
// treat it as absent so the typed value applies cleanly to
|
|
441
|
+
// every selected object instead of producing
|
|
442
|
+
// "Mixed rotate(…)". This field always writes the Z
|
|
443
|
+
// rotation — back-compat: existing designs'
|
|
444
|
+
// `transform: rotate()` is the Z axis. When the 3D
|
|
445
|
+
// expander below is active (non-zero X/Y/perspective),
|
|
446
|
+
// mergeRotationValue's plain rotate() slot still round-
|
|
447
|
+
// trips correctly since composeTransform3D always emits a
|
|
448
|
+
// trailing rotateZ() once 3D is active, which
|
|
449
|
+
// ROTATE_FN_PATTERN also matches.
|
|
450
|
+
mergeRotationValue(
|
|
451
|
+
isMixedValue(styles.transform)
|
|
452
|
+
? undefined
|
|
453
|
+
: styles.transform,
|
|
454
|
+
v,
|
|
455
|
+
),
|
|
456
|
+
meta,
|
|
457
|
+
)
|
|
458
|
+
}
|
|
459
|
+
/>
|
|
460
|
+
</div>
|
|
461
|
+
<FieldTrailer
|
|
462
|
+
element={element}
|
|
463
|
+
motionCssProperty="rotate"
|
|
464
|
+
overrideProperty="transform"
|
|
465
|
+
motionKeyframeContext={motionKeyframeContext}
|
|
466
|
+
breakpointOverrideContext={breakpointOverrideContext}
|
|
467
|
+
hoverRevealClassName="opacity-0 group-hover:opacity-100"
|
|
468
|
+
/>
|
|
469
|
+
<InspectorSegment>
|
|
470
|
+
<InspectorIconButton
|
|
471
|
+
label={t("editPanel.labels.flipHorizontal")}
|
|
472
|
+
onClick={() => {
|
|
473
|
+
const [sx, sy] = parseScaleValue(styles.scale);
|
|
474
|
+
onStyleChange("scale", `${sx === -1 ? 1 : -1} ${sy}`);
|
|
475
|
+
}}
|
|
476
|
+
>
|
|
477
|
+
<IconFlipHorizontal className="size-4" />
|
|
478
|
+
</InspectorIconButton>
|
|
479
|
+
<InspectorIconButton
|
|
480
|
+
label={t("editPanel.labels.flipVertical")}
|
|
481
|
+
onClick={() => {
|
|
482
|
+
const [sx, sy] = parseScaleValue(styles.scale);
|
|
483
|
+
onStyleChange("scale", `${sx} ${sy === -1 ? 1 : -1}`);
|
|
484
|
+
}}
|
|
485
|
+
>
|
|
486
|
+
<IconFlipVertical className="size-4" />
|
|
487
|
+
</InspectorIconButton>
|
|
488
|
+
</InspectorSegment>
|
|
489
|
+
<Tooltip>
|
|
490
|
+
<TooltipTrigger asChild>
|
|
491
|
+
<button
|
|
492
|
+
type="button"
|
|
493
|
+
aria-label={t("editPanel.labels.rotation3d")}
|
|
494
|
+
aria-pressed={rotation3DExpanded}
|
|
495
|
+
onClick={() => setRotation3DExpanded((expanded) => !expanded)}
|
|
496
|
+
className={cn(
|
|
497
|
+
"flex size-7 shrink-0 items-center justify-center rounded-md transition-colors",
|
|
498
|
+
"hover:bg-[var(--design-editor-control-bg)] hover:text-foreground",
|
|
499
|
+
"focus:outline-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
|
|
500
|
+
rotation3DExpanded
|
|
501
|
+
? "bg-[var(--design-editor-selection-color)] text-[var(--design-editor-accent-color)] hover:text-[var(--design-editor-accent-color)]"
|
|
502
|
+
: "text-muted-foreground",
|
|
503
|
+
)}
|
|
504
|
+
>
|
|
505
|
+
<IconRotate3d className="size-3.5" />
|
|
506
|
+
</button>
|
|
507
|
+
</TooltipTrigger>
|
|
508
|
+
<TooltipContent>{t("editPanel.labels.rotation3d")}</TooltipContent>
|
|
509
|
+
</Tooltip>
|
|
510
|
+
</div>
|
|
511
|
+
{rotation3DExpanded ? (
|
|
512
|
+
<Rotation3DControls styles={styles} onStyleChange={onStyleChange} />
|
|
513
|
+
) : null}
|
|
514
|
+
</div>
|
|
515
|
+
</PanelSection>
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Progressive-disclosure X/Y/Z rotation + perspective controls, revealed by
|
|
521
|
+
* the 3D-rotation expander next to the plain (Z-axis) rotation field. See
|
|
522
|
+
* `composeTransform3D`/`parseTransform3DParts` (shared/canvas-math.ts) for
|
|
523
|
+
* the parse/compose contract this wraps.
|
|
524
|
+
*
|
|
525
|
+
* - Transform composition order: `perspective(Npx) rotateX(Xdeg)
|
|
526
|
+
* rotateY(Ydeg) rotateZ(Zdeg) <preserved translate/scale/etc>` — see the
|
|
527
|
+
* `composeTransform3D` doc comment for the full rationale (X→Y→Z is a
|
|
528
|
+
* common 3D-engine Euler convention; Figma hasn't published a composition
|
|
529
|
+
* order since 3D transforms are unshipped there as of this build).
|
|
530
|
+
* - When X, Y, and Perspective are all zero/empty, the composed transform is
|
|
531
|
+
* the plain 2D `rotate(Zdeg)` form — zero output churn for existing
|
|
532
|
+
* designs that never touch this expander.
|
|
533
|
+
* - `transform-style: preserve-3d` is intentionally NOT applied here:
|
|
534
|
+
* defaulting to flattened (no preserve-3d) matches the conservative,
|
|
535
|
+
* minimal-footprint choice for this first pass — see the build report for
|
|
536
|
+
* the preserve-3d-on-children tradeoff.
|
|
537
|
+
*/
|
|
538
|
+
function Rotation3DControls({
|
|
539
|
+
styles,
|
|
540
|
+
onStyleChange,
|
|
541
|
+
}: {
|
|
542
|
+
styles: Record<string, string>;
|
|
543
|
+
onStyleChange: StyleChangeHandler;
|
|
544
|
+
}) {
|
|
545
|
+
const t = useT();
|
|
546
|
+
const transformMixed = isMixedValue(styles.transform);
|
|
547
|
+
const parts = transformMixed ? null : parseTransform3DParts(styles.transform);
|
|
548
|
+
// `parts === null` (and not mixed) means the authored transform is a
|
|
549
|
+
// matrix()/matrix3d()/rotate3d() composite (or an unrecognized token) that
|
|
550
|
+
// parseTransform3DParts can't safely invert into independent X/Y/Z/
|
|
551
|
+
// perspective fields — show the fields disabled with a note instead of
|
|
552
|
+
// guessing, matching how Mixed values disable commit rather than silently
|
|
553
|
+
// defaulting to 0. See parseTransform3DParts's doc comment.
|
|
554
|
+
const isCustomTransform = !transformMixed && parts === null;
|
|
555
|
+
const disabled = transformMixed || isCustomTransform;
|
|
556
|
+
const displayParts: Transform3DParts = parts ?? {
|
|
557
|
+
rotateX: 0,
|
|
558
|
+
rotateY: 0,
|
|
559
|
+
rotateZ: 0,
|
|
560
|
+
perspective: 0,
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
const commitPart = (
|
|
564
|
+
patch: Partial<Transform3DParts>,
|
|
565
|
+
meta?: ScrubInputChangeMeta,
|
|
566
|
+
) => {
|
|
567
|
+
if (disabled) return;
|
|
568
|
+
const nextParts: Transform3DParts = { ...displayParts, ...patch };
|
|
569
|
+
onStyleChange(
|
|
570
|
+
"transform",
|
|
571
|
+
composeTransform3D(styles.transform, nextParts),
|
|
572
|
+
meta,
|
|
573
|
+
);
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
return (
|
|
577
|
+
<div className="space-y-1.5 pt-1">
|
|
578
|
+
{isCustomTransform ? (
|
|
579
|
+
<p className="!text-[11px] text-muted-foreground">
|
|
580
|
+
{t("editPanel.labels.customTransform")}
|
|
581
|
+
</p>
|
|
582
|
+
) : null}
|
|
583
|
+
<div className="grid grid-cols-2 gap-1.5">
|
|
584
|
+
<AppearanceScrubField
|
|
585
|
+
label={t("editPanel.labels.rotationX")}
|
|
586
|
+
icon={IconAxisX}
|
|
587
|
+
value={transformMixed ? 0 : displayParts.rotateX}
|
|
588
|
+
onChange={(value, meta) => commitPart({ rotateX: value }, meta)}
|
|
589
|
+
mixed={transformMixed}
|
|
590
|
+
disabled={isCustomTransform}
|
|
591
|
+
step={1}
|
|
592
|
+
unit="deg"
|
|
593
|
+
precision={1}
|
|
594
|
+
/>
|
|
595
|
+
<AppearanceScrubField
|
|
596
|
+
label={t("editPanel.labels.rotationY")}
|
|
597
|
+
icon={IconAxisY}
|
|
598
|
+
value={transformMixed ? 0 : displayParts.rotateY}
|
|
599
|
+
onChange={(value, meta) => commitPart({ rotateY: value }, meta)}
|
|
600
|
+
mixed={transformMixed}
|
|
601
|
+
disabled={isCustomTransform}
|
|
602
|
+
step={1}
|
|
603
|
+
unit="deg"
|
|
604
|
+
precision={1}
|
|
605
|
+
/>
|
|
606
|
+
<ScrubInput
|
|
607
|
+
label={t("editPanel.labels.perspective")}
|
|
608
|
+
ariaLabel={t("editPanel.labels.perspective")}
|
|
609
|
+
tooltipLabel={t("editPanel.labels.perspectiveHint")}
|
|
610
|
+
icon={IconPerspective}
|
|
611
|
+
value={transformMixed ? 0 : displayParts.perspective}
|
|
612
|
+
onChange={(value, meta) =>
|
|
613
|
+
commitPart({ perspective: Math.max(0, value) }, meta)
|
|
614
|
+
}
|
|
615
|
+
mixed={transformMixed}
|
|
616
|
+
disabled={isCustomTransform}
|
|
617
|
+
min={0}
|
|
618
|
+
step={10}
|
|
619
|
+
unit="px"
|
|
620
|
+
precision={0}
|
|
621
|
+
className="col-span-2 gap-0"
|
|
622
|
+
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)] [&>span]:sr-only"
|
|
623
|
+
inputClassName="h-6 rounded-l-none rounded-r-md border-[var(--design-editor-control-border)] bg-[var(--design-editor-control-bg)] shadow-none focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]"
|
|
624
|
+
/>
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
);
|
|
628
|
+
}
|