@agent-native/core 0.92.9 → 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 +29 -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/deploy/build.ts +133 -5
- 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/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.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/collab/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +24 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +102 -5
- package/dist/deploy/build.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/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +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
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
* __EDITOR_CHROME_SCALE_Y__ — number string, e.g. "1.5"
|
|
14
14
|
* __DESIGN_CANVAS_SCREEN_ID__ — string literal for the owning screen/file id
|
|
15
15
|
* __DESIGN_CANVAS_BOARD_SURFACE__ — boolean literal for top-level board iframe
|
|
16
|
+
* __DESIGN_CANVAS_CONTENT_OFFSET_X__ — embedded board render-window x offset
|
|
17
|
+
* __DESIGN_CANVAS_CONTENT_OFFSET_Y__ — embedded board render-window y offset
|
|
18
|
+
* __RUNTIME_LAYER_SNAPSHOT_ENABLED__ — true only for URL-backed localhost apps
|
|
16
19
|
*
|
|
17
20
|
* Rules:
|
|
18
21
|
* • No import/require of any module (DOM globals only).
|
|
@@ -28,6 +31,9 @@ declare var __EDITOR_CHROME_SCALE_X__: string;
|
|
|
28
31
|
declare var __EDITOR_CHROME_SCALE_Y__: string;
|
|
29
32
|
declare var __DESIGN_CANVAS_SCREEN_ID__: string;
|
|
30
33
|
declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
34
|
+
declare var __DESIGN_CANVAS_CONTENT_OFFSET_X__: number;
|
|
35
|
+
declare var __DESIGN_CANVAS_CONTENT_OFFSET_Y__: number;
|
|
36
|
+
declare var __RUNTIME_LAYER_SNAPSHOT_ENABLED__: boolean;
|
|
31
37
|
|
|
32
38
|
(function () {
|
|
33
39
|
// Idempotency guard: replace-document-content / srcdoc rebuilds can end up
|
|
@@ -51,6 +57,11 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
51
57
|
var textEditingEnabled = !readOnly && textEditingEnabledFlag;
|
|
52
58
|
var designCanvasScreenId = __DESIGN_CANVAS_SCREEN_ID__ || "";
|
|
53
59
|
var designCanvasBoardSurface = !!__DESIGN_CANVAS_BOARD_SURFACE__;
|
|
60
|
+
var designCanvasContentOffsetX =
|
|
61
|
+
Number(__DESIGN_CANVAS_CONTENT_OFFSET_X__) || 0;
|
|
62
|
+
var designCanvasContentOffsetY =
|
|
63
|
+
Number(__DESIGN_CANVAS_CONTENT_OFFSET_Y__) || 0;
|
|
64
|
+
var runtimeLayerSnapshotEnabled = !!__RUNTIME_LAYER_SNAPSHOT_ENABLED__;
|
|
54
65
|
var scaleToolEnabled = false;
|
|
55
66
|
// Interaction-state forced preview (phase 2 — see shared/interaction-states.ts's
|
|
56
67
|
// "Forced-preview mechanism" doc comment). Tracks which single node id
|
|
@@ -211,6 +222,359 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
211
222
|
);
|
|
212
223
|
}
|
|
213
224
|
|
|
225
|
+
/**
|
|
226
|
+
* React 19 development builds keep the exact jsxDEV call site on the host
|
|
227
|
+
* element's Fiber `_debugStack`, even though React does not emit that source
|
|
228
|
+
* location as a DOM attribute. Recover it without depending on React internals
|
|
229
|
+
* for mutations: this is read-only provenance used to identify the selected
|
|
230
|
+
* source file and line. Explicit data-source-* / data-loc attributes still
|
|
231
|
+
* win below, and production builds simply return undefined.
|
|
232
|
+
*/
|
|
233
|
+
// Fiber debug stacks are immutable for the lifetime of a mounted host node.
|
|
234
|
+
// Keep the relatively expensive Object.keys + owner walk off the snapshot
|
|
235
|
+
// hot path after the first successful read. A WeakMap also means React can
|
|
236
|
+
// collect unmounted nodes normally. Do not cache misses: the bridge can run
|
|
237
|
+
// before a slow/Suspense hydration attaches Fiber to an existing DOM node.
|
|
238
|
+
var reactDebugProvenanceCache =
|
|
239
|
+
typeof WeakMap !== "undefined"
|
|
240
|
+
? new WeakMap<Element, ReturnType<typeof reactDebugProvenance>>()
|
|
241
|
+
: null;
|
|
242
|
+
|
|
243
|
+
function reactDebugProvenance(el: Element):
|
|
244
|
+
| {
|
|
245
|
+
sourceFile: string;
|
|
246
|
+
line: number;
|
|
247
|
+
column?: number;
|
|
248
|
+
component?: string;
|
|
249
|
+
}
|
|
250
|
+
| undefined {
|
|
251
|
+
var cached = reactDebugProvenanceCache?.get(el);
|
|
252
|
+
if (cached !== undefined) return cached;
|
|
253
|
+
var fiberKey = Object.keys(el).find(function (key) {
|
|
254
|
+
return key.indexOf("__reactFiber$") === 0;
|
|
255
|
+
});
|
|
256
|
+
if (!fiberKey) return undefined;
|
|
257
|
+
var fiber = (el as unknown as Record<string, any>)[fiberKey];
|
|
258
|
+
for (var depth = 0; fiber && depth < 12; depth += 1) {
|
|
259
|
+
var stack = String(fiber._debugStack?.stack || "");
|
|
260
|
+
var lines = stack.split("\n");
|
|
261
|
+
for (var index = 0; index < lines.length; index += 1) {
|
|
262
|
+
var lineText = lines[index];
|
|
263
|
+
var match = lineText.match(
|
|
264
|
+
/(?:\(|\s)(https?:\/\/[^\s)]+?):(\d+):(\d+)\)?\s*$/,
|
|
265
|
+
);
|
|
266
|
+
if (!match) continue;
|
|
267
|
+
try {
|
|
268
|
+
var sourceUrl = new URL(match[1]);
|
|
269
|
+
var pathname = decodeURIComponent(sourceUrl.pathname);
|
|
270
|
+
if (pathname.indexOf("/node_modules/") >= 0) continue;
|
|
271
|
+
var sourceFile =
|
|
272
|
+
pathname.indexOf("/@fs/") === 0
|
|
273
|
+
? pathname.slice("/@fs".length)
|
|
274
|
+
: pathname.replace(/^\/+/, "");
|
|
275
|
+
if (!sourceFile) continue;
|
|
276
|
+
var componentMatch = lineText.match(/\bat\s+([^\s(]+)\s*\(/);
|
|
277
|
+
var provenance = {
|
|
278
|
+
sourceFile: sourceFile,
|
|
279
|
+
line: parseInt(match[2], 10),
|
|
280
|
+
column: parseInt(match[3], 10),
|
|
281
|
+
component: componentMatch ? componentMatch[1] : undefined,
|
|
282
|
+
};
|
|
283
|
+
reactDebugProvenanceCache?.set(el, provenance);
|
|
284
|
+
return provenance;
|
|
285
|
+
} catch (_error) {
|
|
286
|
+
// Ignore malformed/non-URL debug frames and keep walking owners.
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
fiber = fiber.return;
|
|
290
|
+
}
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
var runtimeLayerSnapshotTimer: number | null = null;
|
|
295
|
+
var runtimeLayerSnapshotMaxTimer: number | null = null;
|
|
296
|
+
var lastRuntimeLayerSnapshotHtml = "";
|
|
297
|
+
|
|
298
|
+
function runtimeLayerHash(value: string): string {
|
|
299
|
+
var hash = 0x811c9dc5;
|
|
300
|
+
for (var index = 0; index < value.length; index += 1) {
|
|
301
|
+
hash ^= value.charCodeAt(index);
|
|
302
|
+
hash = Math.imul(hash, 0x01000193);
|
|
303
|
+
}
|
|
304
|
+
return (hash >>> 0).toString(36);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function runtimeLayerStructuralPath(el: Element): string {
|
|
308
|
+
var parts: string[] = [];
|
|
309
|
+
var node: Element | null = el;
|
|
310
|
+
while (node && node !== document.body) {
|
|
311
|
+
var tag = node.tagName.toLowerCase();
|
|
312
|
+
var parent = node.parentElement;
|
|
313
|
+
if (parent) {
|
|
314
|
+
var sameTag = Array.prototype.filter.call(
|
|
315
|
+
parent.children,
|
|
316
|
+
function (sibling) {
|
|
317
|
+
return sibling.tagName === node!.tagName;
|
|
318
|
+
},
|
|
319
|
+
);
|
|
320
|
+
tag += ":nth-of-type(" + (sameTag.indexOf(node) + 1) + ")";
|
|
321
|
+
}
|
|
322
|
+
parts.unshift(tag);
|
|
323
|
+
node = parent;
|
|
324
|
+
}
|
|
325
|
+
return parts.join(" > ") || "body";
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function ensureRuntimeLayerNodeId(el: Element): string {
|
|
329
|
+
var existing = el.getAttribute("data-agent-native-node-id")?.trim();
|
|
330
|
+
if (existing) return existing;
|
|
331
|
+
var provenance = reactDebugProvenance(el);
|
|
332
|
+
var provenanceKey = provenance
|
|
333
|
+
? [
|
|
334
|
+
provenance.sourceFile,
|
|
335
|
+
provenance.line,
|
|
336
|
+
provenance.column || 0,
|
|
337
|
+
provenance.component || "",
|
|
338
|
+
].join(":")
|
|
339
|
+
: "dom";
|
|
340
|
+
var nodeId =
|
|
341
|
+
"runtime-" +
|
|
342
|
+
runtimeLayerHash(
|
|
343
|
+
designCanvasScreenId +
|
|
344
|
+
":" +
|
|
345
|
+
provenanceKey +
|
|
346
|
+
":" +
|
|
347
|
+
runtimeLayerStructuralPath(el),
|
|
348
|
+
);
|
|
349
|
+
// This attribute is editor-only and never persisted. Stamping the live DOM
|
|
350
|
+
// gives the runtime projection and the selectable element one exact shared
|
|
351
|
+
// identity. Qualifying the hash with the owning screen/file id prevents a
|
|
352
|
+
// shared React shell from producing the same layer id in two route iframes
|
|
353
|
+
// (which would make the editor's document-wide owner map route selection
|
|
354
|
+
// and hover to whichever screen registered last). The structural path
|
|
355
|
+
// keeps the id stable across reloads within that iframe while still
|
|
356
|
+
// disambiguating repeated JSX emitted from the same source location.
|
|
357
|
+
el.setAttribute("data-agent-native-node-id", nodeId);
|
|
358
|
+
return nodeId;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function isRuntimeLayerVisualNode(el: Element): boolean {
|
|
362
|
+
if (
|
|
363
|
+
/^(script|style|template|noscript|link|meta|title)$/i.test(el.tagName)
|
|
364
|
+
) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
return !(
|
|
368
|
+
isOverlayElement(el) || el.closest("[data-agent-native-edit-overlay]")
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function serializeRuntimeLayerSnapshot(): {
|
|
373
|
+
html: string;
|
|
374
|
+
nodeCount: number;
|
|
375
|
+
} | null {
|
|
376
|
+
if (!document.body) return null;
|
|
377
|
+
var sourceNodes = Array.prototype.slice.call(
|
|
378
|
+
document.body.querySelectorAll("*"),
|
|
379
|
+
) as Element[];
|
|
380
|
+
var cloneBody = document.body.cloneNode(true) as HTMLElement;
|
|
381
|
+
var cloneNodes = Array.prototype.slice.call(
|
|
382
|
+
cloneBody.querySelectorAll("*"),
|
|
383
|
+
) as Element[];
|
|
384
|
+
var nodeCount = 0;
|
|
385
|
+
for (
|
|
386
|
+
var index = 0;
|
|
387
|
+
index < sourceNodes.length && index < cloneNodes.length;
|
|
388
|
+
index += 1
|
|
389
|
+
) {
|
|
390
|
+
var sourceNode = sourceNodes[index];
|
|
391
|
+
var cloneNode = cloneNodes[index];
|
|
392
|
+
if (!isRuntimeLayerVisualNode(sourceNode)) {
|
|
393
|
+
cloneNode.setAttribute("data-an-runtime-layer-remove", "true");
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
cloneNode.setAttribute(
|
|
397
|
+
"data-agent-native-node-id",
|
|
398
|
+
ensureRuntimeLayerNodeId(sourceNode),
|
|
399
|
+
);
|
|
400
|
+
var provenance = reactDebugProvenance(sourceNode);
|
|
401
|
+
if (provenance) {
|
|
402
|
+
cloneNode.setAttribute("data-source-file", provenance.sourceFile);
|
|
403
|
+
cloneNode.setAttribute("data-source-line", String(provenance.line));
|
|
404
|
+
if (provenance.column) {
|
|
405
|
+
cloneNode.setAttribute(
|
|
406
|
+
"data-source-column",
|
|
407
|
+
String(provenance.column),
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
if (provenance.component) {
|
|
411
|
+
cloneNode.setAttribute("data-component-name", provenance.component);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
nodeCount += 1;
|
|
415
|
+
}
|
|
416
|
+
cloneBody
|
|
417
|
+
.querySelectorAll("[data-an-runtime-layer-remove]")
|
|
418
|
+
.forEach(function (node) {
|
|
419
|
+
node.remove();
|
|
420
|
+
});
|
|
421
|
+
cloneBody
|
|
422
|
+
.querySelectorAll("script,style,template,noscript,link,meta,title")
|
|
423
|
+
.forEach(function (node) {
|
|
424
|
+
node.remove();
|
|
425
|
+
});
|
|
426
|
+
cloneBody.setAttribute(
|
|
427
|
+
"data-agent-native-node-id",
|
|
428
|
+
ensureRuntimeLayerNodeId(document.body),
|
|
429
|
+
);
|
|
430
|
+
cloneBody.setAttribute("data-an-runtime-layer-snapshot", "true");
|
|
431
|
+
var html = "<!doctype html><html>" + cloneBody.outerHTML + "</html>"; // i18n-ignore serialized runtime-layer HTML payload, not visible UI copy
|
|
432
|
+
if (html.length > 2_000_000) return null;
|
|
433
|
+
return { html: html, nodeCount: nodeCount };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function postRuntimeLayerSnapshot(): void {
|
|
437
|
+
if (runtimeLayerSnapshotTimer !== null) {
|
|
438
|
+
window.clearTimeout(runtimeLayerSnapshotTimer);
|
|
439
|
+
}
|
|
440
|
+
if (runtimeLayerSnapshotMaxTimer !== null) {
|
|
441
|
+
window.clearTimeout(runtimeLayerSnapshotMaxTimer);
|
|
442
|
+
}
|
|
443
|
+
runtimeLayerSnapshotTimer = null;
|
|
444
|
+
runtimeLayerSnapshotMaxTimer = null;
|
|
445
|
+
var snapshot = serializeRuntimeLayerSnapshot();
|
|
446
|
+
if (!snapshot || snapshot.html === lastRuntimeLayerSnapshotHtml) return;
|
|
447
|
+
lastRuntimeLayerSnapshotHtml = snapshot.html;
|
|
448
|
+
(window.parent as Window).postMessage(
|
|
449
|
+
{
|
|
450
|
+
type: "agent-native:runtime-layer-snapshot",
|
|
451
|
+
payload: snapshot,
|
|
452
|
+
},
|
|
453
|
+
"*",
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function scheduleRuntimeLayerSnapshot(): void {
|
|
458
|
+
// A leading-edge throttle still serializes a fast clock, streaming list,
|
|
459
|
+
// or hydration loop five times per second forever. Debounce on the trailing
|
|
460
|
+
// edge so a normal React commit becomes one snapshot, with a bounded max
|
|
461
|
+
// wait so a genuinely continuous stream still refreshes Layers eventually
|
|
462
|
+
// without monopolising the iframe main thread.
|
|
463
|
+
if (runtimeLayerSnapshotTimer !== null) {
|
|
464
|
+
window.clearTimeout(runtimeLayerSnapshotTimer);
|
|
465
|
+
}
|
|
466
|
+
runtimeLayerSnapshotTimer = window.setTimeout(
|
|
467
|
+
postRuntimeLayerSnapshot,
|
|
468
|
+
300,
|
|
469
|
+
);
|
|
470
|
+
if (runtimeLayerSnapshotMaxTimer === null) {
|
|
471
|
+
runtimeLayerSnapshotMaxTimer = window.setTimeout(
|
|
472
|
+
postRuntimeLayerSnapshot,
|
|
473
|
+
1500,
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Runtime Layers describes hierarchy, names, and layout containers. It does
|
|
479
|
+
// not need a new full-document snapshot for animation-frame churn such as
|
|
480
|
+
// transform/opacity/style streaming or state-only utility classes. Compare
|
|
481
|
+
// only the class/style subset that can change the projected Layers tree.
|
|
482
|
+
function runtimeLayerClassSignature(value: string | null): string {
|
|
483
|
+
return String(value || "")
|
|
484
|
+
.split(/\s+/)
|
|
485
|
+
.map(function (token) {
|
|
486
|
+
var parts = token.split(":");
|
|
487
|
+
var utility = String(parts[parts.length - 1] || "").replace(/^!/, "");
|
|
488
|
+
if (
|
|
489
|
+
/^(?:flex|inline-flex|grid|inline-grid|hidden|block|inline-block|flex-row|flex-col)$/.test(
|
|
490
|
+
utility,
|
|
491
|
+
) ||
|
|
492
|
+
/^(?:items|justify)-/.test(utility)
|
|
493
|
+
) {
|
|
494
|
+
return utility;
|
|
495
|
+
}
|
|
496
|
+
// The projection only asks whether a component-like class exists; a
|
|
497
|
+
// transition from `button-idle` to `button-active` does not change the
|
|
498
|
+
// layer type and should not trigger another full snapshot.
|
|
499
|
+
return /(?:component|card|button|control)/.test(utility)
|
|
500
|
+
? "component-like"
|
|
501
|
+
: "";
|
|
502
|
+
})
|
|
503
|
+
.filter(Boolean)
|
|
504
|
+
.sort()
|
|
505
|
+
.join(" ");
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function runtimeLayerStyleSignature(value: string | null): string {
|
|
509
|
+
var relevant: Record<string, string> = {};
|
|
510
|
+
String(value || "")
|
|
511
|
+
.split(";")
|
|
512
|
+
.forEach(function (declaration) {
|
|
513
|
+
var separator = declaration.indexOf(":");
|
|
514
|
+
if (separator < 0) return;
|
|
515
|
+
var property = declaration.slice(0, separator).trim().toLowerCase();
|
|
516
|
+
if (
|
|
517
|
+
!/^(?:display|flex-direction|align-items|justify-content)$/.test(
|
|
518
|
+
property,
|
|
519
|
+
)
|
|
520
|
+
) {
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
relevant[property] = declaration.slice(separator + 1).trim();
|
|
524
|
+
});
|
|
525
|
+
return Object.keys(relevant)
|
|
526
|
+
.sort()
|
|
527
|
+
.map(function (property) {
|
|
528
|
+
return property + ":" + relevant[property];
|
|
529
|
+
})
|
|
530
|
+
.join(";");
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function runtimeLayerMutationIsMeaningful(mutation: MutationRecord): boolean {
|
|
534
|
+
var target = mutation.target as Element;
|
|
535
|
+
if (
|
|
536
|
+
target.nodeType === 1 &&
|
|
537
|
+
(isOverlayElement(target) ||
|
|
538
|
+
target.closest?.("[data-agent-native-edit-overlay]"))
|
|
539
|
+
) {
|
|
540
|
+
return false;
|
|
541
|
+
}
|
|
542
|
+
if (mutation.type === "childList") {
|
|
543
|
+
var changedNodes = Array.prototype.slice
|
|
544
|
+
.call(mutation.addedNodes)
|
|
545
|
+
.concat(Array.prototype.slice.call(mutation.removedNodes));
|
|
546
|
+
return changedNodes.some(function (node: Node) {
|
|
547
|
+
var element =
|
|
548
|
+
node.nodeType === 1
|
|
549
|
+
? (node as Element)
|
|
550
|
+
: node.parentElement || mutation.target;
|
|
551
|
+
return !(
|
|
552
|
+
element.nodeType === 1 &&
|
|
553
|
+
(isOverlayElement(element as Element) ||
|
|
554
|
+
(element as Element).closest?.("[data-agent-native-edit-overlay]"))
|
|
555
|
+
);
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
if (mutation.type === "characterData") {
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
if (mutation.type !== "attributes") return false;
|
|
562
|
+
var name = mutation.attributeName || "";
|
|
563
|
+
if (name === "class") {
|
|
564
|
+
return (
|
|
565
|
+
runtimeLayerClassSignature(mutation.oldValue) !==
|
|
566
|
+
runtimeLayerClassSignature(target.getAttribute("class"))
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
if (name === "style") {
|
|
570
|
+
return (
|
|
571
|
+
runtimeLayerStyleSignature(mutation.oldValue) !==
|
|
572
|
+
runtimeLayerStyleSignature(target.getAttribute("style"))
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
return true;
|
|
576
|
+
}
|
|
577
|
+
|
|
214
578
|
function isDocumentRootElement(el: Element | null): boolean {
|
|
215
579
|
return el === document.body || el === document.documentElement;
|
|
216
580
|
}
|
|
@@ -806,10 +1170,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
806
1170
|
"Parent layout context decides whether movement means gap, order, alignment, or wrapper structure.",
|
|
807
1171
|
});
|
|
808
1172
|
}
|
|
809
|
-
// --- provenance: read source-location attributes when present
|
|
810
|
-
//
|
|
811
|
-
// Babel source plugin. Cross-origin localhost iframes cannot be read (CSP /
|
|
812
|
-
// same-origin policy), so this will be undefined in that case — expected.
|
|
1173
|
+
// --- provenance: read explicit source-location attributes when present,
|
|
1174
|
+
// then fall back to React's development-only jsxDEV Fiber stack. ---
|
|
813
1175
|
var provenance:
|
|
814
1176
|
| {
|
|
815
1177
|
sourceFile?: string;
|
|
@@ -844,6 +1206,18 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
844
1206
|
if (hasColumn) dataSourceColumn = lastPart;
|
|
845
1207
|
}
|
|
846
1208
|
}
|
|
1209
|
+
if (!dataSourceFile) {
|
|
1210
|
+
var reactProvenance = reactDebugProvenance(el);
|
|
1211
|
+
if (reactProvenance) {
|
|
1212
|
+
dataSourceFile = reactProvenance.sourceFile;
|
|
1213
|
+
dataSourceLine = String(reactProvenance.line);
|
|
1214
|
+
dataSourceColumn = reactProvenance.column
|
|
1215
|
+
? String(reactProvenance.column)
|
|
1216
|
+
: null;
|
|
1217
|
+
dataComponentName =
|
|
1218
|
+
dataComponentName || reactProvenance.component || null;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
847
1221
|
if (
|
|
848
1222
|
dataSourceFile ||
|
|
849
1223
|
dataSourceLine ||
|
|
@@ -884,11 +1258,18 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
884
1258
|
lineHeight: cs.lineHeight,
|
|
885
1259
|
letterSpacing: cs.letterSpacing,
|
|
886
1260
|
textAlign: cs.textAlign,
|
|
1261
|
+
// Clean longhand for decoration-toggle state (Cmd+U underline /
|
|
1262
|
+
// Cmd+Shift+X strikethrough). Deliberately the longhand, not the
|
|
1263
|
+
// `textDecoration` shorthand — see typography-helpers.ts's
|
|
1264
|
+
// PERSISTENCE GOTCHA comment: reads use this clean value, writes
|
|
1265
|
+
// still commit through the shorthand property name.
|
|
1266
|
+
textDecorationLine: cs.textDecorationLine,
|
|
887
1267
|
display: cs.display,
|
|
888
1268
|
overflow: cs.overflow,
|
|
889
1269
|
flexDirection: cs.flexDirection,
|
|
890
1270
|
justifyContent: cs.justifyContent,
|
|
891
1271
|
alignItems: cs.alignItems,
|
|
1272
|
+
justifyItems: cs.justifyItems,
|
|
892
1273
|
alignSelf: cs.alignSelf,
|
|
893
1274
|
flexGrow: cs.flexGrow,
|
|
894
1275
|
flexShrink: cs.flexShrink,
|
|
@@ -896,12 +1277,17 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
896
1277
|
order: cs.order,
|
|
897
1278
|
gridColumn: cs.gridColumn,
|
|
898
1279
|
gridRow: cs.gridRow,
|
|
1280
|
+
gridTemplateColumns: cs.gridTemplateColumns,
|
|
1281
|
+
gridTemplateRows: cs.gridTemplateRows,
|
|
1282
|
+
gridAutoFlow: cs.gridAutoFlow,
|
|
899
1283
|
position: cs.position,
|
|
900
1284
|
top: cs.top,
|
|
901
1285
|
right: cs.right,
|
|
902
1286
|
bottom: cs.bottom,
|
|
903
1287
|
left: cs.left,
|
|
904
1288
|
gap: cs.gap,
|
|
1289
|
+
rowGap: cs.rowGap,
|
|
1290
|
+
columnGap: cs.columnGap,
|
|
905
1291
|
width: cs.width,
|
|
906
1292
|
height: cs.height,
|
|
907
1293
|
opacity: cs.opacity,
|
|
@@ -961,6 +1347,9 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
961
1347
|
width: rect.width,
|
|
962
1348
|
height: rect.height,
|
|
963
1349
|
},
|
|
1350
|
+
parentBoundingRect: el.parentElement
|
|
1351
|
+
? rectInfoForElement(el.parentElement)
|
|
1352
|
+
: undefined,
|
|
964
1353
|
textContent: el.textContent ? el.textContent.slice(0, 200) : undefined,
|
|
965
1354
|
htmlContent:
|
|
966
1355
|
el.innerHTML && el.innerHTML !== el.textContent
|
|
@@ -1475,6 +1864,27 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1475
1864
|
|
|
1476
1865
|
var selectedEl: Element | null = null;
|
|
1477
1866
|
var hoveredEl: Element | null = null;
|
|
1867
|
+
// Last element an "element-hover" message was actually posted for. Lets
|
|
1868
|
+
// the shield's pointermove handler skip getLightElementInfo's two
|
|
1869
|
+
// getComputedStyle calls plus the postMessage on every one of the dozens
|
|
1870
|
+
// of raw pointermove ticks a slow hover over one unchanged element
|
|
1871
|
+
// produces — only the FIRST tick that lands on a given element needs to
|
|
1872
|
+
// tell the host anything new. The parent already de-dupes on its side
|
|
1873
|
+
// (see the "PF9" equality-bail comment in DesignEditor.tsx), so skipping
|
|
1874
|
+
// the redundant sends here is a pure perf win with no behavior change.
|
|
1875
|
+
var lastHoverInfoPostedEl: Element | null = null;
|
|
1876
|
+
|
|
1877
|
+
// Every path that clears (or invalidates) the current hover must re-arm the
|
|
1878
|
+
// post gate above through this helper, not just null out hoveredEl. If a
|
|
1879
|
+
// path nulls hoveredEl without resetting lastHoverInfoPostedEl, the pointer
|
|
1880
|
+
// returning to the SAME element the gate already posted for will silently
|
|
1881
|
+
// skip re-posting "element-hover" to the host, leaving its hover state
|
|
1882
|
+
// stuck until a different element is hovered.
|
|
1883
|
+
function clearHoverGate(): void {
|
|
1884
|
+
hoveredEl = null;
|
|
1885
|
+
lastHoverInfoPostedEl = null;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1478
1888
|
var passiveSelectionEls: Element[] = [];
|
|
1479
1889
|
var passiveSelectionOverlays: HTMLElement[] = [];
|
|
1480
1890
|
var activeMarqueeSelection: {
|
|
@@ -1648,6 +2058,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1648
2058
|
var spacingHatchNodesByKey: Record<string, Element> = {};
|
|
1649
2059
|
var spacingOverlayRenderKey = "";
|
|
1650
2060
|
var activeDragCancel: (() => boolean) | null = null;
|
|
2061
|
+
var bridgeSpaceKeyPressed = false;
|
|
2062
|
+
var bridgeSpaceKeyConsumedByDrag = false;
|
|
1651
2063
|
var activeCrossScreenStyleSnapshot: unknown | undefined = undefined;
|
|
1652
2064
|
var spacingDrag: {
|
|
1653
2065
|
key: string;
|
|
@@ -1668,7 +2080,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1668
2080
|
|
|
1669
2081
|
function clearRuntimeSelection(): void {
|
|
1670
2082
|
selectedEl = null;
|
|
1671
|
-
|
|
2083
|
+
clearHoverGate();
|
|
1672
2084
|
setPassiveSelectionElements([]);
|
|
1673
2085
|
clearSpacingHoverTimer();
|
|
1674
2086
|
selectedSpacingHovered = false;
|
|
@@ -2015,7 +2427,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2015
2427
|
selectedEl = document.querySelector(matchedSelector);
|
|
2016
2428
|
} catch (_err) {}
|
|
2017
2429
|
}
|
|
2018
|
-
|
|
2430
|
+
clearHoverGate();
|
|
2019
2431
|
if (selectedEl && !isLayerInteractionBlocked(selectedEl)) {
|
|
2020
2432
|
positionOverlay(selectionOverlay, selectedEl);
|
|
2021
2433
|
postElementSelect(selectedEl);
|
|
@@ -2049,7 +2461,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2049
2461
|
applyHiddenSelectors();
|
|
2050
2462
|
|
|
2051
2463
|
selectedEl = null;
|
|
2052
|
-
|
|
2464
|
+
clearHoverGate();
|
|
2053
2465
|
for (var i = 0; i < activeCandidates.length && !selectedEl; i += 1) {
|
|
2054
2466
|
try {
|
|
2055
2467
|
var match = document.querySelector(activeCandidates[i]);
|
|
@@ -3372,6 +3784,86 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
3372
3784
|
}
|
|
3373
3785
|
}
|
|
3374
3786
|
|
|
3787
|
+
// Transition/animation overlay tracking: the ResizeObserver above only
|
|
3788
|
+
// fires on border-box SIZE changes, so a purely transform- or left/top-
|
|
3789
|
+
// driven CSS transition/animation on the selected or hovered element (very
|
|
3790
|
+
// common for hover states, toggles, carousels in AI-generated prototypes)
|
|
3791
|
+
// never triggers it. Without this, the one-shot MutationObserver callback
|
|
3792
|
+
// that fires when the triggering class/style attribute changes reads
|
|
3793
|
+
// getBoundingClientRect() at essentially the START of the transition, and
|
|
3794
|
+
// the overlay then freezes there while the real element visually slides/
|
|
3795
|
+
// fades to its final position — the selection outline and its handles
|
|
3796
|
+
// visibly detach from the animating element for the transition's whole
|
|
3797
|
+
// duration. Fixed by running a bounded rAF refresh loop for the duration of
|
|
3798
|
+
// any transition/animation that starts on a tracked element, so the
|
|
3799
|
+
// overlay follows every intermediate frame instead of only the first and
|
|
3800
|
+
// (via the next unrelated mutation/resize/scroll) last.
|
|
3801
|
+
var overlayAnimationTrackingActive = false;
|
|
3802
|
+
var overlayAnimationTrackingUntil = 0;
|
|
3803
|
+
var overlayAnimationTrackingStartedAt = 0;
|
|
3804
|
+
// Covers the vast majority of real UI transitions/animations (hover/toggle
|
|
3805
|
+
// durations are almost always well under a second); the max below is a
|
|
3806
|
+
// safety net for a transition whose end event never fires (e.g. cancelled
|
|
3807
|
+
// by a later style write with no transitionend), so a slow/held animation
|
|
3808
|
+
// can't pin this loop on forever.
|
|
3809
|
+
var OVERLAY_ANIMATION_TRACKING_WINDOW_MS = 1000;
|
|
3810
|
+
var OVERLAY_ANIMATION_TRACKING_MAX_MS = 4000;
|
|
3811
|
+
|
|
3812
|
+
function isOverlayAnimationTrackingTarget(
|
|
3813
|
+
target: EventTarget | null,
|
|
3814
|
+
): boolean {
|
|
3815
|
+
if (!target) return false;
|
|
3816
|
+
return target === selectedEl || target === hoveredEl;
|
|
3817
|
+
}
|
|
3818
|
+
|
|
3819
|
+
function tickOverlayAnimationTracking(): void {
|
|
3820
|
+
if (!overlayAnimationTrackingActive) return;
|
|
3821
|
+
refreshOverlays();
|
|
3822
|
+
var now = Date.now();
|
|
3823
|
+
if (
|
|
3824
|
+
now >= overlayAnimationTrackingUntil ||
|
|
3825
|
+
now - overlayAnimationTrackingStartedAt >=
|
|
3826
|
+
OVERLAY_ANIMATION_TRACKING_MAX_MS
|
|
3827
|
+
) {
|
|
3828
|
+
overlayAnimationTrackingActive = false;
|
|
3829
|
+
return;
|
|
3830
|
+
}
|
|
3831
|
+
window.requestAnimationFrame(tickOverlayAnimationTracking);
|
|
3832
|
+
}
|
|
3833
|
+
|
|
3834
|
+
// Re-armed (window extended) by every qualifying transitionrun/
|
|
3835
|
+
// animationstart, so a sequence of staggered transitions on the same
|
|
3836
|
+
// element keeps the loop running for their combined duration rather than
|
|
3837
|
+
// stopping partway through.
|
|
3838
|
+
function startOverlayAnimationTracking(): void {
|
|
3839
|
+
var now = Date.now();
|
|
3840
|
+
overlayAnimationTrackingUntil = now + OVERLAY_ANIMATION_TRACKING_WINDOW_MS;
|
|
3841
|
+
if (overlayAnimationTrackingActive) return;
|
|
3842
|
+
overlayAnimationTrackingActive = true;
|
|
3843
|
+
overlayAnimationTrackingStartedAt = now;
|
|
3844
|
+
window.requestAnimationFrame(tickOverlayAnimationTracking);
|
|
3845
|
+
}
|
|
3846
|
+
|
|
3847
|
+
function onOverlayAnimationTrackingEvent(e: Event): void {
|
|
3848
|
+
if (isOverlayAnimationTrackingTarget(e.target as EventTarget | null)) {
|
|
3849
|
+
startOverlayAnimationTracking();
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
// Capture-phase, delegated on document (not per-element add/remove-
|
|
3853
|
+
// listener bookkeeping tied to selection changes): transitionrun/
|
|
3854
|
+
// animationstart bubble, so one pair of listeners registered once at
|
|
3855
|
+
// bridge init covers whichever element is currently selected/hovered.
|
|
3856
|
+
document.addEventListener(
|
|
3857
|
+
"transitionrun",
|
|
3858
|
+
onOverlayAnimationTrackingEvent,
|
|
3859
|
+
true,
|
|
3860
|
+
);
|
|
3861
|
+
document.addEventListener(
|
|
3862
|
+
"animationstart",
|
|
3863
|
+
onOverlayAnimationTrackingEvent,
|
|
3864
|
+
true,
|
|
3865
|
+
);
|
|
3866
|
+
|
|
3375
3867
|
function hideMeasurements(): void {
|
|
3376
3868
|
measurementOverlay.style.display = "none";
|
|
3377
3869
|
measurementOverlay.innerHTML = "";
|
|
@@ -3717,7 +4209,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
3717
4209
|
// not intercept Tab when the user is tabbing through browser UI with nothing
|
|
3718
4210
|
// selected (preserves native keyboard accessibility).
|
|
3719
4211
|
if (key === "Tab") return !!selectedEl;
|
|
3720
|
-
if (key === "Delete" || key === "Backspace")
|
|
4212
|
+
if (key === "Delete" || key === "Backspace") {
|
|
4213
|
+
// Cmd/Ctrl+Backspace is Figma's "ungroup" chord (see onUngroup in
|
|
4214
|
+
// useDesignHotkeys.ts) — carve out that one primary-modifier exception
|
|
4215
|
+
// so it isn't swallowed by the blanket "no modifier" rule below. Any
|
|
4216
|
+
// other primary+Delete/Backspace combo has no host binding, so it stays
|
|
4217
|
+
// local (matches prior behavior).
|
|
4218
|
+
if (primary) return key === "Backspace" && !e.altKey && !e.shiftKey;
|
|
4219
|
+
return true;
|
|
4220
|
+
}
|
|
3721
4221
|
if (/^Arrow/.test(key || "")) return !e.altKey;
|
|
3722
4222
|
if (primary) {
|
|
3723
4223
|
return (
|
|
@@ -3736,11 +4236,44 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
3736
4236
|
"0",
|
|
3737
4237
|
"]",
|
|
3738
4238
|
"[",
|
|
4239
|
+
// Cmd/Ctrl+U — toggle underline (useDesignHotkeys.ts onToggleUnderline).
|
|
4240
|
+
"u",
|
|
4241
|
+
// Cmd/Ctrl+F — find (onFind). Bridge's "primary" doesn't distinguish
|
|
4242
|
+
// Cmd from Ctrl the way isPlatformPrimaryModifier does host-side, but
|
|
4243
|
+
// forwarding is harmless when the host has no match for the combo.
|
|
4244
|
+
"f",
|
|
4245
|
+
// Cmd/Ctrl+R rename / Cmd/Ctrl+Shift+R paste-to-replace (onRename /
|
|
4246
|
+
// onPasteToReplace) — both live under bare primary+r.
|
|
4247
|
+
"r",
|
|
4248
|
+
// Cmd/Ctrl+\ — toggle UI (onToggleUi).
|
|
4249
|
+
"\\",
|
|
3739
4250
|
].indexOf(normalized) !== -1 ||
|
|
3740
4251
|
e.code === "Digit1" ||
|
|
3741
4252
|
e.code === "Digit2" ||
|
|
3742
4253
|
key === "1" ||
|
|
3743
|
-
key === "2"
|
|
4254
|
+
key === "2" ||
|
|
4255
|
+
// Cmd/Ctrl+Shift+H / +L — toggle hidden / toggle locked
|
|
4256
|
+
// (onToggleHidden / onToggleLocked). Gated on shiftKey so bare
|
|
4257
|
+
// Cmd+H / Cmd+L — common OS "Hide app" / browser "focus address bar"
|
|
4258
|
+
// shortcuts the host has no bare-primary binding for — are left
|
|
4259
|
+
// alone (see useDesignHotkeys.ts: both require event.shiftKey).
|
|
4260
|
+
(e.shiftKey && (normalized === "h" || normalized === "l")) ||
|
|
4261
|
+
// Cmd/Ctrl+Alt+B detach instance / Cmd/Ctrl+Alt+K create component
|
|
4262
|
+
// (onDetachInstance / onCreateComponent). Gated on altKey so bare
|
|
4263
|
+
// Cmd+B / Cmd+K are left alone — the host has no bare-primary
|
|
4264
|
+
// binding for either.
|
|
4265
|
+
(e.altKey && (normalized === "b" || normalized === "k")) ||
|
|
4266
|
+
// Ctrl+Alt+H / Ctrl+Alt+T — distribute horizontal / tidy up
|
|
4267
|
+
// (onDistributeSelection / onTidyUp). useDesignHotkeys.ts keeps these
|
|
4268
|
+
// on LITERAL Control on every platform (never remapped to Cmd), so
|
|
4269
|
+
// this mirrors that exact gate instead of the generic "primary" flag
|
|
4270
|
+
// — a blanket "t" entry above would otherwise swallow the common
|
|
4271
|
+
// Cmd+T "new tab" browser shortcut for a combo the host never binds.
|
|
4272
|
+
(e.ctrlKey &&
|
|
4273
|
+
e.altKey &&
|
|
4274
|
+
!e.metaKey &&
|
|
4275
|
+
!e.shiftKey &&
|
|
4276
|
+
(normalized === "h" || normalized === "t"))
|
|
3744
4277
|
);
|
|
3745
4278
|
}
|
|
3746
4279
|
if (
|
|
@@ -4164,8 +4697,64 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4164
4697
|
function openContextMenuAtEvent(e) {
|
|
4165
4698
|
stopNativeInteraction(e);
|
|
4166
4699
|
blurActiveTextEditor();
|
|
4167
|
-
var
|
|
4168
|
-
|
|
4700
|
+
var shieldPointerEvents = shieldOverlay.style.pointerEvents;
|
|
4701
|
+
var selectionPointerEvents = selectionOverlay.style.pointerEvents;
|
|
4702
|
+
var highlightPointerEvents = highlightOverlay.style.pointerEvents;
|
|
4703
|
+
shieldOverlay.style.pointerEvents = "none";
|
|
4704
|
+
selectionOverlay.style.pointerEvents = "none";
|
|
4705
|
+
highlightOverlay.style.pointerEvents = "none";
|
|
4706
|
+
var pointTargets = document.elementsFromPoint
|
|
4707
|
+
? document.elementsFromPoint(e.clientX, e.clientY)
|
|
4708
|
+
: [document.elementFromPoint(e.clientX, e.clientY)];
|
|
4709
|
+
shieldOverlay.style.pointerEvents = shieldPointerEvents;
|
|
4710
|
+
selectionOverlay.style.pointerEvents = selectionPointerEvents;
|
|
4711
|
+
highlightOverlay.style.pointerEvents = highlightPointerEvents;
|
|
4712
|
+
|
|
4713
|
+
var candidateElements: Element[] = [];
|
|
4714
|
+
var layerCandidates: Array<{
|
|
4715
|
+
key: string;
|
|
4716
|
+
label: string;
|
|
4717
|
+
info: unknown;
|
|
4718
|
+
}> = [];
|
|
4719
|
+
pointTargets.forEach(function (pointTarget) {
|
|
4720
|
+
if (!pointTarget || pointTarget.nodeType !== 1) return;
|
|
4721
|
+
if (isOverlayElement(pointTarget)) return;
|
|
4722
|
+
var candidate = selectionTargetForHit(pointTarget);
|
|
4723
|
+
if (
|
|
4724
|
+
!candidate ||
|
|
4725
|
+
isDocumentRootElement(candidate) ||
|
|
4726
|
+
isOverlayElement(candidate) ||
|
|
4727
|
+
isLayerInteractionBlocked(candidate) ||
|
|
4728
|
+
isTemplateCloneElement(candidate) ||
|
|
4729
|
+
candidateElements.indexOf(candidate) !== -1
|
|
4730
|
+
) {
|
|
4731
|
+
return;
|
|
4732
|
+
}
|
|
4733
|
+
candidateElements.push(candidate);
|
|
4734
|
+
var candidateInfo = getElementInfo(candidate);
|
|
4735
|
+
var explicitLabel =
|
|
4736
|
+
(candidate.getAttribute &&
|
|
4737
|
+
candidate.getAttribute("data-agent-native-layer-name")) ||
|
|
4738
|
+
"";
|
|
4739
|
+
var textLabel = (candidate.textContent || "").trim().replace(/\s+/g, " ");
|
|
4740
|
+
var label =
|
|
4741
|
+
explicitLabel ||
|
|
4742
|
+
candidateInfo.componentName ||
|
|
4743
|
+
candidate.id ||
|
|
4744
|
+
(textLabel && textLabel.length <= 48 ? textLabel : "") ||
|
|
4745
|
+
candidate.tagName.toLowerCase();
|
|
4746
|
+
var identity =
|
|
4747
|
+
candidateInfo.sourceId ||
|
|
4748
|
+
candidateInfo.selector ||
|
|
4749
|
+
String(layerCandidates.length);
|
|
4750
|
+
layerCandidates.push({
|
|
4751
|
+
key: String(identity) + ":" + String(layerCandidates.length),
|
|
4752
|
+
label: String(label).slice(0, 80),
|
|
4753
|
+
info: candidateInfo,
|
|
4754
|
+
});
|
|
4755
|
+
});
|
|
4756
|
+
|
|
4757
|
+
var target = candidateElements[0] || null;
|
|
4169
4758
|
var info = null;
|
|
4170
4759
|
if (target) {
|
|
4171
4760
|
selectedSpacingHovered = false;
|
|
@@ -4174,7 +4763,6 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4174
4763
|
if (selectedEl && !isLayerInteractionBlocked(selectedEl)) {
|
|
4175
4764
|
info = getElementInfo(selectedEl);
|
|
4176
4765
|
positionOverlay(selectionOverlay, selectedEl);
|
|
4177
|
-
postElementSelect(selectedEl, e);
|
|
4178
4766
|
} else {
|
|
4179
4767
|
selectedEl = null;
|
|
4180
4768
|
hideSelectionOverlay();
|
|
@@ -4183,9 +4771,11 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4183
4771
|
(window.parent as Window).postMessage(
|
|
4184
4772
|
{
|
|
4185
4773
|
type: "element-contextmenu",
|
|
4774
|
+
screenId: designCanvasScreenId,
|
|
4186
4775
|
clientX: e.clientX,
|
|
4187
4776
|
clientY: e.clientY,
|
|
4188
4777
|
payload: info,
|
|
4778
|
+
layerCandidates: layerCandidates,
|
|
4189
4779
|
},
|
|
4190
4780
|
"*",
|
|
4191
4781
|
);
|
|
@@ -4223,6 +4813,61 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4223
4813
|
return null;
|
|
4224
4814
|
}
|
|
4225
4815
|
|
|
4816
|
+
// Host-driven Layers-panel moves must never inherit findRuntimeTarget's
|
|
4817
|
+
// selected-element shortcut or first-match querySelector behavior. Runtime
|
|
4818
|
+
// React trees routinely repeat classes and component markup, so a selector
|
|
4819
|
+
// is only safe when it identifies exactly one live element. Prefer the
|
|
4820
|
+
// runtime projection's stable source id and fall back to the selector only
|
|
4821
|
+
// when that id has no match at all.
|
|
4822
|
+
function findUniqueRuntimeStructureTarget(selector, sourceId) {
|
|
4823
|
+
var matches = new Set<Element>();
|
|
4824
|
+
if (typeof sourceId === "string" && sourceId) {
|
|
4825
|
+
var attributes = [
|
|
4826
|
+
"data-agent-native-node-id",
|
|
4827
|
+
"data-code-layer-id",
|
|
4828
|
+
"data-layer-id",
|
|
4829
|
+
"data-builder-id",
|
|
4830
|
+
"data-loc",
|
|
4831
|
+
"id",
|
|
4832
|
+
];
|
|
4833
|
+
for (var i = 0; i < attributes.length; i += 1) {
|
|
4834
|
+
try {
|
|
4835
|
+
var sourceMatches = document.querySelectorAll(
|
|
4836
|
+
"[" + attributes[i] + '=\"' + escapeAttribute(sourceId) + '\"]',
|
|
4837
|
+
);
|
|
4838
|
+
for (var j = 0; j < sourceMatches.length; j += 1) {
|
|
4839
|
+
matches.add(sourceMatches[j]);
|
|
4840
|
+
}
|
|
4841
|
+
} catch (_err) {}
|
|
4842
|
+
}
|
|
4843
|
+
if (matches.size > 1) return null;
|
|
4844
|
+
if (matches.size === 1) {
|
|
4845
|
+
var sourceMatch = Array.from(matches)[0];
|
|
4846
|
+
return sourceMatch &&
|
|
4847
|
+
sourceMatch !== document.body &&
|
|
4848
|
+
sourceMatch !== document.documentElement &&
|
|
4849
|
+
!isOverlayElement(sourceMatch) &&
|
|
4850
|
+
!isLayerInteractionBlocked(sourceMatch)
|
|
4851
|
+
? sourceMatch
|
|
4852
|
+
: null;
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
if (typeof selector !== "string" || !selector) return null;
|
|
4856
|
+
try {
|
|
4857
|
+
var selectorMatches = document.querySelectorAll(selector);
|
|
4858
|
+
if (selectorMatches.length !== 1) return null;
|
|
4859
|
+
var selectorMatch = selectorMatches[0];
|
|
4860
|
+
return selectorMatch !== document.body &&
|
|
4861
|
+
selectorMatch !== document.documentElement &&
|
|
4862
|
+
!isOverlayElement(selectorMatch) &&
|
|
4863
|
+
!isLayerInteractionBlocked(selectorMatch)
|
|
4864
|
+
? selectorMatch
|
|
4865
|
+
: null;
|
|
4866
|
+
} catch (_err) {
|
|
4867
|
+
return null;
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
|
|
4226
4871
|
function removeRuntimeTarget(selector, selectorCandidates) {
|
|
4227
4872
|
var target = findRuntimeTarget(selector, selectorCandidates);
|
|
4228
4873
|
if (
|
|
@@ -4243,7 +4888,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4243
4888
|
selectedEl = null;
|
|
4244
4889
|
hideSelectionOverlay();
|
|
4245
4890
|
}
|
|
4246
|
-
|
|
4891
|
+
clearHoverGate();
|
|
4247
4892
|
highlightOverlay.style.display = "none";
|
|
4248
4893
|
hideMeasurements();
|
|
4249
4894
|
refreshOverlays();
|
|
@@ -5368,7 +6013,12 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5368
6013
|
el.getAttribute("data-agent-native-primitive") ||
|
|
5369
6014
|
""
|
|
5370
6015
|
).toLowerCase();
|
|
5371
|
-
if (
|
|
6016
|
+
if (
|
|
6017
|
+
primitive !== "rectangle" &&
|
|
6018
|
+
primitive !== "rect" &&
|
|
6019
|
+
primitive !== "frame"
|
|
6020
|
+
)
|
|
6021
|
+
return false;
|
|
5372
6022
|
var cs = window.getComputedStyle(el);
|
|
5373
6023
|
return cs.position === "absolute" || cs.position === "fixed";
|
|
5374
6024
|
}
|
|
@@ -6020,6 +6670,125 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6020
6670
|
};
|
|
6021
6671
|
}
|
|
6022
6672
|
|
|
6673
|
+
/** Resolve a flow child's full Figma-style move target. The legacy reorder
|
|
6674
|
+
* resolver always fell back to the current parent/root, so a child could be
|
|
6675
|
+
* reordered but never dragged out of auto layout onto the freeform screen.
|
|
6676
|
+
* Keep ordinary flow insertion intact, but turn a root/background release
|
|
6677
|
+
* into an absolute-container move. Space preserves the current parent;
|
|
6678
|
+
* Control drops into an auto-layout parent as Ignore auto layout. */
|
|
6679
|
+
function flowMoveTargetForPoint(
|
|
6680
|
+
el,
|
|
6681
|
+
clientX,
|
|
6682
|
+
clientY,
|
|
6683
|
+
excludeEls,
|
|
6684
|
+
keepCurrentParent,
|
|
6685
|
+
ignoreTargetAutoLayout,
|
|
6686
|
+
) {
|
|
6687
|
+
if (!el || !el.parentElement) return null;
|
|
6688
|
+
var currentParent = el.parentElement;
|
|
6689
|
+
var dragged: Element[] = [el].concat(excludeEls || []);
|
|
6690
|
+
var parentRect = currentParent.getBoundingClientRect();
|
|
6691
|
+
var pointerOutsideCurrentParent =
|
|
6692
|
+
clientX < parentRect.left ||
|
|
6693
|
+
clientX > parentRect.right ||
|
|
6694
|
+
clientY < parentRect.top ||
|
|
6695
|
+
clientY > parentRect.bottom;
|
|
6696
|
+
|
|
6697
|
+
if (keepCurrentParent && pointerOutsideCurrentParent) {
|
|
6698
|
+
var retainedSlot = nearestChildInsertionTarget(
|
|
6699
|
+
currentParent,
|
|
6700
|
+
clientX,
|
|
6701
|
+
clientY,
|
|
6702
|
+
dragged,
|
|
6703
|
+
);
|
|
6704
|
+
return (
|
|
6705
|
+
retainedSlot || {
|
|
6706
|
+
anchor: currentParent,
|
|
6707
|
+
placement: "inside",
|
|
6708
|
+
axis: parentFlowAxis(currentParent),
|
|
6709
|
+
dropMode: "flow-insert",
|
|
6710
|
+
}
|
|
6711
|
+
);
|
|
6712
|
+
}
|
|
6713
|
+
|
|
6714
|
+
var target = reorderTargetForPoint(el, clientX, clientY, excludeEls);
|
|
6715
|
+
var container = dropContainerForTarget(target);
|
|
6716
|
+
|
|
6717
|
+
// Figma Ignore auto layout: Control-drag into an auto-layout frame keeps
|
|
6718
|
+
// the new parent but excludes the object from its flow.
|
|
6719
|
+
if (
|
|
6720
|
+
ignoreTargetAutoLayout &&
|
|
6721
|
+
container &&
|
|
6722
|
+
container !== document.body &&
|
|
6723
|
+
isAutoLayoutElement(container)
|
|
6724
|
+
) {
|
|
6725
|
+
return {
|
|
6726
|
+
anchor: container,
|
|
6727
|
+
placement: "inside",
|
|
6728
|
+
axis: parentFlowAxis(container),
|
|
6729
|
+
dropMode: "absolute-container",
|
|
6730
|
+
};
|
|
6731
|
+
}
|
|
6732
|
+
|
|
6733
|
+
// A target whose resolved container is body is the freeform screen/root,
|
|
6734
|
+
// not an auto-layout list. Reparent inside body and preserve the release
|
|
6735
|
+
// point with absolute positioning instead of inserting before/after a
|
|
6736
|
+
// top-level frame as another flow child.
|
|
6737
|
+
if (
|
|
6738
|
+
currentParent !== document.body &&
|
|
6739
|
+
(container === document.body ||
|
|
6740
|
+
container === document.documentElement ||
|
|
6741
|
+
target?.anchor === document.body)
|
|
6742
|
+
) {
|
|
6743
|
+
return {
|
|
6744
|
+
anchor: document.body,
|
|
6745
|
+
placement: "inside",
|
|
6746
|
+
axis: "y",
|
|
6747
|
+
dropMode: "absolute-container",
|
|
6748
|
+
};
|
|
6749
|
+
}
|
|
6750
|
+
|
|
6751
|
+
// Flow child dropped into a plain container: match the absolute-drag path
|
|
6752
|
+
// by converting that container to auto layout before the structural move.
|
|
6753
|
+
if (
|
|
6754
|
+
target &&
|
|
6755
|
+
target.dropMode === "flow-insert" &&
|
|
6756
|
+
container &&
|
|
6757
|
+
container !== document.body &&
|
|
6758
|
+
isContainerDropTarget(container) &&
|
|
6759
|
+
!isAutoLayoutElement(container)
|
|
6760
|
+
) {
|
|
6761
|
+
target.needsAutoLayoutConversion = true;
|
|
6762
|
+
target.conversionTarget = container;
|
|
6763
|
+
}
|
|
6764
|
+
return target;
|
|
6765
|
+
}
|
|
6766
|
+
|
|
6767
|
+
/** Apply Figma's Control-drag "Ignore auto layout" modifier to an
|
|
6768
|
+
* absolute/freeform drag target. The flow-origin path above already made
|
|
6769
|
+
* this conversion, but the ordinary absolute drag path used to ignore the
|
|
6770
|
+
* modifier and strip position/left/top on drop. Resolve to the auto-layout
|
|
6771
|
+
* container itself so the object keeps absolute positioning inside its new
|
|
6772
|
+
* parent, regardless of whether the pointer is over a child insertion slot
|
|
6773
|
+
* or the container background. */
|
|
6774
|
+
function ignoreAutoLayoutForDropTarget(target) {
|
|
6775
|
+
var container = dropContainerForTarget(target);
|
|
6776
|
+
if (
|
|
6777
|
+
!target ||
|
|
6778
|
+
!container ||
|
|
6779
|
+
container === document.body ||
|
|
6780
|
+
!isAutoLayoutElement(container)
|
|
6781
|
+
) {
|
|
6782
|
+
return target;
|
|
6783
|
+
}
|
|
6784
|
+
return {
|
|
6785
|
+
anchor: container,
|
|
6786
|
+
placement: "inside",
|
|
6787
|
+
axis: parentFlowAxis(container),
|
|
6788
|
+
dropMode: "absolute-container",
|
|
6789
|
+
};
|
|
6790
|
+
}
|
|
6791
|
+
|
|
6023
6792
|
// Accepts a single element or an array (group drags temporarily disable
|
|
6024
6793
|
// pointer-events on EVERY dragged member so the hit test sees what's
|
|
6025
6794
|
// underneath the whole group, not a sibling member riding along under the
|
|
@@ -6374,6 +7143,24 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6374
7143
|
for (var i = 0; i < ABS_POSITION_INLINE_PROPS.length; i += 1) {
|
|
6375
7144
|
htmlEl.style.removeProperty(ABS_POSITION_INLINE_PROPS[i]);
|
|
6376
7145
|
}
|
|
7146
|
+
// Utility classes and authored stylesheets can still resolve the member
|
|
7147
|
+
// to absolute/fixed after the inline properties are removed. The host
|
|
7148
|
+
// strips those source declarations in the same structural history step,
|
|
7149
|
+
// but waiting for its in-place document round-trip leaves one rendered
|
|
7150
|
+
// frame where the optimistically reparented node is still absolute and
|
|
7151
|
+
// appears to jump or overlap. Override only that residual computed state
|
|
7152
|
+
// until the source replacement arrives. Inline-authored absolute nodes
|
|
7153
|
+
// naturally compute as static after the removal and keep the historical
|
|
7154
|
+
// empty-inline-style behavior.
|
|
7155
|
+
var afterRemoval = window.getComputedStyle(htmlEl).position;
|
|
7156
|
+
if (afterRemoval === "absolute" || afterRemoval === "fixed") {
|
|
7157
|
+
// An authored stylesheet may carry !important, so the optimistic
|
|
7158
|
+
// override must match that priority. Tell the host to persist the same
|
|
7159
|
+
// narrow override; otherwise its source round-trip would re-apply the
|
|
7160
|
+
// stylesheet and pop the newly-flowed child back out of auto layout.
|
|
7161
|
+
htmlEl.style.setProperty("position", "static", "important");
|
|
7162
|
+
target.forceFlowPositionOverride = true;
|
|
7163
|
+
}
|
|
6377
7164
|
}
|
|
6378
7165
|
|
|
6379
7166
|
// Absolute-container nest rebase: an "absolute-container" drop keeps the
|
|
@@ -6395,6 +7182,11 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6395
7182
|
// (transform-inflated) bounding box.
|
|
6396
7183
|
function rebaseAbsoluteMemberForContainerDrop(el, target): void {
|
|
6397
7184
|
if (!el || !target || target.dropMode !== "absolute-container") return;
|
|
7185
|
+
// Flow children are prepared directly in the destination containing-block
|
|
7186
|
+
// coordinate space immediately before their DOM move. Rebasing those
|
|
7187
|
+
// coordinates as if they came from an old absolute containing block would
|
|
7188
|
+
// apply the parent-origin delta twice.
|
|
7189
|
+
if (target.absoluteCoordinatesPrepared) return;
|
|
6398
7190
|
var container = dropContainerForTarget(target);
|
|
6399
7191
|
if (!container || container === document.body || container === el) return;
|
|
6400
7192
|
if (el.contains && el.contains(container)) return;
|
|
@@ -6405,12 +7197,20 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6405
7197
|
// coordinates (border box + border widths − its own scroll offsets).
|
|
6406
7198
|
var containerRect = container.getBoundingClientRect();
|
|
6407
7199
|
var containerCS = window.getComputedStyle(container);
|
|
7200
|
+
var boardOffsetX = designCanvasBoardSurface
|
|
7201
|
+
? designCanvasContentOffsetX
|
|
7202
|
+
: 0;
|
|
7203
|
+
var boardOffsetY = designCanvasBoardSurface
|
|
7204
|
+
? designCanvasContentOffsetY
|
|
7205
|
+
: 0;
|
|
6408
7206
|
var newOriginX =
|
|
6409
|
-
containerRect.left
|
|
7207
|
+
containerRect.left -
|
|
7208
|
+
boardOffsetX +
|
|
6410
7209
|
readPx(containerCS.borderLeftWidth) -
|
|
6411
7210
|
container.scrollLeft;
|
|
6412
7211
|
var newOriginY =
|
|
6413
|
-
containerRect.top
|
|
7212
|
+
containerRect.top -
|
|
7213
|
+
boardOffsetY +
|
|
6414
7214
|
readPx(containerCS.borderTopWidth) -
|
|
6415
7215
|
container.scrollTop;
|
|
6416
7216
|
// Current containing block origin: the member's offsetParent when it is
|
|
@@ -6432,10 +7232,30 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6432
7232
|
if (offsetParentIsRealContainingBlock) {
|
|
6433
7233
|
var opRect = offsetParent.getBoundingClientRect();
|
|
6434
7234
|
var opCS = window.getComputedStyle(offsetParent);
|
|
7235
|
+
// The finite board offset is applied to `body > [data-node-id]`, not
|
|
7236
|
+
// to body itself. A top-level member whose containing block is the
|
|
7237
|
+
// positioned body therefore still has a true origin of 0; subtracting
|
|
7238
|
+
// the render offset here poisoned a frame nest by exactly one chunk
|
|
7239
|
+
// (for example -4096px). Nested containing blocks do inherit the
|
|
7240
|
+
// translated top-level visual space, so only those need normalization.
|
|
7241
|
+
var oldContainingBlockOffsetX =
|
|
7242
|
+
designCanvasBoardSurface && offsetParent !== document.body
|
|
7243
|
+
? boardOffsetX
|
|
7244
|
+
: 0;
|
|
7245
|
+
var oldContainingBlockOffsetY =
|
|
7246
|
+
designCanvasBoardSurface && offsetParent !== document.body
|
|
7247
|
+
? boardOffsetY
|
|
7248
|
+
: 0;
|
|
6435
7249
|
oldOriginX =
|
|
6436
|
-
opRect.left
|
|
7250
|
+
opRect.left -
|
|
7251
|
+
oldContainingBlockOffsetX +
|
|
7252
|
+
readPx(opCS.borderLeftWidth) -
|
|
7253
|
+
offsetParent.scrollLeft;
|
|
6437
7254
|
oldOriginY =
|
|
6438
|
-
opRect.top
|
|
7255
|
+
opRect.top -
|
|
7256
|
+
oldContainingBlockOffsetY +
|
|
7257
|
+
readPx(opCS.borderTopWidth) -
|
|
7258
|
+
offsetParent.scrollTop;
|
|
6439
7259
|
}
|
|
6440
7260
|
}
|
|
6441
7261
|
var currentLeft = readPx(htmlEl.style.left || cs.left);
|
|
@@ -6444,6 +7264,59 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6444
7264
|
htmlEl.style.top = currentTop + (oldOriginY - newOriginY) + "px";
|
|
6445
7265
|
}
|
|
6446
7266
|
|
|
7267
|
+
/** Convert flow members to absolute positioning at their drag release point
|
|
7268
|
+
* before moving them into a freeform root/absolute container. The DOM move
|
|
7269
|
+
* happens synchronously in the same event turn, so no intermediate frame is
|
|
7270
|
+
* painted; postVisualStructureChange then measures the final geometry for
|
|
7271
|
+
* the source persistence/undo entry. */
|
|
7272
|
+
function prepareFlowMembersForAbsoluteDrop(
|
|
7273
|
+
members: Element[],
|
|
7274
|
+
target,
|
|
7275
|
+
startRects: DOMRect[],
|
|
7276
|
+
gestureStartRect: DOMRect,
|
|
7277
|
+
pointerOffset: { x: number; y: number },
|
|
7278
|
+
clientX: number,
|
|
7279
|
+
clientY: number,
|
|
7280
|
+
): void {
|
|
7281
|
+
if (!target || target.dropMode !== "absolute-container") return;
|
|
7282
|
+
var container = dropContainerForTarget(target);
|
|
7283
|
+
if (!container) return;
|
|
7284
|
+
var containerRect = container.getBoundingClientRect();
|
|
7285
|
+
var containerCS = window.getComputedStyle(container);
|
|
7286
|
+
var containerIsBodyContainingBlock = true;
|
|
7287
|
+
if (container === document.body) {
|
|
7288
|
+
containerIsBodyContainingBlock =
|
|
7289
|
+
containerCS.position !== "static" ||
|
|
7290
|
+
containerCS.transform !== "none" ||
|
|
7291
|
+
(containerCS.getPropertyValue("translate") || "none") !== "none";
|
|
7292
|
+
}
|
|
7293
|
+
var originX = containerIsBodyContainingBlock
|
|
7294
|
+
? containerRect.left +
|
|
7295
|
+
readPx(containerCS.borderLeftWidth) -
|
|
7296
|
+
container.scrollLeft
|
|
7297
|
+
: -(window.scrollX || 0);
|
|
7298
|
+
var originY = containerIsBodyContainingBlock
|
|
7299
|
+
? containerRect.top +
|
|
7300
|
+
readPx(containerCS.borderTopWidth) -
|
|
7301
|
+
container.scrollTop
|
|
7302
|
+
: -(window.scrollY || 0);
|
|
7303
|
+
var desiredGestureLeft = clientX - pointerOffset.x;
|
|
7304
|
+
var desiredGestureTop = clientY - pointerOffset.y;
|
|
7305
|
+
var deltaX = desiredGestureLeft - gestureStartRect.left;
|
|
7306
|
+
var deltaY = desiredGestureTop - gestureStartRect.top;
|
|
7307
|
+
|
|
7308
|
+
members.forEach(function (member, index) {
|
|
7309
|
+
var startRect = startRects[index] || member.getBoundingClientRect();
|
|
7310
|
+
var htmlEl = member as HTMLElement;
|
|
7311
|
+
htmlEl.style.position = "absolute";
|
|
7312
|
+
htmlEl.style.left = Math.round(startRect.left + deltaX - originX) + "px";
|
|
7313
|
+
htmlEl.style.top = Math.round(startRect.top + deltaY - originY) + "px";
|
|
7314
|
+
htmlEl.style.removeProperty("right");
|
|
7315
|
+
htmlEl.style.removeProperty("bottom");
|
|
7316
|
+
});
|
|
7317
|
+
target.absoluteCoordinatesPrepared = true;
|
|
7318
|
+
}
|
|
7319
|
+
|
|
6447
7320
|
function applyRuntimeReorder(el, target) {
|
|
6448
7321
|
if (!el || !target || !target.anchor || !target.anchor.parentElement)
|
|
6449
7322
|
return;
|
|
@@ -6487,9 +7360,11 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6487
7360
|
anchorSourceId: getSourceId(target.anchor),
|
|
6488
7361
|
placement: target.placement,
|
|
6489
7362
|
dropMode: target.dropMode || "flow-insert",
|
|
7363
|
+
forceFlowPositionOverride: Boolean(target.forceFlowPositionOverride),
|
|
6490
7364
|
sourceRect: rectInfoForElement(el),
|
|
6491
7365
|
anchorRect: rectInfoForElement(target.anchor),
|
|
6492
7366
|
payload: getElementInfo(el),
|
|
7367
|
+
anchorPayload: getElementInfo(target.anchor),
|
|
6493
7368
|
},
|
|
6494
7369
|
"*",
|
|
6495
7370
|
);
|
|
@@ -6756,7 +7631,11 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6756
7631
|
// pointer went down on when this drag preserves a 2+ selection instead of
|
|
6757
7632
|
// collapsing to one element (see beginPotentialShieldDrag's group branch).
|
|
6758
7633
|
// Defaults to selectedEl — the selection-overlay drag path.
|
|
6759
|
-
function startMove(
|
|
7634
|
+
function startMove(
|
|
7635
|
+
e,
|
|
7636
|
+
gestureElParam?: Element,
|
|
7637
|
+
pointerStartParam?: { clientX: number; clientY: number },
|
|
7638
|
+
) {
|
|
6760
7639
|
var gestureEl = gestureElParam || selectedEl;
|
|
6761
7640
|
if (!gestureEl) return;
|
|
6762
7641
|
if (isLayerInteractionBlocked(gestureEl)) return;
|
|
@@ -6870,11 +7749,18 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6870
7749
|
// Snapshot the element being reordered so a concurrent select-element or
|
|
6871
7750
|
// clear-selection postMessage cannot mutate the wrong element mid-drag.
|
|
6872
7751
|
var reorderEl = gestureEl;
|
|
6873
|
-
var
|
|
7752
|
+
var reorderGroupStartRects = groupEls.map(function (member) {
|
|
7753
|
+
return member.getBoundingClientRect();
|
|
7754
|
+
});
|
|
7755
|
+
var reorderGestureStartRect = reorderEl.getBoundingClientRect();
|
|
7756
|
+
var keepCurrentFlowParent = bridgeSpaceKeyPressed;
|
|
7757
|
+
var currentTarget = flowMoveTargetForPoint(
|
|
6874
7758
|
reorderEl,
|
|
6875
7759
|
e.clientX,
|
|
6876
7760
|
e.clientY,
|
|
6877
7761
|
groupOthers,
|
|
7762
|
+
keepCurrentFlowParent,
|
|
7763
|
+
Boolean(e.ctrlKey),
|
|
6878
7764
|
);
|
|
6879
7765
|
showInsertionGuideFor(currentTarget);
|
|
6880
7766
|
// Cross-screen drag state: true when the pointer is outside this iframe's
|
|
@@ -6885,9 +7771,10 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6885
7771
|
var reorderSourceId = getSourceId(reorderEl);
|
|
6886
7772
|
var reorderStyleSnapshot = collectPortableStyleSnapshot(reorderEl);
|
|
6887
7773
|
var reorderRect = reorderEl.getBoundingClientRect();
|
|
7774
|
+
var reorderPointerStart = pointerStartParam || e;
|
|
6888
7775
|
var reorderPointerOffset = {
|
|
6889
|
-
x:
|
|
6890
|
-
y:
|
|
7776
|
+
x: reorderPointerStart.clientX - reorderRect.left,
|
|
7777
|
+
y: reorderPointerStart.clientY - reorderRect.top,
|
|
6891
7778
|
};
|
|
6892
7779
|
function onReorderMove(ev) {
|
|
6893
7780
|
var vw = window.innerWidth;
|
|
@@ -6924,7 +7811,14 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6924
7811
|
showTransformBadge("Move layer", cx, cy);
|
|
6925
7812
|
} else {
|
|
6926
7813
|
// Cursor is inside this iframe — use existing in-iframe behavior.
|
|
6927
|
-
currentTarget =
|
|
7814
|
+
currentTarget = flowMoveTargetForPoint(
|
|
7815
|
+
reorderEl,
|
|
7816
|
+
cx,
|
|
7817
|
+
cy,
|
|
7818
|
+
groupOthers,
|
|
7819
|
+
keepCurrentFlowParent,
|
|
7820
|
+
Boolean(ev.ctrlKey),
|
|
7821
|
+
);
|
|
6928
7822
|
showInsertionGuideFor(currentTarget);
|
|
6929
7823
|
showTransformBadge(currentTarget ? "Move layer" : "Move", cx, cy);
|
|
6930
7824
|
}
|
|
@@ -6933,6 +7827,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6933
7827
|
document.removeEventListener(events.move, onReorderMove, true);
|
|
6934
7828
|
document.removeEventListener(events.up, onReorderUp, true);
|
|
6935
7829
|
document.removeEventListener("keydown", onReorderKeyDown, true);
|
|
7830
|
+
document.removeEventListener("keyup", onReorderKeyUp, true);
|
|
6936
7831
|
clearActiveDragCancel(onReorderEscape);
|
|
6937
7832
|
}
|
|
6938
7833
|
function onReorderEscape() {
|
|
@@ -6959,11 +7854,21 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6959
7854
|
return true;
|
|
6960
7855
|
}
|
|
6961
7856
|
function onReorderKeyDown(ev) {
|
|
7857
|
+
if (ev.code === "Space" || ev.key === " ") {
|
|
7858
|
+
keepCurrentFlowParent = true;
|
|
7859
|
+
ev.preventDefault();
|
|
7860
|
+
return;
|
|
7861
|
+
}
|
|
6962
7862
|
if (ev.key === "Escape") {
|
|
6963
7863
|
stopNativeInteraction(ev);
|
|
6964
7864
|
onReorderEscape();
|
|
6965
7865
|
}
|
|
6966
7866
|
}
|
|
7867
|
+
function onReorderKeyUp(ev) {
|
|
7868
|
+
if (ev.code !== "Space" && ev.key !== " ") return;
|
|
7869
|
+
keepCurrentFlowParent = false;
|
|
7870
|
+
ev.preventDefault();
|
|
7871
|
+
}
|
|
6967
7872
|
function onReorderUp(ev) {
|
|
6968
7873
|
cleanupReorderDrag();
|
|
6969
7874
|
hideTransformBadge();
|
|
@@ -7003,6 +7908,14 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7003
7908
|
// already clears cross-screen state on re-entry so checking the
|
|
7004
7909
|
// momentary excursion flag here would wrongly drop the element nowhere.
|
|
7005
7910
|
if (outsideOnDrop) return;
|
|
7911
|
+
currentTarget = flowMoveTargetForPoint(
|
|
7912
|
+
reorderEl,
|
|
7913
|
+
cx,
|
|
7914
|
+
cy,
|
|
7915
|
+
groupOthers,
|
|
7916
|
+
keepCurrentFlowParent,
|
|
7917
|
+
Boolean(ev?.ctrlKey),
|
|
7918
|
+
);
|
|
7006
7919
|
if (!currentTarget) {
|
|
7007
7920
|
// No valid drop target — clean up the clone if one was inserted so
|
|
7008
7921
|
// no ghost element is left in the DOM.
|
|
@@ -7019,6 +7932,24 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7019
7932
|
}
|
|
7020
7933
|
return;
|
|
7021
7934
|
}
|
|
7935
|
+
if (
|
|
7936
|
+
currentTarget.needsAutoLayoutConversion &&
|
|
7937
|
+
currentTarget.conversionTarget
|
|
7938
|
+
) {
|
|
7939
|
+
applyAutoLayoutConversionForDrop(
|
|
7940
|
+
currentTarget.conversionTarget,
|
|
7941
|
+
groupEls,
|
|
7942
|
+
);
|
|
7943
|
+
}
|
|
7944
|
+
prepareFlowMembersForAbsoluteDrop(
|
|
7945
|
+
groupEls,
|
|
7946
|
+
currentTarget,
|
|
7947
|
+
reorderGroupStartRects,
|
|
7948
|
+
reorderGestureStartRect,
|
|
7949
|
+
reorderPointerOffset,
|
|
7950
|
+
cx,
|
|
7951
|
+
cy,
|
|
7952
|
+
);
|
|
7022
7953
|
if (duplicatedForDrag) {
|
|
7023
7954
|
applyRuntimeReorder(reorderEl, currentTarget);
|
|
7024
7955
|
postVisualDuplicateChange(
|
|
@@ -7058,6 +7989,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7058
7989
|
document.addEventListener(events.move, onReorderMove, true);
|
|
7059
7990
|
document.addEventListener(events.up, onReorderUp, true);
|
|
7060
7991
|
document.addEventListener("keydown", onReorderKeyDown, true);
|
|
7992
|
+
document.addEventListener("keyup", onReorderKeyUp, true);
|
|
7061
7993
|
setActiveDragCancel(onReorderEscape);
|
|
7062
7994
|
return;
|
|
7063
7995
|
}
|
|
@@ -7120,6 +8052,34 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7120
8052
|
if (!duplicatedForDrag && !isGroupDrag) {
|
|
7121
8053
|
postCrossScreenDrag("start", dragEl, e);
|
|
7122
8054
|
}
|
|
8055
|
+
// rAF-coalesce the "move" phase postMessage: a raw mousemove/pointermove
|
|
8056
|
+
// stream can fire well above 60/s on a high-poll-rate mouse or trackpad,
|
|
8057
|
+
// and every tick of this handler already recomputes a getBoundingClientRect
|
|
8058
|
+
// for postCrossScreenDrag — batching to one postMessage per animation
|
|
8059
|
+
// frame matches the parent's own equivalent coalescing (see
|
|
8060
|
+
// MultiScreenCanvas.tsx's rAF-batched cross-screen hit-test) without
|
|
8061
|
+
// changing what gets sent, only how often. cleanupMoveDrag cancels any
|
|
8062
|
+
// still-pending tick so a stale "move" can never post after "end"/"cancel".
|
|
8063
|
+
var crossScreenDragMoveScheduled = false;
|
|
8064
|
+
var crossScreenDragMovePendingEv: {
|
|
8065
|
+
clientX: number;
|
|
8066
|
+
clientY: number;
|
|
8067
|
+
} | null = null;
|
|
8068
|
+
function flushCrossScreenDragMove() {
|
|
8069
|
+
crossScreenDragMoveScheduled = false;
|
|
8070
|
+
var pendingEv = crossScreenDragMovePendingEv;
|
|
8071
|
+
crossScreenDragMovePendingEv = null;
|
|
8072
|
+
if (pendingEv) postCrossScreenDrag("move", dragEl, pendingEv);
|
|
8073
|
+
}
|
|
8074
|
+
function scheduleCrossScreenDragMove(ev): void {
|
|
8075
|
+
crossScreenDragMovePendingEv = {
|
|
8076
|
+
clientX: ev.clientX,
|
|
8077
|
+
clientY: ev.clientY,
|
|
8078
|
+
};
|
|
8079
|
+
if (crossScreenDragMoveScheduled) return;
|
|
8080
|
+
crossScreenDragMoveScheduled = true;
|
|
8081
|
+
window.requestAnimationFrame(flushCrossScreenDragMove);
|
|
8082
|
+
}
|
|
7123
8083
|
function onMove(ev) {
|
|
7124
8084
|
if (
|
|
7125
8085
|
!moved &&
|
|
@@ -7174,7 +8134,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7174
8134
|
state.el.style.top = Math.round(state.originTop + appliedDy) + "px";
|
|
7175
8135
|
});
|
|
7176
8136
|
if (!duplicatedForDrag && !isGroupDrag) {
|
|
7177
|
-
|
|
8137
|
+
scheduleCrossScreenDragMove(ev);
|
|
7178
8138
|
}
|
|
7179
8139
|
if (
|
|
7180
8140
|
!duplicatedForDrag &&
|
|
@@ -7184,14 +8144,20 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7184
8144
|
hideInsertionGuide();
|
|
7185
8145
|
setMembersOpacity(null);
|
|
7186
8146
|
} else {
|
|
7187
|
-
currentAutoLayoutTarget =
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
8147
|
+
currentAutoLayoutTarget =
|
|
8148
|
+
!duplicatedForDrag && !bridgeSpaceKeyPressed
|
|
8149
|
+
? autoLayoutInsertionTargetForPoint(
|
|
8150
|
+
dragEl,
|
|
8151
|
+
ev.clientX,
|
|
8152
|
+
ev.clientY,
|
|
8153
|
+
groupOthers,
|
|
8154
|
+
)
|
|
8155
|
+
: null;
|
|
8156
|
+
if (currentAutoLayoutTarget && ev.ctrlKey) {
|
|
8157
|
+
currentAutoLayoutTarget = ignoreAutoLayoutForDropTarget(
|
|
8158
|
+
currentAutoLayoutTarget,
|
|
8159
|
+
);
|
|
8160
|
+
}
|
|
7195
8161
|
if (currentAutoLayoutTarget) {
|
|
7196
8162
|
showInsertionGuideFor(currentAutoLayoutTarget);
|
|
7197
8163
|
setMembersOpacity("0.4");
|
|
@@ -7235,6 +8201,10 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7235
8201
|
document.removeEventListener(events.up, onUp, true);
|
|
7236
8202
|
document.removeEventListener("keydown", onMoveKeyDown, true);
|
|
7237
8203
|
clearActiveDragCancel(cancelMoveDrag);
|
|
8204
|
+
// Drop any rAF-scheduled "move" tick so it can never fire and post
|
|
8205
|
+
// after this gesture's "end"/"cancel" phase has already gone out.
|
|
8206
|
+
crossScreenDragMoveScheduled = false;
|
|
8207
|
+
crossScreenDragMovePendingEv = null;
|
|
7238
8208
|
}
|
|
7239
8209
|
function cancelMoveDrag() {
|
|
7240
8210
|
cleanupMoveDrag();
|
|
@@ -7286,16 +8256,32 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7286
8256
|
restoreSourceDragPosition();
|
|
7287
8257
|
return;
|
|
7288
8258
|
}
|
|
7289
|
-
if (
|
|
8259
|
+
if (
|
|
8260
|
+
ev &&
|
|
8261
|
+
!duplicatedForDrag &&
|
|
8262
|
+
!outsideOnDrop &&
|
|
8263
|
+
!bridgeSpaceKeyPressed
|
|
8264
|
+
) {
|
|
7290
8265
|
var finalAutoLayoutTarget = autoLayoutInsertionTargetForPoint(
|
|
7291
8266
|
dragEl,
|
|
7292
8267
|
ev.clientX,
|
|
7293
8268
|
ev.clientY,
|
|
7294
8269
|
groupOthers,
|
|
7295
8270
|
);
|
|
8271
|
+
if (finalAutoLayoutTarget && ev.ctrlKey) {
|
|
8272
|
+
finalAutoLayoutTarget = ignoreAutoLayoutForDropTarget(
|
|
8273
|
+
finalAutoLayoutTarget,
|
|
8274
|
+
);
|
|
8275
|
+
}
|
|
7296
8276
|
if (finalAutoLayoutTarget) {
|
|
7297
8277
|
currentAutoLayoutTarget = finalAutoLayoutTarget;
|
|
7298
8278
|
}
|
|
8279
|
+
} else if (bridgeSpaceKeyPressed) {
|
|
8280
|
+
// Space is Figma's retain-parent modifier. Absolute/freeform drags
|
|
8281
|
+
// already move in their current containing-block coordinates, so
|
|
8282
|
+
// suppressing the nest target keeps the existing parent while still
|
|
8283
|
+
// committing the new left/top in one style change.
|
|
8284
|
+
currentAutoLayoutTarget = null;
|
|
7299
8285
|
}
|
|
7300
8286
|
if (duplicatedForDrag && !moved) {
|
|
7301
8287
|
// Alt-click with no real drag — remove the premature clone and restore the original selection.
|
|
@@ -7414,11 +8400,24 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7414
8400
|
var originalInlineFontSize = resizeEl.style.fontSize;
|
|
7415
8401
|
ensurePositionable(resizeEl);
|
|
7416
8402
|
var cs = window.getComputedStyle(resizeEl);
|
|
7417
|
-
// Bug fix: use
|
|
7418
|
-
//
|
|
7419
|
-
//
|
|
7420
|
-
|
|
7421
|
-
|
|
8403
|
+
// Bug fix: use COMPUTED width/height (never the raw inline style string)
|
|
8404
|
+
// for the resize origin dimensions. Two distinct hazards, one fix:
|
|
8405
|
+
// 1. Rotated elements — getBoundingClientRect() returns the inflated
|
|
8406
|
+
// axis-aligned bounding box of the rotated box, not its own
|
|
8407
|
+
// width/height, so it can't seed the origin either.
|
|
8408
|
+
// 2. Non-px inline values — an inline style of "100%" / "50vw" / "2rem"
|
|
8409
|
+
// / "auto" / "calc(...)" is NOT a pixel measurement. Reading
|
|
8410
|
+
// `resizeEl.style.width` directly and running it through
|
|
8411
|
+
// parseFloat (readPx) previously parsed "100%" as the number 100
|
|
8412
|
+
// and treated it as 100PX, so a +50px drag produced 150px instead
|
|
8413
|
+
// of correctly growing from the ~358px the element actually
|
|
8414
|
+
// rendered at. getComputedStyle always resolves to the element's
|
|
8415
|
+
// used-value size in px regardless of the authored unit (and is
|
|
8416
|
+
// unaffected by a rotate transform, unlike getBoundingClientRect),
|
|
8417
|
+
// so it's the one source that's simultaneously rotation-safe and
|
|
8418
|
+
// unit-agnostic.
|
|
8419
|
+
var originW = readPx(cs.width);
|
|
8420
|
+
var originH = readPx(cs.height);
|
|
7422
8421
|
// K-scale (Figma "Scale" tool) parity: capture the element's own border
|
|
7423
8422
|
// width and font size once at drag-start so a uniform per-tick scale
|
|
7424
8423
|
// factor (derived from width/height growth, see nextRect below) can
|
|
@@ -7448,6 +8447,18 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7448
8447
|
// Capture the element rotation once at drag-start so per-move projection is
|
|
7449
8448
|
// cheap and consistent even if the transform changes during the drag.
|
|
7450
8449
|
var resizeTheta = (currentRotation(resizeEl) * Math.PI) / 180;
|
|
8450
|
+
// Figma commit-semantics parity: a resize must only ever write the
|
|
8451
|
+
// axis/axes the user actually dragged. A pure vertical edge-drag (handle
|
|
8452
|
+
// "n"/"s", no Shift, scale tool off) must leave width completely alone —
|
|
8453
|
+
// e.g. a `width: 100%` element stays percentage-based after a
|
|
8454
|
+
// height-only resize instead of being silently pinned to a px value the
|
|
8455
|
+
// user never touched. These accumulate for the life of the gesture (once
|
|
8456
|
+
// an axis is touched — including transiently, e.g. Shift held mid-drag
|
|
8457
|
+
// then released — it stays "touched" for this gesture's commit) and
|
|
8458
|
+
// gate both the live style writes in onMove and the committed style keys
|
|
8459
|
+
// in onUp.
|
|
8460
|
+
var widthTouched = false;
|
|
8461
|
+
var heightTouched = false;
|
|
7451
8462
|
function nextRect(ev) {
|
|
7452
8463
|
var screenDx = ev.clientX - startX;
|
|
7453
8464
|
var screenDy = ev.clientY - startY;
|
|
@@ -7525,15 +8536,42 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7525
8536
|
if (handle.indexOf("n") !== -1 || handle.indexOf("s") !== -1)
|
|
7526
8537
|
top = origin.top - (height - origin.height) / 2;
|
|
7527
8538
|
}
|
|
7528
|
-
|
|
8539
|
+
// Which axis/axes this handle actually drives: the handle's own
|
|
8540
|
+
// letters directly touch their axis; an active aspect-ratio lock
|
|
8541
|
+
// (Shift or the K-scale tool) additionally derives the OTHER axis from
|
|
8542
|
+
// the touched one for a single-axis edge handle (e.g. dragging "n"
|
|
8543
|
+
// with Shift held also changes width to hold the ratio). Two-letter
|
|
8544
|
+
// corner handles already touch both axes regardless of the lock.
|
|
8545
|
+
var handlesWidth =
|
|
8546
|
+
handle.indexOf("w") !== -1 || handle.indexOf("e") !== -1;
|
|
8547
|
+
var handlesHeight =
|
|
8548
|
+
handle.indexOf("n") !== -1 || handle.indexOf("s") !== -1;
|
|
8549
|
+
var aspectLocked = !!(ev.shiftKey || scaleToolEnabled);
|
|
8550
|
+
var touchesWidth = handlesWidth || (aspectLocked && handlesHeight);
|
|
8551
|
+
var touchesHeight = handlesHeight || (aspectLocked && handlesWidth);
|
|
8552
|
+
return {
|
|
8553
|
+
left: left,
|
|
8554
|
+
top: top,
|
|
8555
|
+
width: width,
|
|
8556
|
+
height: height,
|
|
8557
|
+
touchesWidth: touchesWidth,
|
|
8558
|
+
touchesHeight: touchesHeight,
|
|
8559
|
+
};
|
|
7529
8560
|
}
|
|
7530
8561
|
function onMove(ev) {
|
|
7531
8562
|
if (!resizeEl) return;
|
|
7532
8563
|
var rect = nextRect(ev);
|
|
8564
|
+
if (rect.touchesWidth) widthTouched = true;
|
|
8565
|
+
if (rect.touchesHeight) heightTouched = true;
|
|
7533
8566
|
resizeEl.style.left = Math.round(rect.left) + "px";
|
|
7534
8567
|
resizeEl.style.top = Math.round(rect.top) + "px";
|
|
7535
|
-
|
|
7536
|
-
|
|
8568
|
+
// Only write width/height for an axis this gesture actually touched —
|
|
8569
|
+
// writing the untouched axis every tick (even to its own unchanged
|
|
8570
|
+
// origin value) would silently convert e.g. `width: 100%` to a px
|
|
8571
|
+
// value on a pure vertical drag, which is exactly the "shrank instead
|
|
8572
|
+
// of preserved" class of bug this fixes.
|
|
8573
|
+
if (widthTouched) resizeEl.style.width = Math.round(rect.width) + "px";
|
|
8574
|
+
if (heightTouched) resizeEl.style.height = Math.round(rect.height) + "px";
|
|
7537
8575
|
if (scaleToolEnabled) {
|
|
7538
8576
|
// Uniform scale factor: scaleToolEnabled already forces the
|
|
7539
8577
|
// aspect-ratio lock above (nextRect), so width/origin.width and
|
|
@@ -7597,9 +8635,14 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7597
8635
|
position: resizeEl.style.position,
|
|
7598
8636
|
left: resizeEl.style.left,
|
|
7599
8637
|
top: resizeEl.style.top,
|
|
7600
|
-
width: resizeEl.style.width,
|
|
7601
|
-
height: resizeEl.style.height,
|
|
7602
8638
|
};
|
|
8639
|
+
// Only commit width/height for an axis this gesture actually touched
|
|
8640
|
+
// (see widthTouched/heightTouched above) — a pure vertical or
|
|
8641
|
+
// horizontal edge-drag must not also commit a px value for the axis
|
|
8642
|
+
// the user never dragged, which would silently convert e.g.
|
|
8643
|
+
// `width: 100%` to a fixed px width.
|
|
8644
|
+
if (widthTouched) styles.width = resizeEl.style.width;
|
|
8645
|
+
if (heightTouched) styles.height = resizeEl.style.height;
|
|
7603
8646
|
// Only include borderWidth/fontSize when the K-scale tool actually
|
|
7604
8647
|
// changed them (originBorderWidth/originFontSize > 0 AND
|
|
7605
8648
|
// scaleToolEnabled) — a normal resize must never introduce these keys,
|
|
@@ -7804,12 +8847,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7804
8847
|
collectMoveGroupMembers(groupGestureMember).length > 1
|
|
7805
8848
|
) {
|
|
7806
8849
|
suppressNextShieldClickBriefly();
|
|
7807
|
-
startMove(ev, groupGestureMember
|
|
8850
|
+
startMove(ev, groupGestureMember, {
|
|
8851
|
+
clientX: startX,
|
|
8852
|
+
clientY: startY,
|
|
8853
|
+
});
|
|
7808
8854
|
return;
|
|
7809
8855
|
}
|
|
7810
8856
|
selectTarget(dragTarget, ev);
|
|
7811
8857
|
suppressNextShieldClickBriefly();
|
|
7812
|
-
startMove(ev);
|
|
8858
|
+
startMove(ev, undefined, { clientX: startX, clientY: startY });
|
|
7813
8859
|
}
|
|
7814
8860
|
function onUp(ev) {
|
|
7815
8861
|
clearPendingShieldDrag();
|
|
@@ -7941,6 +8987,19 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
7941
8987
|
document.addEventListener(
|
|
7942
8988
|
"keydown",
|
|
7943
8989
|
function (e) {
|
|
8990
|
+
if (
|
|
8991
|
+
e.key === " " &&
|
|
8992
|
+
e.code === "Space" &&
|
|
8993
|
+
!activeTextEditEl &&
|
|
8994
|
+
!isEditorTypingTarget(e.target)
|
|
8995
|
+
) {
|
|
8996
|
+
bridgeSpaceKeyPressed = true;
|
|
8997
|
+
if (activeDragCancel) {
|
|
8998
|
+
bridgeSpaceKeyConsumedByDrag = true;
|
|
8999
|
+
stopNativeInteraction(e);
|
|
9000
|
+
return;
|
|
9001
|
+
}
|
|
9002
|
+
}
|
|
7944
9003
|
// T25: pending-window keydown routing — a begin-text-edit is still
|
|
7945
9004
|
// waiting for its node. Keystrokes that land in THIS document during
|
|
7946
9005
|
// the wait belong to the upcoming text session: buffer printable
|
|
@@ -8086,6 +9145,12 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
8086
9145
|
"keyup",
|
|
8087
9146
|
function (e) {
|
|
8088
9147
|
if (e.key !== " " || e.code !== "Space") return;
|
|
9148
|
+
bridgeSpaceKeyPressed = false;
|
|
9149
|
+
if (bridgeSpaceKeyConsumedByDrag) {
|
|
9150
|
+
bridgeSpaceKeyConsumedByDrag = false;
|
|
9151
|
+
stopNativeInteraction(e);
|
|
9152
|
+
return;
|
|
9153
|
+
}
|
|
8089
9154
|
if (activeTextEditEl || isEditorTypingTarget(e.target)) return;
|
|
8090
9155
|
stopNativeInteraction(e);
|
|
8091
9156
|
(window.parent as Window).postMessage(
|
|
@@ -8317,6 +9382,45 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
8317
9382
|
}
|
|
8318
9383
|
return;
|
|
8319
9384
|
}
|
|
9385
|
+
// Template-clone text-edit rejection (mirrors the reorder-rejection fix
|
|
9386
|
+
// above): `target` here can be a raw Alpine `<template x-for>`/`x-if`
|
|
9387
|
+
// runtime clone — findTextEditTarget's climb only requires every
|
|
9388
|
+
// descendant tag to be inline-editable, it never checks whether the
|
|
9389
|
+
// ancestor chain crosses a clone boundary, so a repeated list/card item
|
|
9390
|
+
// whose own content is plain text passes straight through. Entering
|
|
9391
|
+
// contenteditable on that clone lets the user type and see the change
|
|
9392
|
+
// live (it's a real DOM node), but the clone has no per-instance
|
|
9393
|
+
// counterpart in the static source HTML (only the single `<template>`
|
|
9394
|
+
// stays there), so getSelector's live-DOM nth-of-type path — unlike
|
|
9395
|
+
// hit-test.bridge.ts's source-equivalent selector builder — cannot
|
|
9396
|
+
// resolve it on commit. Previously the edit silently failed on the host
|
|
9397
|
+
// (error toast, or a no-op) and vanished for good on the next Alpine
|
|
9398
|
+
// re-render, with no indication anything was wrong. Reject up front
|
|
9399
|
+
// instead — same UX contract as the reorder-rejection badge — and fall
|
|
9400
|
+
// back to selecting the nearest source-backed ancestor (typically the
|
|
9401
|
+
// repeated item's container) so the user isn't left with a stale or
|
|
9402
|
+
// empty selection.
|
|
9403
|
+
if (!programmaticFlag && isTemplateCloneElement(target)) {
|
|
9404
|
+
showRejectedDragBadge(
|
|
9405
|
+
"Can't edit repeated items directly",
|
|
9406
|
+
e.clientX,
|
|
9407
|
+
e.clientY,
|
|
9408
|
+
);
|
|
9409
|
+
// No ongoing gesture here (unlike the reorder-rejection badge, which
|
|
9410
|
+
// hides on the drag's own mouseup/Escape) to hide it on, so time it
|
|
9411
|
+
// out on its own instead of leaving it on screen indefinitely.
|
|
9412
|
+
window.setTimeout(hideTransformBadge, 1400);
|
|
9413
|
+
var rejectedTextEditFallback = selectionTargetForHit(target);
|
|
9414
|
+
if (
|
|
9415
|
+
rejectedTextEditFallback &&
|
|
9416
|
+
!isLayerInteractionBlocked(rejectedTextEditFallback)
|
|
9417
|
+
) {
|
|
9418
|
+
selectedEl = rejectedTextEditFallback;
|
|
9419
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
9420
|
+
postElementSelect(selectedEl, e);
|
|
9421
|
+
}
|
|
9422
|
+
return;
|
|
9423
|
+
}
|
|
8320
9424
|
// Anchor the selection identity to the nearest source-backed element. Text
|
|
8321
9425
|
// editing still operates on the actual target text node, but a later
|
|
8322
9426
|
// style edit posts from selectedEl, so it must point at a patchable
|
|
@@ -8700,6 +9804,12 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
8700
9804
|
scheduleSpacingHoverClear(e);
|
|
8701
9805
|
}
|
|
8702
9806
|
hideMeasurements();
|
|
9807
|
+
// Re-arm the hover-info post gate below: leaving all content (e.g.
|
|
9808
|
+
// pointer over empty canvas or off the iframe entirely) means the
|
|
9809
|
+
// NEXT element this pointer lands on — even if it's the same one
|
|
9810
|
+
// hovered before — is a genuinely new hover the host hasn't heard
|
|
9811
|
+
// about since.
|
|
9812
|
+
lastHoverInfoPostedEl = null;
|
|
8703
9813
|
return;
|
|
8704
9814
|
}
|
|
8705
9815
|
if (hoveredEl && hoveredEl.closest("[data-agent-native-text-editing]"))
|
|
@@ -8743,11 +9853,14 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
8743
9853
|
} else {
|
|
8744
9854
|
hideMeasurements();
|
|
8745
9855
|
}
|
|
8746
|
-
|
|
8747
|
-
|
|
8748
|
-
|
|
8749
|
-
|
|
8750
|
-
|
|
9856
|
+
if (hoveredEl !== lastHoverInfoPostedEl) {
|
|
9857
|
+
lastHoverInfoPostedEl = hoveredEl;
|
|
9858
|
+
var info = getLightElementInfo(hoveredEl);
|
|
9859
|
+
(window.parent as Window).postMessage(
|
|
9860
|
+
{ type: "element-hover", payload: info },
|
|
9861
|
+
"*",
|
|
9862
|
+
);
|
|
9863
|
+
}
|
|
8751
9864
|
},
|
|
8752
9865
|
true,
|
|
8753
9866
|
);
|
|
@@ -8781,7 +9894,11 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
8781
9894
|
updateSpacingOverlay(selectedEl);
|
|
8782
9895
|
return;
|
|
8783
9896
|
}
|
|
8784
|
-
|
|
9897
|
+
// Leaving the iframe entirely (e.g. to a panel or outside the window)
|
|
9898
|
+
// must re-arm the post gate, not just clear hoveredEl — otherwise the
|
|
9899
|
+
// pointer returning to this SAME element never re-posts "element-hover"
|
|
9900
|
+
// and the host's hover highlight stays stuck at "nothing".
|
|
9901
|
+
clearHoverGate();
|
|
8785
9902
|
if (!spacingDrag) {
|
|
8786
9903
|
scheduleSpacingHoverClear(e);
|
|
8787
9904
|
}
|
|
@@ -8858,6 +9975,17 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
8858
9975
|
}
|
|
8859
9976
|
return;
|
|
8860
9977
|
}
|
|
9978
|
+
if (e.data.type === "set-content-offset") {
|
|
9979
|
+
var nextContentOffsetX = Number(e.data.x);
|
|
9980
|
+
var nextContentOffsetY = Number(e.data.y);
|
|
9981
|
+
designCanvasContentOffsetX = Number.isFinite(nextContentOffsetX)
|
|
9982
|
+
? nextContentOffsetX
|
|
9983
|
+
: 0;
|
|
9984
|
+
designCanvasContentOffsetY = Number.isFinite(nextContentOffsetY)
|
|
9985
|
+
? nextContentOffsetY
|
|
9986
|
+
: 0;
|
|
9987
|
+
return;
|
|
9988
|
+
}
|
|
8861
9989
|
// begin-text-edit: enter text-editing mode for the element identified by
|
|
8862
9990
|
// nodeId immediately (no double-click needed). Used after programmatic
|
|
8863
9991
|
// text-element creation so the user can type right away and the autosize
|
|
@@ -9165,13 +10293,28 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
9165
10293
|
// whenever the cursor RESTED on a handle (the user only ever saw the
|
|
9166
10294
|
// badge flash, i.e. "the value box never shows"). A same-element
|
|
9167
10295
|
// replay must be a no-op for hover state.
|
|
9168
|
-
|
|
10296
|
+
var selectionChangedByHost = target !== selectedEl;
|
|
10297
|
+
if (selectionChangedByHost) {
|
|
9169
10298
|
selectedSpacingHovered = false;
|
|
9170
10299
|
hoveredSpacingHandleKey = "";
|
|
9171
10300
|
}
|
|
9172
10301
|
selectedEl = target;
|
|
9173
10302
|
positionOverlay(selectionOverlay, target);
|
|
9174
10303
|
if (hoveredEl === selectedEl) highlightOverlay.style.display = "none";
|
|
10304
|
+
// A host-driven selection (e.g. picking a layer in the Layers panel)
|
|
10305
|
+
// only ever moved the overlay above — it never sent the rich
|
|
10306
|
+
// getElementInfo() payload (computedStyles, portableStyleSnapshot,
|
|
10307
|
+
// gradients, etc.) back up, unlike pointer-driven selection which
|
|
10308
|
+
// calls postElementSelect() immediately. The properties panel's
|
|
10309
|
+
// "element-select" listener was therefore left with whatever payload
|
|
10310
|
+
// (or lack of one) it already had, which surfaced as an empty Fill
|
|
10311
|
+
// section and zeroed-out layout fields right after a Layers-panel
|
|
10312
|
+
// click even though the same element selected by clicking the canvas
|
|
10313
|
+
// rendered correctly. Post the full payload on genuine selection
|
|
10314
|
+
// changes only — the `selectionChangedByHost` guard above already
|
|
10315
|
+
// keeps this a no-op on the ~1-2s poll-tick replay so it can't turn
|
|
10316
|
+
// into a message-spam loop.
|
|
10317
|
+
if (selectionChangedByHost) postElementSelect(target);
|
|
9175
10318
|
return;
|
|
9176
10319
|
}
|
|
9177
10320
|
if (e.data.type === "hover-element") {
|
|
@@ -9194,7 +10337,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
9194
10337
|
hoverCandidates.push(String(e.data.selector));
|
|
9195
10338
|
}
|
|
9196
10339
|
if (hoverCandidates.length === 0) {
|
|
9197
|
-
|
|
10340
|
+
clearHoverGate();
|
|
9198
10341
|
highlightOverlay.style.display = "none";
|
|
9199
10342
|
hideMeasurements();
|
|
9200
10343
|
return;
|
|
@@ -9232,12 +10375,72 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
9232
10375
|
hideSelectionOverlay();
|
|
9233
10376
|
}
|
|
9234
10377
|
if (hoveredEl && isLayerInteractionBlocked(hoveredEl)) {
|
|
9235
|
-
|
|
10378
|
+
clearHoverGate();
|
|
9236
10379
|
highlightOverlay.style.display = "none";
|
|
9237
10380
|
}
|
|
9238
10381
|
applyHiddenSelectors();
|
|
9239
10382
|
return;
|
|
9240
10383
|
}
|
|
10384
|
+
if (e.data.type === "runtime-structure-move") {
|
|
10385
|
+
if (readOnly) return;
|
|
10386
|
+
var runtimePlacement = String(e.data.placement || "");
|
|
10387
|
+
if (
|
|
10388
|
+
runtimePlacement !== "before" &&
|
|
10389
|
+
runtimePlacement !== "after" &&
|
|
10390
|
+
runtimePlacement !== "inside"
|
|
10391
|
+
) {
|
|
10392
|
+
return;
|
|
10393
|
+
}
|
|
10394
|
+
var runtimeSubject = findUniqueRuntimeStructureTarget(
|
|
10395
|
+
String(e.data.subjectSelector || ""),
|
|
10396
|
+
typeof e.data.subjectSourceId === "string"
|
|
10397
|
+
? e.data.subjectSourceId
|
|
10398
|
+
: "",
|
|
10399
|
+
);
|
|
10400
|
+
var runtimeAnchor = findUniqueRuntimeStructureTarget(
|
|
10401
|
+
String(e.data.anchorSelector || ""),
|
|
10402
|
+
typeof e.data.anchorSourceId === "string" ? e.data.anchorSourceId : "",
|
|
10403
|
+
);
|
|
10404
|
+
if (
|
|
10405
|
+
!runtimeSubject ||
|
|
10406
|
+
!runtimeAnchor ||
|
|
10407
|
+
runtimeSubject === runtimeAnchor ||
|
|
10408
|
+
runtimeSubject.contains(runtimeAnchor)
|
|
10409
|
+
) {
|
|
10410
|
+
return;
|
|
10411
|
+
}
|
|
10412
|
+
if (
|
|
10413
|
+
(runtimePlacement === "inside" &&
|
|
10414
|
+
!isContainerDropTarget(runtimeAnchor)) ||
|
|
10415
|
+
(runtimePlacement !== "inside" && !runtimeAnchor.parentElement)
|
|
10416
|
+
) {
|
|
10417
|
+
return;
|
|
10418
|
+
}
|
|
10419
|
+
var runtimeTarget = {
|
|
10420
|
+
anchor: runtimeAnchor,
|
|
10421
|
+
placement: runtimePlacement,
|
|
10422
|
+
axis: parentFlowAxis(
|
|
10423
|
+
runtimePlacement === "inside"
|
|
10424
|
+
? runtimeAnchor
|
|
10425
|
+
: runtimeAnchor.parentElement!,
|
|
10426
|
+
),
|
|
10427
|
+
dropMode:
|
|
10428
|
+
runtimePlacement === "inside" &&
|
|
10429
|
+
isAbsolutePrimitiveContainer(runtimeAnchor)
|
|
10430
|
+
? "absolute-container"
|
|
10431
|
+
: "flow-insert",
|
|
10432
|
+
};
|
|
10433
|
+
var runtimeOrigin = {
|
|
10434
|
+
prevParent: runtimeSubject.parentElement!,
|
|
10435
|
+
prevNextSibling: runtimeSubject.nextSibling,
|
|
10436
|
+
prevInlinePositionStyles: snapshotInlinePositionStyles(runtimeSubject),
|
|
10437
|
+
};
|
|
10438
|
+
applyRuntimeReorder(runtimeSubject, runtimeTarget);
|
|
10439
|
+
selectedEl = runtimeSubject;
|
|
10440
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
10441
|
+
postVisualStructureChange(runtimeSubject, runtimeTarget, runtimeOrigin);
|
|
10442
|
+
return;
|
|
10443
|
+
}
|
|
9241
10444
|
if (e.data.type === "visual-structure-ack") {
|
|
9242
10445
|
var move = pendingStructureMoves[e.data.requestId];
|
|
9243
10446
|
if (!move) return;
|
|
@@ -9434,6 +10637,42 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
9434
10637
|
window.addEventListener("resize", scheduleRefreshOverlays);
|
|
9435
10638
|
applyEditorChromeScale();
|
|
9436
10639
|
|
|
10640
|
+
if (
|
|
10641
|
+
runtimeLayerSnapshotEnabled &&
|
|
10642
|
+
typeof MutationObserver !== "undefined" &&
|
|
10643
|
+
document.body
|
|
10644
|
+
) {
|
|
10645
|
+
var runtimeLayerObserver = new MutationObserver(function (mutations) {
|
|
10646
|
+
var hasMeaningfulMutation = mutations.some(
|
|
10647
|
+
runtimeLayerMutationIsMeaningful,
|
|
10648
|
+
);
|
|
10649
|
+
if (hasMeaningfulMutation) scheduleRuntimeLayerSnapshot();
|
|
10650
|
+
});
|
|
10651
|
+
runtimeLayerObserver.observe(document.body, {
|
|
10652
|
+
attributes: true,
|
|
10653
|
+
attributeOldValue: true,
|
|
10654
|
+
childList: true,
|
|
10655
|
+
characterData: true,
|
|
10656
|
+
subtree: true,
|
|
10657
|
+
attributeFilter: [
|
|
10658
|
+
"aria-label",
|
|
10659
|
+
"class",
|
|
10660
|
+
"data-agent-native-component",
|
|
10661
|
+
"data-agent-native-layer-name",
|
|
10662
|
+
"data-an-primitive",
|
|
10663
|
+
"data-component-name",
|
|
10664
|
+
"data-source-column",
|
|
10665
|
+
"data-source-file",
|
|
10666
|
+
"data-source-line",
|
|
10667
|
+
"hidden",
|
|
10668
|
+
"id",
|
|
10669
|
+
"style",
|
|
10670
|
+
"title",
|
|
10671
|
+
],
|
|
10672
|
+
});
|
|
10673
|
+
}
|
|
10674
|
+
if (runtimeLayerSnapshotEnabled) scheduleRuntimeLayerSnapshot();
|
|
10675
|
+
|
|
9437
10676
|
// One-time ready signal: tells the host that every message listener above is
|
|
9438
10677
|
// now attached, so one-shot commands (begin-text-edit, set-editor-chrome-scale,
|
|
9439
10678
|
// style-change, delete-element, replace-document-content) sent immediately
|