@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
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
import { useT } from "@agent-native/core/client";
|
|
2
|
+
import {
|
|
3
|
+
IconLayoutGrid,
|
|
4
|
+
IconLink,
|
|
5
|
+
IconLinkOff,
|
|
6
|
+
IconMinus,
|
|
7
|
+
IconPlus,
|
|
8
|
+
} from "@tabler/icons-react";
|
|
9
|
+
import { useEffect, useState } from "react";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
Tooltip,
|
|
13
|
+
TooltipContent,
|
|
14
|
+
TooltipTrigger,
|
|
15
|
+
} from "@/components/ui/tooltip";
|
|
16
|
+
import { cn } from "@/lib/utils";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
AutoLayoutMatrix,
|
|
20
|
+
ScrubInput,
|
|
21
|
+
SizingField,
|
|
22
|
+
type AutoLayoutMatrixValue,
|
|
23
|
+
type ScrubInputChangeMeta,
|
|
24
|
+
} from "../inspector";
|
|
25
|
+
import type { ElementInfo } from "../types";
|
|
26
|
+
import {
|
|
27
|
+
autoLayoutAlignmentFromStyles,
|
|
28
|
+
availableSizingForElement,
|
|
29
|
+
commitElementMinMax,
|
|
30
|
+
commitElementSizing,
|
|
31
|
+
cssElementSize,
|
|
32
|
+
elementHasLayoutChildren,
|
|
33
|
+
horizontalToJustify,
|
|
34
|
+
inferElementSizing,
|
|
35
|
+
isContainerElement,
|
|
36
|
+
isParentFlex,
|
|
37
|
+
isParentGrid,
|
|
38
|
+
readElementMinMax,
|
|
39
|
+
verticalToAlign,
|
|
40
|
+
} from "./element-classification";
|
|
41
|
+
import {
|
|
42
|
+
deriveLockedAspectSize,
|
|
43
|
+
elementIdentityKey,
|
|
44
|
+
useAspectRatioLock,
|
|
45
|
+
} from "./element-identity";
|
|
46
|
+
import { FieldTrailer, ScrubStyleInput } from "./field-primitives";
|
|
47
|
+
import { joinCssLayers, splitCssLayers } from "./fill-gradient-helpers";
|
|
48
|
+
import { SectionIconButton } from "./inspector-controls";
|
|
49
|
+
import {
|
|
50
|
+
PanelSection,
|
|
51
|
+
PropInput,
|
|
52
|
+
PropSelect,
|
|
53
|
+
SubsectionLabel,
|
|
54
|
+
} from "./panel-primitives";
|
|
55
|
+
import { compactCssValue, fourValuesEqual } from "./position-helpers";
|
|
56
|
+
import { isMixedValue } from "./selection-helpers";
|
|
57
|
+
import type {
|
|
58
|
+
BreakpointOverrideFieldContext,
|
|
59
|
+
MotionKeyframeFieldContext,
|
|
60
|
+
StyleChangeHandler,
|
|
61
|
+
StylesChangeHandler,
|
|
62
|
+
} from "./style-change-types";
|
|
63
|
+
import {
|
|
64
|
+
ALIGN_SELF_OPTIONS,
|
|
65
|
+
optionValue,
|
|
66
|
+
parseNumericValue,
|
|
67
|
+
} from "./style-options";
|
|
68
|
+
|
|
69
|
+
/** Flex container properties */
|
|
70
|
+
function FlexContainerControls({
|
|
71
|
+
element,
|
|
72
|
+
onStyleChange,
|
|
73
|
+
onStylesChange,
|
|
74
|
+
}: {
|
|
75
|
+
element: ElementInfo;
|
|
76
|
+
onStyleChange: StyleChangeHandler;
|
|
77
|
+
onStylesChange?: StylesChangeHandler;
|
|
78
|
+
}) {
|
|
79
|
+
const t = useT();
|
|
80
|
+
const styles = element.computedStyles;
|
|
81
|
+
// The element's CURRENT layout flow as authored in code, read from its own
|
|
82
|
+
// computed `display`: block/flow-root/grid/etc. = "normal flow",
|
|
83
|
+
// flex/inline-flex = auto layout. We forward it so the AutoLayoutMatrix Flow
|
|
84
|
+
// control can show the right state (normal vs horizontal/vertical/wrap)
|
|
85
|
+
// instead of an empty "add" affordance.
|
|
86
|
+
const display = (styles.display || "").toLowerCase();
|
|
87
|
+
const isFlex = element.isFlexContainer || display.includes("flex");
|
|
88
|
+
const displayMode: AutoLayoutMatrixValue["display"] = isFlex
|
|
89
|
+
? "flex"
|
|
90
|
+
: "block";
|
|
91
|
+
const hasLayoutChildren = elementHasLayoutChildren(element);
|
|
92
|
+
const flexDirection: AutoLayoutMatrixValue["direction"] =
|
|
93
|
+
styles.flexDirection?.includes("column") ? "vertical" : "horizontal";
|
|
94
|
+
const mainGapAxis =
|
|
95
|
+
flexDirection === "horizontal" ? "horizontal" : "vertical";
|
|
96
|
+
// When the element is in normal flow (not flex yet), picking any flow option
|
|
97
|
+
// must first turn it into a flex container; otherwise setting flex-direction
|
|
98
|
+
// alone is a no-op against a block element.
|
|
99
|
+
const ensureFlex = () => {
|
|
100
|
+
if (!isFlex) onStyleChange("display", "flex");
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Handle the Flow control switching between flex and normal-flow (block).
|
|
105
|
+
*
|
|
106
|
+
* For 'flex': ensures display:flex is set (ensureFlex path).
|
|
107
|
+
* For 'block': sets display:block and leaves children unchanged — mirrors
|
|
108
|
+
* the { kind:"autoLayout", enabled:false } substrate intent exactly.
|
|
109
|
+
*/
|
|
110
|
+
const handleDisplayChange = (nextDisplay: "flex" | "block") => {
|
|
111
|
+
if (nextDisplay === "flex") {
|
|
112
|
+
ensureFlex();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Turn auto-layout off: set display:block, leaving children unchanged.
|
|
116
|
+
// This is the direct equivalent of the autoLayout substrate with enabled:false.
|
|
117
|
+
onStyleChange("display", "block");
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const padding = {
|
|
121
|
+
top: parseNumericValue(styles.paddingTop || "0"),
|
|
122
|
+
right: parseNumericValue(styles.paddingRight || "0"),
|
|
123
|
+
bottom: parseNumericValue(styles.paddingBottom || "0"),
|
|
124
|
+
left: parseNumericValue(styles.paddingLeft || "0"),
|
|
125
|
+
};
|
|
126
|
+
const allPaddingEqual = fourValuesEqual([
|
|
127
|
+
padding.top,
|
|
128
|
+
padding.right,
|
|
129
|
+
padding.bottom,
|
|
130
|
+
padding.left,
|
|
131
|
+
]);
|
|
132
|
+
// Seeds the linked/unlinked view once per selection (this component is
|
|
133
|
+
// remounted per element via the `key={elementIdentityKey(element)}` at its
|
|
134
|
+
// call site, matching CornerRadiusControl's pattern) and is otherwise a
|
|
135
|
+
// pure user-controlled toggle (see onPaddingLinkedChange below). Do NOT add
|
|
136
|
+
// a useEffect that re-derives this from `allPaddingEqual` on every render:
|
|
137
|
+
// that previously auto-unlinked as soon as the four sides became unequal,
|
|
138
|
+
// which fires mid-drag the instant a user scrubs one axis of the linked
|
|
139
|
+
// horizontal/vertical fields (e.g. changing left/right while top/bottom
|
|
140
|
+
// stay put) — collapsing the linked 2-field view into the unlinked 4-field
|
|
141
|
+
// view *during* the gesture and destroying the drag (STEVE TEST BATCH 4 #4).
|
|
142
|
+
const [paddingLinked, setPaddingLinked] = useState(allPaddingEqual);
|
|
143
|
+
|
|
144
|
+
const autoLayoutValue: AutoLayoutMatrixValue = {
|
|
145
|
+
direction: flexDirection,
|
|
146
|
+
wrap: styles.flexWrap === "wrap" ? "wrap" : "nowrap",
|
|
147
|
+
alignment: autoLayoutAlignmentFromStyles(styles, flexDirection),
|
|
148
|
+
gap: parseNumericValue(styles.gap || "0"),
|
|
149
|
+
padding,
|
|
150
|
+
paddingLinked,
|
|
151
|
+
childSizing: {
|
|
152
|
+
horizontal: inferElementSizing(element, "horizontal"),
|
|
153
|
+
vertical: inferElementSizing(element, "vertical"),
|
|
154
|
+
},
|
|
155
|
+
childMinMax: {
|
|
156
|
+
horizontal: readElementMinMax(element, "horizontal"),
|
|
157
|
+
vertical: readElementMinMax(element, "vertical"),
|
|
158
|
+
},
|
|
159
|
+
clipContent: styles.overflow === "hidden",
|
|
160
|
+
resolvedSize: {
|
|
161
|
+
horizontal: cssElementSize(element, "horizontal"),
|
|
162
|
+
vertical: cssElementSize(element, "vertical"),
|
|
163
|
+
},
|
|
164
|
+
mixedSize: {
|
|
165
|
+
horizontal: isMixedValue(styles.width),
|
|
166
|
+
vertical: isMixedValue(styles.height),
|
|
167
|
+
},
|
|
168
|
+
display: displayMode,
|
|
169
|
+
spaceBetween: styles.justifyContent === "space-between",
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div className="space-y-2">
|
|
174
|
+
<AutoLayoutMatrix
|
|
175
|
+
value={autoLayoutValue}
|
|
176
|
+
onDisplayChange={handleDisplayChange}
|
|
177
|
+
onDirectionChange={(direction) => {
|
|
178
|
+
ensureFlex();
|
|
179
|
+
onStyleChange(
|
|
180
|
+
"flexDirection",
|
|
181
|
+
direction === "vertical" ? "column" : "row",
|
|
182
|
+
);
|
|
183
|
+
}}
|
|
184
|
+
onWrapChange={(wrap) => {
|
|
185
|
+
ensureFlex();
|
|
186
|
+
onStyleChange("flexWrap", wrap);
|
|
187
|
+
}}
|
|
188
|
+
onAlignmentChange={(alignment) => {
|
|
189
|
+
if (autoLayoutValue.direction === "vertical") {
|
|
190
|
+
onStyleChange(
|
|
191
|
+
"alignItems",
|
|
192
|
+
horizontalToJustify(alignment.horizontal),
|
|
193
|
+
);
|
|
194
|
+
onStyleChange(
|
|
195
|
+
"justifyContent",
|
|
196
|
+
verticalToAlign(alignment.vertical),
|
|
197
|
+
);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
onStyleChange(
|
|
201
|
+
"justifyContent",
|
|
202
|
+
horizontalToJustify(alignment.horizontal),
|
|
203
|
+
);
|
|
204
|
+
onStyleChange("alignItems", verticalToAlign(alignment.vertical));
|
|
205
|
+
}}
|
|
206
|
+
onGapChange={(gap, meta) => onStyleChange("gap", `${gap}px`, meta)}
|
|
207
|
+
onPaddingChange={(nextPadding, meta) => {
|
|
208
|
+
// Forward ScrubInput's gesture meta so preview ticks ride the host's
|
|
209
|
+
// live fast path and only the release commit persists (B5-14:
|
|
210
|
+
// dropping it here made padding scrubs invisible until reselect).
|
|
211
|
+
// Batch all four sides into one styles change when the host
|
|
212
|
+
// supports it so each tick/commit is a single message instead of
|
|
213
|
+
// four.
|
|
214
|
+
const patch = {
|
|
215
|
+
paddingTop: `${nextPadding.top}px`,
|
|
216
|
+
paddingRight: `${nextPadding.right}px`,
|
|
217
|
+
paddingBottom: `${nextPadding.bottom}px`,
|
|
218
|
+
paddingLeft: `${nextPadding.left}px`,
|
|
219
|
+
};
|
|
220
|
+
if (onStylesChange) {
|
|
221
|
+
onStylesChange(patch, meta);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
Object.entries(patch).forEach(([property, value]) =>
|
|
225
|
+
onStyleChange(property, value, meta),
|
|
226
|
+
);
|
|
227
|
+
}}
|
|
228
|
+
onPaddingLinkedChange={(linked) => {
|
|
229
|
+
setPaddingLinked(linked);
|
|
230
|
+
if (!linked) return;
|
|
231
|
+
const avg = Math.round(
|
|
232
|
+
(padding.top + padding.right + padding.bottom + padding.left) / 4,
|
|
233
|
+
);
|
|
234
|
+
onStyleChange("paddingTop", `${avg}px`);
|
|
235
|
+
onStyleChange("paddingRight", `${avg}px`);
|
|
236
|
+
onStyleChange("paddingBottom", `${avg}px`);
|
|
237
|
+
onStyleChange("paddingLeft", `${avg}px`);
|
|
238
|
+
}}
|
|
239
|
+
onClipContentChange={(clipContent) =>
|
|
240
|
+
onStyleChange("overflow", clipContent ? "hidden" : "visible")
|
|
241
|
+
}
|
|
242
|
+
onDistribute={(axis) => {
|
|
243
|
+
if (axis === mainGapAxis) {
|
|
244
|
+
onStyleChange("justifyContent", "space-between");
|
|
245
|
+
} else if (autoLayoutValue.wrap === "wrap") {
|
|
246
|
+
onStyleChange("alignContent", "space-between");
|
|
247
|
+
}
|
|
248
|
+
}}
|
|
249
|
+
onGapModeChange={(gapMode, axis) => {
|
|
250
|
+
if (axis !== mainGapAxis) return;
|
|
251
|
+
ensureFlex();
|
|
252
|
+
onStyleChange(
|
|
253
|
+
"justifyContent",
|
|
254
|
+
gapMode === "auto" ? "space-between" : "flex-start",
|
|
255
|
+
);
|
|
256
|
+
}}
|
|
257
|
+
availableChildSizing={availableSizingForElement(element)}
|
|
258
|
+
onChildSizingChange={(axis, sizing) => {
|
|
259
|
+
commitElementSizing(
|
|
260
|
+
element,
|
|
261
|
+
axis,
|
|
262
|
+
sizing,
|
|
263
|
+
onStyleChange,
|
|
264
|
+
onStylesChange,
|
|
265
|
+
);
|
|
266
|
+
}}
|
|
267
|
+
onChildSizeChange={(axis, px, meta) =>
|
|
268
|
+
onStyleChange(
|
|
269
|
+
axis === "horizontal" ? "width" : "height",
|
|
270
|
+
`${px}px`,
|
|
271
|
+
meta,
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
onChildMinMaxChange={(axis, kind, val, meta) =>
|
|
275
|
+
commitElementMinMax(axis, kind, val, onStyleChange, meta)
|
|
276
|
+
}
|
|
277
|
+
showChildLayoutControls={hasLayoutChildren}
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function FlexChildControls({
|
|
284
|
+
element,
|
|
285
|
+
onStyleChange,
|
|
286
|
+
}: {
|
|
287
|
+
element: ElementInfo;
|
|
288
|
+
onStyleChange: StyleChangeHandler;
|
|
289
|
+
}) {
|
|
290
|
+
const t = useT();
|
|
291
|
+
const styles = element.computedStyles;
|
|
292
|
+
const alignSelfOptions = ALIGN_SELF_OPTIONS.map((option) => ({
|
|
293
|
+
value: option.value,
|
|
294
|
+
label: t(`editPanel.alignSelfOptions.${option.key}`),
|
|
295
|
+
}));
|
|
296
|
+
|
|
297
|
+
return (
|
|
298
|
+
<div className="space-y-2">
|
|
299
|
+
<SubsectionLabel>{t("editPanel.layoutContext.child")}</SubsectionLabel>
|
|
300
|
+
<PropInput
|
|
301
|
+
label={t("editPanel.labels.flexGrow")}
|
|
302
|
+
value={styles.flexGrow || ""}
|
|
303
|
+
onChange={(v) => onStyleChange("flexGrow", v)}
|
|
304
|
+
placeholder="0"
|
|
305
|
+
/>
|
|
306
|
+
<PropInput
|
|
307
|
+
label={t("editPanel.labels.flexShrink")}
|
|
308
|
+
value={styles.flexShrink || ""}
|
|
309
|
+
onChange={(v) => onStyleChange("flexShrink", v)}
|
|
310
|
+
placeholder="1"
|
|
311
|
+
/>
|
|
312
|
+
<PropInput
|
|
313
|
+
label={t("editPanel.labels.flexBasis")}
|
|
314
|
+
value={styles.flexBasis || ""}
|
|
315
|
+
onChange={(v) => onStyleChange("flexBasis", v)}
|
|
316
|
+
placeholder="auto"
|
|
317
|
+
defaultUnit="px"
|
|
318
|
+
/>
|
|
319
|
+
<PropInput
|
|
320
|
+
label={t("editPanel.labels.order")}
|
|
321
|
+
value={styles.order || ""}
|
|
322
|
+
onChange={(v) => onStyleChange("order", v)}
|
|
323
|
+
placeholder="0"
|
|
324
|
+
/>
|
|
325
|
+
<PropSelect
|
|
326
|
+
label={t("editPanel.labels.alignSelf")}
|
|
327
|
+
value={optionValue(ALIGN_SELF_OPTIONS, styles.alignSelf, "auto")}
|
|
328
|
+
onChange={(v) => onStyleChange("alignSelf", v)}
|
|
329
|
+
options={alignSelfOptions}
|
|
330
|
+
/>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function GridChildControls({
|
|
336
|
+
element,
|
|
337
|
+
onStyleChange,
|
|
338
|
+
}: {
|
|
339
|
+
element: ElementInfo;
|
|
340
|
+
onStyleChange: StyleChangeHandler;
|
|
341
|
+
}) {
|
|
342
|
+
const t = useT();
|
|
343
|
+
const styles = element.computedStyles;
|
|
344
|
+
const alignSelfOptions = ALIGN_SELF_OPTIONS.map((option) => ({
|
|
345
|
+
value: option.value,
|
|
346
|
+
label: t(`editPanel.alignSelfOptions.${option.key}`),
|
|
347
|
+
}));
|
|
348
|
+
|
|
349
|
+
return (
|
|
350
|
+
<div className="space-y-2">
|
|
351
|
+
<SubsectionLabel>
|
|
352
|
+
{t("editPanel.layoutContext.gridChild")}
|
|
353
|
+
</SubsectionLabel>
|
|
354
|
+
<PropInput
|
|
355
|
+
label={t("editPanel.labels.gridColumn")}
|
|
356
|
+
value={styles.gridColumn || ""}
|
|
357
|
+
onChange={(v) => onStyleChange("gridColumn", v)}
|
|
358
|
+
placeholder="auto"
|
|
359
|
+
/>
|
|
360
|
+
<PropInput
|
|
361
|
+
label={t("editPanel.labels.gridRow")}
|
|
362
|
+
value={styles.gridRow || ""}
|
|
363
|
+
onChange={(v) => onStyleChange("gridRow", v)}
|
|
364
|
+
placeholder="auto"
|
|
365
|
+
/>
|
|
366
|
+
<PropSelect
|
|
367
|
+
label={t("editPanel.labels.alignSelf")}
|
|
368
|
+
value={optionValue(ALIGN_SELF_OPTIONS, styles.alignSelf, "auto")}
|
|
369
|
+
onChange={(v) => onStyleChange("alignSelf", v)}
|
|
370
|
+
options={alignSelfOptions}
|
|
371
|
+
/>
|
|
372
|
+
</div>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function LayoutContextProperties({
|
|
377
|
+
element,
|
|
378
|
+
onStyleChange,
|
|
379
|
+
onStylesChange,
|
|
380
|
+
motionKeyframeContext,
|
|
381
|
+
breakpointOverrideContext,
|
|
382
|
+
}: {
|
|
383
|
+
element: ElementInfo;
|
|
384
|
+
onStyleChange: StyleChangeHandler;
|
|
385
|
+
onStylesChange?: StylesChangeHandler;
|
|
386
|
+
motionKeyframeContext?: MotionKeyframeFieldContext;
|
|
387
|
+
breakpointOverrideContext?: BreakpointOverrideFieldContext;
|
|
388
|
+
}) {
|
|
389
|
+
const t = useT();
|
|
390
|
+
const flexChild = isParentFlex(element);
|
|
391
|
+
const gridChild = isParentGrid(element);
|
|
392
|
+
const availableSizing = availableSizingForElement(element);
|
|
393
|
+
const isContainer = isContainerElement(element);
|
|
394
|
+
const aspectLock = useAspectRatioLock(element);
|
|
395
|
+
|
|
396
|
+
const childControls = (
|
|
397
|
+
<>
|
|
398
|
+
{flexChild ? (
|
|
399
|
+
<div className="border-t border-border/70 pt-2">
|
|
400
|
+
<FlexChildControls element={element} onStyleChange={onStyleChange} />
|
|
401
|
+
</div>
|
|
402
|
+
) : null}
|
|
403
|
+
{gridChild ? (
|
|
404
|
+
<div className="border-t border-border/70 pt-2">
|
|
405
|
+
<GridChildControls element={element} onStyleChange={onStyleChange} />
|
|
406
|
+
</div>
|
|
407
|
+
) : null}
|
|
408
|
+
</>
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
// Leaf elements (text, img, svg, etc.) never get auto layout — show the plain
|
|
412
|
+
// design W/H sizing block instead.
|
|
413
|
+
if (!isContainer) {
|
|
414
|
+
const widthSizing = inferElementSizing(element, "horizontal");
|
|
415
|
+
const heightSizing = inferElementSizing(element, "vertical");
|
|
416
|
+
// The aspect lock only makes sense between two fixed numeric dimensions —
|
|
417
|
+
// hug/fill don't have an independent px value to scale. Match Figma: the
|
|
418
|
+
// toggle is disabled (not hidden) otherwise, so its state/affordance stays
|
|
419
|
+
// visible but inert.
|
|
420
|
+
const canLockAspect = widthSizing === "fixed" && heightSizing === "fixed";
|
|
421
|
+
const resolvedWidth = cssElementSize(element, "horizontal");
|
|
422
|
+
const resolvedHeight = cssElementSize(element, "vertical");
|
|
423
|
+
|
|
424
|
+
const toggleAspectLock = () => {
|
|
425
|
+
if (!canLockAspect) return;
|
|
426
|
+
aspectLock.setLocked(
|
|
427
|
+
!aspectLock.locked,
|
|
428
|
+
resolvedHeight > 0 ? resolvedWidth / resolvedHeight : undefined,
|
|
429
|
+
);
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// Shared W/H commit path: when locked, derive the other axis from the
|
|
433
|
+
// captured ratio and commit both in one patch/history step; otherwise
|
|
434
|
+
// fall back to the existing single-property write. `meta` is the
|
|
435
|
+
// ScrubInput gesture-coalescing metadata forwarded from SizingField's
|
|
436
|
+
// onSizeChange (see AutoLayoutMatrix.tsx) — threading it through here,
|
|
437
|
+
// exactly like the X/Y ScrubStyleInput fields already do, is what lets a
|
|
438
|
+
// W/H drag-scrub coalesce into one undo step instead of one per tick.
|
|
439
|
+
// When locked, the same single `meta` describes the *one* combined
|
|
440
|
+
// gesture driving both axes, so it's forwarded unchanged to whichever
|
|
441
|
+
// commit call carries the patch (StylesChangeHandler/StyleChangeHandler
|
|
442
|
+
// both accept an optional meta already).
|
|
443
|
+
const commitWidth = (px: number, meta?: ScrubInputChangeMeta) => {
|
|
444
|
+
if (aspectLock.locked && canLockAspect && aspectLock.ratio) {
|
|
445
|
+
const nextHeight = deriveLockedAspectSize(
|
|
446
|
+
"width",
|
|
447
|
+
px,
|
|
448
|
+
aspectLock.ratio,
|
|
449
|
+
);
|
|
450
|
+
const patch = { width: `${px}px`, height: `${nextHeight}px` };
|
|
451
|
+
if (onStylesChange) onStylesChange(patch, meta);
|
|
452
|
+
else {
|
|
453
|
+
onStyleChange("width", patch.width, meta);
|
|
454
|
+
onStyleChange("height", patch.height, meta);
|
|
455
|
+
}
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
onStyleChange("width", `${px}px`, meta);
|
|
459
|
+
};
|
|
460
|
+
const commitHeight = (px: number, meta?: ScrubInputChangeMeta) => {
|
|
461
|
+
if (aspectLock.locked && canLockAspect && aspectLock.ratio) {
|
|
462
|
+
const nextWidth = deriveLockedAspectSize(
|
|
463
|
+
"height",
|
|
464
|
+
px,
|
|
465
|
+
aspectLock.ratio,
|
|
466
|
+
);
|
|
467
|
+
const patch = { width: `${nextWidth}px`, height: `${px}px` };
|
|
468
|
+
if (onStylesChange) onStylesChange(patch, meta);
|
|
469
|
+
else {
|
|
470
|
+
onStyleChange("width", patch.width, meta);
|
|
471
|
+
onStyleChange("height", patch.height, meta);
|
|
472
|
+
}
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
onStyleChange("height", `${px}px`, meta);
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
return (
|
|
479
|
+
<PanelSection title={t("editPanel.sections.layout")}>
|
|
480
|
+
{/* design-editor single-row-per-axis: [W | value | Fixed/Hug/Fill ▾]
|
|
481
|
+
with the full sizing menu (modes + min/max + variable) per axis,
|
|
482
|
+
plus a chain-link aspect-ratio lock at the FAR RIGHT of the row
|
|
483
|
+
(Figma parity — the constrain-proportions link sits after both W
|
|
484
|
+
and H, not between them). */}
|
|
485
|
+
<div className="grid grid-cols-[1fr_1fr_auto] items-start gap-1.5">
|
|
486
|
+
<div className="group/field relative min-w-0">
|
|
487
|
+
<SizingField
|
|
488
|
+
axis="W"
|
|
489
|
+
sizingAxis="horizontal"
|
|
490
|
+
value={widthSizing}
|
|
491
|
+
resolvedSize={resolvedWidth}
|
|
492
|
+
mixed={isMixedValue(element.computedStyles.width)}
|
|
493
|
+
minMax={readElementMinMax(element, "horizontal")}
|
|
494
|
+
options={availableSizing.horizontal ?? ["fixed"]}
|
|
495
|
+
disabled={false}
|
|
496
|
+
onChange={(mode) =>
|
|
497
|
+
commitElementSizing(
|
|
498
|
+
element,
|
|
499
|
+
"horizontal",
|
|
500
|
+
mode,
|
|
501
|
+
onStyleChange,
|
|
502
|
+
onStylesChange,
|
|
503
|
+
)
|
|
504
|
+
}
|
|
505
|
+
onSizeChange={commitWidth}
|
|
506
|
+
onMinMaxChange={(axis, kind, val, meta) =>
|
|
507
|
+
commitElementMinMax(axis, kind, val, onStyleChange, meta)
|
|
508
|
+
}
|
|
509
|
+
/>
|
|
510
|
+
<FieldTrailer
|
|
511
|
+
element={element}
|
|
512
|
+
overrideProperty="width"
|
|
513
|
+
motionKeyframeContext={motionKeyframeContext}
|
|
514
|
+
breakpointOverrideContext={breakpointOverrideContext}
|
|
515
|
+
className="absolute -top-3.5 right-0"
|
|
516
|
+
/>
|
|
517
|
+
</div>
|
|
518
|
+
<div className="group/field relative min-w-0">
|
|
519
|
+
<SizingField
|
|
520
|
+
axis="H"
|
|
521
|
+
sizingAxis="vertical"
|
|
522
|
+
value={heightSizing}
|
|
523
|
+
resolvedSize={resolvedHeight}
|
|
524
|
+
mixed={isMixedValue(element.computedStyles.height)}
|
|
525
|
+
minMax={readElementMinMax(element, "vertical")}
|
|
526
|
+
options={availableSizing.vertical ?? ["fixed"]}
|
|
527
|
+
disabled={false}
|
|
528
|
+
onChange={(mode) =>
|
|
529
|
+
commitElementSizing(
|
|
530
|
+
element,
|
|
531
|
+
"vertical",
|
|
532
|
+
mode,
|
|
533
|
+
onStyleChange,
|
|
534
|
+
onStylesChange,
|
|
535
|
+
)
|
|
536
|
+
}
|
|
537
|
+
onSizeChange={commitHeight}
|
|
538
|
+
onMinMaxChange={(axis, kind, val, meta) =>
|
|
539
|
+
commitElementMinMax(axis, kind, val, onStyleChange, meta)
|
|
540
|
+
}
|
|
541
|
+
/>
|
|
542
|
+
<FieldTrailer
|
|
543
|
+
element={element}
|
|
544
|
+
overrideProperty="height"
|
|
545
|
+
motionKeyframeContext={motionKeyframeContext}
|
|
546
|
+
breakpointOverrideContext={breakpointOverrideContext}
|
|
547
|
+
className="absolute -top-3.5 right-0"
|
|
548
|
+
/>
|
|
549
|
+
</div>
|
|
550
|
+
<Tooltip>
|
|
551
|
+
<TooltipTrigger asChild>
|
|
552
|
+
<button
|
|
553
|
+
type="button"
|
|
554
|
+
aria-label={
|
|
555
|
+
aspectLock.locked
|
|
556
|
+
? t("editPanel.labels.unlockAspectRatio")
|
|
557
|
+
: t("editPanel.labels.lockAspectRatio")
|
|
558
|
+
}
|
|
559
|
+
aria-pressed={aspectLock.locked}
|
|
560
|
+
disabled={!canLockAspect}
|
|
561
|
+
onClick={toggleAspectLock}
|
|
562
|
+
className={cn(
|
|
563
|
+
"mt-0.5 flex size-6 shrink-0 items-center justify-center self-start rounded-md text-muted-foreground transition-colors",
|
|
564
|
+
"hover:bg-[var(--design-editor-control-bg)] hover:text-foreground",
|
|
565
|
+
aspectLock.locked &&
|
|
566
|
+
"text-[var(--design-editor-accent-color)] hover:text-[var(--design-editor-accent-color)]",
|
|
567
|
+
!canLockAspect && "pointer-events-none opacity-40",
|
|
568
|
+
)}
|
|
569
|
+
>
|
|
570
|
+
{aspectLock.locked ? (
|
|
571
|
+
<IconLink className="size-3.5" />
|
|
572
|
+
) : (
|
|
573
|
+
<IconLinkOff className="size-3.5" />
|
|
574
|
+
)}
|
|
575
|
+
</button>
|
|
576
|
+
</TooltipTrigger>
|
|
577
|
+
<TooltipContent>
|
|
578
|
+
{aspectLock.locked
|
|
579
|
+
? t("editPanel.labels.unlockAspectRatio")
|
|
580
|
+
: t("editPanel.labels.lockAspectRatio")}
|
|
581
|
+
</TooltipContent>
|
|
582
|
+
</Tooltip>
|
|
583
|
+
</div>
|
|
584
|
+
{childControls}
|
|
585
|
+
</PanelSection>
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Any container element ALREADY has a layout in code — normal flow (block) by
|
|
590
|
+
// default, or flex when it uses flexbox. the design editor never makes you "add" auto
|
|
591
|
+
// layout for a frame, so we always render the full layout controls and let
|
|
592
|
+
// the Flow control reflect/switch the element's current `display`. Choosing a
|
|
593
|
+
// horizontal/vertical/wrap/grid flow applies `display:flex`; choosing the
|
|
594
|
+
// normal-flow option resets to `display:block`.
|
|
595
|
+
return (
|
|
596
|
+
<PanelSection title={t("editPanel.sections.autoLayout")}>
|
|
597
|
+
{/* Selection-stable key so per-selection UI state (paddingLinked, which
|
|
598
|
+
must not silently flip while the user is mid-scrub — see the
|
|
599
|
+
FlexContainerControls comment) resets on selection change instead of
|
|
600
|
+
leaking to the next element — same pattern as CornerRadiusControl /
|
|
601
|
+
ExportSettingsPanel. */}
|
|
602
|
+
<FlexContainerControls
|
|
603
|
+
key={elementIdentityKey(element)}
|
|
604
|
+
element={element}
|
|
605
|
+
onStyleChange={onStyleChange}
|
|
606
|
+
onStylesChange={onStylesChange}
|
|
607
|
+
/>
|
|
608
|
+
{childControls}
|
|
609
|
+
</PanelSection>
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* design layout-guide section. Shown for frame/container
|
|
615
|
+
* elements. Renders an overlay column/row guide by applying a non-destructive
|
|
616
|
+
* `backgroundImage` repeating gradient layer tagged so it can be toggled off
|
|
617
|
+
* without disturbing real fills.
|
|
618
|
+
*/
|
|
619
|
+
const LAYOUT_GUIDE_MARKER = "/* an-layout-guide */";
|
|
620
|
+
|
|
621
|
+
function hasLayoutGuide(styles: Record<string, string>): boolean {
|
|
622
|
+
return Boolean(styles.backgroundImage?.includes(LAYOUT_GUIDE_MARKER));
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export function LayoutGuideProperties({
|
|
626
|
+
element,
|
|
627
|
+
onStyleChange,
|
|
628
|
+
}: {
|
|
629
|
+
element: ElementInfo;
|
|
630
|
+
onStyleChange: StyleChangeHandler;
|
|
631
|
+
}) {
|
|
632
|
+
const styles = element.computedStyles;
|
|
633
|
+
const active = hasLayoutGuide(styles);
|
|
634
|
+
|
|
635
|
+
const addGuide = () => {
|
|
636
|
+
// 12-column overlay guide — the design editor's default columns layout grid.
|
|
637
|
+
// The LAYOUT_GUIDE_MARKER comment is embedded so hasLayoutGuide and removeGuide
|
|
638
|
+
// can detect/remove it without touching unrelated repeating-linear-gradient fills.
|
|
639
|
+
const guide = `repeating-linear-gradient(to right, color-mix(in srgb, var(--design-editor-accent-color) 22%, transparent) 0 1px, transparent 1px calc(100% / 12)) ${LAYOUT_GUIDE_MARKER}`;
|
|
640
|
+
const existing = compactCssValue(styles.backgroundImage, "");
|
|
641
|
+
onStyleChange(
|
|
642
|
+
"backgroundImage",
|
|
643
|
+
existing ? `${guide}, ${existing}` : guide,
|
|
644
|
+
);
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
const removeGuide = () => {
|
|
648
|
+
const layers = splitCssLayers(styles.backgroundImage || "").filter(
|
|
649
|
+
(layer) => !layer.includes(LAYOUT_GUIDE_MARKER),
|
|
650
|
+
);
|
|
651
|
+
onStyleChange(
|
|
652
|
+
"backgroundImage",
|
|
653
|
+
layers.length ? joinCssLayers(layers) : "none",
|
|
654
|
+
);
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
return (
|
|
658
|
+
<PanelSection
|
|
659
|
+
title={"Layout guide" /* i18n-ignore design inspector label */}
|
|
660
|
+
defaultCollapsed
|
|
661
|
+
actions={
|
|
662
|
+
<SectionIconButton
|
|
663
|
+
label={
|
|
664
|
+
active
|
|
665
|
+
? "Remove layout guide" /* i18n-ignore design inspector action */
|
|
666
|
+
: "Add layout guide" /* i18n-ignore design inspector action */
|
|
667
|
+
}
|
|
668
|
+
onClick={active ? removeGuide : addGuide}
|
|
669
|
+
>
|
|
670
|
+
{active ? (
|
|
671
|
+
<IconMinus className="size-3.5" />
|
|
672
|
+
) : (
|
|
673
|
+
<IconPlus className="size-3.5" />
|
|
674
|
+
)}
|
|
675
|
+
</SectionIconButton>
|
|
676
|
+
}
|
|
677
|
+
>
|
|
678
|
+
{active ? (
|
|
679
|
+
<div className="flex items-center gap-2 rounded-md bg-[var(--design-editor-control-bg)] px-2 py-1.5 !text-[11px] text-muted-foreground">
|
|
680
|
+
<IconLayoutGrid className="size-3.5 shrink-0" />
|
|
681
|
+
<span className="min-w-0 flex-1 truncate text-foreground">
|
|
682
|
+
{"Columns" /* i18n-ignore design inspector label */}
|
|
683
|
+
</span>
|
|
684
|
+
<span className="shrink-0 tabular-nums">12</span>
|
|
685
|
+
</div>
|
|
686
|
+
) : (
|
|
687
|
+
<p className="!text-[11px] text-muted-foreground">
|
|
688
|
+
{"No layout guides" /* i18n-ignore design inspector empty state */}
|
|
689
|
+
</p>
|
|
690
|
+
)}
|
|
691
|
+
</PanelSection>
|
|
692
|
+
);
|
|
693
|
+
}
|