@agent-native/core 0.79.27 → 0.80.0
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 +2 -2
- package/corpus/core/CHANGELOG.md +26 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +41 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +51 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +50 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +49 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +44 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +46 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +44 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +48 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +36 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +38 -0
- package/corpus/core/docs/content/template-design.mdx +45 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/agent/engine/index.ts +1 -0
- package/corpus/core/src/agent/engine/registry.ts +40 -0
- package/corpus/core/src/agent/production-agent.ts +6 -2
- package/corpus/core/src/cli/design-connect.ts +490 -0
- package/corpus/core/src/cli/index.ts +16 -0
- package/corpus/core/src/cli/skills.ts +126 -3
- package/corpus/core/src/client/AssistantChat.tsx +43 -7
- package/corpus/core/src/client/agent-chat.ts +27 -3
- package/corpus/core/src/client/blocks/library/MermaidBlock.tsx +1 -1
- package/corpus/core/src/client/blocks/library/diagram.tsx +2 -2
- package/corpus/core/src/client/blocks/library/wireframe.tsx +1 -1
- package/corpus/core/src/client/chat/message-components.tsx +1 -1
- package/corpus/core/src/client/chat-model-groups.ts +1 -2
- package/corpus/core/src/client/components/LiveCursorOverlay.tsx +33 -66
- package/corpus/core/src/client/composer/TiptapComposer.tsx +99 -24
- package/corpus/core/src/client/composer/useVoiceDictation.ts +429 -260
- package/corpus/core/src/client/frame-protocol.ts +17 -2
- package/corpus/core/src/client/rich-markdown-editor/RegistryBlockNode.tsx +2 -2
- package/corpus/core/src/client/sharing/ShareButton.tsx +20 -15
- package/corpus/core/src/client/use-agent-engine-configured.ts +80 -28
- package/corpus/core/src/server/transcribe-voice.ts +57 -22
- package/corpus/core/src/shared/mcp-embed-headers.ts +23 -1
- package/corpus/core/src/styles/agent-native.css +56 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/corpus/core/src/vite/client.ts +2 -0
- package/corpus/core/src/voice/index.ts +18 -0
- package/corpus/core/src/voice/voice-cleanup-prompt.ts +38 -0
- package/corpus/core/src/voice/voice-context.ts +217 -0
- package/corpus/core/src/voice/voice-replacements.ts +75 -0
- package/corpus/templates/analytics/AGENTS.md +4 -3
- package/corpus/templates/analytics/actions/list-session-recordings.ts +6 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +557 -400
- package/corpus/templates/analytics/app/global.css +0 -4
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/analytics/app/i18n-data.ts +20 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +33 -8
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +7 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +7 -0
- package/corpus/templates/analytics/changelog/2026-06-28-ask-tab-now-matches-the-darker-app-background.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-dashboard-email-reports-now-use-cleaner-light-mode-snapshots.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-sessions-now-show-only-signed-in-recordings-with-playable-re.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +3 -3
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +29 -7
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +5 -2
- package/corpus/templates/analytics/server/lib/session-replay.ts +24 -4
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -4
- package/corpus/templates/assets/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +29 -2
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +131 -42
- package/corpus/templates/brain/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/brain/app/i18n-data.ts +20 -0
- package/corpus/templates/brain/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +29 -1
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +417 -299
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/calendar/app/i18n-data.ts +20 -0
- package/corpus/templates/calendar/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +6 -19
- package/corpus/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/cleanup-transcript.ts +39 -9
- package/corpus/templates/clips/actions/finalize-meeting.ts +81 -12
- package/corpus/templates/clips/actions/lib/agents-md-context.ts +4 -1
- package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -3
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +1 -1
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +2 -2
- package/corpus/templates/clips/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/personal-vocabulary.ts +13 -8
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +60 -1
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +1 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +1 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -5
- package/corpus/templates/content/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +137 -0
- package/corpus/templates/design/AGENTS.md +95 -5
- package/corpus/templates/design/DESIGN.md +82 -0
- package/corpus/templates/design/DEVELOPING.md +43 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +426 -0
- package/corpus/templates/design/actions/apply-shader.ts +277 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +303 -0
- package/corpus/templates/design/actions/connect-localhost.ts +183 -0
- package/corpus/templates/design/actions/edit-design.ts +195 -42
- package/corpus/templates/design/actions/generate-design.ts +71 -1
- package/corpus/templates/design/actions/generate-screens.ts +193 -0
- package/corpus/templates/design/actions/get-code-layer-projection.ts +191 -0
- package/corpus/templates/design/actions/get-shader.ts +119 -0
- package/corpus/templates/design/actions/list-localhost-connections.ts +95 -0
- package/corpus/templates/design/actions/navigate.ts +55 -2
- package/corpus/templates/design/actions/view-screen.ts +68 -1
- package/corpus/templates/design/agent-native.app-skill.json +10 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +761 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +2057 -103
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +74 -38
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +399 -0
- package/corpus/templates/design/app/components/design/EditPanel.tsx +3185 -439
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +1124 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +3116 -147
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +700 -13
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +107 -63
- package/corpus/templates/design/app/components/design/index.ts +0 -5
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +286 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +699 -0
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +461 -0
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +166 -0
- package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +1420 -0
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +420 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +215 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.stories.tsx +71 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +518 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +106 -0
- package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +361 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +60 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +201 -0
- package/corpus/templates/design/app/components/design/types.ts +23 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +24 -13
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +4 -4
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +474 -36
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +22 -5
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +24 -11
- package/corpus/templates/design/app/global.css +121 -3
- package/corpus/templates/design/app/hooks/use-agent-generating.ts +4 -0
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +130 -8
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +329 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +264 -0
- package/corpus/templates/design/app/i18n-data.ts +2790 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +5108 -851
- package/corpus/templates/design/changelog/2026-06-28-annotate-mode-now-combines-drawing-and-comment-pins-in-one-c.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-design-editor-panel-borders-are-cleaner-so-the-canvas-edge-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-drawing-prompts-now-reliably-open-and-submit-to-the-design-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-editor-app-menus-now-use-an-outline-agent-native-mark-with-b.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-layer-selection-now-updates-chat-context-without-opening-the.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-layer-selections-now-preserve-hidden-and-locked-canvas-state.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-handles-zooming-drawing-opening-and-dele.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-selects-screens-on-click-and-opens-them-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-zooms-into-the-active-screen-with-smoother-e.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-selected-element-chips-in-chat-are-shorter-and-easier-to-sca.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-the-design-editor-toolbar-is-more-compact-and-the-app-menu-m.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-at-the-top-of-the-design-sidebar-instead-of-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-in-the-right-inspector-beside-the-design-tab.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-active-sidebar-rows-use-soft-highlights.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-agents-can-now-open-screen-overview-focus-screens-and-add.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-agents-now-edit-broad-copy-changes-more-reliably.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-editor-extensions-now-run-inline-beside-the-artboard.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-sidebars-use-darker-chrome-and-chat-warns-immediately.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-visual-edit-can-open-localhost-routes-as-url-backed-screens.md +6 -0
- package/corpus/templates/design/package.json +1 -0
- package/corpus/templates/design/server/db/schema.ts +26 -0
- package/corpus/templates/design/server/lib/design-snapshot.ts +1 -1
- package/corpus/templates/design/server/plugins/agent-chat.ts +3 -0
- package/corpus/templates/design/shared/api.ts +27 -0
- package/corpus/templates/design/shared/canvas-frames.ts +103 -0
- package/corpus/templates/design/shared/canvas-math.ts +1058 -0
- package/corpus/templates/design/shared/code-layer.ts +2212 -0
- package/corpus/templates/design/shared/color-utils.ts +180 -0
- package/corpus/templates/design/shared/generation-session.ts +61 -0
- package/corpus/templates/design/shared/shader-presets.ts +782 -0
- package/corpus/templates/design/shared/shader-safety.ts +137 -0
- package/corpus/templates/design/shared/source-mode.ts +237 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +149 -6
- package/corpus/templates/forms/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/forms/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/forms/app/i18n/en-US.ts +2 -0
- package/corpus/templates/forms/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/forms/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/forms/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/forms/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/forms/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/forms/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/forms/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +2 -2
- package/corpus/templates/macros/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +408 -247
- package/corpus/templates/mail/app/i18n/ar-SA.ts +4 -0
- package/corpus/templates/mail/app/i18n/de-DE.ts +4 -0
- package/corpus/templates/mail/app/i18n/en-US.ts +4 -0
- package/corpus/templates/mail/app/i18n/es-ES.ts +4 -0
- package/corpus/templates/mail/app/i18n/fr-FR.ts +4 -0
- package/corpus/templates/mail/app/i18n/hi-IN.ts +4 -0
- package/corpus/templates/mail/app/i18n/ja-JP.ts +4 -0
- package/corpus/templates/mail/app/i18n/ko-KR.ts +4 -0
- package/corpus/templates/mail/app/i18n/pt-BR.ts +4 -0
- package/corpus/templates/mail/app/i18n/zh-CN.ts +4 -0
- package/corpus/templates/mail/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/mail/changelog/2026-06-28-pinned-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +6 -8
- package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +1 -1
- package/corpus/templates/plan/app/global.css +61 -78
- package/corpus/templates/plan/app/pages/PlansPage.tsx +8 -0
- package/corpus/templates/plan/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-28-plans-use-the-shared-neutral-light-and-dark-theme.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-28-sending-open-feedback-from-a-plan-now-opens-the-inline-plan-.md +6 -0
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +5 -5
- package/corpus/templates/slides/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/videos/app/components/Sidebar.tsx +2 -2
- package/corpus/templates/videos/app/components/layout/Layout.tsx +29 -2
- package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +113 -30
- package/corpus/templates/videos/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/videos/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/videos/app/i18n/en-US.ts +2 -0
- package/corpus/templates/videos/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/videos/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/videos/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/videos/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/videos/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/videos/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/videos/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/videos/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +9 -0
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +37 -0
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -3
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/design-connect.d.ts +52 -0
- package/dist/cli/design-connect.d.ts.map +1 -0
- package/dist/cli/design-connect.js +409 -0
- package/dist/cli/design-connect.js.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -0
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +121 -3
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +38 -8
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +6 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +17 -3
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.js +1 -1
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/blocks/library/wireframe.js +1 -1
- package/dist/client/blocks/library/wireframe.js.map +1 -1
- package/dist/client/chat/message-components.js +1 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +1 -2
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/components/LiveCursorOverlay.d.ts +1 -2
- package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
- package/dist/client/components/LiveCursorOverlay.js +19 -40
- package/dist/client/components/LiveCursorOverlay.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +3 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +86 -24
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/useVoiceDictation.d.ts +9 -0
- package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
- package/dist/client/composer/useVoiceDictation.js +154 -32
- package/dist/client/composer/useVoiceDictation.js.map +1 -1
- package/dist/client/frame-protocol.d.ts +17 -2
- package/dist/client/frame-protocol.d.ts.map +1 -1
- package/dist/client/frame-protocol.js.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +2 -2
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +2 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +7 -6
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts +5 -0
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +51 -25
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +8 -8
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +40 -24
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -0
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +20 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/styles/agent-native.css +56 -7
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/dist/voice/index.d.ts +5 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +4 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/voice-cleanup-prompt.d.ts +6 -0
- package/dist/voice/voice-cleanup-prompt.d.ts.map +1 -0
- package/dist/voice/voice-cleanup-prompt.js +26 -0
- package/dist/voice/voice-cleanup-prompt.js.map +1 -0
- package/dist/voice/voice-context.d.ts +25 -0
- package/dist/voice/voice-context.d.ts.map +1 -0
- package/dist/voice/voice-context.js +156 -0
- package/dist/voice/voice-context.js.map +1 -0
- package/dist/voice/voice-replacements.d.ts +4 -0
- package/dist/voice/voice-replacements.d.ts.map +1 -0
- package/dist/voice/voice-replacements.js +52 -0
- package/dist/voice/voice-replacements.js.map +1 -0
- package/docs/content/locales/ar-SA/template-design.mdx +41 -0
- package/docs/content/locales/de-DE/template-design.mdx +51 -0
- package/docs/content/locales/es-ES/template-design.mdx +50 -0
- package/docs/content/locales/fr-FR/template-design.mdx +49 -0
- package/docs/content/locales/hi-IN/template-design.mdx +44 -0
- package/docs/content/locales/ja-JP/template-design.mdx +46 -0
- package/docs/content/locales/ko-KR/template-design.mdx +44 -0
- package/docs/content/locales/pt-BR/template-design.mdx +48 -0
- package/docs/content/locales/zh-CN/template-design.mdx +36 -0
- package/docs/content/locales/zh-TW/template-design.mdx +38 -0
- package/docs/content/template-design.mdx +45 -0
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/corpus/templates/design/app/components/design/DesignToolbar.tsx +0 -339
- package/corpus/templates/design/app/components/design/PresentMode.tsx +0 -68
- package/corpus/templates/design/app/components/design/ViewportTabs.tsx +0 -149
- package/corpus/templates/design/app/components/design/ZoomControls.tsx +0 -157
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { useRef, useEffect, useCallback, useMemo } from "react";
|
|
1
|
+
import { sendToAgentChat, usePinchZoom, useT } from "@agent-native/core/client";
|
|
2
|
+
import { useRef, useEffect, useCallback, useMemo, useState } from "react";
|
|
4
3
|
|
|
5
4
|
// NOTE: This wires up the NEW shared visual-editor DrawOverlay + comment-pin
|
|
6
5
|
// components from `@/components/visual-editor`. The legacy iframe-only
|
|
@@ -9,6 +8,7 @@ import { useRef, useEffect, useCallback, useMemo } from "react";
|
|
|
9
8
|
import {
|
|
10
9
|
DrawOverlay as SharedDrawOverlay,
|
|
11
10
|
CanvasCommentPins,
|
|
11
|
+
type CanvasPin,
|
|
12
12
|
} from "@/components/visual-editor";
|
|
13
13
|
|
|
14
14
|
import { isTrustedCanvasBridgeMessage } from "./bridge-security";
|
|
@@ -67,6 +67,49 @@ const ZOOM_BRIDGE_SCRIPT = `
|
|
|
67
67
|
</script>
|
|
68
68
|
`;
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Embedded overview bridge. A screen preview is a real iframe, so normal wheel
|
|
72
|
+
* events never bubble to the overview canvas underneath. In embedded mode we
|
|
73
|
+
* forward a bounded wheel payload to the parent so the existing canvas wheel
|
|
74
|
+
* handler can pan/zoom exactly as if the pointer were over empty canvas.
|
|
75
|
+
*/
|
|
76
|
+
const EMBEDDED_WHEEL_BRIDGE_SCRIPT = `
|
|
77
|
+
<script data-agent-native-embedded-wheel-bridge>
|
|
78
|
+
(function() {
|
|
79
|
+
var enabled = __EMBEDDED_WHEEL_FORWARDING_ENABLED__;
|
|
80
|
+
if (!enabled) return;
|
|
81
|
+
function clamp(value, limit) {
|
|
82
|
+
var number = Number(value) || 0;
|
|
83
|
+
if (number > limit) return limit;
|
|
84
|
+
if (number < -limit) return -limit;
|
|
85
|
+
return number;
|
|
86
|
+
}
|
|
87
|
+
function onWheel(e) {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
e.stopPropagation();
|
|
90
|
+
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
|
|
91
|
+
try {
|
|
92
|
+
window.parent.postMessage({
|
|
93
|
+
type: 'embedded-canvas-wheel',
|
|
94
|
+
deltaX: clamp(e.deltaX, 240),
|
|
95
|
+
deltaY: clamp(e.deltaY, 240),
|
|
96
|
+
deltaZ: clamp(e.deltaZ, 240),
|
|
97
|
+
deltaMode: e.deltaMode,
|
|
98
|
+
clientX: e.clientX,
|
|
99
|
+
clientY: e.clientY,
|
|
100
|
+
ctrlKey: !!e.ctrlKey,
|
|
101
|
+
metaKey: !!e.metaKey,
|
|
102
|
+
shiftKey: !!e.shiftKey,
|
|
103
|
+
altKey: !!e.altKey,
|
|
104
|
+
}, '*');
|
|
105
|
+
} catch (err) {}
|
|
106
|
+
}
|
|
107
|
+
var target = document.documentElement || document.body || document;
|
|
108
|
+
target.addEventListener('wheel', onWheel, { passive: false, capture: true });
|
|
109
|
+
})();
|
|
110
|
+
</script>
|
|
111
|
+
`;
|
|
112
|
+
|
|
70
113
|
/**
|
|
71
114
|
* Navigation bridge. ALWAYS injected. A prototype lives in a `srcdoc` iframe,
|
|
72
115
|
* so a plain `<a href="/pricing">` resolves the relative URL against the PARENT
|
|
@@ -158,13 +201,55 @@ const NAV_BRIDGE_SCRIPT = `
|
|
|
158
201
|
</script>
|
|
159
202
|
`;
|
|
160
203
|
|
|
204
|
+
const EDITOR_BRIDGE_VAR_NAMES = [
|
|
205
|
+
"--design-editor-accent-color",
|
|
206
|
+
"--design-editor-accent-hover-color",
|
|
207
|
+
"--design-editor-selection-color",
|
|
208
|
+
"--design-editor-accent-strong-color",
|
|
209
|
+
"--design-editor-accent-contrast-color",
|
|
210
|
+
"--design-editor-measure-color",
|
|
211
|
+
"--background",
|
|
212
|
+
"--foreground",
|
|
213
|
+
"--border",
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
function readEditorBridgeThemeVars(): Record<string, string> {
|
|
217
|
+
if (typeof window === "undefined") return {};
|
|
218
|
+
const styles = window.getComputedStyle(document.documentElement);
|
|
219
|
+
return Object.fromEntries(
|
|
220
|
+
EDITOR_BRIDGE_VAR_NAMES.map((name) => [
|
|
221
|
+
name,
|
|
222
|
+
styles.getPropertyValue(name).trim(),
|
|
223
|
+
]).filter(([, value]) => value.length > 0),
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function createEditorBridgeThemeScript(vars: Record<string, string>) {
|
|
228
|
+
const serializedVars = JSON.stringify(vars).replace(/</g, "\\u003c");
|
|
229
|
+
return `
|
|
230
|
+
<script data-agent-native-editor-theme>
|
|
231
|
+
(function() {
|
|
232
|
+
var vars = ${serializedVars};
|
|
233
|
+
var root = document.documentElement;
|
|
234
|
+
Object.keys(vars).forEach(function(name) {
|
|
235
|
+
root.style.setProperty(name, vars[name]);
|
|
236
|
+
});
|
|
237
|
+
})();
|
|
238
|
+
</script>
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
|
|
161
242
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
243
|
+
* Editor chrome bridge: blocks native iframe app interaction outside Interact
|
|
244
|
+
* mode and replaces it with element hover/selection overlays. Double-click text
|
|
245
|
+
* editing is enabled only while the editor is specifically in Edit mode.
|
|
164
246
|
*/
|
|
165
|
-
const
|
|
166
|
-
<script data-agent-native-
|
|
247
|
+
const EDITOR_CHROME_BRIDGE_SCRIPT = `
|
|
248
|
+
<script data-agent-native-editor-chrome-bridge>
|
|
167
249
|
(function() {
|
|
250
|
+
var textEditingEnabled = __TEXT_EDITING_ENABLED__;
|
|
251
|
+
var scaleToolEnabled = false;
|
|
252
|
+
|
|
168
253
|
function escapeIdent(value) {
|
|
169
254
|
if (window.CSS && typeof window.CSS.escape === 'function') {
|
|
170
255
|
return window.CSS.escape(value);
|
|
@@ -172,65 +257,199 @@ const EDIT_BRIDGE_SCRIPT = `
|
|
|
172
257
|
return String(value).replace(/[^a-zA-Z0-9_-]/g, '\\\\$&');
|
|
173
258
|
}
|
|
174
259
|
|
|
175
|
-
function
|
|
260
|
+
function escapeAttribute(value) {
|
|
261
|
+
return String(value).replace(/\\\\/g, '\\\\\\\\').replace(/"/g, '\\\\"');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function attributeSelector(el, name) {
|
|
265
|
+
var value = el && el.getAttribute && el.getAttribute(name);
|
|
266
|
+
return value ? '[' + name + '="' + escapeAttribute(value) + '"]' : '';
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function classSelectorSuffix(el, maxCount) {
|
|
270
|
+
if (!el || !el.classList) return '';
|
|
271
|
+
return Array.prototype.slice.call(el.classList, 0, maxCount)
|
|
272
|
+
.map(function(token) { return '.' + escapeIdent(token); })
|
|
273
|
+
.join('');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function selectorPart(el) {
|
|
277
|
+
if (!el || !el.tagName) return '';
|
|
278
|
+
var stableSelector =
|
|
279
|
+
attributeSelector(el, 'data-agent-native-node-id') ||
|
|
280
|
+
attributeSelector(el, 'data-code-layer-id') ||
|
|
281
|
+
attributeSelector(el, 'data-layer-id') ||
|
|
282
|
+
attributeSelector(el, 'data-builder-id') ||
|
|
283
|
+
attributeSelector(el, 'data-loc');
|
|
284
|
+
if (stableSelector) return el.tagName.toLowerCase() + stableSelector;
|
|
176
285
|
if (el.id) return '#' + escapeIdent(el.id);
|
|
177
|
-
var
|
|
178
|
-
|
|
179
|
-
if (
|
|
286
|
+
var part = el.tagName.toLowerCase() + (stableSelector || classSelectorSuffix(el, 2));
|
|
287
|
+
var parent = el.parentElement;
|
|
288
|
+
if (parent) {
|
|
289
|
+
var sameTag = Array.prototype.filter.call(
|
|
290
|
+
parent.children,
|
|
291
|
+
function(child) { return child.tagName === el.tagName; }
|
|
292
|
+
);
|
|
293
|
+
if (sameTag.length > 1) {
|
|
294
|
+
part += ':nth-of-type(' + (sameTag.indexOf(el) + 1) + ')';
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return part;
|
|
298
|
+
}
|
|
180
299
|
|
|
300
|
+
function selectorPath(el, stopEl) {
|
|
181
301
|
var parts = [];
|
|
182
302
|
var node = el;
|
|
183
|
-
while (node && node.nodeType === 1
|
|
184
|
-
|
|
185
|
-
if (node
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
303
|
+
while (node && node.nodeType === 1) {
|
|
304
|
+
if (node !== stopEl) parts.unshift(selectorPart(node));
|
|
305
|
+
if (node === stopEl) break;
|
|
306
|
+
node = node.parentElement;
|
|
307
|
+
}
|
|
308
|
+
return parts.slice(-5).join(' > ');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function getSourceId(el) {
|
|
312
|
+
if (!el || !el.getAttribute) return '';
|
|
313
|
+
return (
|
|
314
|
+
el.getAttribute('data-agent-native-node-id') ||
|
|
315
|
+
el.getAttribute('data-code-layer-id') ||
|
|
316
|
+
el.getAttribute('data-layer-id') ||
|
|
317
|
+
el.getAttribute('data-builder-id') ||
|
|
318
|
+
el.getAttribute('data-loc') ||
|
|
319
|
+
el.id ||
|
|
320
|
+
''
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function freshRuntimeNodeId(prefix) {
|
|
325
|
+
var random = '';
|
|
326
|
+
try {
|
|
327
|
+
if (window.crypto && window.crypto.getRandomValues) {
|
|
328
|
+
var bytes = new Uint32Array(2);
|
|
329
|
+
window.crypto.getRandomValues(bytes);
|
|
330
|
+
random = Array.prototype.map.call(bytes, function(part) {
|
|
331
|
+
return part.toString(36);
|
|
332
|
+
}).join('');
|
|
189
333
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
334
|
+
} catch (_err) {}
|
|
335
|
+
if (!random) random = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
|
|
336
|
+
return 'an-' + String(prefix || 'copy') + '-' + random;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function resetRuntimeStableIds(root) {
|
|
340
|
+
if (!root || !root.querySelectorAll) return;
|
|
341
|
+
var nodes = [root].concat(Array.prototype.slice.call(root.querySelectorAll('[data-agent-native-node-id]')));
|
|
342
|
+
nodes.forEach(function(node, index) {
|
|
343
|
+
if (node && node.setAttribute) {
|
|
344
|
+
node.setAttribute('data-agent-native-node-id', freshRuntimeNodeId(index === 0 ? 'copy' : 'copy-child'));
|
|
199
345
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function getSelector(el) {
|
|
350
|
+
var stableOwnSelector =
|
|
351
|
+
attributeSelector(el, 'data-agent-native-node-id') ||
|
|
352
|
+
attributeSelector(el, 'data-code-layer-id') ||
|
|
353
|
+
attributeSelector(el, 'data-layer-id') ||
|
|
354
|
+
attributeSelector(el, 'data-builder-id') ||
|
|
355
|
+
attributeSelector(el, 'data-loc');
|
|
356
|
+
if (stableOwnSelector) return stableOwnSelector;
|
|
357
|
+
|
|
358
|
+
if (el.id) return '#' + escapeIdent(el.id);
|
|
359
|
+
var stableAncestor = el.closest('[data-agent-native-node-id],[data-code-layer-id],[data-layer-id],[data-builder-id],[data-loc]');
|
|
360
|
+
if (stableAncestor && stableAncestor !== el) {
|
|
361
|
+
var stableAncestorSelector = selectorPart(stableAncestor);
|
|
362
|
+
if (stableAncestorSelector) {
|
|
363
|
+
var descendantPath = selectorPath(el, stableAncestor);
|
|
364
|
+
var descendantParts = descendantPath ? descendantPath.split(' > ') : [];
|
|
365
|
+
if (descendantParts.length) {
|
|
366
|
+
return stableAncestorSelector + ' > ' + descendantParts.join(' > ');
|
|
367
|
+
}
|
|
368
|
+
return stableAncestorSelector;
|
|
205
369
|
}
|
|
206
370
|
}
|
|
207
|
-
|
|
371
|
+
|
|
372
|
+
return selectorPath(el);
|
|
208
373
|
}
|
|
209
374
|
|
|
210
375
|
function getElementInfo(el) {
|
|
211
376
|
var cs = window.getComputedStyle(el);
|
|
212
377
|
var rect = el.getBoundingClientRect();
|
|
213
|
-
var
|
|
214
|
-
? window.getComputedStyle(el.parentElement)
|
|
378
|
+
var parentStyles = el.parentElement
|
|
379
|
+
? window.getComputedStyle(el.parentElement)
|
|
380
|
+
: null;
|
|
381
|
+
var parentDisplay = parentStyles ? parentStyles.display : undefined;
|
|
382
|
+
var sourceId = getSourceId(el) || getSelector(el);
|
|
383
|
+
var parentLayout = parentStyles
|
|
384
|
+
? {
|
|
385
|
+
display: parentStyles.display,
|
|
386
|
+
flexDirection: parentStyles.flexDirection,
|
|
387
|
+
alignItems: parentStyles.alignItems,
|
|
388
|
+
justifyContent: parentStyles.justifyContent,
|
|
389
|
+
gap: parentStyles.gap,
|
|
390
|
+
gridTemplateColumns: parentStyles.gridTemplateColumns,
|
|
391
|
+
gridTemplateRows: parentStyles.gridTemplateRows,
|
|
392
|
+
position: parentStyles.position,
|
|
393
|
+
}
|
|
215
394
|
: undefined;
|
|
395
|
+
var capabilities = [
|
|
396
|
+
{
|
|
397
|
+
kind: 'deterministic-style-edit',
|
|
398
|
+
label: 'deterministic-style-edit',
|
|
399
|
+
confidence: 0.92,
|
|
400
|
+
reason: 'Inline style can be patched and replayed through HMR/collab.',
|
|
401
|
+
},
|
|
402
|
+
];
|
|
403
|
+
if (el.classList && el.classList.length > 0) {
|
|
404
|
+
capabilities.push({
|
|
405
|
+
kind: 'deterministic-class-edit',
|
|
406
|
+
label: 'deterministic-class-edit',
|
|
407
|
+
confidence: 0.78,
|
|
408
|
+
reason: 'Class tokens are visible on the selected element.',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
if (parentDisplay === 'flex' || parentDisplay === 'inline-flex' || parentDisplay === 'grid' || parentDisplay === 'inline-grid') {
|
|
412
|
+
capabilities.push({
|
|
413
|
+
kind: 'agent-structural-edit',
|
|
414
|
+
label: 'agent-structural-edit',
|
|
415
|
+
confidence: 0.54,
|
|
416
|
+
reason: 'Parent layout context decides whether movement means gap, order, alignment, or wrapper structure.',
|
|
417
|
+
});
|
|
418
|
+
}
|
|
216
419
|
return {
|
|
217
420
|
tagName: el.tagName.toLowerCase(),
|
|
218
421
|
id: el.id || undefined,
|
|
422
|
+
sourceId: sourceId,
|
|
219
423
|
selector: getSelector(el),
|
|
220
424
|
classes: Array.from(el.classList),
|
|
221
425
|
computedStyles: {
|
|
222
426
|
color: cs.color,
|
|
223
427
|
backgroundColor: cs.backgroundColor,
|
|
428
|
+
backgroundImage: cs.backgroundImage,
|
|
429
|
+
backgroundBlendMode: cs.backgroundBlendMode,
|
|
224
430
|
fontSize: cs.fontSize,
|
|
225
431
|
fontFamily: cs.fontFamily,
|
|
226
432
|
fontWeight: cs.fontWeight,
|
|
227
433
|
lineHeight: cs.lineHeight,
|
|
228
434
|
letterSpacing: cs.letterSpacing,
|
|
229
435
|
textAlign: cs.textAlign,
|
|
230
|
-
|
|
231
|
-
|
|
436
|
+
display: cs.display,
|
|
437
|
+
overflow: cs.overflow,
|
|
438
|
+
flexDirection: cs.flexDirection,
|
|
232
439
|
justifyContent: cs.justifyContent,
|
|
233
440
|
alignItems: cs.alignItems,
|
|
441
|
+
alignSelf: cs.alignSelf,
|
|
442
|
+
flexGrow: cs.flexGrow,
|
|
443
|
+
flexShrink: cs.flexShrink,
|
|
444
|
+
flexBasis: cs.flexBasis,
|
|
445
|
+
order: cs.order,
|
|
446
|
+
gridColumn: cs.gridColumn,
|
|
447
|
+
gridRow: cs.gridRow,
|
|
448
|
+
position: cs.position,
|
|
449
|
+
top: cs.top,
|
|
450
|
+
right: cs.right,
|
|
451
|
+
bottom: cs.bottom,
|
|
452
|
+
left: cs.left,
|
|
234
453
|
gap: cs.gap,
|
|
235
454
|
width: cs.width,
|
|
236
455
|
height: cs.height,
|
|
@@ -244,29 +463,302 @@ const EDIT_BRIDGE_SCRIPT = `
|
|
|
244
463
|
marginBottom: cs.marginBottom,
|
|
245
464
|
marginLeft: cs.marginLeft,
|
|
246
465
|
borderWidth: cs.borderWidth,
|
|
466
|
+
borderStyle: cs.borderStyle,
|
|
247
467
|
borderColor: cs.borderColor,
|
|
248
468
|
borderRadius: cs.borderRadius,
|
|
469
|
+
borderTopLeftRadius: cs.borderTopLeftRadius,
|
|
470
|
+
borderTopRightRadius: cs.borderTopRightRadius,
|
|
471
|
+
borderBottomRightRadius: cs.borderBottomRightRadius,
|
|
472
|
+
borderBottomLeftRadius: cs.borderBottomLeftRadius,
|
|
473
|
+
outlineWidth: cs.outlineWidth,
|
|
474
|
+
outlineStyle: cs.outlineStyle,
|
|
475
|
+
outlineColor: cs.outlineColor,
|
|
476
|
+
outlineOffset: cs.outlineOffset,
|
|
477
|
+
boxShadow: cs.boxShadow,
|
|
478
|
+
textShadow: cs.textShadow,
|
|
479
|
+
filter: cs.filter,
|
|
480
|
+
mixBlendMode: cs.mixBlendMode,
|
|
481
|
+
zIndex: cs.zIndex,
|
|
249
482
|
},
|
|
250
483
|
boundingRect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
|
|
251
484
|
textContent: el.textContent ? el.textContent.slice(0, 200) : undefined,
|
|
485
|
+
htmlContent: el.innerHTML && el.innerHTML !== el.textContent ? el.innerHTML.slice(0, 4000) : undefined,
|
|
252
486
|
isFlexContainer: cs.display === 'flex' || cs.display === 'inline-flex',
|
|
253
487
|
isFlexChild: parentDisplay === 'flex' || parentDisplay === 'inline-flex',
|
|
254
488
|
parentDisplay: parentDisplay,
|
|
489
|
+
parentLayout: parentLayout,
|
|
490
|
+
editCapabilities: capabilities,
|
|
491
|
+
confidence: capabilities.reduce(function(best, item) {
|
|
492
|
+
return Math.max(best, item.confidence || 0);
|
|
493
|
+
}, 0),
|
|
255
494
|
};
|
|
256
495
|
}
|
|
257
496
|
|
|
497
|
+
var shieldOverlay = document.createElement('div');
|
|
498
|
+
shieldOverlay.setAttribute('data-agent-native-edit-overlay', 'shield');
|
|
499
|
+
shieldOverlay.style.cssText = 'position:fixed;inset:0;z-index:99990;background:transparent;pointer-events:auto;touch-action:none;cursor:default;';
|
|
500
|
+
document.body.appendChild(shieldOverlay);
|
|
501
|
+
|
|
258
502
|
var highlightOverlay = document.createElement('div');
|
|
259
503
|
highlightOverlay.setAttribute('data-agent-native-edit-overlay', 'highlight');
|
|
260
|
-
highlightOverlay.style.cssText = 'position:fixed;pointer-events:none;z-index:99999;border:
|
|
504
|
+
highlightOverlay.style.cssText = 'position:fixed;pointer-events:none;z-index:99999;border:1.5px solid var(--design-editor-accent-color);background:transparent;display:none;box-sizing:border-box;';
|
|
261
505
|
document.body.appendChild(highlightOverlay);
|
|
262
506
|
|
|
263
507
|
var selectionOverlay = document.createElement('div');
|
|
264
508
|
selectionOverlay.setAttribute('data-agent-native-edit-overlay', 'selection');
|
|
265
|
-
selectionOverlay.style.cssText = 'position:fixed;pointer-events:
|
|
509
|
+
selectionOverlay.style.cssText = 'position:fixed;pointer-events:auto;z-index:99998;border:1.5px solid var(--design-editor-accent-color);background:transparent;display:none;box-sizing:border-box;cursor:move;';
|
|
510
|
+
['n','e','s','w'].forEach(function(pos) {
|
|
511
|
+
var edge = document.createElement('span');
|
|
512
|
+
edge.setAttribute('data-agent-native-edge-handle', pos);
|
|
513
|
+
var cursor = pos === 'n' || pos === 's' ? 'ns-resize' : 'ew-resize';
|
|
514
|
+
edge.style.cssText = 'position:absolute;pointer-events:auto;cursor:' + cursor + ';background:transparent;';
|
|
515
|
+
if (pos === 'n') {
|
|
516
|
+
edge.style.left = '0';
|
|
517
|
+
edge.style.right = '0';
|
|
518
|
+
edge.style.top = '-5px';
|
|
519
|
+
edge.style.height = '10px';
|
|
520
|
+
}
|
|
521
|
+
if (pos === 's') {
|
|
522
|
+
edge.style.left = '0';
|
|
523
|
+
edge.style.right = '0';
|
|
524
|
+
edge.style.bottom = '-5px';
|
|
525
|
+
edge.style.height = '10px';
|
|
526
|
+
}
|
|
527
|
+
if (pos === 'e') {
|
|
528
|
+
edge.style.top = '0';
|
|
529
|
+
edge.style.bottom = '0';
|
|
530
|
+
edge.style.right = '-5px';
|
|
531
|
+
edge.style.width = '10px';
|
|
532
|
+
}
|
|
533
|
+
if (pos === 'w') {
|
|
534
|
+
edge.style.top = '0';
|
|
535
|
+
edge.style.bottom = '0';
|
|
536
|
+
edge.style.left = '-5px';
|
|
537
|
+
edge.style.width = '10px';
|
|
538
|
+
}
|
|
539
|
+
selectionOverlay.appendChild(edge);
|
|
540
|
+
});
|
|
541
|
+
['nw','ne','se','sw'].forEach(function(pos) {
|
|
542
|
+
var handle = document.createElement('span');
|
|
543
|
+
handle.setAttribute('data-agent-native-edit-handle', pos);
|
|
544
|
+
var cursor = pos === 'n' || pos === 's' ? 'ns-resize' : pos === 'e' || pos === 'w' ? 'ew-resize' : pos === 'nw' || pos === 'se' ? 'nwse-resize' : 'nesw-resize';
|
|
545
|
+
handle.style.cssText = 'position:absolute;width:7px;height:7px;border:1px solid var(--design-editor-accent-color);background:var(--design-editor-accent-contrast-color);box-sizing:border-box;border-radius:1px;pointer-events:auto;cursor:' + cursor + ';';
|
|
546
|
+
if (pos.indexOf('n') !== -1) handle.style.top = '-4px';
|
|
547
|
+
if (pos.indexOf('s') !== -1) handle.style.bottom = '-4px';
|
|
548
|
+
if (pos.indexOf('w') !== -1) handle.style.left = '-4px';
|
|
549
|
+
if (pos.indexOf('e') !== -1) handle.style.right = '-4px';
|
|
550
|
+
if (pos === 'n' || pos === 's') {
|
|
551
|
+
handle.style.left = '50%';
|
|
552
|
+
handle.style.transform = 'translateX(-50%)';
|
|
553
|
+
}
|
|
554
|
+
if (pos === 'e' || pos === 'w') {
|
|
555
|
+
handle.style.top = '50%';
|
|
556
|
+
handle.style.transform = 'translateY(-50%)';
|
|
557
|
+
}
|
|
558
|
+
selectionOverlay.appendChild(handle);
|
|
559
|
+
});
|
|
560
|
+
['nw','ne','se','sw'].forEach(function(pos) {
|
|
561
|
+
var rotate = document.createElement('span');
|
|
562
|
+
rotate.setAttribute('data-agent-native-rotate-handle', pos);
|
|
563
|
+
rotate.style.cssText = 'position:absolute;width:18px;height:18px;border-radius:999px;pointer-events:auto;cursor:grab;';
|
|
564
|
+
if (pos.indexOf('n') !== -1) rotate.style.top = '-26px';
|
|
565
|
+
if (pos.indexOf('s') !== -1) rotate.style.bottom = '-26px';
|
|
566
|
+
if (pos.indexOf('w') !== -1) rotate.style.left = '-26px';
|
|
567
|
+
if (pos.indexOf('e') !== -1) rotate.style.right = '-26px';
|
|
568
|
+
selectionOverlay.appendChild(rotate);
|
|
569
|
+
});
|
|
570
|
+
var paddingOverlay = document.createElement('div');
|
|
571
|
+
paddingOverlay.setAttribute('data-agent-native-padding-overlay', '');
|
|
572
|
+
paddingOverlay.style.cssText = 'position:absolute;border:1px dashed var(--design-editor-accent-strong-color);border-radius:2px;pointer-events:none;';
|
|
573
|
+
selectionOverlay.appendChild(paddingOverlay);
|
|
266
574
|
document.body.appendChild(selectionOverlay);
|
|
267
575
|
|
|
268
|
-
var
|
|
269
|
-
|
|
576
|
+
var transformBadge = document.createElement('div');
|
|
577
|
+
transformBadge.setAttribute('data-agent-native-transform-badge', '');
|
|
578
|
+
transformBadge.style.cssText = 'position:fixed;z-index:100000;display:none;pointer-events:none;border:1px solid hsl(var(--border));border-radius:4px;background:hsl(var(--background) / 0.96);color:hsl(var(--foreground));font:11px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;padding:3px 5px;box-shadow:0 8px 20px color-mix(in srgb, hsl(var(--foreground)) 16%, transparent);';
|
|
579
|
+
document.body.appendChild(transformBadge);
|
|
580
|
+
|
|
581
|
+
var insertionGuide = document.createElement('div');
|
|
582
|
+
insertionGuide.setAttribute('data-agent-native-insertion-guide', '');
|
|
583
|
+
insertionGuide.style.cssText = 'position:fixed;z-index:100000;display:none;pointer-events:none;background:var(--design-editor-accent-color);border-radius:999px;box-shadow:0 0 0 1px var(--design-editor-accent-color);';
|
|
584
|
+
document.body.appendChild(insertionGuide);
|
|
585
|
+
|
|
586
|
+
var measurementOverlay = document.createElement('div');
|
|
587
|
+
measurementOverlay.setAttribute('data-agent-native-measurement-overlay', '');
|
|
588
|
+
measurementOverlay.style.cssText = 'position:fixed;inset:0;z-index:100001;display:none;pointer-events:none;color:var(--design-editor-measure-color);font:11px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace;';
|
|
589
|
+
document.body.appendChild(measurementOverlay);
|
|
590
|
+
|
|
591
|
+
var selectedEl = null;
|
|
592
|
+
var hoveredEl = null;
|
|
593
|
+
var activeTextEditEl = null;
|
|
594
|
+
var textEditPointerState = null;
|
|
595
|
+
var pendingStructureMove = null;
|
|
596
|
+
var lockedSelectors = [];
|
|
597
|
+
var hiddenSelectors = [];
|
|
598
|
+
|
|
599
|
+
function matchesSelectorList(el, selectors) {
|
|
600
|
+
if (!el || !selectors || selectors.length === 0) return false;
|
|
601
|
+
for (var i = 0; i < selectors.length; i += 1) {
|
|
602
|
+
try {
|
|
603
|
+
if (el.matches(selectors[i]) || el.closest(selectors[i])) return true;
|
|
604
|
+
} catch (_err) {}
|
|
605
|
+
}
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function isLayerInteractionBlocked(el) {
|
|
610
|
+
return matchesSelectorList(el, lockedSelectors) || matchesSelectorList(el, hiddenSelectors);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function applyHiddenSelectors() {
|
|
614
|
+
document.querySelectorAll('[data-agent-native-runtime-hidden]').forEach(function(el) {
|
|
615
|
+
var previous = el.getAttribute('data-agent-native-previous-display');
|
|
616
|
+
if (previous === null) {
|
|
617
|
+
el.style.removeProperty('display');
|
|
618
|
+
} else {
|
|
619
|
+
el.style.display = previous;
|
|
620
|
+
}
|
|
621
|
+
el.removeAttribute('data-agent-native-runtime-hidden');
|
|
622
|
+
el.removeAttribute('data-agent-native-previous-display');
|
|
623
|
+
});
|
|
624
|
+
hiddenSelectors.forEach(function(selector) {
|
|
625
|
+
try {
|
|
626
|
+
document.querySelectorAll(selector).forEach(function(el) {
|
|
627
|
+
if (!el.hasAttribute('data-agent-native-runtime-hidden')) {
|
|
628
|
+
el.setAttribute('data-agent-native-previous-display', el.style.display || '');
|
|
629
|
+
}
|
|
630
|
+
el.setAttribute('data-agent-native-runtime-hidden', 'true');
|
|
631
|
+
el.style.display = 'none';
|
|
632
|
+
});
|
|
633
|
+
} catch (_err) {}
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function replaceRuntimeDocument(html, preferredSelector, selectorCandidates) {
|
|
638
|
+
if (typeof html !== 'string') return;
|
|
639
|
+
if (activeTextEditEl) {
|
|
640
|
+
applyHiddenSelectors();
|
|
641
|
+
refreshOverlays();
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
var parser = new DOMParser();
|
|
645
|
+
var nextDoc = parser.parseFromString(html, 'text/html');
|
|
646
|
+
if (!nextDoc || !nextDoc.body) return;
|
|
647
|
+
|
|
648
|
+
var persistentNodes = Array.prototype.slice.call(
|
|
649
|
+
document.querySelectorAll('[data-agent-native-edit-overlay]'),
|
|
650
|
+
);
|
|
651
|
+
var activeSelector = preferredSelector || (selectedEl ? getSelector(selectedEl) : '');
|
|
652
|
+
var activeCandidates = [];
|
|
653
|
+
if (Array.isArray(selectorCandidates)) {
|
|
654
|
+
selectorCandidates.forEach(function(selector) {
|
|
655
|
+
if (typeof selector === 'string' && selector && activeCandidates.indexOf(selector) === -1) {
|
|
656
|
+
activeCandidates.push(selector);
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
if (activeSelector && activeCandidates.indexOf(activeSelector) === -1) {
|
|
661
|
+
activeCandidates.push(activeSelector);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
var nextHeadHtml = nextDoc.head ? nextDoc.head.innerHTML : '';
|
|
665
|
+
if (nextHeadHtml === document.head.innerHTML && activeCandidates.length > 0) {
|
|
666
|
+
var currentMatch = null;
|
|
667
|
+
var nextMatch = null;
|
|
668
|
+
var matchedSelector = '';
|
|
669
|
+
for (var matchIndex = 0; matchIndex < activeCandidates.length; matchIndex += 1) {
|
|
670
|
+
try {
|
|
671
|
+
currentMatch = document.querySelector(activeCandidates[matchIndex]);
|
|
672
|
+
nextMatch = nextDoc.querySelector(activeCandidates[matchIndex]);
|
|
673
|
+
matchedSelector = activeCandidates[matchIndex];
|
|
674
|
+
} catch (_err) {
|
|
675
|
+
currentMatch = null;
|
|
676
|
+
nextMatch = null;
|
|
677
|
+
}
|
|
678
|
+
if (currentMatch) break;
|
|
679
|
+
}
|
|
680
|
+
if (
|
|
681
|
+
currentMatch &&
|
|
682
|
+
currentMatch !== document.body &&
|
|
683
|
+
currentMatch !== document.documentElement &&
|
|
684
|
+
!isOverlayElement(currentMatch)
|
|
685
|
+
) {
|
|
686
|
+
if (nextMatch) {
|
|
687
|
+
currentMatch.replaceWith(document.importNode(nextMatch, true));
|
|
688
|
+
} else if (currentMatch !== document.body && currentMatch !== document.documentElement) {
|
|
689
|
+
currentMatch.parentElement && currentMatch.parentElement.removeChild(currentMatch);
|
|
690
|
+
}
|
|
691
|
+
applyHiddenSelectors();
|
|
692
|
+
selectedEl = null;
|
|
693
|
+
if (nextMatch) {
|
|
694
|
+
try {
|
|
695
|
+
selectedEl = document.querySelector(matchedSelector);
|
|
696
|
+
} catch (_err) {}
|
|
697
|
+
}
|
|
698
|
+
hoveredEl = null;
|
|
699
|
+
if (selectedEl && !isLayerInteractionBlocked(selectedEl)) {
|
|
700
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
701
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
|
|
702
|
+
} else {
|
|
703
|
+
selectionOverlay.style.display = 'none';
|
|
704
|
+
}
|
|
705
|
+
highlightOverlay.style.display = 'none';
|
|
706
|
+
hideMeasurements();
|
|
707
|
+
refreshOverlays();
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (document.head.innerHTML !== nextHeadHtml) {
|
|
712
|
+
document.head.innerHTML = nextHeadHtml;
|
|
713
|
+
}
|
|
714
|
+
Array.prototype.slice.call(document.body.attributes).forEach(function(attribute) {
|
|
715
|
+
document.body.removeAttribute(attribute.name);
|
|
716
|
+
});
|
|
717
|
+
Array.prototype.slice.call(nextDoc.body.attributes).forEach(function(attribute) {
|
|
718
|
+
document.body.setAttribute(attribute.name, attribute.value);
|
|
719
|
+
});
|
|
720
|
+
document.body.innerHTML = nextDoc.body.innerHTML;
|
|
721
|
+
persistentNodes.forEach(function(node) {
|
|
722
|
+
document.body.appendChild(node);
|
|
723
|
+
});
|
|
724
|
+
applyHiddenSelectors();
|
|
725
|
+
|
|
726
|
+
selectedEl = null;
|
|
727
|
+
hoveredEl = null;
|
|
728
|
+
for (var i = 0; i < activeCandidates.length && !selectedEl; i += 1) {
|
|
729
|
+
try {
|
|
730
|
+
var match = document.querySelector(activeCandidates[i]);
|
|
731
|
+
if (match && !isLayerInteractionBlocked(match)) selectedEl = match;
|
|
732
|
+
} catch (_err) {}
|
|
733
|
+
}
|
|
734
|
+
if (selectedEl) {
|
|
735
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
736
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
|
|
737
|
+
} else {
|
|
738
|
+
selectionOverlay.style.display = 'none';
|
|
739
|
+
}
|
|
740
|
+
highlightOverlay.style.display = 'none';
|
|
741
|
+
hideMeasurements();
|
|
742
|
+
refreshOverlays();
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function updatePaddingOverlay(el) {
|
|
746
|
+
if (!el) {
|
|
747
|
+
paddingOverlay.style.display = 'none';
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
var cs = window.getComputedStyle(el);
|
|
751
|
+
var top = readPx(cs.paddingTop) + readPx(cs.borderTopWidth);
|
|
752
|
+
var right = readPx(cs.paddingRight) + readPx(cs.borderRightWidth);
|
|
753
|
+
var bottom = readPx(cs.paddingBottom) + readPx(cs.borderBottomWidth);
|
|
754
|
+
var left = readPx(cs.paddingLeft) + readPx(cs.borderLeftWidth);
|
|
755
|
+
var hasPadding = top > 0 || right > 0 || bottom > 0 || left > 0;
|
|
756
|
+
paddingOverlay.style.display = hasPadding ? 'block' : 'none';
|
|
757
|
+
paddingOverlay.style.top = top + 'px';
|
|
758
|
+
paddingOverlay.style.right = right + 'px';
|
|
759
|
+
paddingOverlay.style.bottom = bottom + 'px';
|
|
760
|
+
paddingOverlay.style.left = left + 'px';
|
|
761
|
+
}
|
|
270
762
|
|
|
271
763
|
function positionOverlay(overlay, el) {
|
|
272
764
|
if (!el || !document.documentElement.contains(el)) {
|
|
@@ -279,6 +771,7 @@ const EDIT_BRIDGE_SCRIPT = `
|
|
|
279
771
|
overlay.style.left = rect.left + 'px';
|
|
280
772
|
overlay.style.width = rect.width + 'px';
|
|
281
773
|
overlay.style.height = rect.height + 'px';
|
|
774
|
+
if (overlay === selectionOverlay) updatePaddingOverlay(el);
|
|
282
775
|
}
|
|
283
776
|
|
|
284
777
|
function refreshOverlays() {
|
|
@@ -286,38 +779,1031 @@ const EDIT_BRIDGE_SCRIPT = `
|
|
|
286
779
|
if (selectedEl) positionOverlay(selectionOverlay, selectedEl);
|
|
287
780
|
}
|
|
288
781
|
|
|
289
|
-
|
|
290
|
-
|
|
782
|
+
function hideMeasurements() {
|
|
783
|
+
measurementOverlay.style.display = 'none';
|
|
784
|
+
measurementOverlay.innerHTML = '';
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function addMeasurementLine(x1, y1, x2, y2, label) {
|
|
788
|
+
var horizontal = Math.abs(x2 - x1) >= Math.abs(y2 - y1);
|
|
789
|
+
var line = document.createElement('div');
|
|
790
|
+
var labelEl = document.createElement('div');
|
|
791
|
+
if (horizontal) {
|
|
792
|
+
var left = Math.min(x1, x2);
|
|
793
|
+
var width = Math.max(1, Math.abs(x2 - x1));
|
|
794
|
+
line.style.cssText = 'position:fixed;left:' + left + 'px;top:' + y1 + 'px;width:' + width + 'px;border-top:1px dashed var(--design-editor-measure-color);';
|
|
795
|
+
labelEl.style.cssText = 'position:fixed;left:' + (left + width / 2) + 'px;top:' + (y1 - 9) + 'px;transform:translateX(-50%);border-radius:3px;background:var(--design-editor-measure-color);color:white;padding:1px 4px;';
|
|
796
|
+
} else {
|
|
797
|
+
var top = Math.min(y1, y2);
|
|
798
|
+
var height = Math.max(1, Math.abs(y2 - y1));
|
|
799
|
+
line.style.cssText = 'position:fixed;left:' + x1 + 'px;top:' + top + 'px;height:' + height + 'px;border-left:1px dashed var(--design-editor-measure-color);';
|
|
800
|
+
labelEl.style.cssText = 'position:fixed;left:' + (x1 + 5) + 'px;top:' + (top + height / 2) + 'px;transform:translateY(-50%);border-radius:3px;background:var(--design-editor-measure-color);color:white;padding:1px 4px;';
|
|
801
|
+
}
|
|
802
|
+
labelEl.textContent = label;
|
|
803
|
+
measurementOverlay.appendChild(line);
|
|
804
|
+
measurementOverlay.appendChild(labelEl);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function showMeasurements(a, b) {
|
|
808
|
+
if (!a || !b || a === b) {
|
|
809
|
+
hideMeasurements();
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
var selectedRect = a.getBoundingClientRect();
|
|
813
|
+
var hoverRect = b.getBoundingClientRect();
|
|
814
|
+
measurementOverlay.innerHTML = '';
|
|
815
|
+
measurementOverlay.style.display = 'block';
|
|
816
|
+
|
|
817
|
+
if (hoverRect.right <= selectedRect.left) {
|
|
818
|
+
var yLeft = Math.max(hoverRect.top, Math.min(hoverRect.bottom, selectedRect.top + selectedRect.height / 2));
|
|
819
|
+
addMeasurementLine(hoverRect.right, yLeft, selectedRect.left, yLeft, Math.round(selectedRect.left - hoverRect.right) + 'px');
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
if (selectedRect.right <= hoverRect.left) {
|
|
823
|
+
var yRight = Math.max(selectedRect.top, Math.min(selectedRect.bottom, hoverRect.top + hoverRect.height / 2));
|
|
824
|
+
addMeasurementLine(selectedRect.right, yRight, hoverRect.left, yRight, Math.round(hoverRect.left - selectedRect.right) + 'px');
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
if (hoverRect.bottom <= selectedRect.top) {
|
|
828
|
+
var xTop = Math.max(hoverRect.left, Math.min(hoverRect.right, selectedRect.left + selectedRect.width / 2));
|
|
829
|
+
addMeasurementLine(xTop, hoverRect.bottom, xTop, selectedRect.top, Math.round(selectedRect.top - hoverRect.bottom) + 'px');
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if (selectedRect.bottom <= hoverRect.top) {
|
|
833
|
+
var xBottom = Math.max(selectedRect.left, Math.min(selectedRect.right, hoverRect.left + hoverRect.width / 2));
|
|
834
|
+
addMeasurementLine(xBottom, selectedRect.bottom, xBottom, hoverRect.top, Math.round(hoverRect.top - selectedRect.bottom) + 'px');
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
addMeasurementLine(
|
|
838
|
+
selectedRect.left + selectedRect.width / 2,
|
|
839
|
+
selectedRect.top + selectedRect.height / 2,
|
|
840
|
+
hoverRect.left + hoverRect.width / 2,
|
|
841
|
+
hoverRect.top + hoverRect.height / 2,
|
|
842
|
+
Math.round(Math.hypot(
|
|
843
|
+
hoverRect.left + hoverRect.width / 2 - (selectedRect.left + selectedRect.width / 2),
|
|
844
|
+
hoverRect.top + hoverRect.height / 2 - (selectedRect.top + selectedRect.height / 2)
|
|
845
|
+
)) + 'px'
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function elementFromEditorPoint(clientX, clientY) {
|
|
850
|
+
var shieldPointerEvents = shieldOverlay.style.pointerEvents;
|
|
851
|
+
var selectionPointerEvents = selectionOverlay.style.pointerEvents;
|
|
852
|
+
var highlightPointerEvents = highlightOverlay.style.pointerEvents;
|
|
853
|
+
shieldOverlay.style.pointerEvents = 'none';
|
|
854
|
+
selectionOverlay.style.pointerEvents = 'none';
|
|
855
|
+
highlightOverlay.style.pointerEvents = 'none';
|
|
856
|
+
var target = document.elementFromPoint(clientX, clientY);
|
|
857
|
+
shieldOverlay.style.pointerEvents = shieldPointerEvents;
|
|
858
|
+
selectionOverlay.style.pointerEvents = selectionPointerEvents;
|
|
859
|
+
highlightOverlay.style.pointerEvents = highlightPointerEvents;
|
|
860
|
+
if (!target || target.nodeType !== 1) return null;
|
|
861
|
+
if (isLayerInteractionBlocked(target)) return null;
|
|
862
|
+
return target;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
function stopNativeInteraction(e) {
|
|
291
866
|
e.preventDefault();
|
|
292
867
|
e.stopPropagation();
|
|
293
|
-
|
|
868
|
+
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function isEditorTypingTarget(target) {
|
|
872
|
+
if (!target || !target.closest) return false;
|
|
873
|
+
return !!target.closest('input, textarea, select, [contenteditable], [role="textbox"], [data-agent-native-text-editing]');
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function shouldForwardDesignHotkey(e) {
|
|
877
|
+
if (activeTextEditEl || isEditorTypingTarget(e.target) || e.isComposing) return false;
|
|
878
|
+
var key = e.key;
|
|
879
|
+
var normalized = key && key.length === 1 ? key.toLowerCase() : key;
|
|
880
|
+
var primary = e.metaKey || e.ctrlKey;
|
|
881
|
+
if (key === 'Escape' || key === 'Enter' || key === 'Tab') return true;
|
|
882
|
+
if (key === 'Delete' || key === 'Backspace') return !primary;
|
|
883
|
+
if (/^Arrow/.test(key || '')) return !e.altKey;
|
|
884
|
+
if (primary) {
|
|
885
|
+
return ['z','y','a','x','c','v','d','g','=','+','-','0',']','['].indexOf(normalized) !== -1 ||
|
|
886
|
+
e.code === 'Digit1' ||
|
|
887
|
+
e.code === 'Digit2' ||
|
|
888
|
+
key === '1' ||
|
|
889
|
+
key === '2';
|
|
890
|
+
}
|
|
891
|
+
if (e.shiftKey && (e.code === 'Digit1' || e.code === 'Digit2' || key === '1' || key === '2')) return true;
|
|
892
|
+
return !e.altKey && !e.shiftKey && ['v','f','r','t','p','h','c','k'].indexOf(normalized) !== -1;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function blurActiveTextEditor() {
|
|
896
|
+
var active = document.activeElement;
|
|
897
|
+
if (
|
|
898
|
+
active &&
|
|
899
|
+
active.closest &&
|
|
900
|
+
active.closest('[data-agent-native-text-editing]') &&
|
|
901
|
+
typeof active.blur === 'function'
|
|
902
|
+
) {
|
|
903
|
+
active.blur();
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
function setTextEditingPointerPassthrough(enabled) {
|
|
908
|
+
if (enabled) {
|
|
909
|
+
if (!textEditPointerState) {
|
|
910
|
+
textEditPointerState = {
|
|
911
|
+
shield: shieldOverlay.style.pointerEvents,
|
|
912
|
+
selection: selectionOverlay.style.pointerEvents,
|
|
913
|
+
highlight: highlightOverlay.style.pointerEvents,
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
shieldOverlay.style.pointerEvents = 'none';
|
|
917
|
+
selectionOverlay.style.pointerEvents = 'none';
|
|
918
|
+
highlightOverlay.style.pointerEvents = 'none';
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
if (!textEditPointerState) return;
|
|
922
|
+
shieldOverlay.style.pointerEvents = textEditPointerState.shield;
|
|
923
|
+
selectionOverlay.style.pointerEvents = textEditPointerState.selection;
|
|
924
|
+
highlightOverlay.style.pointerEvents = textEditPointerState.highlight;
|
|
925
|
+
textEditPointerState = null;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
function hasTextContent(el) {
|
|
929
|
+
return !!(el && el.textContent && el.textContent.trim().length > 0);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function isInlineEditableDescendant(el) {
|
|
933
|
+
if (!el || !el.tagName) return false;
|
|
934
|
+
return [
|
|
935
|
+
'a',
|
|
936
|
+
'abbr',
|
|
937
|
+
'b',
|
|
938
|
+
'br',
|
|
939
|
+
'cite',
|
|
940
|
+
'code',
|
|
941
|
+
'em',
|
|
942
|
+
'i',
|
|
943
|
+
'mark',
|
|
944
|
+
'small',
|
|
945
|
+
'span',
|
|
946
|
+
'strong',
|
|
947
|
+
'sub',
|
|
948
|
+
'sup',
|
|
949
|
+
'time',
|
|
950
|
+
'u',
|
|
951
|
+
'wbr'
|
|
952
|
+
].indexOf(el.tagName.toLowerCase()) !== -1;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
function hasOnlyInlineEditableChildren(el) {
|
|
956
|
+
if (!el || !hasTextContent(el)) return false;
|
|
957
|
+
var descendants = el.querySelectorAll ? el.querySelectorAll('*') : [];
|
|
958
|
+
for (var i = 0; i < descendants.length; i += 1) {
|
|
959
|
+
if (!isInlineEditableDescendant(descendants[i])) return false;
|
|
960
|
+
}
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function findTextEditTarget(hit) {
|
|
965
|
+
if (!hit || hit.nodeType !== 1 || hit === document.body || hit === document.documentElement) return null;
|
|
966
|
+
var selectedContainsHit = selectedEl && selectedEl.contains && selectedEl.contains(hit);
|
|
967
|
+
if (selectedContainsHit && hasOnlyInlineEditableChildren(selectedEl)) return selectedEl;
|
|
968
|
+
|
|
969
|
+
var candidate = null;
|
|
970
|
+
var node = hit;
|
|
971
|
+
while (node && node.nodeType === 1 && node !== document.body && node !== document.documentElement) {
|
|
972
|
+
if (hasOnlyInlineEditableChildren(node)) {
|
|
973
|
+
candidate = node;
|
|
974
|
+
}
|
|
975
|
+
if (selectedEl && node === selectedEl) break;
|
|
976
|
+
node = node.parentElement;
|
|
977
|
+
}
|
|
978
|
+
return candidate || hit;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function selectElementAtEvent(e) {
|
|
982
|
+
stopNativeInteraction(e);
|
|
983
|
+
blurActiveTextEditor();
|
|
984
|
+
var target = elementFromEditorPoint(e.clientX, e.clientY);
|
|
985
|
+
if (!target) return;
|
|
986
|
+
selectedEl = target;
|
|
294
987
|
var info = getElementInfo(selectedEl);
|
|
295
988
|
positionOverlay(selectionOverlay, selectedEl);
|
|
296
989
|
window.parent.postMessage({ type: 'element-select', payload: info }, '*');
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
function openContextMenuAtEvent(e) {
|
|
993
|
+
stopNativeInteraction(e);
|
|
994
|
+
blurActiveTextEditor();
|
|
995
|
+
var target = elementFromEditorPoint(e.clientX, e.clientY);
|
|
996
|
+
var info = null;
|
|
997
|
+
if (target) {
|
|
998
|
+
selectedEl = target;
|
|
999
|
+
info = getElementInfo(target);
|
|
1000
|
+
positionOverlay(selectionOverlay, target);
|
|
1001
|
+
window.parent.postMessage({ type: 'element-select', payload: info }, '*');
|
|
1002
|
+
}
|
|
1003
|
+
window.parent.postMessage({
|
|
1004
|
+
type: 'element-contextmenu',
|
|
1005
|
+
clientX: e.clientX,
|
|
1006
|
+
clientY: e.clientY,
|
|
1007
|
+
payload: info
|
|
1008
|
+
}, '*');
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function findRuntimeTarget(selector, selectorCandidates) {
|
|
1012
|
+
var candidates = [];
|
|
1013
|
+
if (Array.isArray(selectorCandidates)) {
|
|
1014
|
+
selectorCandidates.forEach(function(candidate) {
|
|
1015
|
+
if (typeof candidate === 'string' && candidate && candidates.indexOf(candidate) === -1) {
|
|
1016
|
+
candidates.push(candidate);
|
|
1017
|
+
}
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
if (selector && candidates.indexOf(selector) === -1) candidates.push(selector);
|
|
1021
|
+
if (selectedEl && document.documentElement.contains(selectedEl)) return selectedEl;
|
|
1022
|
+
for (var i = 0; i < candidates.length; i += 1) {
|
|
1023
|
+
try {
|
|
1024
|
+
var match = document.querySelector(candidates[i]);
|
|
1025
|
+
if (match && !isLayerInteractionBlocked(match)) return match;
|
|
1026
|
+
} catch (_err) {}
|
|
1027
|
+
}
|
|
1028
|
+
return null;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
function removeRuntimeTarget(selector, selectorCandidates) {
|
|
1032
|
+
var target = findRuntimeTarget(selector, selectorCandidates);
|
|
1033
|
+
if (!target || target === document.body || target === document.documentElement) return false;
|
|
1034
|
+
if (target.parentElement) target.parentElement.removeChild(target);
|
|
1035
|
+
if (selectedEl === target || !document.documentElement.contains(selectedEl)) {
|
|
1036
|
+
selectedEl = null;
|
|
1037
|
+
selectionOverlay.style.display = 'none';
|
|
1038
|
+
}
|
|
1039
|
+
hoveredEl = null;
|
|
1040
|
+
highlightOverlay.style.display = 'none';
|
|
1041
|
+
hideMeasurements();
|
|
1042
|
+
refreshOverlays();
|
|
1043
|
+
return true;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
function readPx(value) {
|
|
1047
|
+
var num = parseFloat(value);
|
|
1048
|
+
return Number.isFinite(num) ? num : 0;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
function currentRotation(el) {
|
|
1052
|
+
var transform = el.style.transform || window.getComputedStyle(el).transform || '';
|
|
1053
|
+
var match = transform.match(/rotate\\((-?\\d+(?:\\.\\d+)?)deg\\)/);
|
|
1054
|
+
if (match) return parseFloat(match[1]) || 0;
|
|
1055
|
+
if (transform && transform !== 'none' && window.DOMMatrixReadOnly) {
|
|
1056
|
+
try {
|
|
1057
|
+
var matrix = new DOMMatrixReadOnly(transform);
|
|
1058
|
+
return Math.round(Math.atan2(matrix.b, matrix.a) * 180 / Math.PI);
|
|
1059
|
+
} catch (err) {}
|
|
1060
|
+
}
|
|
1061
|
+
return 0;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function mergeRotation(el, degrees) {
|
|
1065
|
+
var inline = el.style.transform || '';
|
|
1066
|
+
var next = inline.match(/rotate\\((-?\\d+(?:\\.\\d+)?)deg\\)/)
|
|
1067
|
+
? inline.replace(/rotate\\((-?\\d+(?:\\.\\d+)?)deg\\)/, 'rotate(' + degrees + 'deg)')
|
|
1068
|
+
: (inline && inline !== 'none' ? inline + ' ' : '') + 'rotate(' + degrees + 'deg)';
|
|
1069
|
+
return next.trim();
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
function ensurePositionable(el) {
|
|
1073
|
+
var cs = window.getComputedStyle(el);
|
|
1074
|
+
if (cs.position === 'static') {
|
|
1075
|
+
el.style.position = 'relative';
|
|
1076
|
+
if (!el.style.left) el.style.left = '0px';
|
|
1077
|
+
if (!el.style.top) el.style.top = '0px';
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function postVisualStyleChange(styles) {
|
|
1082
|
+
if (!selectedEl) return;
|
|
1083
|
+
window.parent.postMessage({
|
|
1084
|
+
type: 'visual-style-change',
|
|
1085
|
+
selector: getSelector(selectedEl),
|
|
1086
|
+
styles: styles,
|
|
1087
|
+
payload: getElementInfo(selectedEl),
|
|
1088
|
+
}, '*');
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
function postTextContentChange(el, value, html) {
|
|
1092
|
+
window.parent.postMessage({
|
|
1093
|
+
type: 'text-content-change',
|
|
1094
|
+
selector: getSelector(el),
|
|
1095
|
+
value: value,
|
|
1096
|
+
html: html,
|
|
1097
|
+
payload: getElementInfo(el),
|
|
1098
|
+
}, '*');
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
function postTextEditingState(el, active) {
|
|
1102
|
+
var selection = window.getSelection && window.getSelection();
|
|
1103
|
+
window.parent.postMessage({
|
|
1104
|
+
type: 'text-editing-state',
|
|
1105
|
+
active: !!active,
|
|
1106
|
+
selector: el ? getSelector(el) : '',
|
|
1107
|
+
hasRange: !!(active && selection && selection.rangeCount > 0 && !selection.isCollapsed && selectionBelongsToElement(selection, el)),
|
|
1108
|
+
}, '*');
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function insertPlainTextAtSelection(text) {
|
|
1112
|
+
if (!text) return;
|
|
1113
|
+
if (document.queryCommandSupported && document.queryCommandSupported('insertText')) {
|
|
1114
|
+
document.execCommand('insertText', false, text);
|
|
1115
|
+
return;
|
|
1116
|
+
}
|
|
1117
|
+
var selection = window.getSelection && window.getSelection();
|
|
1118
|
+
if (!selection || selection.rangeCount === 0) return;
|
|
1119
|
+
var range = selection.getRangeAt(0);
|
|
1120
|
+
range.deleteContents();
|
|
1121
|
+
var textNode = document.createTextNode(text);
|
|
1122
|
+
range.insertNode(textNode);
|
|
1123
|
+
range.setStartAfter(textNode);
|
|
1124
|
+
range.setEndAfter(textNode);
|
|
1125
|
+
selection.removeAllRanges();
|
|
1126
|
+
selection.addRange(range);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function selectionBelongsToElement(selection, el) {
|
|
1130
|
+
if (!selection || !el || selection.rangeCount === 0) return false;
|
|
1131
|
+
var range = selection.getRangeAt(0);
|
|
1132
|
+
var ancestor = range.commonAncestorContainer;
|
|
1133
|
+
var ancestorEl = ancestor && ancestor.nodeType === 1 ? ancestor : ancestor && ancestor.parentElement;
|
|
1134
|
+
return !!(ancestorEl && (ancestorEl === el || el.contains(ancestorEl)));
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
function applyTextRangeStyle(property, value) {
|
|
1138
|
+
if (!activeTextEditEl || !property) return false;
|
|
1139
|
+
var selection = window.getSelection && window.getSelection();
|
|
1140
|
+
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return false;
|
|
1141
|
+
if (!selectionBelongsToElement(selection, activeTextEditEl)) return false;
|
|
1142
|
+
var range = selection.getRangeAt(0);
|
|
1143
|
+
var span = document.createElement('span');
|
|
1144
|
+
span.style[property] = value;
|
|
1145
|
+
if (!span.getAttribute('style')) return false;
|
|
1146
|
+
try {
|
|
1147
|
+
range.surroundContents(span);
|
|
1148
|
+
} catch (err) {
|
|
1149
|
+
var contents = range.extractContents();
|
|
1150
|
+
span.appendChild(contents);
|
|
1151
|
+
range.insertNode(span);
|
|
1152
|
+
}
|
|
1153
|
+
selection.removeAllRanges();
|
|
1154
|
+
var nextRange = document.createRange();
|
|
1155
|
+
nextRange.selectNodeContents(span);
|
|
1156
|
+
selection.addRange(nextRange);
|
|
1157
|
+
return true;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function showTransformBadge(text, clientX, clientY) {
|
|
1161
|
+
transformBadge.textContent = text;
|
|
1162
|
+
transformBadge.style.display = 'block';
|
|
1163
|
+
transformBadge.style.left = clientX + 12 + 'px';
|
|
1164
|
+
transformBadge.style.top = clientY + 12 + 'px';
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
function hideTransformBadge() {
|
|
1168
|
+
transformBadge.style.display = 'none';
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
function hideInsertionGuide() {
|
|
1172
|
+
insertionGuide.style.display = 'none';
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
function isOverlayElement(el) {
|
|
1176
|
+
return Boolean(el && el.getAttribute && el.getAttribute('data-agent-native-edit-overlay'));
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
function draggableElementChildren(parent) {
|
|
1180
|
+
return Array.prototype.slice.call(parent.children).filter(function(child) {
|
|
1181
|
+
return child.nodeType === 1 && !isOverlayElement(child) && !isLayerInteractionBlocked(child);
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function isFlowReorderCandidate(el) {
|
|
1186
|
+
if (!el || !el.parentElement) return false;
|
|
1187
|
+
if (el === document.body || el === document.documentElement) return false;
|
|
1188
|
+
var cs = window.getComputedStyle(el);
|
|
1189
|
+
if (cs.position === 'absolute' || cs.position === 'fixed') return false;
|
|
1190
|
+
var parent = el.parentElement;
|
|
1191
|
+
if (parent === document.body && draggableElementChildren(parent).length <= 2) return false;
|
|
1192
|
+
return draggableElementChildren(parent).filter(function(child) { return child !== el; }).length > 0;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function parentFlowAxis(parent) {
|
|
1196
|
+
var cs = window.getComputedStyle(parent);
|
|
1197
|
+
if (cs.display === 'flex' || cs.display === 'inline-flex') {
|
|
1198
|
+
return cs.flexDirection && cs.flexDirection.indexOf('row') === 0 ? 'x' : 'y';
|
|
1199
|
+
}
|
|
1200
|
+
if (cs.display === 'grid' || cs.display === 'inline-grid') {
|
|
1201
|
+
var cols = (cs.gridTemplateColumns || '').split(' ').filter(Boolean).length;
|
|
1202
|
+
return cols > 1 ? 'x' : 'y';
|
|
1203
|
+
}
|
|
1204
|
+
return 'y';
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
function reorderTargetForPoint(el, clientX, clientY) {
|
|
1208
|
+
if (!el || !el.parentElement) return null;
|
|
1209
|
+
var hit = elementFromEditorPoint(clientX, clientY);
|
|
1210
|
+
if (
|
|
1211
|
+
hit &&
|
|
1212
|
+
hit !== document.body &&
|
|
1213
|
+
hit !== document.documentElement &&
|
|
1214
|
+
hit !== el &&
|
|
1215
|
+
!el.contains(hit) &&
|
|
1216
|
+
!isOverlayElement(hit)
|
|
1217
|
+
) {
|
|
1218
|
+
var hitChildren = draggableElementChildren(hit).filter(function(child) {
|
|
1219
|
+
return child !== el;
|
|
1220
|
+
});
|
|
1221
|
+
if (hitChildren.length === 0) {
|
|
1222
|
+
return { anchor: hit, placement: 'inside', axis: parentFlowAxis(hit) };
|
|
1223
|
+
}
|
|
1224
|
+
var hitParent = hit.parentElement;
|
|
1225
|
+
if (hitParent) {
|
|
1226
|
+
var hitAxis = parentFlowAxis(hitParent);
|
|
1227
|
+
var hitRect = hit.getBoundingClientRect();
|
|
1228
|
+
var hitCenter = hitAxis === 'x'
|
|
1229
|
+
? hitRect.left + hitRect.width / 2
|
|
1230
|
+
: hitRect.top + hitRect.height / 2;
|
|
1231
|
+
var hitPointer = hitAxis === 'x' ? clientX : clientY;
|
|
1232
|
+
return {
|
|
1233
|
+
anchor: hit,
|
|
1234
|
+
placement: hitPointer < hitCenter ? 'before' : 'after',
|
|
1235
|
+
axis: hitAxis,
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
var parent = el.parentElement;
|
|
1240
|
+
var axis = parentFlowAxis(parent);
|
|
1241
|
+
var siblings = draggableElementChildren(parent).filter(function(child) {
|
|
1242
|
+
return child !== el;
|
|
1243
|
+
});
|
|
1244
|
+
if (!siblings.length) return null;
|
|
1245
|
+
var beforeTarget = null;
|
|
1246
|
+
for (var i = 0; i < siblings.length; i += 1) {
|
|
1247
|
+
var rect = siblings[i].getBoundingClientRect();
|
|
1248
|
+
var center = axis === 'x' ? rect.left + rect.width / 2 : rect.top + rect.height / 2;
|
|
1249
|
+
var pointer = axis === 'x' ? clientX : clientY;
|
|
1250
|
+
if (pointer < center) {
|
|
1251
|
+
beforeTarget = siblings[i];
|
|
1252
|
+
break;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
var anchor = beforeTarget || siblings[siblings.length - 1];
|
|
1256
|
+
var placement = beforeTarget ? 'before' : 'after';
|
|
1257
|
+
return { anchor: anchor, placement: placement, axis: axis };
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
function showInsertionGuideFor(target) {
|
|
1261
|
+
if (!target || !target.anchor) {
|
|
1262
|
+
hideInsertionGuide();
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
var rect = target.anchor.getBoundingClientRect();
|
|
1266
|
+
insertionGuide.style.display = 'block';
|
|
1267
|
+
insertionGuide.style.background = 'var(--design-editor-accent-color)';
|
|
1268
|
+
insertionGuide.style.border = '0';
|
|
1269
|
+
insertionGuide.style.boxShadow = '0 0 0 1px var(--design-editor-accent-color)';
|
|
1270
|
+
if (target.placement === 'inside') {
|
|
1271
|
+
insertionGuide.style.left = rect.left + 'px';
|
|
1272
|
+
insertionGuide.style.top = rect.top + 'px';
|
|
1273
|
+
insertionGuide.style.width = rect.width + 'px';
|
|
1274
|
+
insertionGuide.style.height = rect.height + 'px';
|
|
1275
|
+
insertionGuide.style.background = 'color-mix(in srgb, var(--design-editor-accent-color) 14%, transparent)';
|
|
1276
|
+
insertionGuide.style.border = '2px solid var(--design-editor-accent-color)';
|
|
1277
|
+
insertionGuide.style.boxShadow = 'none';
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
if (target.axis === 'x') {
|
|
1281
|
+
var x = target.placement === 'before' ? rect.left : rect.right;
|
|
1282
|
+
insertionGuide.style.left = x + 'px';
|
|
1283
|
+
insertionGuide.style.top = rect.top + 'px';
|
|
1284
|
+
insertionGuide.style.width = '2px';
|
|
1285
|
+
insertionGuide.style.height = rect.height + 'px';
|
|
1286
|
+
} else {
|
|
1287
|
+
var y = target.placement === 'before' ? rect.top : rect.bottom;
|
|
1288
|
+
insertionGuide.style.left = rect.left + 'px';
|
|
1289
|
+
insertionGuide.style.top = y + 'px';
|
|
1290
|
+
insertionGuide.style.width = rect.width + 'px';
|
|
1291
|
+
insertionGuide.style.height = '2px';
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
function applyRuntimeReorder(el, target) {
|
|
1296
|
+
if (!el || !target || !target.anchor || !target.anchor.parentElement) return;
|
|
1297
|
+
if (target.placement === 'inside') {
|
|
1298
|
+
target.anchor.appendChild(el);
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
var parent = target.anchor.parentElement;
|
|
1302
|
+
if (target.placement === 'before') {
|
|
1303
|
+
parent.insertBefore(el, target.anchor);
|
|
1304
|
+
} else {
|
|
1305
|
+
parent.insertBefore(el, target.anchor.nextSibling);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
function postVisualStructureChange(el, target) {
|
|
1310
|
+
if (!el || !target || !target.anchor) return;
|
|
1311
|
+
var requestId = 'move-' + Date.now() + '-' + Math.random().toString(16).slice(2);
|
|
1312
|
+
pendingStructureMove = { requestId: requestId, el: el, target: target };
|
|
1313
|
+
window.parent.postMessage({
|
|
1314
|
+
type: 'visual-structure-change',
|
|
1315
|
+
requestId: requestId,
|
|
1316
|
+
selector: getSelector(el),
|
|
1317
|
+
sourceId: getSourceId(el),
|
|
1318
|
+
anchorSelector: getSelector(target.anchor),
|
|
1319
|
+
anchorSourceId: getSourceId(target.anchor),
|
|
1320
|
+
placement: target.placement,
|
|
1321
|
+
payload: getElementInfo(el),
|
|
1322
|
+
}, '*');
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
function postVisualDuplicateChange(originalEl, cloneEl, target) {
|
|
1326
|
+
if (!originalEl || !cloneEl) return;
|
|
1327
|
+
window.parent.postMessage({
|
|
1328
|
+
type: 'visual-duplicate-change',
|
|
1329
|
+
selector: getSelector(originalEl),
|
|
1330
|
+
sourceId: getSourceId(originalEl),
|
|
1331
|
+
anchorSelector: target && target.anchor ? getSelector(target.anchor) : '',
|
|
1332
|
+
anchorSourceId: target && target.anchor ? getSourceId(target.anchor) : '',
|
|
1333
|
+
placement: target && target.placement ? target.placement : 'after',
|
|
1334
|
+
cloneHtml: cloneEl.outerHTML,
|
|
1335
|
+
payload: getElementInfo(cloneEl),
|
|
1336
|
+
}, '*');
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
function startMove(e) {
|
|
1340
|
+
if (!selectedEl) return;
|
|
1341
|
+
if (isLayerInteractionBlocked(selectedEl)) return;
|
|
1342
|
+
e.preventDefault();
|
|
1343
|
+
e.stopPropagation();
|
|
1344
|
+
var originalSelectedEl = selectedEl;
|
|
1345
|
+
var duplicatedForDrag = false;
|
|
1346
|
+
if (e.altKey && selectedEl !== document.body && selectedEl !== document.documentElement) {
|
|
1347
|
+
var clone = selectedEl.cloneNode(true);
|
|
1348
|
+
resetRuntimeStableIds(clone);
|
|
1349
|
+
selectedEl.parentElement.insertBefore(clone, selectedEl.nextSibling);
|
|
1350
|
+
selectedEl = clone;
|
|
1351
|
+
duplicatedForDrag = true;
|
|
1352
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
1353
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
|
|
1354
|
+
}
|
|
1355
|
+
if (isFlowReorderCandidate(selectedEl)) {
|
|
1356
|
+
var currentTarget = reorderTargetForPoint(selectedEl, e.clientX, e.clientY);
|
|
1357
|
+
showInsertionGuideFor(currentTarget);
|
|
1358
|
+
function onReorderMove(ev) {
|
|
1359
|
+
currentTarget = reorderTargetForPoint(selectedEl, ev.clientX, ev.clientY);
|
|
1360
|
+
showInsertionGuideFor(currentTarget);
|
|
1361
|
+
showTransformBadge(currentTarget ? 'Move layer' : 'Move', ev.clientX, ev.clientY);
|
|
1362
|
+
}
|
|
1363
|
+
function onReorderUp() {
|
|
1364
|
+
document.removeEventListener('mousemove', onReorderMove, true);
|
|
1365
|
+
document.removeEventListener('mouseup', onReorderUp, true);
|
|
1366
|
+
hideTransformBadge();
|
|
1367
|
+
hideInsertionGuide();
|
|
1368
|
+
if (!currentTarget) return;
|
|
1369
|
+
if (duplicatedForDrag) {
|
|
1370
|
+
applyRuntimeReorder(selectedEl, currentTarget);
|
|
1371
|
+
postVisualDuplicateChange(originalSelectedEl, selectedEl, currentTarget);
|
|
1372
|
+
}
|
|
1373
|
+
else postVisualStructureChange(selectedEl, currentTarget);
|
|
1374
|
+
}
|
|
1375
|
+
document.addEventListener('mousemove', onReorderMove, true);
|
|
1376
|
+
document.addEventListener('mouseup', onReorderUp, true);
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
ensurePositionable(selectedEl);
|
|
1380
|
+
var cs = window.getComputedStyle(selectedEl);
|
|
1381
|
+
var originLeft = readPx(selectedEl.style.left || cs.left);
|
|
1382
|
+
var originTop = readPx(selectedEl.style.top || cs.top);
|
|
1383
|
+
var startX = e.clientX;
|
|
1384
|
+
var startY = e.clientY;
|
|
1385
|
+
function onMove(ev) {
|
|
1386
|
+
var nextLeft = originLeft + ev.clientX - startX;
|
|
1387
|
+
var nextTop = originTop + ev.clientY - startY;
|
|
1388
|
+
selectedEl.style.left = Math.round(nextLeft) + 'px';
|
|
1389
|
+
selectedEl.style.top = Math.round(nextTop) + 'px';
|
|
1390
|
+
showTransformBadge('X ' + Math.round(nextLeft) + ' Y ' + Math.round(nextTop), ev.clientX, ev.clientY);
|
|
1391
|
+
refreshOverlays();
|
|
1392
|
+
}
|
|
1393
|
+
function onUp() {
|
|
1394
|
+
document.removeEventListener('mousemove', onMove, true);
|
|
1395
|
+
document.removeEventListener('mouseup', onUp, true);
|
|
1396
|
+
hideTransformBadge();
|
|
1397
|
+
if (duplicatedForDrag) {
|
|
1398
|
+
postVisualDuplicateChange(originalSelectedEl, selectedEl);
|
|
1399
|
+
} else {
|
|
1400
|
+
postVisualStyleChange({
|
|
1401
|
+
position: selectedEl.style.position,
|
|
1402
|
+
left: selectedEl.style.left,
|
|
1403
|
+
top: selectedEl.style.top,
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
document.addEventListener('mousemove', onMove, true);
|
|
1408
|
+
document.addEventListener('mouseup', onUp, true);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
function startResize(handle, e) {
|
|
1412
|
+
if (!selectedEl) return;
|
|
1413
|
+
if (isLayerInteractionBlocked(selectedEl)) return;
|
|
1414
|
+
e.preventDefault();
|
|
1415
|
+
e.stopPropagation();
|
|
1416
|
+
ensurePositionable(selectedEl);
|
|
1417
|
+
var cs = window.getComputedStyle(selectedEl);
|
|
1418
|
+
var origin = {
|
|
1419
|
+
left: readPx(selectedEl.style.left || cs.left),
|
|
1420
|
+
top: readPx(selectedEl.style.top || cs.top),
|
|
1421
|
+
width: selectedEl.getBoundingClientRect().width,
|
|
1422
|
+
height: selectedEl.getBoundingClientRect().height,
|
|
1423
|
+
ratio: selectedEl.getBoundingClientRect().width / Math.max(1, selectedEl.getBoundingClientRect().height),
|
|
1424
|
+
};
|
|
1425
|
+
var startX = e.clientX;
|
|
1426
|
+
var startY = e.clientY;
|
|
1427
|
+
function nextRect(ev) {
|
|
1428
|
+
var dx = ev.clientX - startX;
|
|
1429
|
+
var dy = ev.clientY - startY;
|
|
1430
|
+
var left = origin.left;
|
|
1431
|
+
var top = origin.top;
|
|
1432
|
+
var width = origin.width;
|
|
1433
|
+
var height = origin.height;
|
|
1434
|
+
if (handle.indexOf('w') !== -1) {
|
|
1435
|
+
left = origin.left + dx;
|
|
1436
|
+
width = origin.width - dx;
|
|
1437
|
+
}
|
|
1438
|
+
if (handle.indexOf('e') !== -1) width = origin.width + dx;
|
|
1439
|
+
if (handle.indexOf('n') !== -1) {
|
|
1440
|
+
top = origin.top + dy;
|
|
1441
|
+
height = origin.height - dy;
|
|
1442
|
+
}
|
|
1443
|
+
if (handle.indexOf('s') !== -1) height = origin.height + dy;
|
|
1444
|
+
width = Math.max(8, width);
|
|
1445
|
+
height = Math.max(8, height);
|
|
1446
|
+
if ((ev.shiftKey || scaleToolEnabled) && handle.length === 2) {
|
|
1447
|
+
if (Math.abs(dx) > Math.abs(dy)) height = width / origin.ratio;
|
|
1448
|
+
else width = height * origin.ratio;
|
|
1449
|
+
}
|
|
1450
|
+
if (ev.altKey) {
|
|
1451
|
+
if (handle.indexOf('w') !== -1 || handle.indexOf('e') !== -1) left = origin.left - (width - origin.width) / 2;
|
|
1452
|
+
if (handle.indexOf('n') !== -1 || handle.indexOf('s') !== -1) top = origin.top - (height - origin.height) / 2;
|
|
1453
|
+
}
|
|
1454
|
+
return { left: left, top: top, width: width, height: height };
|
|
1455
|
+
}
|
|
1456
|
+
function onMove(ev) {
|
|
1457
|
+
var rect = nextRect(ev);
|
|
1458
|
+
selectedEl.style.left = Math.round(rect.left) + 'px';
|
|
1459
|
+
selectedEl.style.top = Math.round(rect.top) + 'px';
|
|
1460
|
+
selectedEl.style.width = Math.round(rect.width) + 'px';
|
|
1461
|
+
selectedEl.style.height = Math.round(rect.height) + 'px';
|
|
1462
|
+
showTransformBadge(Math.round(rect.width) + ' x ' + Math.round(rect.height), ev.clientX, ev.clientY);
|
|
1463
|
+
refreshOverlays();
|
|
1464
|
+
}
|
|
1465
|
+
function onUp() {
|
|
1466
|
+
document.removeEventListener('mousemove', onMove, true);
|
|
1467
|
+
document.removeEventListener('mouseup', onUp, true);
|
|
1468
|
+
hideTransformBadge();
|
|
1469
|
+
postVisualStyleChange({
|
|
1470
|
+
position: selectedEl.style.position,
|
|
1471
|
+
left: selectedEl.style.left,
|
|
1472
|
+
top: selectedEl.style.top,
|
|
1473
|
+
width: selectedEl.style.width,
|
|
1474
|
+
height: selectedEl.style.height,
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
document.addEventListener('mousemove', onMove, true);
|
|
1478
|
+
document.addEventListener('mouseup', onUp, true);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
function startRotate(e) {
|
|
1482
|
+
if (!selectedEl) return;
|
|
1483
|
+
if (isLayerInteractionBlocked(selectedEl)) return;
|
|
1484
|
+
e.preventDefault();
|
|
1485
|
+
e.stopPropagation();
|
|
1486
|
+
var rect = selectedEl.getBoundingClientRect();
|
|
1487
|
+
var center = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
1488
|
+
var originAngle = Math.atan2(e.clientY - center.y, e.clientX - center.x) * 180 / Math.PI;
|
|
1489
|
+
var originRotation = currentRotation(selectedEl);
|
|
1490
|
+
function onMove(ev) {
|
|
1491
|
+
var pointerAngle = Math.atan2(ev.clientY - center.y, ev.clientX - center.x) * 180 / Math.PI;
|
|
1492
|
+
var next = originRotation + pointerAngle - originAngle;
|
|
1493
|
+
if (ev.shiftKey) next = Math.round(next / 15) * 15;
|
|
1494
|
+
next = Math.round(next);
|
|
1495
|
+
selectedEl.style.transform = mergeRotation(selectedEl, next);
|
|
1496
|
+
showTransformBadge(next + 'deg', ev.clientX, ev.clientY);
|
|
1497
|
+
refreshOverlays();
|
|
1498
|
+
}
|
|
1499
|
+
function onUp() {
|
|
1500
|
+
document.removeEventListener('mousemove', onMove, true);
|
|
1501
|
+
document.removeEventListener('mouseup', onUp, true);
|
|
1502
|
+
hideTransformBadge();
|
|
1503
|
+
postVisualStyleChange({
|
|
1504
|
+
transform: selectedEl.style.transform,
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
document.addEventListener('mousemove', onMove, true);
|
|
1508
|
+
document.addEventListener('mouseup', onUp, true);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
selectionOverlay.addEventListener('mousedown', function(e) {
|
|
1512
|
+
var resizeHandle = e.target && e.target.getAttribute && e.target.getAttribute('data-agent-native-edit-handle');
|
|
1513
|
+
if (!resizeHandle && e.target && e.target.getAttribute) {
|
|
1514
|
+
resizeHandle = e.target.getAttribute('data-agent-native-edge-handle');
|
|
1515
|
+
}
|
|
1516
|
+
if (resizeHandle) {
|
|
1517
|
+
startResize(resizeHandle, e);
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
var rotateHandle = e.target && e.target.getAttribute && e.target.getAttribute('data-agent-native-rotate-handle');
|
|
1521
|
+
if (rotateHandle) {
|
|
1522
|
+
startRotate(e);
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
startMove(e);
|
|
1526
|
+
}, true);
|
|
1527
|
+
|
|
1528
|
+
['pointerdown','pointerup','mousedown','mouseup','auxclick'].forEach(function(type) {
|
|
1529
|
+
shieldOverlay.addEventListener(type, stopNativeInteraction, true);
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
shieldOverlay.addEventListener('click', selectElementAtEvent, true);
|
|
1533
|
+
shieldOverlay.addEventListener('contextmenu', openContextMenuAtEvent, true);
|
|
1534
|
+
selectionOverlay.addEventListener('contextmenu', openContextMenuAtEvent, true);
|
|
1535
|
+
document.addEventListener('contextmenu', function(e) {
|
|
1536
|
+
if (isOverlayElement(e.target)) return;
|
|
1537
|
+
openContextMenuAtEvent(e);
|
|
297
1538
|
}, true);
|
|
298
1539
|
|
|
299
|
-
document.addEventListener('
|
|
300
|
-
if (e
|
|
301
|
-
|
|
1540
|
+
document.addEventListener('keydown', function(e) {
|
|
1541
|
+
if (!shouldForwardDesignHotkey(e)) return;
|
|
1542
|
+
stopNativeInteraction(e);
|
|
1543
|
+
window.parent.postMessage({
|
|
1544
|
+
type: 'design-hotkey',
|
|
1545
|
+
key: e.key,
|
|
1546
|
+
code: e.code,
|
|
1547
|
+
metaKey: !!e.metaKey,
|
|
1548
|
+
ctrlKey: !!e.ctrlKey,
|
|
1549
|
+
shiftKey: !!e.shiftKey,
|
|
1550
|
+
altKey: !!e.altKey,
|
|
1551
|
+
repeat: !!e.repeat
|
|
1552
|
+
}, '*');
|
|
1553
|
+
}, true);
|
|
1554
|
+
|
|
1555
|
+
function placeTextCaretFromPoint(target, clientX, clientY) {
|
|
1556
|
+
try {
|
|
1557
|
+
var range = null;
|
|
1558
|
+
if (document.caretRangeFromPoint) {
|
|
1559
|
+
range = document.caretRangeFromPoint(clientX, clientY);
|
|
1560
|
+
} else if (document.caretPositionFromPoint) {
|
|
1561
|
+
var position = document.caretPositionFromPoint(clientX, clientY);
|
|
1562
|
+
if (position) {
|
|
1563
|
+
range = document.createRange();
|
|
1564
|
+
range.setStart(position.offsetNode, position.offset);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
if (!range) {
|
|
1568
|
+
range = document.createRange();
|
|
1569
|
+
range.selectNodeContents(target);
|
|
1570
|
+
range.collapse(false);
|
|
1571
|
+
}
|
|
1572
|
+
var selection = window.getSelection();
|
|
1573
|
+
selection.removeAllRanges();
|
|
1574
|
+
selection.addRange(range);
|
|
1575
|
+
} catch (err) {
|
|
1576
|
+
try {
|
|
1577
|
+
var fallbackRange = document.createRange();
|
|
1578
|
+
fallbackRange.selectNodeContents(target);
|
|
1579
|
+
fallbackRange.collapse(false);
|
|
1580
|
+
var fallbackSelection = window.getSelection();
|
|
1581
|
+
fallbackSelection.removeAllRanges();
|
|
1582
|
+
fallbackSelection.addRange(fallbackRange);
|
|
1583
|
+
} catch (_err) {}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
function beginTextEditingFromEvent(e) {
|
|
1588
|
+
if (activeTextEditEl && e.target && activeTextEditEl.contains(e.target)) return;
|
|
1589
|
+
if (!textEditingEnabled) {
|
|
1590
|
+
stopNativeInteraction(e);
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
stopNativeInteraction(e);
|
|
1594
|
+
var target = findTextEditTarget(elementFromEditorPoint(e.clientX, e.clientY));
|
|
1595
|
+
if (!target || target.nodeType !== 1) return;
|
|
1596
|
+
selectedEl = target;
|
|
1597
|
+
var originalText = target.textContent || '';
|
|
1598
|
+
var originalHtml = target.innerHTML || '';
|
|
1599
|
+
var committed = false;
|
|
1600
|
+
activeTextEditEl = target;
|
|
1601
|
+
target.setAttribute('contenteditable', 'true');
|
|
1602
|
+
target.setAttribute('data-agent-native-text-editing', 'true');
|
|
1603
|
+
target.style.cursor = 'text';
|
|
1604
|
+
target.style.outline = '1.5px solid var(--design-editor-accent-color)';
|
|
1605
|
+
target.style.outlineOffset = '2px';
|
|
1606
|
+
setTextEditingPointerPassthrough(true);
|
|
1607
|
+
positionOverlay(selectionOverlay, target);
|
|
1608
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(target) }, '*');
|
|
1609
|
+
postTextEditingState(target, true);
|
|
1610
|
+
|
|
1611
|
+
function finish(commit) {
|
|
1612
|
+
if (committed) return;
|
|
1613
|
+
committed = true;
|
|
1614
|
+
target.removeEventListener('blur', onBlur, true);
|
|
1615
|
+
target.removeEventListener('keydown', onKeyDown, true);
|
|
1616
|
+
target.removeEventListener('paste', onPaste, true);
|
|
1617
|
+
target.removeEventListener('keyup', onSelectionChange, true);
|
|
1618
|
+
target.removeEventListener('mouseup', onSelectionChange, true);
|
|
1619
|
+
document.removeEventListener('selectionchange', onSelectionChange);
|
|
1620
|
+
target.removeAttribute('contenteditable');
|
|
1621
|
+
target.removeAttribute('data-agent-native-text-editing');
|
|
1622
|
+
target.style.cursor = '';
|
|
1623
|
+
target.style.outline = '';
|
|
1624
|
+
target.style.outlineOffset = '';
|
|
1625
|
+
setTextEditingPointerPassthrough(false);
|
|
1626
|
+
if (activeTextEditEl === target) activeTextEditEl = null;
|
|
1627
|
+
postTextEditingState(target, false);
|
|
1628
|
+
if (!commit) {
|
|
1629
|
+
target.innerHTML = originalHtml;
|
|
1630
|
+
refreshOverlays();
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
var next = target.textContent || '';
|
|
1634
|
+
var nextHtml = target.innerHTML || '';
|
|
1635
|
+
refreshOverlays();
|
|
1636
|
+
if (next !== originalText || nextHtml !== originalHtml) {
|
|
1637
|
+
postTextContentChange(target, next, nextHtml);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
function onBlur() {
|
|
1642
|
+
finish(true);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
function onKeyDown(ev) {
|
|
1646
|
+
if (ev.key === 'Escape') {
|
|
1647
|
+
ev.preventDefault();
|
|
1648
|
+
finish(true);
|
|
1649
|
+
target.blur();
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
if (ev.key === 'Enter' && !ev.shiftKey) {
|
|
1653
|
+
ev.preventDefault();
|
|
1654
|
+
finish(true);
|
|
1655
|
+
target.blur();
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
function onPaste(ev) {
|
|
1660
|
+
ev.preventDefault();
|
|
1661
|
+
insertPlainTextAtSelection((ev.clipboardData && ev.clipboardData.getData('text/plain')) || '');
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
function onSelectionChange() {
|
|
1665
|
+
postTextEditingState(target, true);
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
target.addEventListener('blur', onBlur, true);
|
|
1669
|
+
target.addEventListener('keydown', onKeyDown, true);
|
|
1670
|
+
target.addEventListener('paste', onPaste, true);
|
|
1671
|
+
target.addEventListener('keyup', onSelectionChange, true);
|
|
1672
|
+
target.addEventListener('mouseup', onSelectionChange, true);
|
|
1673
|
+
document.addEventListener('selectionchange', onSelectionChange);
|
|
1674
|
+
target.focus();
|
|
1675
|
+
placeTextCaretFromPoint(target, e.clientX, e.clientY);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
shieldOverlay.addEventListener('dblclick', beginTextEditingFromEvent, true);
|
|
1679
|
+
selectionOverlay.addEventListener('dblclick', beginTextEditingFromEvent, true);
|
|
1680
|
+
document.addEventListener('dblclick', function(e) {
|
|
1681
|
+
if (isOverlayElement(e.target)) return;
|
|
1682
|
+
beginTextEditingFromEvent(e);
|
|
1683
|
+
}, true);
|
|
1684
|
+
|
|
1685
|
+
shieldOverlay.addEventListener('pointermove', function(e) {
|
|
1686
|
+
stopNativeInteraction(e);
|
|
1687
|
+
hoveredEl = elementFromEditorPoint(e.clientX, e.clientY);
|
|
1688
|
+
if (!hoveredEl) {
|
|
1689
|
+
highlightOverlay.style.display = 'none';
|
|
1690
|
+
hideMeasurements();
|
|
1691
|
+
return;
|
|
1692
|
+
}
|
|
1693
|
+
if (hoveredEl && hoveredEl.closest('[data-agent-native-text-editing]')) return;
|
|
302
1694
|
positionOverlay(highlightOverlay, hoveredEl);
|
|
1695
|
+
if (e.altKey && selectedEl && hoveredEl && selectedEl !== hoveredEl) {
|
|
1696
|
+
showMeasurements(selectedEl, hoveredEl);
|
|
1697
|
+
} else {
|
|
1698
|
+
hideMeasurements();
|
|
1699
|
+
}
|
|
303
1700
|
var info = getElementInfo(hoveredEl);
|
|
304
1701
|
window.parent.postMessage({ type: 'element-hover', payload: info }, '*');
|
|
305
1702
|
}, true);
|
|
306
1703
|
|
|
307
|
-
|
|
1704
|
+
shieldOverlay.addEventListener('pointerleave', function(e) {
|
|
1705
|
+
stopNativeInteraction(e);
|
|
308
1706
|
hoveredEl = null;
|
|
309
1707
|
highlightOverlay.style.display = 'none';
|
|
1708
|
+
hideMeasurements();
|
|
1709
|
+
}, true);
|
|
1710
|
+
|
|
1711
|
+
window.addEventListener('keyup', function(e) {
|
|
1712
|
+
if (e.key === 'Alt') hideMeasurements();
|
|
310
1713
|
}, true);
|
|
311
1714
|
|
|
312
1715
|
window.addEventListener('message', function(e) {
|
|
313
1716
|
if (e.source !== window.parent) return;
|
|
314
|
-
if (!e.data
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (
|
|
320
|
-
|
|
1717
|
+
if (!e.data) return;
|
|
1718
|
+
if (e.data.type === 'scale-tool-mode') {
|
|
1719
|
+
scaleToolEnabled = !!e.data.enabled;
|
|
1720
|
+
return;
|
|
1721
|
+
}
|
|
1722
|
+
if (e.data.type === 'clear-selection') {
|
|
1723
|
+
selectedEl = null;
|
|
1724
|
+
hoveredEl = null;
|
|
1725
|
+
selectionOverlay.style.display = 'none';
|
|
1726
|
+
highlightOverlay.style.display = 'none';
|
|
1727
|
+
hideMeasurements();
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
if (e.data.type === 'select-element') {
|
|
1731
|
+
var candidates = [];
|
|
1732
|
+
if (Array.isArray(e.data.selectorCandidates)) {
|
|
1733
|
+
e.data.selectorCandidates.forEach(function(selector) {
|
|
1734
|
+
if (typeof selector === 'string' && selector && candidates.indexOf(selector) === -1) {
|
|
1735
|
+
candidates.push(selector);
|
|
1736
|
+
}
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
if (e.data.selector && candidates.indexOf(String(e.data.selector)) === -1) {
|
|
1740
|
+
candidates.push(String(e.data.selector));
|
|
1741
|
+
}
|
|
1742
|
+
var target = null;
|
|
1743
|
+
for (var i = 0; i < candidates.length && !target; i += 1) {
|
|
1744
|
+
try {
|
|
1745
|
+
var matches = document.querySelectorAll(candidates[i]);
|
|
1746
|
+
for (var j = 0; j < matches.length; j += 1) {
|
|
1747
|
+
if (!isLayerInteractionBlocked(matches[j])) {
|
|
1748
|
+
target = matches[j];
|
|
1749
|
+
break;
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
} catch (_err) {}
|
|
1753
|
+
}
|
|
1754
|
+
if (!target) return;
|
|
1755
|
+
selectedEl = target;
|
|
1756
|
+
var selectedInfo = getElementInfo(target);
|
|
1757
|
+
positionOverlay(selectionOverlay, target);
|
|
1758
|
+
window.parent.postMessage({ type: 'element-select', payload: selectedInfo }, '*');
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
if (e.data.type === 'layer-states') {
|
|
1762
|
+
lockedSelectors = Array.isArray(e.data.lockedSelectors) ? e.data.lockedSelectors.filter(function(item) { return typeof item === 'string'; }) : [];
|
|
1763
|
+
hiddenSelectors = Array.isArray(e.data.hiddenSelectors) ? e.data.hiddenSelectors.filter(function(item) { return typeof item === 'string'; }) : [];
|
|
1764
|
+
if (selectedEl && isLayerInteractionBlocked(selectedEl)) {
|
|
1765
|
+
selectedEl = null;
|
|
1766
|
+
selectionOverlay.style.display = 'none';
|
|
1767
|
+
}
|
|
1768
|
+
if (hoveredEl && isLayerInteractionBlocked(hoveredEl)) {
|
|
1769
|
+
hoveredEl = null;
|
|
1770
|
+
highlightOverlay.style.display = 'none';
|
|
1771
|
+
}
|
|
1772
|
+
applyHiddenSelectors();
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1775
|
+
if (e.data.type === 'visual-structure-ack') {
|
|
1776
|
+
if (!pendingStructureMove || e.data.requestId !== pendingStructureMove.requestId) return;
|
|
1777
|
+
var move = pendingStructureMove;
|
|
1778
|
+
pendingStructureMove = null;
|
|
1779
|
+
if (e.data.applied) {
|
|
1780
|
+
applyRuntimeReorder(move.el, move.target);
|
|
1781
|
+
selectedEl = move.el;
|
|
1782
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
1783
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
|
|
1784
|
+
}
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
if (e.data.type === 'replace-document-content') {
|
|
1788
|
+
replaceRuntimeDocument(e.data.content, e.data.selectedSelector, e.data.selectorCandidates);
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
if (e.data.type === 'delete-element') {
|
|
1792
|
+
removeRuntimeTarget(e.data.selector, e.data.selectorCandidates);
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
if (e.data.type !== 'style-change') return;
|
|
1796
|
+
var sel = e.data.selector;
|
|
1797
|
+
var prop = e.data.property;
|
|
1798
|
+
var val = e.data.value;
|
|
1799
|
+
var el = sel ? document.querySelector(sel) : null;
|
|
1800
|
+
if (activeTextEditEl && el === activeTextEditEl && applyTextRangeStyle(prop, val)) {
|
|
1801
|
+
postTextContentChange(activeTextEditEl, activeTextEditEl.textContent || '', activeTextEditEl.innerHTML || '');
|
|
1802
|
+
refreshOverlays();
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
if (el) el.style[prop] = val;
|
|
1806
|
+
});
|
|
321
1807
|
|
|
322
1808
|
window.addEventListener('scroll', refreshOverlays, true);
|
|
323
1809
|
window.addEventListener('resize', refreshOverlays);
|
|
@@ -327,12 +1813,61 @@ const EDIT_BRIDGE_SCRIPT = `
|
|
|
327
1813
|
|
|
328
1814
|
interface DesignCanvasProps {
|
|
329
1815
|
content: string;
|
|
1816
|
+
contentKey?: string;
|
|
330
1817
|
zoom: number;
|
|
331
1818
|
onZoomChange?: (zoom: number) => void;
|
|
332
1819
|
deviceFrame: DeviceFrameType;
|
|
1820
|
+
embeddedFrame?: {
|
|
1821
|
+
viewportWidth: number;
|
|
1822
|
+
viewportHeight: number;
|
|
1823
|
+
displayWidth: number;
|
|
1824
|
+
displayHeight: number;
|
|
1825
|
+
};
|
|
333
1826
|
editMode: boolean;
|
|
1827
|
+
interactMode: boolean;
|
|
1828
|
+
scaleMode?: boolean;
|
|
334
1829
|
onElementSelect: (info: ElementInfo) => void;
|
|
335
1830
|
onElementHover: (info: ElementInfo) => void;
|
|
1831
|
+
onVisualStyleChange?: (
|
|
1832
|
+
selector: string,
|
|
1833
|
+
styles: Record<string, string>,
|
|
1834
|
+
info?: ElementInfo,
|
|
1835
|
+
) => void;
|
|
1836
|
+
onTextContentChange?: (
|
|
1837
|
+
selector: string,
|
|
1838
|
+
value: string,
|
|
1839
|
+
info?: ElementInfo,
|
|
1840
|
+
details?: { html?: string },
|
|
1841
|
+
) => void;
|
|
1842
|
+
onTextEditingStateChange?: (state: {
|
|
1843
|
+
active: boolean;
|
|
1844
|
+
selector?: string;
|
|
1845
|
+
hasRange?: boolean;
|
|
1846
|
+
}) => void;
|
|
1847
|
+
onIframeHotkey?: (event: IframeHotkeyPayload) => void;
|
|
1848
|
+
onIframeContextMenu?: (event: IframeContextMenuPayload) => void;
|
|
1849
|
+
onVisualStructureChange?: (
|
|
1850
|
+
selector: string,
|
|
1851
|
+
anchorSelector: string,
|
|
1852
|
+
placement: "before" | "after" | "inside",
|
|
1853
|
+
info?: ElementInfo,
|
|
1854
|
+
details?: {
|
|
1855
|
+
sourceId?: string;
|
|
1856
|
+
anchorSourceId?: string;
|
|
1857
|
+
requestId?: string;
|
|
1858
|
+
},
|
|
1859
|
+
) => boolean | void;
|
|
1860
|
+
onVisualDuplicateChange?: (
|
|
1861
|
+
selector: string,
|
|
1862
|
+
cloneHtml: string,
|
|
1863
|
+
info?: ElementInfo,
|
|
1864
|
+
details?: {
|
|
1865
|
+
sourceId?: string;
|
|
1866
|
+
anchorSelector?: string;
|
|
1867
|
+
anchorSourceId?: string;
|
|
1868
|
+
placement?: "before" | "after" | "inside";
|
|
1869
|
+
},
|
|
1870
|
+
) => boolean | void;
|
|
336
1871
|
tweakValues: Record<string, string>;
|
|
337
1872
|
/** Whether draw-to-prompt mode is active (overlays the iframe). */
|
|
338
1873
|
drawMode?: boolean;
|
|
@@ -340,6 +1875,13 @@ interface DesignCanvasProps {
|
|
|
340
1875
|
onExitDrawMode?: () => void;
|
|
341
1876
|
/** Whether comment-pin drop mode is active. */
|
|
342
1877
|
pinMode?: boolean;
|
|
1878
|
+
selectedSelector?: string | null;
|
|
1879
|
+
selectedSelectorCandidates?: string[];
|
|
1880
|
+
hoveredSelector?: string | null;
|
|
1881
|
+
hoveredSelectorCandidates?: string[];
|
|
1882
|
+
lockedSelectors?: string[];
|
|
1883
|
+
hiddenSelectors?: string[];
|
|
1884
|
+
clearSelectionRequest?: number;
|
|
343
1885
|
/** Called when the user exits pin mode. */
|
|
344
1886
|
onExitPinMode?: () => void;
|
|
345
1887
|
/** Stable id of the open design (used for pin scoping + agent prompt). */
|
|
@@ -359,18 +1901,66 @@ interface DesignCanvasProps {
|
|
|
359
1901
|
onPrototypeNavigate?: (screen: string, href: string) => void;
|
|
360
1902
|
}
|
|
361
1903
|
|
|
1904
|
+
function getExternalPreviewUrl(content: string): string | null {
|
|
1905
|
+
const trimmed = content.trim();
|
|
1906
|
+
if (!/^https?:\/\//i.test(trimmed)) return null;
|
|
1907
|
+
try {
|
|
1908
|
+
const url = new URL(trimmed);
|
|
1909
|
+
url.hash = "";
|
|
1910
|
+
return url.toString();
|
|
1911
|
+
} catch {
|
|
1912
|
+
return null;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
export interface IframeHotkeyPayload {
|
|
1917
|
+
key: string;
|
|
1918
|
+
code: string;
|
|
1919
|
+
metaKey: boolean;
|
|
1920
|
+
ctrlKey: boolean;
|
|
1921
|
+
shiftKey: boolean;
|
|
1922
|
+
altKey: boolean;
|
|
1923
|
+
repeat: boolean;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
export interface IframeContextMenuPayload {
|
|
1927
|
+
clientX: number;
|
|
1928
|
+
clientY: number;
|
|
1929
|
+
viewportClientX?: number;
|
|
1930
|
+
viewportClientY?: number;
|
|
1931
|
+
info?: ElementInfo | null;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
362
1934
|
export function DesignCanvas({
|
|
363
1935
|
content,
|
|
1936
|
+
contentKey,
|
|
364
1937
|
zoom,
|
|
365
1938
|
onZoomChange,
|
|
366
1939
|
deviceFrame,
|
|
1940
|
+
embeddedFrame,
|
|
367
1941
|
editMode,
|
|
1942
|
+
interactMode,
|
|
1943
|
+
scaleMode = false,
|
|
1944
|
+
clearSelectionRequest,
|
|
368
1945
|
onElementSelect,
|
|
369
1946
|
onElementHover,
|
|
1947
|
+
onVisualStyleChange,
|
|
1948
|
+
onTextContentChange,
|
|
1949
|
+
onTextEditingStateChange,
|
|
1950
|
+
onIframeHotkey,
|
|
1951
|
+
onIframeContextMenu,
|
|
1952
|
+
onVisualStructureChange,
|
|
1953
|
+
onVisualDuplicateChange,
|
|
370
1954
|
tweakValues,
|
|
371
1955
|
drawMode,
|
|
372
1956
|
onExitDrawMode,
|
|
373
1957
|
pinMode,
|
|
1958
|
+
selectedSelector,
|
|
1959
|
+
selectedSelectorCandidates = [],
|
|
1960
|
+
hoveredSelector,
|
|
1961
|
+
hoveredSelectorCandidates = [],
|
|
1962
|
+
lockedSelectors = [],
|
|
1963
|
+
hiddenSelectors = [],
|
|
374
1964
|
onExitPinMode,
|
|
375
1965
|
designId,
|
|
376
1966
|
designTitle,
|
|
@@ -382,8 +1972,37 @@ export function DesignCanvas({
|
|
|
382
1972
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
383
1973
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
384
1974
|
const zoomRef = useRef(zoom);
|
|
1975
|
+
const previousContentKeyRef = useRef(contentKey);
|
|
1976
|
+
const [renderedContent, setRenderedContent] = useState(content);
|
|
1977
|
+
const [annotationPins, setAnnotationPins] = useState<CanvasPin[]>([]);
|
|
1978
|
+
const [pinSubmitSignal, setPinSubmitSignal] = useState(0);
|
|
1979
|
+
const isEmbeddedFrame = Boolean(embeddedFrame);
|
|
1980
|
+
const externalPreviewUrl = useMemo(
|
|
1981
|
+
() => getExternalPreviewUrl(renderedContent),
|
|
1982
|
+
[renderedContent],
|
|
1983
|
+
);
|
|
385
1984
|
zoomRef.current = zoom;
|
|
386
1985
|
|
|
1986
|
+
const queuedAnnotationPins = useMemo(
|
|
1987
|
+
() =>
|
|
1988
|
+
annotationPins.filter(
|
|
1989
|
+
(pin) => pin.queued && !pin.submitted && (pin.draft || "").trim(),
|
|
1990
|
+
),
|
|
1991
|
+
[annotationPins],
|
|
1992
|
+
);
|
|
1993
|
+
|
|
1994
|
+
useEffect(() => {
|
|
1995
|
+
if (previousContentKeyRef.current !== contentKey) {
|
|
1996
|
+
previousContentKeyRef.current = contentKey;
|
|
1997
|
+
setRenderedContent(content);
|
|
1998
|
+
}
|
|
1999
|
+
// Same-screen visual edits are already applied optimistically inside the
|
|
2000
|
+
// iframe before the source write is queued. Rebuilding srcdoc for that echo
|
|
2001
|
+
// reloads the iframe, flashes unstyled content, and drops selection. Only a
|
|
2002
|
+
// content-key change (screen switch / explicit remount) should replace the
|
|
2003
|
+
// iframe document here; the bridge replays inspector state after that load.
|
|
2004
|
+
}, [content, contentKey]);
|
|
2005
|
+
|
|
387
2006
|
usePinchZoom({
|
|
388
2007
|
containerRef: scrollContainerRef,
|
|
389
2008
|
zoom,
|
|
@@ -395,22 +2014,41 @@ export function DesignCanvas({
|
|
|
395
2014
|
});
|
|
396
2015
|
|
|
397
2016
|
// Build the srcdoc. The tweak bridge ALWAYS goes in so the panel works
|
|
398
|
-
// outside Edit mode. The
|
|
2017
|
+
// outside Edit mode. The editor chrome bridge is omitted only for Interact.
|
|
399
2018
|
const srcdoc = useMemo(() => {
|
|
2019
|
+
if (externalPreviewUrl) return undefined;
|
|
2020
|
+
const editorChromeBridge = interactMode
|
|
2021
|
+
? ""
|
|
2022
|
+
: createEditorBridgeThemeScript(readEditorBridgeThemeVars()) +
|
|
2023
|
+
EDITOR_CHROME_BRIDGE_SCRIPT.replace(
|
|
2024
|
+
"__TEXT_EDITING_ENABLED__",
|
|
2025
|
+
editMode ? "true" : "false",
|
|
2026
|
+
);
|
|
2027
|
+
const embeddedWheelBridge = EMBEDDED_WHEEL_BRIDGE_SCRIPT.replace(
|
|
2028
|
+
"__EMBEDDED_WHEEL_FORWARDING_ENABLED__",
|
|
2029
|
+
isEmbeddedFrame ? "true" : "false",
|
|
2030
|
+
);
|
|
400
2031
|
const bridgeToInject =
|
|
401
2032
|
TWEAK_BRIDGE_SCRIPT +
|
|
402
2033
|
ZOOM_BRIDGE_SCRIPT +
|
|
403
2034
|
NAV_BRIDGE_SCRIPT +
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
2035
|
+
embeddedWheelBridge +
|
|
2036
|
+
editorChromeBridge;
|
|
2037
|
+
if (renderedContent.includes("</body>")) {
|
|
2038
|
+
return renderedContent.replace("</body>", bridgeToInject + "</body>"); // i18n-ignore generated iframe HTML injection
|
|
407
2039
|
}
|
|
408
|
-
if (
|
|
409
|
-
return
|
|
2040
|
+
if (renderedContent.includes("</html>")) {
|
|
2041
|
+
return renderedContent.replace("</html>", bridgeToInject + "</html>"); // i18n-ignore generated iframe HTML injection
|
|
410
2042
|
}
|
|
411
2043
|
// No body/html tags — wrap it
|
|
412
|
-
return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${
|
|
413
|
-
}, [
|
|
2044
|
+
return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${renderedContent}${bridgeToInject}</body></html>`;
|
|
2045
|
+
}, [
|
|
2046
|
+
editMode,
|
|
2047
|
+
externalPreviewUrl,
|
|
2048
|
+
interactMode,
|
|
2049
|
+
isEmbeddedFrame,
|
|
2050
|
+
renderedContent,
|
|
2051
|
+
]);
|
|
414
2052
|
|
|
415
2053
|
// Listen for messages from the iframe
|
|
416
2054
|
useEffect(() => {
|
|
@@ -432,6 +2070,143 @@ export function DesignCanvas({
|
|
|
432
2070
|
if (e.data.type === "element-hover") {
|
|
433
2071
|
onElementHover(e.data.payload);
|
|
434
2072
|
}
|
|
2073
|
+
if (e.data.type === "visual-style-change") {
|
|
2074
|
+
const selector = String(e.data.selector || "");
|
|
2075
|
+
const styles =
|
|
2076
|
+
e.data.styles && typeof e.data.styles === "object"
|
|
2077
|
+
? (e.data.styles as Record<string, string>)
|
|
2078
|
+
: {};
|
|
2079
|
+
if (selector && Object.keys(styles).length > 0) {
|
|
2080
|
+
onVisualStyleChange?.(selector, styles, e.data.payload);
|
|
2081
|
+
}
|
|
2082
|
+
return;
|
|
2083
|
+
}
|
|
2084
|
+
if (e.data.type === "text-content-change") {
|
|
2085
|
+
const selector = String(e.data.selector || "");
|
|
2086
|
+
const value = String(e.data.value ?? "");
|
|
2087
|
+
const html =
|
|
2088
|
+
typeof e.data.html === "string" ? String(e.data.html) : undefined;
|
|
2089
|
+
if (selector) {
|
|
2090
|
+
onTextContentChange?.(selector, value, e.data.payload, { html });
|
|
2091
|
+
}
|
|
2092
|
+
return;
|
|
2093
|
+
}
|
|
2094
|
+
if (e.data.type === "visual-structure-change") {
|
|
2095
|
+
const selector = String(e.data.selector || "");
|
|
2096
|
+
const anchorSelector = String(e.data.anchorSelector || "");
|
|
2097
|
+
const placement = String(e.data.placement || "after");
|
|
2098
|
+
const requestId =
|
|
2099
|
+
typeof e.data.requestId === "string" ? e.data.requestId : undefined;
|
|
2100
|
+
if (
|
|
2101
|
+
selector &&
|
|
2102
|
+
anchorSelector &&
|
|
2103
|
+
(placement === "before" ||
|
|
2104
|
+
placement === "after" ||
|
|
2105
|
+
placement === "inside")
|
|
2106
|
+
) {
|
|
2107
|
+
const applied = onVisualStructureChange?.(
|
|
2108
|
+
selector,
|
|
2109
|
+
anchorSelector,
|
|
2110
|
+
placement,
|
|
2111
|
+
e.data.payload,
|
|
2112
|
+
{
|
|
2113
|
+
requestId,
|
|
2114
|
+
sourceId:
|
|
2115
|
+
typeof e.data.sourceId === "string"
|
|
2116
|
+
? e.data.sourceId
|
|
2117
|
+
: undefined,
|
|
2118
|
+
anchorSourceId:
|
|
2119
|
+
typeof e.data.anchorSourceId === "string"
|
|
2120
|
+
? e.data.anchorSourceId
|
|
2121
|
+
: undefined,
|
|
2122
|
+
},
|
|
2123
|
+
);
|
|
2124
|
+
if (requestId) {
|
|
2125
|
+
iframeRef.current?.contentWindow?.postMessage(
|
|
2126
|
+
{
|
|
2127
|
+
type: "visual-structure-ack",
|
|
2128
|
+
requestId,
|
|
2129
|
+
applied: applied !== false,
|
|
2130
|
+
},
|
|
2131
|
+
"*",
|
|
2132
|
+
);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
if (e.data.type === "visual-duplicate-change") {
|
|
2138
|
+
const selector = String(e.data.selector || "");
|
|
2139
|
+
const cloneHtml =
|
|
2140
|
+
typeof e.data.cloneHtml === "string" ? String(e.data.cloneHtml) : "";
|
|
2141
|
+
const placement = String(e.data.placement || "after");
|
|
2142
|
+
if (
|
|
2143
|
+
selector &&
|
|
2144
|
+
cloneHtml &&
|
|
2145
|
+
(placement === "before" ||
|
|
2146
|
+
placement === "after" ||
|
|
2147
|
+
placement === "inside")
|
|
2148
|
+
) {
|
|
2149
|
+
onVisualDuplicateChange?.(selector, cloneHtml, e.data.payload, {
|
|
2150
|
+
sourceId:
|
|
2151
|
+
typeof e.data.sourceId === "string" ? e.data.sourceId : undefined,
|
|
2152
|
+
anchorSelector:
|
|
2153
|
+
typeof e.data.anchorSelector === "string"
|
|
2154
|
+
? e.data.anchorSelector
|
|
2155
|
+
: undefined,
|
|
2156
|
+
anchorSourceId:
|
|
2157
|
+
typeof e.data.anchorSourceId === "string"
|
|
2158
|
+
? e.data.anchorSourceId
|
|
2159
|
+
: undefined,
|
|
2160
|
+
placement,
|
|
2161
|
+
});
|
|
2162
|
+
}
|
|
2163
|
+
return;
|
|
2164
|
+
}
|
|
2165
|
+
if (e.data.type === "text-editing-state") {
|
|
2166
|
+
onTextEditingStateChange?.({
|
|
2167
|
+
active: Boolean(e.data.active),
|
|
2168
|
+
selector:
|
|
2169
|
+
typeof e.data.selector === "string" ? e.data.selector : undefined,
|
|
2170
|
+
hasRange: Boolean(e.data.hasRange),
|
|
2171
|
+
});
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
if (e.data.type === "design-hotkey") {
|
|
2175
|
+
onIframeHotkey?.({
|
|
2176
|
+
key: String(e.data.key || ""),
|
|
2177
|
+
code: String(e.data.code || ""),
|
|
2178
|
+
metaKey: Boolean(e.data.metaKey),
|
|
2179
|
+
ctrlKey: Boolean(e.data.ctrlKey),
|
|
2180
|
+
shiftKey: Boolean(e.data.shiftKey),
|
|
2181
|
+
altKey: Boolean(e.data.altKey),
|
|
2182
|
+
repeat: Boolean(e.data.repeat),
|
|
2183
|
+
});
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
if (e.data.type === "element-contextmenu") {
|
|
2187
|
+
const clientX = Number(e.data.clientX);
|
|
2188
|
+
const clientY = Number(e.data.clientY);
|
|
2189
|
+
if (Number.isFinite(clientX) && Number.isFinite(clientY)) {
|
|
2190
|
+
const iframe = iframeRef.current;
|
|
2191
|
+
const iframeRect = iframe?.getBoundingClientRect();
|
|
2192
|
+
const scaleX =
|
|
2193
|
+
iframe && iframeRect && iframe.clientWidth > 0
|
|
2194
|
+
? iframeRect.width / iframe.clientWidth
|
|
2195
|
+
: 1;
|
|
2196
|
+
const scaleY =
|
|
2197
|
+
iframe && iframeRect && iframe.clientHeight > 0
|
|
2198
|
+
? iframeRect.height / iframe.clientHeight
|
|
2199
|
+
: 1;
|
|
2200
|
+
onIframeContextMenu?.({
|
|
2201
|
+
clientX,
|
|
2202
|
+
clientY,
|
|
2203
|
+
viewportClientX: (iframeRect?.left ?? 0) + clientX * scaleX,
|
|
2204
|
+
viewportClientY: (iframeRect?.top ?? 0) + clientY * scaleY,
|
|
2205
|
+
info: e.data.payload ?? null,
|
|
2206
|
+
});
|
|
2207
|
+
}
|
|
2208
|
+
return;
|
|
2209
|
+
}
|
|
435
2210
|
if (e.data.type === "prototype-navigate") {
|
|
436
2211
|
// External links are opened inside the iframe (sandbox allow-popups);
|
|
437
2212
|
// only internal screen switches reach the parent.
|
|
@@ -441,6 +2216,34 @@ export function DesignCanvas({
|
|
|
441
2216
|
);
|
|
442
2217
|
return;
|
|
443
2218
|
}
|
|
2219
|
+
if (e.data.type === "embedded-canvas-wheel") {
|
|
2220
|
+
if (!isEmbeddedFrame) return;
|
|
2221
|
+
const iframe = iframeRef.current;
|
|
2222
|
+
if (!iframe) return;
|
|
2223
|
+
const rect = iframe.getBoundingClientRect();
|
|
2224
|
+
const scaleX =
|
|
2225
|
+
iframe.clientWidth > 0 ? rect.width / iframe.clientWidth : 1;
|
|
2226
|
+
const scaleY =
|
|
2227
|
+
iframe.clientHeight > 0 ? rect.height / iframe.clientHeight : 1;
|
|
2228
|
+
const clientX = rect.left + Number(e.data.clientX || 0) * scaleX;
|
|
2229
|
+
const clientY = rect.top + Number(e.data.clientY || 0) * scaleY;
|
|
2230
|
+
const forwarded = new WheelEvent("wheel", {
|
|
2231
|
+
bubbles: true,
|
|
2232
|
+
cancelable: true,
|
|
2233
|
+
deltaX: Math.max(-240, Math.min(240, Number(e.data.deltaX) || 0)),
|
|
2234
|
+
deltaY: Math.max(-240, Math.min(240, Number(e.data.deltaY) || 0)),
|
|
2235
|
+
deltaZ: Math.max(-240, Math.min(240, Number(e.data.deltaZ) || 0)),
|
|
2236
|
+
deltaMode: Number(e.data.deltaMode) || WheelEvent.DOM_DELTA_PIXEL,
|
|
2237
|
+
clientX,
|
|
2238
|
+
clientY,
|
|
2239
|
+
ctrlKey: Boolean(e.data.ctrlKey),
|
|
2240
|
+
metaKey: Boolean(e.data.metaKey),
|
|
2241
|
+
shiftKey: Boolean(e.data.shiftKey),
|
|
2242
|
+
altKey: Boolean(e.data.altKey),
|
|
2243
|
+
});
|
|
2244
|
+
iframe.dispatchEvent(forwarded);
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
444
2247
|
if (e.data.type === "pinch-zoom-wheel") {
|
|
445
2248
|
if (!onZoomChange) return;
|
|
446
2249
|
const iframe = iframeRef.current;
|
|
@@ -486,28 +2289,83 @@ export function DesignCanvas({
|
|
|
486
2289
|
}, [
|
|
487
2290
|
onElementSelect,
|
|
488
2291
|
onElementHover,
|
|
2292
|
+
onVisualStyleChange,
|
|
2293
|
+
onTextContentChange,
|
|
2294
|
+
onTextEditingStateChange,
|
|
2295
|
+
onIframeHotkey,
|
|
2296
|
+
onIframeContextMenu,
|
|
2297
|
+
onVisualStructureChange,
|
|
2298
|
+
onVisualDuplicateChange,
|
|
489
2299
|
onZoomChange,
|
|
490
2300
|
deviceFrame,
|
|
491
2301
|
onPrototypeNavigate,
|
|
2302
|
+
isEmbeddedFrame,
|
|
2303
|
+
]);
|
|
2304
|
+
|
|
2305
|
+
const replayIframeEditorState = useCallback(() => {
|
|
2306
|
+
const iframe = iframeRef.current;
|
|
2307
|
+
if (!iframe) return;
|
|
2308
|
+
iframe.contentWindow?.postMessage(
|
|
2309
|
+
{ type: "tweak-values", values: tweakValues },
|
|
2310
|
+
"*",
|
|
2311
|
+
);
|
|
2312
|
+
iframe.contentWindow?.postMessage(
|
|
2313
|
+
{ type: "layer-states", lockedSelectors, hiddenSelectors },
|
|
2314
|
+
"*",
|
|
2315
|
+
);
|
|
2316
|
+
iframe.contentWindow?.postMessage(
|
|
2317
|
+
{ type: "scale-tool-mode", enabled: scaleMode },
|
|
2318
|
+
"*",
|
|
2319
|
+
);
|
|
2320
|
+
iframe.contentWindow?.postMessage(
|
|
2321
|
+
selectedSelector
|
|
2322
|
+
? {
|
|
2323
|
+
type: "select-element",
|
|
2324
|
+
selector: selectedSelector,
|
|
2325
|
+
selectorCandidates: selectedSelectorCandidates,
|
|
2326
|
+
}
|
|
2327
|
+
: { type: "clear-selection" },
|
|
2328
|
+
"*",
|
|
2329
|
+
);
|
|
2330
|
+
iframe.contentWindow?.postMessage(
|
|
2331
|
+
hoveredSelector
|
|
2332
|
+
? {
|
|
2333
|
+
type: "hover-element",
|
|
2334
|
+
selector: hoveredSelector,
|
|
2335
|
+
selectorCandidates: hoveredSelectorCandidates,
|
|
2336
|
+
}
|
|
2337
|
+
: { type: "hover-element", selector: "", selectorCandidates: [] },
|
|
2338
|
+
"*",
|
|
2339
|
+
);
|
|
2340
|
+
}, [
|
|
2341
|
+
hoveredSelector,
|
|
2342
|
+
hoveredSelectorCandidates,
|
|
2343
|
+
hiddenSelectors,
|
|
2344
|
+
lockedSelectors,
|
|
2345
|
+
scaleMode,
|
|
2346
|
+
selectedSelector,
|
|
2347
|
+
selectedSelectorCandidates,
|
|
2348
|
+
tweakValues,
|
|
492
2349
|
]);
|
|
493
2350
|
|
|
494
|
-
//
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
// here, the freshly mounted document loses the user's tweak state.
|
|
2351
|
+
// Replay the editor state whenever it changes OR the iframe (re)loads. The
|
|
2352
|
+
// load case matters for screen switches and mode changes; without replaying
|
|
2353
|
+
// selection/layer state here, the freshly mounted document looks deselected.
|
|
498
2354
|
useEffect(() => {
|
|
499
2355
|
const iframe = iframeRef.current;
|
|
500
2356
|
if (!iframe) return;
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
2357
|
+
replayIframeEditorState();
|
|
2358
|
+
iframe.addEventListener("load", replayIframeEditorState);
|
|
2359
|
+
return () => iframe.removeEventListener("load", replayIframeEditorState);
|
|
2360
|
+
}, [replayIframeEditorState]);
|
|
2361
|
+
|
|
2362
|
+
useEffect(() => {
|
|
2363
|
+
if (clearSelectionRequest === undefined) return;
|
|
2364
|
+
iframeRef.current?.contentWindow?.postMessage(
|
|
2365
|
+
{ type: "clear-selection" },
|
|
2366
|
+
"*",
|
|
2367
|
+
);
|
|
2368
|
+
}, [clearSelectionRequest]);
|
|
511
2369
|
|
|
512
2370
|
const sendStyleChange = useCallback(
|
|
513
2371
|
(selector: string, property: string, value: string) => {
|
|
@@ -521,13 +2379,52 @@ export function DesignCanvas({
|
|
|
521
2379
|
[],
|
|
522
2380
|
);
|
|
523
2381
|
|
|
524
|
-
|
|
2382
|
+
const replacePreviewContent = useCallback(
|
|
2383
|
+
(nextContent: string, selector?: string | null, candidates?: string[]) => {
|
|
2384
|
+
const iframe = iframeRef.current;
|
|
2385
|
+
if (!iframe?.contentWindow) return false;
|
|
2386
|
+
iframe.contentWindow.postMessage(
|
|
2387
|
+
{
|
|
2388
|
+
type: "replace-document-content",
|
|
2389
|
+
content: nextContent,
|
|
2390
|
+
selectedSelector: selector ?? "",
|
|
2391
|
+
selectorCandidates: candidates ?? [],
|
|
2392
|
+
},
|
|
2393
|
+
"*",
|
|
2394
|
+
);
|
|
2395
|
+
return true;
|
|
2396
|
+
},
|
|
2397
|
+
[],
|
|
2398
|
+
);
|
|
2399
|
+
|
|
2400
|
+
const deleteRuntimeElement = useCallback(
|
|
2401
|
+
(selector?: string | null, candidates?: string[]) => {
|
|
2402
|
+
const iframe = iframeRef.current;
|
|
2403
|
+
if (!iframe?.contentWindow) return false;
|
|
2404
|
+
iframe.contentWindow.postMessage(
|
|
2405
|
+
{
|
|
2406
|
+
type: "delete-element",
|
|
2407
|
+
selector: selector ?? "",
|
|
2408
|
+
selectorCandidates: candidates ?? [],
|
|
2409
|
+
},
|
|
2410
|
+
"*",
|
|
2411
|
+
);
|
|
2412
|
+
return true;
|
|
2413
|
+
},
|
|
2414
|
+
[],
|
|
2415
|
+
);
|
|
2416
|
+
|
|
2417
|
+
// Expose iframe runtime mutations for the editor orchestrator.
|
|
525
2418
|
useEffect(() => {
|
|
526
2419
|
(window as any).__designCanvasSendStyle = sendStyleChange;
|
|
2420
|
+
(window as any).__designCanvasReplaceContent = replacePreviewContent;
|
|
2421
|
+
(window as any).__designCanvasDeleteElement = deleteRuntimeElement;
|
|
527
2422
|
return () => {
|
|
528
2423
|
delete (window as any).__designCanvasSendStyle;
|
|
2424
|
+
delete (window as any).__designCanvasReplaceContent;
|
|
2425
|
+
delete (window as any).__designCanvasDeleteElement;
|
|
529
2426
|
};
|
|
530
|
-
}, [sendStyleChange]);
|
|
2427
|
+
}, [deleteRuntimeElement, replacePreviewContent, sendStyleChange]);
|
|
531
2428
|
|
|
532
2429
|
// Device dimensions match real-world devices. iframes are replaced elements
|
|
533
2430
|
// with an intrinsic 300×150 size, so `aspect-ratio` + `height: auto` doesn't
|
|
@@ -550,28 +2447,38 @@ export function DesignCanvas({
|
|
|
550
2447
|
// iframe. The pin component anchors to `.design-canvas-iframe-wrapper`
|
|
551
2448
|
// via canvasSelector.
|
|
552
2449
|
//
|
|
553
|
-
// The wrapper carries a faint outline + soft shadow so the frame edge
|
|
554
|
-
// visible
|
|
555
|
-
// (e.g. both dark). Without this, a dark design dissolves into the canvas.
|
|
2450
|
+
// The wrapper carries a faint outline + soft shadow so the frame edge stays
|
|
2451
|
+
// visible when a design background matches the editor canvas.
|
|
556
2452
|
const iframeElement = (
|
|
557
2453
|
<div
|
|
558
2454
|
className="design-canvas-iframe-wrapper relative inline-block ring-1 ring-border/60 shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_8px_24px_-12px_rgba(0,0,0,0.45)]"
|
|
559
2455
|
style={{
|
|
560
|
-
width: iframeWidth,
|
|
561
|
-
height:
|
|
2456
|
+
width: embeddedFrame ? embeddedFrame.viewportWidth : iframeWidth,
|
|
2457
|
+
height: embeddedFrame
|
|
2458
|
+
? embeddedFrame.viewportHeight
|
|
2459
|
+
: deviceFrame === "none"
|
|
2460
|
+
? "100%"
|
|
2461
|
+
: (iframeHeight ?? undefined),
|
|
562
2462
|
}}
|
|
563
2463
|
>
|
|
564
2464
|
<iframe
|
|
565
2465
|
ref={iframeRef}
|
|
566
|
-
|
|
567
|
-
|
|
2466
|
+
src={externalPreviewUrl ?? undefined}
|
|
2467
|
+
srcDoc={externalPreviewUrl ? undefined : srcdoc}
|
|
2468
|
+
sandbox={
|
|
2469
|
+
externalPreviewUrl
|
|
2470
|
+
? "allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin"
|
|
2471
|
+
: "allow-scripts allow-popups allow-popups-to-escape-sandbox"
|
|
2472
|
+
}
|
|
568
2473
|
data-design-preview-iframe
|
|
569
|
-
className="
|
|
2474
|
+
className="block h-full w-full border-0 bg-transparent"
|
|
570
2475
|
title={t("designEditor.designPreview")}
|
|
571
2476
|
/>
|
|
572
2477
|
{/* Draw-to-prompt overlay — sits over the iframe, NOT inside it. */}
|
|
573
2478
|
<SharedDrawOverlay
|
|
574
2479
|
visible={!!drawMode}
|
|
2480
|
+
canvasInteractive={!pinMode}
|
|
2481
|
+
queuedAnnotationCount={queuedAnnotationPins.length}
|
|
575
2482
|
onClose={() => onExitDrawMode?.()}
|
|
576
2483
|
onSend={(annotations, instruction, canvasSize) => {
|
|
577
2484
|
const summary = annotations
|
|
@@ -581,24 +2488,77 @@ export function DesignCanvas({
|
|
|
581
2488
|
: `[label "${a.text}" at ${a.position.x.toFixed(0)},${a.position.y.toFixed(0)}]`,
|
|
582
2489
|
)
|
|
583
2490
|
.join("\n");
|
|
2491
|
+
const pinSummary = queuedAnnotationPins
|
|
2492
|
+
.flatMap((pin, index) => {
|
|
2493
|
+
const lines = [
|
|
2494
|
+
`[${index + 1}] Comment pin on ${commentContextLabel || designTitle || commentContextId || designId || "design"}`,
|
|
2495
|
+
`Position: ${pin.xPct.toFixed(1)}% from left, ${pin.yPct.toFixed(1)}% from top`,
|
|
2496
|
+
];
|
|
2497
|
+
if (pin.targetAnchorId)
|
|
2498
|
+
lines.push(`Anchor id: ${pin.targetAnchorId}`);
|
|
2499
|
+
if (pin.targetSelector)
|
|
2500
|
+
lines.push(`Element: ${pin.targetSelector}`);
|
|
2501
|
+
if (pin.targetText)
|
|
2502
|
+
lines.push(`Nearby text: "${pin.targetText}"`);
|
|
2503
|
+
lines.push("");
|
|
2504
|
+
lines.push((pin.draft || "").trim());
|
|
2505
|
+
return [...lines, ""];
|
|
2506
|
+
})
|
|
2507
|
+
.join("\n");
|
|
584
2508
|
const lines = [
|
|
585
|
-
`[
|
|
2509
|
+
`[Annotations on design ${designId || ""}${designTitle ? ` (${designTitle})` : ""}]`,
|
|
586
2510
|
`Canvas size: ${canvasSize.width.toFixed(0)}x${canvasSize.height.toFixed(0)}`,
|
|
587
|
-
summary,
|
|
2511
|
+
...(summary ? ["", "[Drawing]", summary] : []),
|
|
2512
|
+
...(pinSummary ? ["", "[Comment pins]", pinSummary] : []),
|
|
588
2513
|
"",
|
|
589
2514
|
instruction || "Apply these annotations to the design.",
|
|
590
2515
|
];
|
|
591
2516
|
try {
|
|
592
|
-
|
|
2517
|
+
sendToAgentChat({
|
|
2518
|
+
message: lines.join("\n"),
|
|
2519
|
+
submit: true,
|
|
2520
|
+
openSidebar: true,
|
|
2521
|
+
});
|
|
593
2522
|
} catch (err) {
|
|
594
2523
|
console.error("[DesignCanvas] failed to submit drawing:", err);
|
|
595
2524
|
}
|
|
2525
|
+
if (queuedAnnotationPins.length > 0) {
|
|
2526
|
+
setPinSubmitSignal((signal) => signal + 1);
|
|
2527
|
+
}
|
|
596
2528
|
onExitDrawMode?.();
|
|
597
2529
|
}}
|
|
598
2530
|
/>
|
|
599
2531
|
</div>
|
|
600
2532
|
);
|
|
601
2533
|
|
|
2534
|
+
if (embeddedFrame) {
|
|
2535
|
+
const scaleX =
|
|
2536
|
+
embeddedFrame.displayWidth / Math.max(1, embeddedFrame.viewportWidth);
|
|
2537
|
+
const scaleY =
|
|
2538
|
+
embeddedFrame.displayHeight / Math.max(1, embeddedFrame.viewportHeight);
|
|
2539
|
+
return (
|
|
2540
|
+
<div
|
|
2541
|
+
ref={scrollContainerRef}
|
|
2542
|
+
className="relative h-full w-full overflow-hidden"
|
|
2543
|
+
style={{
|
|
2544
|
+
width: embeddedFrame.displayWidth,
|
|
2545
|
+
height: embeddedFrame.displayHeight,
|
|
2546
|
+
}}
|
|
2547
|
+
>
|
|
2548
|
+
<div
|
|
2549
|
+
style={{
|
|
2550
|
+
width: embeddedFrame.viewportWidth,
|
|
2551
|
+
height: embeddedFrame.viewportHeight,
|
|
2552
|
+
transform: `scale(${scaleX}, ${scaleY})`,
|
|
2553
|
+
transformOrigin: "top left",
|
|
2554
|
+
}}
|
|
2555
|
+
>
|
|
2556
|
+
{iframeElement}
|
|
2557
|
+
</div>
|
|
2558
|
+
</div>
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2561
|
+
|
|
602
2562
|
const wrappedContent =
|
|
603
2563
|
deviceFrame === "none" ? (
|
|
604
2564
|
iframeElement
|
|
@@ -611,32 +2571,22 @@ export function DesignCanvas({
|
|
|
611
2571
|
ref={scrollContainerRef}
|
|
612
2572
|
className="relative flex-1 h-full overflow-auto"
|
|
613
2573
|
>
|
|
614
|
-
{/* Dot grid background */}
|
|
615
|
-
<div
|
|
616
|
-
className="absolute inset-0"
|
|
617
|
-
style={{
|
|
618
|
-
backgroundImage:
|
|
619
|
-
"radial-gradient(circle, hsl(var(--border)) 1px, transparent 1px)",
|
|
620
|
-
backgroundSize: "24px 24px",
|
|
621
|
-
}}
|
|
622
|
-
/>
|
|
623
|
-
|
|
624
2574
|
{/* Canvas area. "none" mode fills the canvas (responsive preview);
|
|
625
|
-
framed modes are centered inside the
|
|
2575
|
+
framed modes are centered inside the canvas with zoom applied. */}
|
|
626
2576
|
{deviceFrame === "none" ? (
|
|
627
|
-
<div className="relative h-full w-full
|
|
2577
|
+
<div className="relative flex h-full w-full items-center justify-center">
|
|
628
2578
|
<div
|
|
629
2579
|
className="h-full w-full"
|
|
630
2580
|
style={{
|
|
631
2581
|
transform: `scale(${zoom / 100})`,
|
|
632
|
-
transformOrigin: "
|
|
2582
|
+
transformOrigin: "center center",
|
|
633
2583
|
}}
|
|
634
2584
|
>
|
|
635
2585
|
{wrappedContent}
|
|
636
2586
|
</div>
|
|
637
2587
|
</div>
|
|
638
2588
|
) : (
|
|
639
|
-
<div className="relative flex items-center justify-center min-h-full
|
|
2589
|
+
<div className="relative flex items-center justify-center min-h-full">
|
|
640
2590
|
<div
|
|
641
2591
|
style={{
|
|
642
2592
|
transform: `scale(${zoom / 100})`,
|
|
@@ -653,6 +2603,10 @@ export function DesignCanvas({
|
|
|
653
2603
|
the zoom-transformed container to keep coordinates stable. */}
|
|
654
2604
|
<CanvasCommentPins
|
|
655
2605
|
active={!!pinMode}
|
|
2606
|
+
submitMode={drawMode ? "queue" : "direct"}
|
|
2607
|
+
onPinsChange={setAnnotationPins}
|
|
2608
|
+
submitQueuedSignal={pinSubmitSignal}
|
|
2609
|
+
clickPlaneUnderToolbar={!!drawMode}
|
|
656
2610
|
onClose={() => onExitPinMode?.()}
|
|
657
2611
|
canvasSelector=".design-canvas-iframe-wrapper"
|
|
658
2612
|
contextId={commentContextId || designId || "design"}
|