@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,446 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AlignmentMatrixValue,
|
|
3
|
+
type AutoLayoutMatrixValue,
|
|
4
|
+
type AutoLayoutSizing,
|
|
5
|
+
type AutoLayoutSizingAxis,
|
|
6
|
+
} from "../inspector";
|
|
7
|
+
import type { ElementInfo } from "../types";
|
|
8
|
+
import { normalizedElementTagName } from "./code-inspect-helpers";
|
|
9
|
+
import { commitStylePatch } from "./field-primitives";
|
|
10
|
+
import type {
|
|
11
|
+
StyleChangeHandler,
|
|
12
|
+
StyleChangeMeta,
|
|
13
|
+
StylesChangeHandler,
|
|
14
|
+
} from "./style-change-types";
|
|
15
|
+
|
|
16
|
+
export const TEXT_TAGS = new Set([
|
|
17
|
+
"p",
|
|
18
|
+
"h1",
|
|
19
|
+
"h2",
|
|
20
|
+
"h3",
|
|
21
|
+
"h4",
|
|
22
|
+
"h5",
|
|
23
|
+
"h6",
|
|
24
|
+
"span",
|
|
25
|
+
"a",
|
|
26
|
+
"strong",
|
|
27
|
+
"em",
|
|
28
|
+
"label",
|
|
29
|
+
"li",
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
export function inspectorObjectTitle(element: ElementInfo): string {
|
|
33
|
+
const componentName = componentNameForElementInfo(element);
|
|
34
|
+
if (componentName) return componentName;
|
|
35
|
+
const tag = normalizedElementTagName(element.tagName);
|
|
36
|
+
if (TEXT_TAGS.has(tag)) return "Text";
|
|
37
|
+
return tag;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function componentNameForElementInfo(
|
|
41
|
+
element: ElementInfo | null | undefined,
|
|
42
|
+
): string {
|
|
43
|
+
return element?.componentName?.trim() ?? "";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function elementIsComponentSelection(
|
|
47
|
+
element: ElementInfo | null | undefined,
|
|
48
|
+
): boolean {
|
|
49
|
+
return componentNameForElementInfo(element).length > 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function displayLabel(value: string | undefined): string {
|
|
53
|
+
const normalized = value?.trim();
|
|
54
|
+
if (!normalized || normalized === "normal") return "flow";
|
|
55
|
+
return normalized;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function justifyToHorizontal(
|
|
59
|
+
value: string | undefined,
|
|
60
|
+
): AlignmentMatrixValue["horizontal"] {
|
|
61
|
+
if (value === "center") return "center";
|
|
62
|
+
if (value === "flex-end" || value === "end" || value === "right") {
|
|
63
|
+
return "right";
|
|
64
|
+
}
|
|
65
|
+
return "left";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function alignToVertical(
|
|
69
|
+
value: string | undefined,
|
|
70
|
+
): AlignmentMatrixValue["vertical"] {
|
|
71
|
+
if (value === "center") return "middle";
|
|
72
|
+
if (value === "flex-end" || value === "end" || value === "bottom") {
|
|
73
|
+
return "bottom";
|
|
74
|
+
}
|
|
75
|
+
return "top";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function horizontalToJustify(
|
|
79
|
+
value: AlignmentMatrixValue["horizontal"],
|
|
80
|
+
): string {
|
|
81
|
+
if (value === "center") return "center";
|
|
82
|
+
if (value === "right") return "flex-end";
|
|
83
|
+
return "flex-start";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function verticalToAlign(
|
|
87
|
+
value: AlignmentMatrixValue["vertical"],
|
|
88
|
+
): string {
|
|
89
|
+
if (value === "middle") return "center";
|
|
90
|
+
if (value === "bottom") return "flex-end";
|
|
91
|
+
return "flex-start";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function autoLayoutAlignmentFromStyles(
|
|
95
|
+
styles: Record<string, string>,
|
|
96
|
+
direction: AutoLayoutMatrixValue["direction"],
|
|
97
|
+
): AlignmentMatrixValue {
|
|
98
|
+
if (direction === "vertical") {
|
|
99
|
+
return {
|
|
100
|
+
horizontal: justifyToHorizontal(styles.alignItems),
|
|
101
|
+
vertical: alignToVertical(styles.justifyContent),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
horizontal: justifyToHorizontal(styles.justifyContent),
|
|
106
|
+
vertical: alignToVertical(styles.alignItems),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Block-level container tags that act the same way frames. Selecting any of
|
|
112
|
+
* these shows the Auto layout section (in an "add" state when not yet flex),
|
|
113
|
+
* mirroring the editor pattern where any frame/container exposes auto-layout controls.
|
|
114
|
+
*/
|
|
115
|
+
const CONTAINER_TAGS = new Set([
|
|
116
|
+
"div",
|
|
117
|
+
"section",
|
|
118
|
+
"main",
|
|
119
|
+
"header",
|
|
120
|
+
"footer",
|
|
121
|
+
"nav",
|
|
122
|
+
"article",
|
|
123
|
+
"aside",
|
|
124
|
+
"form",
|
|
125
|
+
"ul",
|
|
126
|
+
"ol",
|
|
127
|
+
"figure",
|
|
128
|
+
"fieldset",
|
|
129
|
+
"details",
|
|
130
|
+
"dialog",
|
|
131
|
+
"blockquote",
|
|
132
|
+
"table",
|
|
133
|
+
"tbody",
|
|
134
|
+
"thead",
|
|
135
|
+
"tr",
|
|
136
|
+
]);
|
|
137
|
+
|
|
138
|
+
/** Leaf tags that never get auto-layout (text, media, vectors, controls). */
|
|
139
|
+
const LEAF_TAGS = new Set([
|
|
140
|
+
"img",
|
|
141
|
+
"video",
|
|
142
|
+
"picture",
|
|
143
|
+
"audio",
|
|
144
|
+
"canvas",
|
|
145
|
+
"svg",
|
|
146
|
+
"path",
|
|
147
|
+
"input",
|
|
148
|
+
"textarea",
|
|
149
|
+
"select",
|
|
150
|
+
"br",
|
|
151
|
+
"hr",
|
|
152
|
+
"iframe",
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Whether the element should expose the Auto layout section. True for anything
|
|
157
|
+
* already laid out with flexbox, or any block-level container tag that isn't a
|
|
158
|
+
* known leaf/text element. This is what makes a plain frame/container with
|
|
159
|
+
* children show the full Auto layout section the same way does.
|
|
160
|
+
*/
|
|
161
|
+
export function isContainerElement(element: ElementInfo): boolean {
|
|
162
|
+
if (element.isFlexContainer || element.isGridContainer) return true;
|
|
163
|
+
const tag = (element.tagName || "").toLowerCase();
|
|
164
|
+
if (TEXT_TAGS.has(tag) || LEAF_TAGS.has(tag)) return false;
|
|
165
|
+
return CONTAINER_TAGS.has(tag);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function isParentFlex(element: ElementInfo): boolean {
|
|
169
|
+
return (
|
|
170
|
+
element.isFlexChild ||
|
|
171
|
+
Boolean(element.parentDisplay?.toLowerCase().includes("flex"))
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function isParentGrid(element: ElementInfo): boolean {
|
|
176
|
+
return Boolean(element.parentDisplay?.toLowerCase().includes("grid"));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function elementHasLayoutChildren(element: ElementInfo): boolean {
|
|
180
|
+
if (typeof element.childElementCount === "number") {
|
|
181
|
+
return element.childElementCount > 0;
|
|
182
|
+
}
|
|
183
|
+
return Boolean(element.htmlContent?.match(/<\s*[a-zA-Z][^>]*>/));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function parentFlexDirection(
|
|
187
|
+
element: ElementInfo,
|
|
188
|
+
): AutoLayoutSizingAxis {
|
|
189
|
+
return element.parentLayout?.flexDirection?.includes("column")
|
|
190
|
+
? "vertical"
|
|
191
|
+
: "horizontal";
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function isTextElement(element: ElementInfo): boolean {
|
|
195
|
+
const tag = (element.tagName || "").toLowerCase();
|
|
196
|
+
if (TEXT_TAGS.has(tag)) return true;
|
|
197
|
+
// T-tool text primitives are plain `div`s stamped with
|
|
198
|
+
// data-an-primitive="text" (see DesignEditor primitive creation). The
|
|
199
|
+
// bridge forwards that marker as ElementInfo.primitiveKind — prefer it
|
|
200
|
+
// when present since it's exact.
|
|
201
|
+
if (element.primitiveKind) return element.primitiveKind === "text";
|
|
202
|
+
// Canvas-drawn primitives carry a `draft-<tool>-<timestamp>-<random>` node
|
|
203
|
+
// id (see MultiScreenCanvas's draft-id minting). Some selection payloads —
|
|
204
|
+
// notably board/overview layer-panel selections built by parsing the source
|
|
205
|
+
// HTML rather than by the in-iframe bridge — omit `primitiveKind` entirely
|
|
206
|
+
// even though the DOM node carries data-an-primitive="text". The id prefix
|
|
207
|
+
// identifies the tool that drew the element just as exactly for those
|
|
208
|
+
// payloads.
|
|
209
|
+
const nodeId = element.sourceId || element.pendingNodeId || "";
|
|
210
|
+
if (nodeId.startsWith("draft-text-")) return true;
|
|
211
|
+
if (nodeId.startsWith("draft-rect-") || nodeId.startsWith("draft-frame-")) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
// Fallback for payloads with no primitive marker at all: approximate a
|
|
215
|
+
// text node with a content heuristic — a childless div that has its own
|
|
216
|
+
// text content. This intentionally excludes empty frames/shapes (no text)
|
|
217
|
+
// and containers with element children. NOTE: deliberately no
|
|
218
|
+
// isFlexContainer/isGridContainer exclusion here — the T-tool's own text
|
|
219
|
+
// primitives are `display: flex` divs (flex is how their vertical
|
|
220
|
+
// alignment works), so "is a flex container" does NOT imply "not text" for
|
|
221
|
+
// a leaf node. Excluding flex containers made the Typography section
|
|
222
|
+
// vanish for exactly those text nodes whenever the payload lacked
|
|
223
|
+
// primitiveKind (B5-12: text nested in a rectangle via nest-on-drop).
|
|
224
|
+
if (
|
|
225
|
+
tag === "div" &&
|
|
226
|
+
(element.childElementCount ?? 0) === 0 &&
|
|
227
|
+
Boolean(element.textContent?.trim())
|
|
228
|
+
) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Per-axis sizing availability following the design editor's contextual rules:
|
|
236
|
+
* - Fixed: always.
|
|
237
|
+
* - Hug contents: only CONTAINERS (flex/container frames) and TEXT can hug
|
|
238
|
+
* their content. Leaves like img/svg/input cannot.
|
|
239
|
+
* - Fill container: only when the element is a CHILD of a flex/grid (auto
|
|
240
|
+
* layout) parent, OR a block-flow child (which fills via width:100%).
|
|
241
|
+
* Hug applies to width and height independently; the same set is offered on
|
|
242
|
+
* both axes here and the per-axis CSS in `commitElementSizing` resolves the
|
|
243
|
+
* exact behavior (main-axis grow vs cross-axis stretch).
|
|
244
|
+
*/
|
|
245
|
+
export function availableSizingForElement(
|
|
246
|
+
element: ElementInfo,
|
|
247
|
+
): Partial<Record<AutoLayoutSizingAxis, AutoLayoutSizing[]>> {
|
|
248
|
+
const canHug = isContainerElement(element) || isTextElement(element);
|
|
249
|
+
const isFlexChildEl = isParentFlex(element) || isParentGrid(element);
|
|
250
|
+
// Block-flow children can still "fill" via width:100% on the horizontal axis.
|
|
251
|
+
const isBlockChild = Boolean(element.parentDisplay) && !isFlexChildEl;
|
|
252
|
+
|
|
253
|
+
const buildAxis = (axis: AutoLayoutSizingAxis): AutoLayoutSizing[] => {
|
|
254
|
+
const options: AutoLayoutSizing[] = ["fixed"];
|
|
255
|
+
if (canHug) options.push("hug");
|
|
256
|
+
// Fill: flex/grid child on either axis; block child only fills width.
|
|
257
|
+
if (isFlexChildEl || (isBlockChild && axis === "horizontal")) {
|
|
258
|
+
options.push("fill");
|
|
259
|
+
}
|
|
260
|
+
return options;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
horizontal: buildAxis("horizontal"),
|
|
265
|
+
vertical: buildAxis("vertical"),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Read the currently-set min/max constraints (px) for a sizing axis. */
|
|
270
|
+
export function readElementMinMax(
|
|
271
|
+
element: ElementInfo,
|
|
272
|
+
axis: AutoLayoutSizingAxis,
|
|
273
|
+
): { min: number | null; max: number | null } {
|
|
274
|
+
const styles = element.computedStyles;
|
|
275
|
+
const minRaw = axis === "horizontal" ? styles.minWidth : styles.minHeight;
|
|
276
|
+
const maxRaw = axis === "horizontal" ? styles.maxWidth : styles.maxHeight;
|
|
277
|
+
return {
|
|
278
|
+
min: parseConstraintLength(minRaw),
|
|
279
|
+
max: parseConstraintLength(maxRaw),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Parse a min/max CSS length into a px number, or null when unset. Browser
|
|
285
|
+
* computed values are "0px"/"none" for the defaults — both read as "not set"
|
|
286
|
+
* so we don't surface a constraint sub-row the user never added.
|
|
287
|
+
*/
|
|
288
|
+
export function parseConstraintLength(
|
|
289
|
+
value: string | undefined,
|
|
290
|
+
): number | null {
|
|
291
|
+
const normalized = value?.trim().toLowerCase();
|
|
292
|
+
if (
|
|
293
|
+
!normalized ||
|
|
294
|
+
normalized === "none" ||
|
|
295
|
+
normalized === "auto" ||
|
|
296
|
+
normalized === "0px" ||
|
|
297
|
+
normalized === "0"
|
|
298
|
+
) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
const parsed = Number.parseFloat(normalized);
|
|
302
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Commit a single min/max constraint (px) or clear it when value is null.
|
|
306
|
+
* `meta` forwards the originating ScrubInput's gesture metadata (phase
|
|
307
|
+
* preview/commit) so constraint scrubs ride the host's live fast path per
|
|
308
|
+
* tick and only persist on release — same threading as padding/gap. */
|
|
309
|
+
export function commitElementMinMax(
|
|
310
|
+
axis: AutoLayoutSizingAxis,
|
|
311
|
+
kind: "min" | "max",
|
|
312
|
+
value: number | null,
|
|
313
|
+
onStyleChange: StyleChangeHandler,
|
|
314
|
+
meta?: StyleChangeMeta,
|
|
315
|
+
) {
|
|
316
|
+
const isHorizontal = axis === "horizontal";
|
|
317
|
+
const property =
|
|
318
|
+
kind === "min"
|
|
319
|
+
? isHorizontal
|
|
320
|
+
? "minWidth"
|
|
321
|
+
: "minHeight"
|
|
322
|
+
: isHorizontal
|
|
323
|
+
? "maxWidth"
|
|
324
|
+
: "maxHeight";
|
|
325
|
+
if (value == null) {
|
|
326
|
+
// Clearing: min → 0 (CSS initial), max → none (CSS initial).
|
|
327
|
+
onStyleChange(property, kind === "min" ? "0px" : "none", meta);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
onStyleChange(property, `${Math.max(0, Math.round(value))}px`, meta);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function inferElementSizing(
|
|
334
|
+
element: ElementInfo,
|
|
335
|
+
axis: AutoLayoutSizingAxis,
|
|
336
|
+
): AutoLayoutSizing {
|
|
337
|
+
const styles = element.computedStyles;
|
|
338
|
+
const size = axis === "horizontal" ? styles.width : styles.height;
|
|
339
|
+
const parentDirection = parentFlexDirection(element);
|
|
340
|
+
const isFlex = isParentFlex(element);
|
|
341
|
+
const isMainFlexAxis = isFlex && parentDirection === axis;
|
|
342
|
+
const isCrossFlexAxis = isFlex && parentDirection !== axis;
|
|
343
|
+
const alignSelf = (styles.alignSelf || "").toLowerCase();
|
|
344
|
+
|
|
345
|
+
if (
|
|
346
|
+
size === "100%" ||
|
|
347
|
+
(isMainFlexAxis && Number.parseFloat(styles.flexGrow || "0") > 0) ||
|
|
348
|
+
(isCrossFlexAxis && alignSelf === "stretch")
|
|
349
|
+
) {
|
|
350
|
+
return "fill";
|
|
351
|
+
}
|
|
352
|
+
if (size === "auto" || size === "fit-content" || size === "max-content") {
|
|
353
|
+
return "hug";
|
|
354
|
+
}
|
|
355
|
+
return "fixed";
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Return the element's geometric dimension on the given axis in CSS pixels.
|
|
360
|
+
*
|
|
361
|
+
* `getComputedStyle().width/height` always resolves to a computed px value
|
|
362
|
+
* (even for `width: auto` the browser returns e.g. "200px"). For rotated
|
|
363
|
+
* elements this is the pre-rotation CSS box size — what Figma shows in the
|
|
364
|
+
* inspector — while `getBoundingClientRect().width/height` would be the
|
|
365
|
+
* axis-aligned bounding box which is inflated by the rotation.
|
|
366
|
+
*
|
|
367
|
+
* Falls back to the bounding-rect dimension only when the computed style is
|
|
368
|
+
* missing or unparseable (e.g. the bridge hasn't populated it yet).
|
|
369
|
+
*/
|
|
370
|
+
export function cssElementSize(
|
|
371
|
+
element: ElementInfo,
|
|
372
|
+
axis: AutoLayoutSizingAxis,
|
|
373
|
+
): number {
|
|
374
|
+
const isHorizontal = axis === "horizontal";
|
|
375
|
+
const cssValue = isHorizontal
|
|
376
|
+
? element.computedStyles.width
|
|
377
|
+
: element.computedStyles.height;
|
|
378
|
+
const parsed = parseFloat(cssValue || "");
|
|
379
|
+
const fallback = isHorizontal
|
|
380
|
+
? element.boundingRect.width
|
|
381
|
+
: element.boundingRect.height;
|
|
382
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function commitElementSizing(
|
|
386
|
+
element: ElementInfo,
|
|
387
|
+
axis: AutoLayoutSizingAxis,
|
|
388
|
+
sizing: AutoLayoutSizing,
|
|
389
|
+
onStyleChange: StyleChangeHandler,
|
|
390
|
+
onStylesChange?: StylesChangeHandler,
|
|
391
|
+
) {
|
|
392
|
+
const isHorizontal = axis === "horizontal";
|
|
393
|
+
const sizeProperty = isHorizontal ? "width" : "height";
|
|
394
|
+
// Use CSS computed dimension (pre-rotation box size) as the seed for "fixed"
|
|
395
|
+
// sizing so a rotated element is locked to its actual CSS width/height rather
|
|
396
|
+
// than the inflated axis-aligned bounding rect.
|
|
397
|
+
const resolvedSize = Math.max(1, Math.round(cssElementSize(element, axis)));
|
|
398
|
+
const parentDirection = parentFlexDirection(element);
|
|
399
|
+
const isFlex = isParentFlex(element);
|
|
400
|
+
const isGrid = isParentGrid(element);
|
|
401
|
+
const isMainFlexAxis = isFlex && parentDirection === axis;
|
|
402
|
+
const patch: Record<string, string> = {};
|
|
403
|
+
|
|
404
|
+
if (sizing === "fixed") {
|
|
405
|
+
// Fixed → explicit px dimension. Reset any grow/stretch on the flex
|
|
406
|
+
// main-axis so the pixel value sticks.
|
|
407
|
+
patch[sizeProperty] = `${resolvedSize}px`;
|
|
408
|
+
if (isMainFlexAxis) {
|
|
409
|
+
patch.flexGrow = "0";
|
|
410
|
+
patch.flexShrink = "0";
|
|
411
|
+
patch.flexBasis = "auto";
|
|
412
|
+
}
|
|
413
|
+
} else if (sizing === "hug") {
|
|
414
|
+
// Hug contents → shrink to fit children/content.
|
|
415
|
+
patch[sizeProperty] = "fit-content";
|
|
416
|
+
if (isMainFlexAxis) {
|
|
417
|
+
// A flex container hugging on its main axis uses flex-basis:auto + no
|
|
418
|
+
// stretch (spec: "flex-basis: auto + no stretch").
|
|
419
|
+
patch.flexGrow = "0";
|
|
420
|
+
patch.flexShrink = "0";
|
|
421
|
+
patch.flexBasis = "auto";
|
|
422
|
+
}
|
|
423
|
+
} else {
|
|
424
|
+
// Fill container.
|
|
425
|
+
if (isMainFlexAxis) {
|
|
426
|
+
// Parent main axis → grow into available space: flex: 1 0 0.
|
|
427
|
+
patch.flexGrow = "1";
|
|
428
|
+
patch.flexShrink = "0";
|
|
429
|
+
patch.flexBasis = "0";
|
|
430
|
+
// Clear any explicit dimension so flex-basis governs.
|
|
431
|
+
patch[sizeProperty] = "auto";
|
|
432
|
+
} else if (isFlex) {
|
|
433
|
+
// Parent cross axis → stretch to the parent's cross size.
|
|
434
|
+
patch.alignSelf = "stretch";
|
|
435
|
+
patch[sizeProperty] = "auto";
|
|
436
|
+
} else if (isGrid) {
|
|
437
|
+
patch[isHorizontal ? "justifySelf" : "alignSelf"] = "stretch";
|
|
438
|
+
patch[sizeProperty] = "auto";
|
|
439
|
+
} else {
|
|
440
|
+
// Child of a non-flex (block) parent → fill width with 100%.
|
|
441
|
+
patch[sizeProperty] = "100%";
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
commitStylePatch(patch, onStyleChange, onStylesChange);
|
|
446
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
|
|
3
|
+
import type { ElementInfo } from "../types";
|
|
4
|
+
import { roundToOneDecimal } from "./position-helpers";
|
|
5
|
+
|
|
6
|
+
export function elementIdentityKey(element: ElementInfo): string {
|
|
7
|
+
return [
|
|
8
|
+
element.sourceId ?? element.id ?? element.selector ?? element.tagName,
|
|
9
|
+
Math.round(element.boundingRect.x),
|
|
10
|
+
Math.round(element.boundingRect.y),
|
|
11
|
+
Math.round(element.boundingRect.width),
|
|
12
|
+
Math.round(element.boundingRect.height),
|
|
13
|
+
].join(":");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Stable per-element identity for UI-only inspector state that must survive
|
|
18
|
+
* resizing (unlike elementIdentityKey, which folds in the bounding rect and
|
|
19
|
+
* therefore changes on every resize — exactly what an aspect-ratio lock needs
|
|
20
|
+
* to persist across). Falls back through the same id chain.
|
|
21
|
+
*/
|
|
22
|
+
function elementStableKey(element: ElementInfo): string {
|
|
23
|
+
return element.sourceId ?? element.id ?? element.selector ?? element.tagName;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Module-level aspect-ratio lock state, keyed by elementStableKey. Module
|
|
28
|
+
* scope (not React state) so the lock survives EditPanel remounts across
|
|
29
|
+
* selection changes, matching this file's existing convention of using
|
|
30
|
+
* plain data structures for cross-render inspector UI state (see
|
|
31
|
+
* hiddenEffectStash for the analogous per-element pattern kept in React
|
|
32
|
+
* state instead — the lock uses module scope specifically so a toggle
|
|
33
|
+
* doesn't need to be re-applied if the panel remounts).
|
|
34
|
+
*/
|
|
35
|
+
const aspectRatioLocks = new Map<string, number>();
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Reads/writes the aspect-ratio lock for the given element. The map stores
|
|
39
|
+
* the locked ratio (width / height) captured at lock time, not just a
|
|
40
|
+
* boolean, so a W or H edit can derive the other axis without re-reading
|
|
41
|
+
* stale computed styles. Returns a React-state-backed `locked`/`ratio` pair
|
|
42
|
+
* plus a `toggle` that forces a re-render (the Map itself is not reactive).
|
|
43
|
+
*/
|
|
44
|
+
export function useAspectRatioLock(element: ElementInfo) {
|
|
45
|
+
const key = elementStableKey(element);
|
|
46
|
+
const [, forceRender] = useState(0);
|
|
47
|
+
const locked = aspectRatioLocks.has(key);
|
|
48
|
+
const ratio = aspectRatioLocks.get(key);
|
|
49
|
+
|
|
50
|
+
const setLocked = (nextLocked: boolean, currentRatio?: number) => {
|
|
51
|
+
if (nextLocked) {
|
|
52
|
+
if (Number.isFinite(currentRatio) && (currentRatio as number) > 0) {
|
|
53
|
+
aspectRatioLocks.set(key, currentRatio as number);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
aspectRatioLocks.delete(key);
|
|
57
|
+
}
|
|
58
|
+
forceRender((n) => n + 1);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return { locked, ratio, setLocked };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Derives the paired dimension for an aspect-locked W/H commit. `ratio` is
|
|
66
|
+
* width / height, captured once when the lock was toggled on. `axis`
|
|
67
|
+
* identifies which dimension the user just edited (`px`); the function
|
|
68
|
+
* returns the other axis's next value, rounded to one decimal to match the
|
|
69
|
+
* precision every other size/position field commits at.
|
|
70
|
+
*/
|
|
71
|
+
export function deriveLockedAspectSize(
|
|
72
|
+
axis: "width" | "height",
|
|
73
|
+
px: number,
|
|
74
|
+
ratio: number,
|
|
75
|
+
): number {
|
|
76
|
+
return axis === "width"
|
|
77
|
+
? roundToOneDecimal(px / ratio)
|
|
78
|
+
: roundToOneDecimal(px * ratio);
|
|
79
|
+
}
|