@agent-native/core 0.92.10 → 0.92.11
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 +23 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +181 -15
- package/corpus/core/src/agent/run-manager.ts +31 -4
- package/corpus/core/src/agent/run-store.ts +80 -0
- package/corpus/core/src/cli/design-connect.ts +433 -151
- package/corpus/core/src/cli/skills.ts +14 -0
- package/corpus/core/src/client/AssistantChat.tsx +25 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
- package/corpus/core/src/client/agent-chat.ts +165 -1
- package/corpus/core/src/client/chat/index.ts +9 -1
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
- package/corpus/core/src/collab/awareness-store.ts +23 -12
- package/corpus/core/src/collab/client.ts +16 -5
- package/corpus/core/src/collab/index.ts +4 -0
- package/corpus/core/src/collab/recent-edits.ts +65 -2
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
- package/corpus/core/src/server/builder-design-systems.ts +30 -4
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/create-recording.ts +2 -0
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
- package/corpus/templates/clips/actions/request-transcript.ts +102 -33
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +48 -4
- package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
- package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
- package/corpus/templates/clips/app/routes/record.tsx +17 -4
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +12 -2
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
- package/corpus/templates/clips/desktop/src/styles.css +6 -3
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
- package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
- package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
- package/corpus/templates/clips/shared/recording-core.ts +12 -0
- package/corpus/templates/content/AGENTS.md +7 -6
- package/corpus/templates/content/actions/_document-flush.ts +132 -17
- package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
- package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/link-notion-page.ts +2 -0
- package/corpus/templates/content/actions/pull-document.ts +5 -7
- package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
- package/corpus/templates/content/actions/push-notion-page.ts +10 -0
- package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
- package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
- package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/content/app/i18n-data.ts +18 -0
- package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
- package/corpus/templates/design/AGENTS.md +45 -10
- package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
- package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
- package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
- package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
- package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
- package/corpus/templates/design/actions/generate-design.ts +79 -12
- package/corpus/templates/design/actions/generate-screens.ts +57 -12
- package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
- package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
- package/corpus/templates/design/actions/import-design-source.ts +4 -31
- package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
- package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/design/actions/list-design-components.ts +104 -0
- package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
- package/corpus/templates/design/actions/open-component-source.ts +2 -4
- package/corpus/templates/design/actions/present-design-variants.ts +412 -16
- package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
- package/corpus/templates/design/actions/run-design-audit.ts +25 -3
- package/corpus/templates/design/actions/show-design-questions.ts +10 -0
- package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
- package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
- package/corpus/templates/design/actions/write-local-file.ts +25 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
- package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
- package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
- package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
- package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
- package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
- package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
- package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
- package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
- package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
- package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
- package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
- package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
- package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
- package/corpus/templates/design/app/components/design/types.ts +33 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
- package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
- package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
- package/corpus/templates/design/app/i18n-data.ts +1217 -10
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
- package/corpus/templates/design/app/lib/design-import.ts +24 -0
- package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
- package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
- package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
- package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
- package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
- package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
- package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +3 -6
- package/corpus/templates/design/e2e/helpers.ts +27 -6
- package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
- package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
- package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
- package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
- package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
- package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
- package/corpus/templates/design/server/plugins/db.ts +41 -0
- package/corpus/templates/design/server/source-workspace.ts +64 -0
- package/corpus/templates/design/shared/board-file.ts +13 -2
- package/corpus/templates/design/shared/canvas-math.ts +231 -33
- package/corpus/templates/design/shared/code-layer.ts +122 -8
- package/corpus/templates/design/shared/component-library.ts +161 -0
- package/corpus/templates/design/shared/db-conflict.ts +27 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
- package/corpus/templates/design/shared/figma-url.ts +149 -0
- package/corpus/templates/design/shared/pen-path.ts +16 -7
- package/corpus/templates/design/shared/source-mode.ts +1 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
- package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
- package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
- package/dist/agent/production-agent.d.ts +79 -6
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +161 -14
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +29 -5
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +46 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +61 -0
- package/dist/agent/run-store.js.map +1 -1
- package/dist/cli/design-connect.d.ts +6 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +336 -139
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +14 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +2 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +19 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +38 -18
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +60 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +122 -1
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +36 -2
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +5 -0
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +19 -12
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +13 -5
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/index.d.ts +1 -0
- package/dist/collab/index.d.ts.map +1 -1
- package/dist/collab/index.js +1 -0
- package/dist/collab/index.js.map +1 -1
- package/dist/collab/recent-edits.d.ts +3 -1
- package/dist/collab/recent-edits.d.ts.map +1 -1
- package/dist/collab/recent-edits.js +57 -2
- package/dist/collab/recent-edits.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +87 -10
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/builder-design-systems.d.ts +15 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +9 -4
- package/dist/server/builder-design-systems.js.map +1 -1
- package/package.json +1 -1
package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx
CHANGED
|
@@ -43,7 +43,11 @@ import {
|
|
|
43
43
|
AppearanceScrubField,
|
|
44
44
|
CornerRadiusControl,
|
|
45
45
|
} from "./appearance-properties";
|
|
46
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
commitStylePatch,
|
|
48
|
+
FieldTrailer,
|
|
49
|
+
ScrubStyleInput,
|
|
50
|
+
} from "./field-primitives";
|
|
47
51
|
import {
|
|
48
52
|
InspectorIconButton,
|
|
49
53
|
InspectorSegment,
|
|
@@ -57,6 +61,7 @@ import type {
|
|
|
57
61
|
BreakpointOverrideFieldContext,
|
|
58
62
|
MotionKeyframeFieldContext,
|
|
59
63
|
StyleChangeHandler,
|
|
64
|
+
StylesChangeHandler,
|
|
60
65
|
} from "./style-change-types";
|
|
61
66
|
import {
|
|
62
67
|
mergeRotationValue,
|
|
@@ -65,16 +70,254 @@ import {
|
|
|
65
70
|
parseScaleValue,
|
|
66
71
|
} from "./transform-helpers";
|
|
67
72
|
|
|
73
|
+
/**
|
|
74
|
+
* `authoredStyleValue()` returns the *inline* value when one is set, but
|
|
75
|
+
* falls back to the *computed* style otherwise — and `getComputedStyle()`
|
|
76
|
+
* reports "auto" for left/right/top/bottom on any element that has never had
|
|
77
|
+
* one of those offsets explicitly authored (the ordinary case for a plain,
|
|
78
|
+
* not-yet-repositioned element). A bare "auto" is not a real authored pin,
|
|
79
|
+
* and the cross-selection Mixed sentinel isn't either. Without this guard,
|
|
80
|
+
* `authoredLeft && authoredRight` truthiness checks below treated "auto" as
|
|
81
|
+
* "yes, pinned", so a completely unconstrained element read as "left-right"/
|
|
82
|
+
* "top-bottom" (pinned to both edges) in the Constraints preview, the X/Y
|
|
83
|
+
* fields showed a parsed "0" instead of the element's real on-canvas
|
|
84
|
+
* position, and picking "Left"/"Top" from an unconstrained element could
|
|
85
|
+
* write the literal string `"auto"` as the new `left`/`top` value instead of
|
|
86
|
+
* anchoring it at its current position. Exported for tests.
|
|
87
|
+
*/
|
|
88
|
+
export function definiteAuthoredOffset(
|
|
89
|
+
raw: string | undefined,
|
|
90
|
+
): string | undefined {
|
|
91
|
+
if (!raw || raw === "auto" || isMixedValue(raw)) return undefined;
|
|
92
|
+
return raw;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function percentageLength(raw: string | undefined): boolean {
|
|
96
|
+
return !!raw && /^-?(?:\d+\.?\d*|\.\d+)%$/.test(raw.trim());
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function geometryPercent(value: number, total: number): string {
|
|
100
|
+
if (!Number.isFinite(value) || !Number.isFinite(total) || total <= 0) {
|
|
101
|
+
return "0%";
|
|
102
|
+
}
|
|
103
|
+
return `${Number(((value / total) * 100).toFixed(6))}%`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function geometryPx(value: number): string {
|
|
107
|
+
return `${Number(value.toFixed(3))}px`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Once the bridge supplies an inline-style snapshot, absence is meaningful:
|
|
111
|
+
* an absolutely-positioned left-only element still has a computed `right`,
|
|
112
|
+
* but that resolved value is not an authored right pin. Older payloads omit
|
|
113
|
+
* `inlineStyles` entirely, so only those fall back to computed styles. */
|
|
114
|
+
function authoredConstraintValue(
|
|
115
|
+
element: ElementInfo,
|
|
116
|
+
property: string,
|
|
117
|
+
): string | undefined {
|
|
118
|
+
if (element.inlineStyles !== undefined) {
|
|
119
|
+
const value = element.inlineStyles[property];
|
|
120
|
+
return value === "auto" ? "" : value;
|
|
121
|
+
}
|
|
122
|
+
return element.computedStyles[property];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Derives the Constraints preview/widget state from an element's authored
|
|
127
|
+
* left/right/top/bottom/width/height/transform. Pulled out as a standalone
|
|
128
|
+
* pure function (mirrors `autoLayoutStylesForFlow` in layout-properties.tsx)
|
|
129
|
+
* so the "auto"/Mixed-safe derivation fixed by `definiteAuthoredOffset` above
|
|
130
|
+
* is directly unit-testable without rendering the whole panel. Exported for
|
|
131
|
+
* tests.
|
|
132
|
+
*/
|
|
133
|
+
export function deriveConstraintsValue(element: ElementInfo): ConstraintsValue {
|
|
134
|
+
const authoredLeft = authoredConstraintValue(element, "left");
|
|
135
|
+
const authoredRight = authoredConstraintValue(element, "right");
|
|
136
|
+
const authoredTop = authoredConstraintValue(element, "top");
|
|
137
|
+
const authoredBottom = authoredConstraintValue(element, "bottom");
|
|
138
|
+
const authoredWidth = authoredConstraintValue(element, "width");
|
|
139
|
+
const authoredHeight = authoredConstraintValue(element, "height");
|
|
140
|
+
const authoredTransform = authoredConstraintValue(element, "transform");
|
|
141
|
+
const definiteLeft = definiteAuthoredOffset(authoredLeft);
|
|
142
|
+
const definiteRight = definiteAuthoredOffset(authoredRight);
|
|
143
|
+
const definiteTop = definiteAuthoredOffset(authoredTop);
|
|
144
|
+
const definiteBottom = definiteAuthoredOffset(authoredBottom);
|
|
145
|
+
const horizontalMixed = [
|
|
146
|
+
authoredLeft,
|
|
147
|
+
authoredRight,
|
|
148
|
+
authoredWidth,
|
|
149
|
+
authoredTransform,
|
|
150
|
+
].some(isMixedValue);
|
|
151
|
+
const verticalMixed = [
|
|
152
|
+
authoredTop,
|
|
153
|
+
authoredBottom,
|
|
154
|
+
authoredHeight,
|
|
155
|
+
authoredTransform,
|
|
156
|
+
].some(isMixedValue);
|
|
157
|
+
return {
|
|
158
|
+
horizontal: horizontalMixed
|
|
159
|
+
? "mixed"
|
|
160
|
+
: // Check scale before left+right: "scale" writes width:100% and clears
|
|
161
|
+
// left/right to auto, but legacy data may have 0px values that are truthy.
|
|
162
|
+
authoredWidth === "100%" ||
|
|
163
|
+
(percentageLength(authoredWidth) && percentageLength(definiteLeft))
|
|
164
|
+
? "scale"
|
|
165
|
+
: definiteLeft && definiteRight
|
|
166
|
+
? "left-right"
|
|
167
|
+
: definiteRight
|
|
168
|
+
? "right"
|
|
169
|
+
: authoredTransform?.includes("translateX(-50%)")
|
|
170
|
+
? "center"
|
|
171
|
+
: "left",
|
|
172
|
+
vertical: verticalMixed
|
|
173
|
+
? "mixed"
|
|
174
|
+
: authoredHeight === "100%" ||
|
|
175
|
+
(percentageLength(authoredHeight) && percentageLength(definiteTop))
|
|
176
|
+
? "scale"
|
|
177
|
+
: definiteTop && definiteBottom
|
|
178
|
+
? "top-bottom"
|
|
179
|
+
: definiteBottom
|
|
180
|
+
? "bottom"
|
|
181
|
+
: authoredTransform?.includes("translateY(-50%)")
|
|
182
|
+
? "center"
|
|
183
|
+
: "top",
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Build the complete constraints edit before writing anything. The previous
|
|
188
|
+
* implementation emitted position, four offsets, size, and transform as
|
|
189
|
+
* separate writes, producing visible intermediate layouts and one undo entry
|
|
190
|
+
* per property. Keeping this pure also makes the exact atomic patch directly
|
|
191
|
+
* regression-testable. A mixed axis is intentionally left untouched until the
|
|
192
|
+
* user chooses a concrete value for that axis. */
|
|
193
|
+
export function constraintsStylePatch(
|
|
194
|
+
element: ElementInfo,
|
|
195
|
+
value: ConstraintsValue,
|
|
196
|
+
): Record<string, string> {
|
|
197
|
+
const authoredTransform = authoredConstraintValue(element, "transform");
|
|
198
|
+
const currentValue = deriveConstraintsValue(element);
|
|
199
|
+
const patch: Record<string, string> = {};
|
|
200
|
+
let transform = isMixedValue(authoredTransform)
|
|
201
|
+
? undefined
|
|
202
|
+
: authoredTransform;
|
|
203
|
+
let transformChanged = false;
|
|
204
|
+
const parentBounds =
|
|
205
|
+
element.parentBoundingRect ?? element.parentAutoLayout?.boundingRect;
|
|
206
|
+
const childBounds = element.boundingRect;
|
|
207
|
+
const relativeLeft = parentBounds
|
|
208
|
+
? childBounds.x - parentBounds.x
|
|
209
|
+
: childBounds.x;
|
|
210
|
+
const relativeTop = parentBounds
|
|
211
|
+
? childBounds.y - parentBounds.y
|
|
212
|
+
: childBounds.y;
|
|
213
|
+
const rightGap = parentBounds
|
|
214
|
+
? parentBounds.width - relativeLeft - childBounds.width
|
|
215
|
+
: 0;
|
|
216
|
+
const bottomGap = parentBounds
|
|
217
|
+
? parentBounds.height - relativeTop - childBounds.height
|
|
218
|
+
: 0;
|
|
219
|
+
|
|
220
|
+
if (
|
|
221
|
+
value.horizontal !== "mixed" &&
|
|
222
|
+
value.horizontal !== currentValue.horizontal
|
|
223
|
+
) {
|
|
224
|
+
patch.position = "absolute";
|
|
225
|
+
transform = mergeTranslateFunction(
|
|
226
|
+
transform,
|
|
227
|
+
"X",
|
|
228
|
+
value.horizontal === "center" ? "-50%" : null,
|
|
229
|
+
);
|
|
230
|
+
transformChanged = true;
|
|
231
|
+
if (value.horizontal === "left") {
|
|
232
|
+
patch.left = geometryPx(relativeLeft);
|
|
233
|
+
patch.right = "auto";
|
|
234
|
+
patch.width = geometryPx(childBounds.width);
|
|
235
|
+
} else if (value.horizontal === "right") {
|
|
236
|
+
patch.right = geometryPx(rightGap);
|
|
237
|
+
patch.left = "auto";
|
|
238
|
+
patch.width = geometryPx(childBounds.width);
|
|
239
|
+
} else if (value.horizontal === "left-right") {
|
|
240
|
+
patch.left = geometryPx(relativeLeft);
|
|
241
|
+
patch.right = geometryPx(rightGap);
|
|
242
|
+
patch.width = "auto";
|
|
243
|
+
} else if (value.horizontal === "center") {
|
|
244
|
+
const centerOffset = parentBounds
|
|
245
|
+
? relativeLeft + childBounds.width / 2 - parentBounds.width / 2
|
|
246
|
+
: 0;
|
|
247
|
+
patch.left =
|
|
248
|
+
Math.abs(centerOffset) < 0.0005
|
|
249
|
+
? "50%"
|
|
250
|
+
: `calc(50% + ${geometryPx(centerOffset)})`;
|
|
251
|
+
patch.right = "auto";
|
|
252
|
+
patch.width = geometryPx(childBounds.width);
|
|
253
|
+
} else {
|
|
254
|
+
patch.left = parentBounds
|
|
255
|
+
? geometryPercent(relativeLeft, parentBounds.width)
|
|
256
|
+
: "0%";
|
|
257
|
+
patch.right = "auto";
|
|
258
|
+
patch.width = parentBounds
|
|
259
|
+
? geometryPercent(childBounds.width, parentBounds.width)
|
|
260
|
+
: "100%";
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (value.vertical !== "mixed" && value.vertical !== currentValue.vertical) {
|
|
265
|
+
patch.position = "absolute";
|
|
266
|
+
transform = mergeTranslateFunction(
|
|
267
|
+
transform,
|
|
268
|
+
"Y",
|
|
269
|
+
value.vertical === "center" ? "-50%" : null,
|
|
270
|
+
);
|
|
271
|
+
transformChanged = true;
|
|
272
|
+
if (value.vertical === "top") {
|
|
273
|
+
patch.top = geometryPx(relativeTop);
|
|
274
|
+
patch.bottom = "auto";
|
|
275
|
+
patch.height = geometryPx(childBounds.height);
|
|
276
|
+
} else if (value.vertical === "bottom") {
|
|
277
|
+
patch.bottom = geometryPx(bottomGap);
|
|
278
|
+
patch.top = "auto";
|
|
279
|
+
patch.height = geometryPx(childBounds.height);
|
|
280
|
+
} else if (value.vertical === "top-bottom") {
|
|
281
|
+
patch.top = geometryPx(relativeTop);
|
|
282
|
+
patch.bottom = geometryPx(bottomGap);
|
|
283
|
+
patch.height = "auto";
|
|
284
|
+
} else if (value.vertical === "center") {
|
|
285
|
+
const centerOffset = parentBounds
|
|
286
|
+
? relativeTop + childBounds.height / 2 - parentBounds.height / 2
|
|
287
|
+
: 0;
|
|
288
|
+
patch.top =
|
|
289
|
+
Math.abs(centerOffset) < 0.0005
|
|
290
|
+
? "50%"
|
|
291
|
+
: `calc(50% + ${geometryPx(centerOffset)})`;
|
|
292
|
+
patch.bottom = "auto";
|
|
293
|
+
patch.height = geometryPx(childBounds.height);
|
|
294
|
+
} else {
|
|
295
|
+
patch.top = parentBounds
|
|
296
|
+
? geometryPercent(relativeTop, parentBounds.height)
|
|
297
|
+
: "0%";
|
|
298
|
+
patch.bottom = "auto";
|
|
299
|
+
patch.height = parentBounds
|
|
300
|
+
? geometryPercent(childBounds.height, parentBounds.height)
|
|
301
|
+
: "100%";
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (transformChanged) patch.transform = transform || "none";
|
|
306
|
+
return patch;
|
|
307
|
+
}
|
|
308
|
+
|
|
68
309
|
/** Position, size, and spacing properties */
|
|
69
310
|
export function PositionLayoutProperties({
|
|
70
311
|
element,
|
|
71
312
|
onStyleChange,
|
|
313
|
+
onStylesChange,
|
|
72
314
|
onAlignSelection,
|
|
73
315
|
motionKeyframeContext,
|
|
74
316
|
breakpointOverrideContext,
|
|
75
317
|
}: {
|
|
76
318
|
element: ElementInfo;
|
|
77
319
|
onStyleChange: StyleChangeHandler;
|
|
320
|
+
onStylesChange?: StylesChangeHandler;
|
|
78
321
|
/**
|
|
79
322
|
* Moves the selection itself (Figma's real "Alignment" row semantics):
|
|
80
323
|
* aligns to the combined selection bounding box for a 2+ multi-selection,
|
|
@@ -113,40 +356,16 @@ export function PositionLayoutProperties({
|
|
|
113
356
|
value === "top" ? "top" : value === "bottom" ? "bottom" : "center-v",
|
|
114
357
|
);
|
|
115
358
|
};
|
|
116
|
-
// Authored (not computed)
|
|
117
|
-
//
|
|
118
|
-
//
|
|
359
|
+
// Authored (not computed) left/top: used directly (not through
|
|
360
|
+
// `definiteAuthoredOffset`) below because the X/Y fields need to tell
|
|
361
|
+
// "Mixed" apart from "unset", and handleConstraintsChange's own fallback
|
|
362
|
+
// already normalizes through `definiteAuthoredOffset` at its call sites.
|
|
363
|
+
// Right/bottom and the scale/rotation checks needed for the Constraints
|
|
364
|
+
// preview live in `deriveConstraintsValue` (above) instead.
|
|
119
365
|
const authoredLeft = authoredStyleValue(element, "left");
|
|
120
|
-
const authoredRight = authoredStyleValue(element, "right");
|
|
121
366
|
const authoredTop = authoredStyleValue(element, "top");
|
|
122
|
-
const authoredBottom = authoredStyleValue(element, "bottom");
|
|
123
|
-
const authoredWidth = authoredStyleValue(element, "width");
|
|
124
|
-
const authoredHeight = authoredStyleValue(element, "height");
|
|
125
367
|
const authoredTransform = authoredStyleValue(element, "transform");
|
|
126
|
-
const constraintsValue
|
|
127
|
-
horizontal:
|
|
128
|
-
// Check scale before left+right: "scale" writes width:100% and clears
|
|
129
|
-
// left/right to auto, but legacy data may have 0px values that are truthy.
|
|
130
|
-
authoredWidth === "100%"
|
|
131
|
-
? "scale"
|
|
132
|
-
: authoredLeft && authoredRight
|
|
133
|
-
? "left-right"
|
|
134
|
-
: authoredRight
|
|
135
|
-
? "right"
|
|
136
|
-
: authoredTransform?.includes("translateX(-50%)")
|
|
137
|
-
? "center"
|
|
138
|
-
: "left",
|
|
139
|
-
vertical:
|
|
140
|
-
authoredHeight === "100%"
|
|
141
|
-
? "scale"
|
|
142
|
-
: authoredTop && authoredBottom
|
|
143
|
-
? "top-bottom"
|
|
144
|
-
: authoredBottom
|
|
145
|
-
? "bottom"
|
|
146
|
-
: authoredTransform?.includes("translateY(-50%)")
|
|
147
|
-
? "center"
|
|
148
|
-
: "top",
|
|
149
|
-
};
|
|
368
|
+
const constraintsValue = deriveConstraintsValue(element);
|
|
150
369
|
const [constraintsExpanded, setConstraintsExpanded] = useState(false);
|
|
151
370
|
// 3D rotation/perspective progressive-disclosure expander — mirrors
|
|
152
371
|
// CornerRadiusControl's showIndependentCorners pattern. Default-expanded
|
|
@@ -164,87 +383,13 @@ export function PositionLayoutProperties({
|
|
|
164
383
|
|
|
165
384
|
const handleConstraintsChange = useCallback(
|
|
166
385
|
(value: ConstraintsValue) => {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// overwrite each other when both change simultaneously.
|
|
172
|
-
const txValue = value.horizontal === "center" ? "-50%" : null;
|
|
173
|
-
const tyValue = value.vertical === "center" ? "-50%" : null;
|
|
174
|
-
// Start from the current transform, apply X, then apply Y on top.
|
|
175
|
-
const transformAfterX = mergeTranslateFunction(
|
|
176
|
-
authoredTransform,
|
|
177
|
-
"X",
|
|
178
|
-
txValue,
|
|
179
|
-
);
|
|
180
|
-
const transformAfterXY = mergeTranslateFunction(
|
|
181
|
-
transformAfterX,
|
|
182
|
-
"Y",
|
|
183
|
-
tyValue,
|
|
386
|
+
commitStylePatch(
|
|
387
|
+
constraintsStylePatch(element, value),
|
|
388
|
+
onStyleChange,
|
|
389
|
+
onStylesChange,
|
|
184
390
|
);
|
|
185
|
-
|
|
186
|
-
if (value.horizontal === "left") {
|
|
187
|
-
onStyleChange(
|
|
188
|
-
"left",
|
|
189
|
-
authoredLeft || `${Math.round(element.boundingRect.x)}px`,
|
|
190
|
-
);
|
|
191
|
-
onStyleChange("right", "auto");
|
|
192
|
-
} else if (value.horizontal === "right") {
|
|
193
|
-
onStyleChange("right", "0px");
|
|
194
|
-
onStyleChange("left", "auto");
|
|
195
|
-
} else if (value.horizontal === "left-right") {
|
|
196
|
-
onStyleChange(
|
|
197
|
-
"left",
|
|
198
|
-
authoredLeft || `${Math.round(element.boundingRect.x)}px`,
|
|
199
|
-
);
|
|
200
|
-
onStyleChange("right", "0px");
|
|
201
|
-
} else if (value.horizontal === "center") {
|
|
202
|
-
onStyleChange("left", "50%");
|
|
203
|
-
onStyleChange("right", "auto");
|
|
204
|
-
} else {
|
|
205
|
-
// scale: use auto (not 0px) so the left && right truthiness check
|
|
206
|
-
// in the reader does not misidentify this as "left-right".
|
|
207
|
-
onStyleChange("left", "auto");
|
|
208
|
-
onStyleChange("right", "auto");
|
|
209
|
-
onStyleChange("width", "100%");
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (value.vertical === "top") {
|
|
213
|
-
onStyleChange(
|
|
214
|
-
"top",
|
|
215
|
-
authoredTop || `${Math.round(element.boundingRect.y)}px`,
|
|
216
|
-
);
|
|
217
|
-
onStyleChange("bottom", "auto");
|
|
218
|
-
} else if (value.vertical === "bottom") {
|
|
219
|
-
onStyleChange("bottom", "0px");
|
|
220
|
-
onStyleChange("top", "auto");
|
|
221
|
-
} else if (value.vertical === "top-bottom") {
|
|
222
|
-
onStyleChange(
|
|
223
|
-
"top",
|
|
224
|
-
authoredTop || `${Math.round(element.boundingRect.y)}px`,
|
|
225
|
-
);
|
|
226
|
-
onStyleChange("bottom", "0px");
|
|
227
|
-
} else if (value.vertical === "center") {
|
|
228
|
-
onStyleChange("top", "50%");
|
|
229
|
-
onStyleChange("bottom", "auto");
|
|
230
|
-
} else {
|
|
231
|
-
// scale
|
|
232
|
-
onStyleChange("top", "auto");
|
|
233
|
-
onStyleChange("bottom", "auto");
|
|
234
|
-
onStyleChange("height", "100%");
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Write the composed transform once, after both axes are resolved.
|
|
238
|
-
onStyleChange("transform", transformAfterXY);
|
|
239
391
|
},
|
|
240
|
-
[
|
|
241
|
-
element.boundingRect.x,
|
|
242
|
-
element.boundingRect.y,
|
|
243
|
-
onStyleChange,
|
|
244
|
-
authoredLeft,
|
|
245
|
-
authoredTop,
|
|
246
|
-
authoredTransform,
|
|
247
|
-
],
|
|
392
|
+
[element, onStyleChange, onStylesChange],
|
|
248
393
|
);
|
|
249
394
|
|
|
250
395
|
return (
|
|
@@ -328,7 +473,9 @@ export function PositionLayoutProperties({
|
|
|
328
473
|
ariaLabel="X-position"
|
|
329
474
|
tooltipLabel="X-position"
|
|
330
475
|
value={
|
|
331
|
-
isMixedValue(authoredLeft)
|
|
476
|
+
isMixedValue(authoredLeft)
|
|
477
|
+
? MIXED_VALUE
|
|
478
|
+
: (definiteAuthoredOffset(authoredLeft) ?? "")
|
|
332
479
|
}
|
|
333
480
|
placeholder={element.boundingRect.x}
|
|
334
481
|
inputClassName="h-6"
|
|
@@ -338,8 +485,17 @@ export function PositionLayoutProperties({
|
|
|
338
485
|
// mirror handleConstraintsChange, which always sets
|
|
339
486
|
// position:absolute (the convention canvas drag/resize and
|
|
340
487
|
// primitive creation both use) before writing left/top.
|
|
341
|
-
|
|
342
|
-
|
|
488
|
+
commitStylePatch(
|
|
489
|
+
{
|
|
490
|
+
...(!constrainedPosition
|
|
491
|
+
? { position: "absolute" }
|
|
492
|
+
: undefined),
|
|
493
|
+
left: `${roundToOneDecimal(v)}px`,
|
|
494
|
+
},
|
|
495
|
+
onStyleChange,
|
|
496
|
+
onStylesChange,
|
|
497
|
+
meta,
|
|
498
|
+
);
|
|
343
499
|
}}
|
|
344
500
|
/>
|
|
345
501
|
<FieldTrailer
|
|
@@ -358,13 +514,24 @@ export function PositionLayoutProperties({
|
|
|
358
514
|
ariaLabel="Y-position"
|
|
359
515
|
tooltipLabel="Y-position"
|
|
360
516
|
value={
|
|
361
|
-
isMixedValue(authoredTop)
|
|
517
|
+
isMixedValue(authoredTop)
|
|
518
|
+
? MIXED_VALUE
|
|
519
|
+
: (definiteAuthoredOffset(authoredTop) ?? "")
|
|
362
520
|
}
|
|
363
521
|
placeholder={element.boundingRect.y}
|
|
364
522
|
inputClassName="h-6"
|
|
365
523
|
onChange={(v, meta) => {
|
|
366
|
-
|
|
367
|
-
|
|
524
|
+
commitStylePatch(
|
|
525
|
+
{
|
|
526
|
+
...(!constrainedPosition
|
|
527
|
+
? { position: "absolute" }
|
|
528
|
+
: undefined),
|
|
529
|
+
top: `${roundToOneDecimal(v)}px`,
|
|
530
|
+
},
|
|
531
|
+
onStyleChange,
|
|
532
|
+
onStylesChange,
|
|
533
|
+
meta,
|
|
534
|
+
);
|
|
368
535
|
}}
|
|
369
536
|
/>
|
|
370
537
|
<FieldTrailer
|
|
@@ -12,6 +12,29 @@ export function sameOrMixed(values: string[]): string {
|
|
|
12
12
|
return values.every((value) => value === first) ? first : MIXED_VALUE;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Structural equality via JSON — good enough for the small, JSON-serializable
|
|
17
|
+
* parent-layout snapshots (`parentDisplay`/`parentAutoLayout`/`parentLayout`)
|
|
18
|
+
* this is used for below; not intended as a general deep-equal.
|
|
19
|
+
*/
|
|
20
|
+
function sameStructure<T>(a: T | undefined, b: T | undefined): boolean {
|
|
21
|
+
return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns `value` when every element in `values` is structurally identical
|
|
26
|
+
* to it, else `undefined`. Used for object/optional-string fields where a
|
|
27
|
+
* `MIXED_VALUE` string sentinel doesn't apply (they aren't rendered as text)
|
|
28
|
+
* but silently leaking one arbitrary element's value across a selection with
|
|
29
|
+
* genuinely different values would still be wrong — see `parentDisplay` /
|
|
30
|
+
* `parentAutoLayout` / `parentLayout` below.
|
|
31
|
+
*/
|
|
32
|
+
function sameValueOrUndefined<T>(values: T[], candidate: T): T | undefined {
|
|
33
|
+
return values.every((value) => sameStructure(value, candidate))
|
|
34
|
+
? candidate
|
|
35
|
+
: undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
15
38
|
export function mixedElementFromSelection(
|
|
16
39
|
elements: ElementInfo[],
|
|
17
40
|
): ElementInfo | null {
|
|
@@ -60,11 +83,35 @@ export function mixedElementFromSelection(
|
|
|
60
83
|
(element) => element.boundingRect.y + element.boundingRect.height,
|
|
61
84
|
),
|
|
62
85
|
);
|
|
86
|
+
// Only report a shared component name when every selected element is the
|
|
87
|
+
// *same* named component instance (mirrors Figma: a multi-instance
|
|
88
|
+
// same-component selection still reads as a component). Anything else —
|
|
89
|
+
// including a component mixed with a plain element — collapses to
|
|
90
|
+
// undefined rather than a "Mixed" sentinel string: elementIsComponentSelection()
|
|
91
|
+
// in element-classification.ts only checks `.length > 0`, so leaking
|
|
92
|
+
// MIXED_VALUE ("Mixed", a non-empty string) through here would make a
|
|
93
|
+
// component+non-component multi-selection misreport as "is a component"
|
|
94
|
+
// (wrong purple icon/tint in SelectionHeader) instead of falling back to
|
|
95
|
+
// undefined the way an actual mismatch should.
|
|
96
|
+
const firstComponentName = elements[0]?.componentName;
|
|
97
|
+
const componentName =
|
|
98
|
+
firstComponentName &&
|
|
99
|
+
elements.every((element) => element.componentName === firstComponentName)
|
|
100
|
+
? firstComponentName
|
|
101
|
+
: undefined;
|
|
102
|
+
|
|
63
103
|
return {
|
|
64
104
|
...base,
|
|
65
105
|
tagName: sameOrMixed(elements.map((element) => element.tagName)),
|
|
66
106
|
id: undefined,
|
|
67
107
|
sourceId: undefined,
|
|
108
|
+
// A merged/synthetic multi-selection has no single stable pending id
|
|
109
|
+
// either — clear it like id/sourceId above instead of leaking whichever
|
|
110
|
+
// element happened to be last, which isTextElement()'s nodeId-prefix
|
|
111
|
+
// fallback (for older payloads without primitiveKind) would otherwise
|
|
112
|
+
// read as if it belonged to the whole selection.
|
|
113
|
+
pendingNodeId: undefined,
|
|
114
|
+
componentName,
|
|
68
115
|
selector: base.selector,
|
|
69
116
|
classes: [],
|
|
70
117
|
computedStyles,
|
|
@@ -87,5 +134,39 @@ export function mixedElementFromSelection(
|
|
|
87
134
|
childElementCount: undefined,
|
|
88
135
|
isFlexChild: elements.every((element) => element.isFlexChild),
|
|
89
136
|
isFlexContainer: elements.every((element) => element.isFlexContainer),
|
|
137
|
+
// Same treatment as isFlexContainer above: isContainerElement() checks
|
|
138
|
+
// isFlexContainer || isGridContainer, so leaving isGridContainer to leak
|
|
139
|
+
// from `base` alone (via the ...base spread) let a mixed grid+non-grid
|
|
140
|
+
// selection misclassify as a uniform container based on selection order.
|
|
141
|
+
isGridContainer: elements.every((element) => element.isGridContainer),
|
|
142
|
+
// isParentFlex/isParentGrid/parentFlexDirection (element-classification.ts)
|
|
143
|
+
// read these three fields to decide whether LayoutContextProperties shows
|
|
144
|
+
// FlexChildControls/GridChildControls at all, and with which direction.
|
|
145
|
+
// Left unhandled, `...base` above leaked whichever element was selected
|
|
146
|
+
// LAST: multi-selecting elements from two different parents (e.g. one
|
|
147
|
+
// flex child + one plain block child, or two flex children with
|
|
148
|
+
// different flexDirection) rendered/hid those controls based on an
|
|
149
|
+
// arbitrary member of the selection, and any align-self/flex-grow edit
|
|
150
|
+
// made through them would apply to every selected element regardless of
|
|
151
|
+
// whether its own parent actually supports that property. Falling back
|
|
152
|
+
// to undefined when the selection's parents disagree matches the
|
|
153
|
+
// isFlexContainer/isGridContainer treatment above: hide the
|
|
154
|
+
// parent-relative controls rather than guess from one element.
|
|
155
|
+
parentDisplay: sameValueOrUndefined(
|
|
156
|
+
elements.map((element) => element.parentDisplay),
|
|
157
|
+
base.parentDisplay,
|
|
158
|
+
),
|
|
159
|
+
parentAutoLayout: sameValueOrUndefined(
|
|
160
|
+
elements.map((element) => element.parentAutoLayout),
|
|
161
|
+
base.parentAutoLayout,
|
|
162
|
+
),
|
|
163
|
+
parentBoundingRect: sameValueOrUndefined(
|
|
164
|
+
elements.map((element) => element.parentBoundingRect),
|
|
165
|
+
base.parentBoundingRect,
|
|
166
|
+
),
|
|
167
|
+
parentLayout: sameValueOrUndefined(
|
|
168
|
+
elements.map((element) => element.parentLayout),
|
|
169
|
+
base.parentLayout,
|
|
170
|
+
),
|
|
90
171
|
};
|
|
91
172
|
}
|
|
@@ -33,10 +33,12 @@ import {
|
|
|
33
33
|
outlineOffsetForPosition,
|
|
34
34
|
readStrokeOutlinePosition,
|
|
35
35
|
readTextStrokeStyle,
|
|
36
|
+
resolveRestoredStrokeStyle,
|
|
36
37
|
resolveTextStrokeColor,
|
|
37
38
|
roundToOneDecimal,
|
|
38
39
|
strokeHiddenByColor,
|
|
39
40
|
strokeIsVisible,
|
|
41
|
+
strokeShowPatch,
|
|
40
42
|
textStrokeAddPatch,
|
|
41
43
|
textStrokeIsVisible,
|
|
42
44
|
} from "./position-helpers";
|
|
@@ -180,22 +182,21 @@ function StrokeLayerControl({
|
|
|
180
182
|
// "none"/"solid". Writing "none" would lose a dashed/dotted
|
|
181
183
|
// style permanently, since there is no round-trippable "unset"
|
|
182
184
|
// for that keyword once it's overwritten.
|
|
183
|
-
const parsed = parseCssColor(color);
|
|
184
185
|
if (visible) {
|
|
186
|
+
const parsed = parseCssColor(color);
|
|
185
187
|
onStyleChange(
|
|
186
188
|
`${prefix}Color`,
|
|
187
189
|
parsed ? rgbaToCss(withColorOpacity(parsed, 0)) : "transparent",
|
|
188
190
|
);
|
|
189
191
|
return;
|
|
190
192
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
width === "0px" ? "1px" : width || "1px",
|
|
193
|
+
// Restore color/width/style as ONE commit (single undo step)
|
|
194
|
+
// rather than three sequential onStyleChange calls — see
|
|
195
|
+
// strokeShowPatch's doc comment.
|
|
196
|
+
commitStylePatch(
|
|
197
|
+
strokeShowPatch(prefix, color, width, styleValue),
|
|
198
|
+
onStyleChange,
|
|
199
|
+
onStylesChange,
|
|
199
200
|
);
|
|
200
201
|
}}
|
|
201
202
|
>
|
|
@@ -384,7 +385,12 @@ export function StrokeProperties({
|
|
|
384
385
|
commitStylePatch(
|
|
385
386
|
{
|
|
386
387
|
borderWidth: "1px",
|
|
387
|
-
|
|
388
|
+
// Preserve a real style (dashed/dotted/etc) that survived
|
|
389
|
+
// on a hidden-via-alpha border — only the outline branch
|
|
390
|
+
// below used to do this; the border branch hardcoded
|
|
391
|
+
// "solid" unconditionally, silently discarding it. See
|
|
392
|
+
// resolveRestoredStrokeStyle's doc comment.
|
|
393
|
+
borderStyle: resolveRestoredStrokeStyle(styles.borderStyle),
|
|
388
394
|
borderColor,
|
|
389
395
|
},
|
|
390
396
|
onStyleChange,
|
|
@@ -396,10 +402,9 @@ export function StrokeProperties({
|
|
|
396
402
|
const outlineWidth = `${
|
|
397
403
|
Math.max(1, cssLengthNumber(styles.outlineWidth, 1)) + 1
|
|
398
404
|
}px`;
|
|
399
|
-
const outlineStyle =
|
|
400
|
-
styles.outlineStyle
|
|
401
|
-
|
|
402
|
-
: styles.outlineStyle || "solid";
|
|
405
|
+
const outlineStyle = resolveRestoredStrokeStyle(
|
|
406
|
+
styles.outlineStyle,
|
|
407
|
+
);
|
|
403
408
|
const outlineColor = cssColorOrFallback(
|
|
404
409
|
styles.outlineColor || styles.borderColor,
|
|
405
410
|
"#000000",
|
|
@@ -74,6 +74,13 @@ export function resolveLineHeight(
|
|
|
74
74
|
if (Number.isFinite(lhPx) && Number.isFinite(fsPx) && fsPx > 0) {
|
|
75
75
|
return Math.round((lhPx / fsPx) * 100) / 100;
|
|
76
76
|
}
|
|
77
|
+
// A px-computed line-height that we can't divide by a valid font-size
|
|
78
|
+
// (missing/invalid fontSize) must NOT fall through to the unitless-ratio
|
|
79
|
+
// parse below — `parseFloat("19.2px")` silently reads as `19.2`, which
|
|
80
|
+
// would render as a wildly wrong multiplier (e.g. "19.2") instead of a
|
|
81
|
+
// sane ~1.2 ratio. Fall back to the same default the empty/"normal" case
|
|
82
|
+
// above uses.
|
|
83
|
+
return 1.2;
|
|
77
84
|
}
|
|
78
85
|
const numeric = parseFloat(lh);
|
|
79
86
|
return Number.isFinite(numeric) && numeric > 0 ? numeric : 1.2;
|