@agent-native/core 0.79.27 → 0.80.1
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 +34 -0
- package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +55 -1
- package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +66 -1
- package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +65 -1
- package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +64 -1
- package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +59 -1
- package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +62 -1
- package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +58 -1
- package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +62 -1
- package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +50 -1
- package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +52 -1
- package/corpus/core/docs/content/skills-guide.mdx +5 -0
- package/corpus/core/docs/content/template-design.mdx +59 -1
- 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/application-state/handlers.ts +1 -1
- package/corpus/core/src/brand-kit/index.ts +1 -1
- 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 +144 -3
- package/corpus/core/src/client/AgentPanel.tsx +1 -1
- package/corpus/core/src/client/AssistantChat.tsx +43 -7
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
- 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/extensions/portable-extension.ts +1 -1
- 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/code-agents/transcript-normalizer.ts +1 -1
- package/corpus/core/src/extensions/path.ts +1 -4
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
- package/corpus/core/src/mcp/build-server.ts +1 -1
- package/corpus/core/src/org/handlers.ts +5 -5
- package/corpus/core/src/org/plugin.ts +2 -2
- package/corpus/core/src/scripts/call-agent.ts +1 -1
- package/corpus/core/src/scripts/db/safety.ts +1 -1
- package/corpus/core/src/server/auth.ts +3 -0
- package/corpus/core/src/server/builder-design-systems.ts +308 -0
- package/corpus/core/src/server/core-routes-plugin.ts +29 -3
- package/corpus/core/src/server/index.ts +8 -0
- 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 +60 -4
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/corpus/core/src/usage/store.ts +2 -2
- 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/dashboard-mutation-api.ts +298 -15
- package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
- package/corpus/templates/analytics/actions/list-session-recordings.ts +6 -1
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
- 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/hooks/use-replay-storage-status.ts +66 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +39 -0
- package/corpus/templates/analytics/app/i18n-data.ts +424 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +82 -34
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +7 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +144 -36
- 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/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
- package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +3 -3
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +75 -30
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +5 -2
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
- package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
- package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +53 -12
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
- package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
- 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/recorder.ts +24 -0
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +60 -1
- package/corpus/templates/content/actions/set-image-alt-text.ts +1 -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/content/shared/document-export.ts +1 -1
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +154 -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 +453 -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/index-design-system-with-builder.ts +259 -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/update-file.ts +9 -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 +719 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +2120 -103
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +74 -38
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +401 -0
- package/corpus/templates/design/app/components/design/EditPanel.tsx +3966 -440
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +1473 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +3940 -147
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +700 -13
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +105 -63
- package/corpus/templates/design/app/components/design/index.ts +0 -5
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +353 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1589 -0
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +511 -0
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +395 -0
- package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +2010 -0
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +420 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +238 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.stories.tsx +71 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +575 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +106 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
- package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +741 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +87 -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 +149 -5
- 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 +277 -0
- package/corpus/templates/design/app/i18n-data.ts +3028 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +5831 -904
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
- 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-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.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-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.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-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.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/handlers/import-figma-system.ts +38 -85
- package/corpus/templates/design/server/lib/design-snapshot.ts +9 -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 +2412 -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/html-content.ts +21 -0
- package/corpus/templates/design/shared/pen-path.ts +207 -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/app/lib/utils.ts +2 -2
- 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/.agents/skills/design-systems/SKILL.md +9 -10
- package/corpus/templates/slides/AGENTS.md +3 -2
- package/corpus/templates/slides/actions/import-file.ts +26 -17
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
- package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +5 -5
- package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
- package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
- package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
- package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
- package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
- package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
- package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
- package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
- package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
- package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
- package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
- package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
- package/corpus/templates/slides/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
- package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
- package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
- 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/application-state/handlers.js +1 -1
- package/dist/application-state/handlers.js.map +1 -1
- package/dist/brand-kit/index.d.ts +1 -1
- package/dist/brand-kit/index.js +1 -1
- package/dist/brand-kit/index.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 +139 -3
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.js +1 -1
- package/dist/client/AgentPanel.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/MultiTabAssistantChat.js +1 -1
- package/dist/client/MultiTabAssistantChat.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/extensions/portable-extension.d.ts +1 -1
- package/dist/client/extensions/portable-extension.d.ts.map +1 -1
- package/dist/client/extensions/portable-extension.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/code-agents/transcript-normalizer.js +1 -1
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/extensions/path.d.ts +1 -1
- package/dist/extensions/path.d.ts.map +1 -1
- package/dist/extensions/path.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/integrations/a2a-continuation-processor.js +1 -1
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/mcp/build-server.d.ts +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/org/handlers.js +5 -5
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/plugin.js +2 -2
- package/dist/org/plugin.js.map +1 -1
- package/dist/scripts/call-agent.js +1 -1
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/db/safety.js +1 -1
- package/dist/scripts/db/safety.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +3 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts +28 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -0
- package/dist/server/builder-design-systems.js +217 -0
- package/dist/server/builder-design-systems.js.map +1 -0
- package/dist/server/core-routes-plugin.d.ts +1 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +20 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/transcribe-voice.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 +60 -4
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/dist/usage/store.js +2 -2
- package/dist/usage/store.js.map +1 -1
- 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/skills-guide.mdx +5 -0
- package/docs/content/locales/ar-SA/template-design.mdx +55 -1
- package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
- package/docs/content/locales/de-DE/template-design.mdx +66 -1
- package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
- package/docs/content/locales/es-ES/template-design.mdx +65 -1
- package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
- package/docs/content/locales/fr-FR/template-design.mdx +64 -1
- package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
- package/docs/content/locales/hi-IN/template-design.mdx +59 -1
- package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
- package/docs/content/locales/ja-JP/template-design.mdx +62 -1
- package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
- package/docs/content/locales/ko-KR/template-design.mdx +58 -1
- package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
- package/docs/content/locales/pt-BR/template-design.mdx +62 -1
- package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
- package/docs/content/locales/zh-CN/template-design.mdx +50 -1
- package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
- package/docs/content/locales/zh-TW/template-design.mdx +52 -1
- package/docs/content/skills-guide.mdx +5 -0
- package/docs/content/template-design.mdx +59 -1
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/corpus/templates/design/actions/import-figma-file.ts +0 -166
- 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
- package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
|
@@ -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,326 @@ 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 matchesExactSelectorList(el, selectors) {
|
|
610
|
+
if (!el || !selectors || selectors.length === 0) return false;
|
|
611
|
+
for (var i = 0; i < selectors.length; i += 1) {
|
|
612
|
+
try {
|
|
613
|
+
if (el.matches(selectors[i])) return true;
|
|
614
|
+
} catch (_err) {}
|
|
615
|
+
}
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
function isLayerInteractionBlocked(el) {
|
|
620
|
+
return matchesSelectorList(el, lockedSelectors) || matchesSelectorList(el, hiddenSelectors);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function applyHiddenSelectors() {
|
|
624
|
+
document.querySelectorAll('[data-agent-native-runtime-hidden]').forEach(function(el) {
|
|
625
|
+
var previous = el.getAttribute('data-agent-native-previous-display');
|
|
626
|
+
if (previous === null) {
|
|
627
|
+
el.style.removeProperty('display');
|
|
628
|
+
} else {
|
|
629
|
+
el.style.display = previous;
|
|
630
|
+
}
|
|
631
|
+
el.removeAttribute('data-agent-native-runtime-hidden');
|
|
632
|
+
el.removeAttribute('data-agent-native-previous-display');
|
|
633
|
+
});
|
|
634
|
+
hiddenSelectors.forEach(function(selector) {
|
|
635
|
+
try {
|
|
636
|
+
document.querySelectorAll(selector).forEach(function(el) {
|
|
637
|
+
if (!el.hasAttribute('data-agent-native-runtime-hidden')) {
|
|
638
|
+
el.setAttribute('data-agent-native-previous-display', el.style.display || '');
|
|
639
|
+
}
|
|
640
|
+
el.setAttribute('data-agent-native-runtime-hidden', 'true');
|
|
641
|
+
el.style.display = 'none';
|
|
642
|
+
});
|
|
643
|
+
} catch (_err) {}
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function replaceRuntimeDocument(html, preferredSelector, selectorCandidates) {
|
|
648
|
+
if (typeof html !== 'string') return;
|
|
649
|
+
if (activeTextEditEl) {
|
|
650
|
+
applyHiddenSelectors();
|
|
651
|
+
refreshOverlays();
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
var parser = new DOMParser();
|
|
655
|
+
var nextDoc = parser.parseFromString(html, 'text/html');
|
|
656
|
+
if (!nextDoc || !nextDoc.body) return;
|
|
657
|
+
|
|
658
|
+
var persistentNodes = Array.prototype.slice.call(
|
|
659
|
+
document.querySelectorAll('[data-agent-native-edit-overlay]'),
|
|
660
|
+
);
|
|
661
|
+
var activeSelector = preferredSelector || (selectedEl ? getSelector(selectedEl) : '');
|
|
662
|
+
var activeCandidates = [];
|
|
663
|
+
if (Array.isArray(selectorCandidates)) {
|
|
664
|
+
selectorCandidates.forEach(function(selector) {
|
|
665
|
+
if (typeof selector === 'string' && selector && activeCandidates.indexOf(selector) === -1) {
|
|
666
|
+
activeCandidates.push(selector);
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
if (activeSelector && activeCandidates.indexOf(activeSelector) === -1) {
|
|
671
|
+
activeCandidates.push(activeSelector);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
var nextHeadHtml = nextDoc.head ? nextDoc.head.innerHTML : '';
|
|
675
|
+
if (nextHeadHtml === document.head.innerHTML && activeCandidates.length > 0) {
|
|
676
|
+
var currentMatch = null;
|
|
677
|
+
var nextMatch = null;
|
|
678
|
+
var matchedSelector = '';
|
|
679
|
+
var fallbackCurrentMatch = null;
|
|
680
|
+
var fallbackSelector = '';
|
|
681
|
+
for (var matchIndex = 0; matchIndex < activeCandidates.length; matchIndex += 1) {
|
|
682
|
+
try {
|
|
683
|
+
var currentCandidate = document.querySelector(activeCandidates[matchIndex]);
|
|
684
|
+
var nextCandidate = nextDoc.querySelector(activeCandidates[matchIndex]);
|
|
685
|
+
if (currentCandidate && !fallbackCurrentMatch) {
|
|
686
|
+
fallbackCurrentMatch = currentCandidate;
|
|
687
|
+
fallbackSelector = activeCandidates[matchIndex];
|
|
688
|
+
}
|
|
689
|
+
if (currentCandidate && nextCandidate) {
|
|
690
|
+
currentMatch = currentCandidate;
|
|
691
|
+
nextMatch = nextCandidate;
|
|
692
|
+
matchedSelector = activeCandidates[matchIndex];
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
} catch (_err) {
|
|
696
|
+
// Keep trying later aliases; bridge selectors can differ between
|
|
697
|
+
// runtime and DOMParser passes.
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (!currentMatch && fallbackCurrentMatch) {
|
|
701
|
+
currentMatch = fallbackCurrentMatch;
|
|
702
|
+
matchedSelector = fallbackSelector;
|
|
703
|
+
}
|
|
704
|
+
if (
|
|
705
|
+
currentMatch &&
|
|
706
|
+
currentMatch !== document.body &&
|
|
707
|
+
currentMatch !== document.documentElement &&
|
|
708
|
+
!isOverlayElement(currentMatch)
|
|
709
|
+
) {
|
|
710
|
+
if (nextMatch) {
|
|
711
|
+
currentMatch.replaceWith(document.importNode(nextMatch, true));
|
|
712
|
+
} else if (currentMatch !== document.body && currentMatch !== document.documentElement) {
|
|
713
|
+
currentMatch.parentElement && currentMatch.parentElement.removeChild(currentMatch);
|
|
714
|
+
}
|
|
715
|
+
applyHiddenSelectors();
|
|
716
|
+
selectedEl = null;
|
|
717
|
+
if (nextMatch) {
|
|
718
|
+
try {
|
|
719
|
+
selectedEl = document.querySelector(matchedSelector);
|
|
720
|
+
} catch (_err) {}
|
|
721
|
+
}
|
|
722
|
+
hoveredEl = null;
|
|
723
|
+
if (selectedEl && !isLayerInteractionBlocked(selectedEl)) {
|
|
724
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
725
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
|
|
726
|
+
} else {
|
|
727
|
+
selectionOverlay.style.display = 'none';
|
|
728
|
+
}
|
|
729
|
+
highlightOverlay.style.display = 'none';
|
|
730
|
+
hideMeasurements();
|
|
731
|
+
refreshOverlays();
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (document.head.innerHTML !== nextHeadHtml) {
|
|
736
|
+
document.head.innerHTML = nextHeadHtml;
|
|
737
|
+
}
|
|
738
|
+
Array.prototype.slice.call(document.body.attributes).forEach(function(attribute) {
|
|
739
|
+
document.body.removeAttribute(attribute.name);
|
|
740
|
+
});
|
|
741
|
+
Array.prototype.slice.call(nextDoc.body.attributes).forEach(function(attribute) {
|
|
742
|
+
document.body.setAttribute(attribute.name, attribute.value);
|
|
743
|
+
});
|
|
744
|
+
document.body.innerHTML = nextDoc.body.innerHTML;
|
|
745
|
+
persistentNodes.forEach(function(node) {
|
|
746
|
+
document.body.appendChild(node);
|
|
747
|
+
});
|
|
748
|
+
applyHiddenSelectors();
|
|
749
|
+
|
|
750
|
+
selectedEl = null;
|
|
751
|
+
hoveredEl = null;
|
|
752
|
+
for (var i = 0; i < activeCandidates.length && !selectedEl; i += 1) {
|
|
753
|
+
try {
|
|
754
|
+
var match = document.querySelector(activeCandidates[i]);
|
|
755
|
+
if (match && !isLayerInteractionBlocked(match)) selectedEl = match;
|
|
756
|
+
} catch (_err) {}
|
|
757
|
+
}
|
|
758
|
+
if (selectedEl) {
|
|
759
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
760
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
|
|
761
|
+
} else {
|
|
762
|
+
selectionOverlay.style.display = 'none';
|
|
763
|
+
}
|
|
764
|
+
highlightOverlay.style.display = 'none';
|
|
765
|
+
hideMeasurements();
|
|
766
|
+
refreshOverlays();
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function updatePaddingOverlay(el) {
|
|
770
|
+
if (!el) {
|
|
771
|
+
paddingOverlay.style.display = 'none';
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
var cs = window.getComputedStyle(el);
|
|
775
|
+
var top = readPx(cs.paddingTop) + readPx(cs.borderTopWidth);
|
|
776
|
+
var right = readPx(cs.paddingRight) + readPx(cs.borderRightWidth);
|
|
777
|
+
var bottom = readPx(cs.paddingBottom) + readPx(cs.borderBottomWidth);
|
|
778
|
+
var left = readPx(cs.paddingLeft) + readPx(cs.borderLeftWidth);
|
|
779
|
+
var hasPadding = top > 0 || right > 0 || bottom > 0 || left > 0;
|
|
780
|
+
paddingOverlay.style.display = hasPadding ? 'block' : 'none';
|
|
781
|
+
paddingOverlay.style.top = top + 'px';
|
|
782
|
+
paddingOverlay.style.right = right + 'px';
|
|
783
|
+
paddingOverlay.style.bottom = bottom + 'px';
|
|
784
|
+
paddingOverlay.style.left = left + 'px';
|
|
785
|
+
}
|
|
270
786
|
|
|
271
787
|
function positionOverlay(overlay, el) {
|
|
272
788
|
if (!el || !document.documentElement.contains(el)) {
|
|
@@ -279,6 +795,7 @@ const EDIT_BRIDGE_SCRIPT = `
|
|
|
279
795
|
overlay.style.left = rect.left + 'px';
|
|
280
796
|
overlay.style.width = rect.width + 'px';
|
|
281
797
|
overlay.style.height = rect.height + 'px';
|
|
798
|
+
if (overlay === selectionOverlay) updatePaddingOverlay(el);
|
|
282
799
|
}
|
|
283
800
|
|
|
284
801
|
function refreshOverlays() {
|
|
@@ -286,38 +803,1070 @@ const EDIT_BRIDGE_SCRIPT = `
|
|
|
286
803
|
if (selectedEl) positionOverlay(selectionOverlay, selectedEl);
|
|
287
804
|
}
|
|
288
805
|
|
|
289
|
-
|
|
290
|
-
|
|
806
|
+
function hideMeasurements() {
|
|
807
|
+
measurementOverlay.style.display = 'none';
|
|
808
|
+
measurementOverlay.innerHTML = '';
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function addMeasurementLine(x1, y1, x2, y2, label) {
|
|
812
|
+
var horizontal = Math.abs(x2 - x1) >= Math.abs(y2 - y1);
|
|
813
|
+
var line = document.createElement('div');
|
|
814
|
+
var labelEl = document.createElement('div');
|
|
815
|
+
if (horizontal) {
|
|
816
|
+
var left = Math.min(x1, x2);
|
|
817
|
+
var width = Math.max(1, Math.abs(x2 - x1));
|
|
818
|
+
line.style.cssText = 'position:fixed;left:' + left + 'px;top:' + y1 + 'px;width:' + width + 'px;border-top:1px dashed var(--design-editor-measure-color);';
|
|
819
|
+
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;';
|
|
820
|
+
} else {
|
|
821
|
+
var top = Math.min(y1, y2);
|
|
822
|
+
var height = Math.max(1, Math.abs(y2 - y1));
|
|
823
|
+
line.style.cssText = 'position:fixed;left:' + x1 + 'px;top:' + top + 'px;height:' + height + 'px;border-left:1px dashed var(--design-editor-measure-color);';
|
|
824
|
+
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;';
|
|
825
|
+
}
|
|
826
|
+
labelEl.textContent = label;
|
|
827
|
+
measurementOverlay.appendChild(line);
|
|
828
|
+
measurementOverlay.appendChild(labelEl);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function showMeasurements(a, b) {
|
|
832
|
+
if (!a || !b || a === b) {
|
|
833
|
+
hideMeasurements();
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
var selectedRect = a.getBoundingClientRect();
|
|
837
|
+
var hoverRect = b.getBoundingClientRect();
|
|
838
|
+
measurementOverlay.innerHTML = '';
|
|
839
|
+
measurementOverlay.style.display = 'block';
|
|
840
|
+
|
|
841
|
+
if (hoverRect.right <= selectedRect.left) {
|
|
842
|
+
var yLeft = Math.max(hoverRect.top, Math.min(hoverRect.bottom, selectedRect.top + selectedRect.height / 2));
|
|
843
|
+
addMeasurementLine(hoverRect.right, yLeft, selectedRect.left, yLeft, Math.round(selectedRect.left - hoverRect.right) + 'px');
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
if (selectedRect.right <= hoverRect.left) {
|
|
847
|
+
var yRight = Math.max(selectedRect.top, Math.min(selectedRect.bottom, hoverRect.top + hoverRect.height / 2));
|
|
848
|
+
addMeasurementLine(selectedRect.right, yRight, hoverRect.left, yRight, Math.round(hoverRect.left - selectedRect.right) + 'px');
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (hoverRect.bottom <= selectedRect.top) {
|
|
852
|
+
var xTop = Math.max(hoverRect.left, Math.min(hoverRect.right, selectedRect.left + selectedRect.width / 2));
|
|
853
|
+
addMeasurementLine(xTop, hoverRect.bottom, xTop, selectedRect.top, Math.round(selectedRect.top - hoverRect.bottom) + 'px');
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (selectedRect.bottom <= hoverRect.top) {
|
|
857
|
+
var xBottom = Math.max(selectedRect.left, Math.min(selectedRect.right, hoverRect.left + hoverRect.width / 2));
|
|
858
|
+
addMeasurementLine(xBottom, selectedRect.bottom, xBottom, hoverRect.top, Math.round(hoverRect.top - selectedRect.bottom) + 'px');
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
addMeasurementLine(
|
|
862
|
+
selectedRect.left + selectedRect.width / 2,
|
|
863
|
+
selectedRect.top + selectedRect.height / 2,
|
|
864
|
+
hoverRect.left + hoverRect.width / 2,
|
|
865
|
+
hoverRect.top + hoverRect.height / 2,
|
|
866
|
+
Math.round(Math.hypot(
|
|
867
|
+
hoverRect.left + hoverRect.width / 2 - (selectedRect.left + selectedRect.width / 2),
|
|
868
|
+
hoverRect.top + hoverRect.height / 2 - (selectedRect.top + selectedRect.height / 2)
|
|
869
|
+
)) + 'px'
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function elementFromEditorPoint(clientX, clientY) {
|
|
874
|
+
var shieldPointerEvents = shieldOverlay.style.pointerEvents;
|
|
875
|
+
var selectionPointerEvents = selectionOverlay.style.pointerEvents;
|
|
876
|
+
var highlightPointerEvents = highlightOverlay.style.pointerEvents;
|
|
877
|
+
shieldOverlay.style.pointerEvents = 'none';
|
|
878
|
+
selectionOverlay.style.pointerEvents = 'none';
|
|
879
|
+
highlightOverlay.style.pointerEvents = 'none';
|
|
880
|
+
var target = document.elementFromPoint(clientX, clientY);
|
|
881
|
+
shieldOverlay.style.pointerEvents = shieldPointerEvents;
|
|
882
|
+
selectionOverlay.style.pointerEvents = selectionPointerEvents;
|
|
883
|
+
highlightOverlay.style.pointerEvents = highlightPointerEvents;
|
|
884
|
+
if (!target || target.nodeType !== 1) return null;
|
|
885
|
+
if (isLayerInteractionBlocked(target)) return null;
|
|
886
|
+
return target;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
function stopNativeInteraction(e) {
|
|
291
890
|
e.preventDefault();
|
|
292
891
|
e.stopPropagation();
|
|
293
|
-
|
|
892
|
+
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function isEditorTypingTarget(target) {
|
|
896
|
+
if (!target || !target.closest) return false;
|
|
897
|
+
return !!target.closest('input, textarea, select, [contenteditable], [role="textbox"], [data-agent-native-text-editing]');
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
function shouldForwardDesignHotkey(e) {
|
|
901
|
+
if (activeTextEditEl || isEditorTypingTarget(e.target) || e.isComposing) return false;
|
|
902
|
+
var key = e.key;
|
|
903
|
+
var normalized = key && key.length === 1 ? key.toLowerCase() : key;
|
|
904
|
+
var primary = e.metaKey || e.ctrlKey;
|
|
905
|
+
if (key === 'Escape' || key === 'Enter' || key === 'Tab') return true;
|
|
906
|
+
if (key === 'Delete' || key === 'Backspace') return !primary;
|
|
907
|
+
if (/^Arrow/.test(key || '')) return !e.altKey;
|
|
908
|
+
if (primary) {
|
|
909
|
+
return ['z','y','a','x','c','v','d','g','=','+','-','0',']','['].indexOf(normalized) !== -1 ||
|
|
910
|
+
e.code === 'Digit1' ||
|
|
911
|
+
e.code === 'Digit2' ||
|
|
912
|
+
key === '1' ||
|
|
913
|
+
key === '2';
|
|
914
|
+
}
|
|
915
|
+
if (e.shiftKey && (e.code === 'Digit1' || e.code === 'Digit2' || key === '1' || key === '2')) return true;
|
|
916
|
+
return !e.altKey && !e.shiftKey && ['v','f','r','t','p','h','c','k'].indexOf(normalized) !== -1;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function blurActiveTextEditor() {
|
|
920
|
+
var active = document.activeElement;
|
|
921
|
+
if (
|
|
922
|
+
active &&
|
|
923
|
+
active.closest &&
|
|
924
|
+
active.closest('[data-agent-native-text-editing]') &&
|
|
925
|
+
typeof active.blur === 'function'
|
|
926
|
+
) {
|
|
927
|
+
active.blur();
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
function setTextEditingPointerPassthrough(enabled) {
|
|
932
|
+
if (enabled) {
|
|
933
|
+
if (!textEditPointerState) {
|
|
934
|
+
textEditPointerState = {
|
|
935
|
+
shield: shieldOverlay.style.pointerEvents,
|
|
936
|
+
selection: selectionOverlay.style.pointerEvents,
|
|
937
|
+
highlight: highlightOverlay.style.pointerEvents,
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
shieldOverlay.style.pointerEvents = 'none';
|
|
941
|
+
selectionOverlay.style.pointerEvents = 'none';
|
|
942
|
+
highlightOverlay.style.pointerEvents = 'none';
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
if (!textEditPointerState) return;
|
|
946
|
+
shieldOverlay.style.pointerEvents = textEditPointerState.shield;
|
|
947
|
+
selectionOverlay.style.pointerEvents = textEditPointerState.selection;
|
|
948
|
+
highlightOverlay.style.pointerEvents = textEditPointerState.highlight;
|
|
949
|
+
textEditPointerState = null;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
function hasTextContent(el) {
|
|
953
|
+
return !!(el && el.textContent && el.textContent.trim().length > 0);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
function isInlineEditableDescendant(el) {
|
|
957
|
+
if (!el || !el.tagName) return false;
|
|
958
|
+
return [
|
|
959
|
+
'a',
|
|
960
|
+
'abbr',
|
|
961
|
+
'b',
|
|
962
|
+
'br',
|
|
963
|
+
'cite',
|
|
964
|
+
'code',
|
|
965
|
+
'em',
|
|
966
|
+
'i',
|
|
967
|
+
'mark',
|
|
968
|
+
'small',
|
|
969
|
+
'span',
|
|
970
|
+
'strong',
|
|
971
|
+
'sub',
|
|
972
|
+
'sup',
|
|
973
|
+
'time',
|
|
974
|
+
'u',
|
|
975
|
+
'wbr'
|
|
976
|
+
].indexOf(el.tagName.toLowerCase()) !== -1;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
function hasOnlyInlineEditableChildren(el) {
|
|
980
|
+
if (!el || !hasTextContent(el)) return false;
|
|
981
|
+
var descendants = el.querySelectorAll ? el.querySelectorAll('*') : [];
|
|
982
|
+
for (var i = 0; i < descendants.length; i += 1) {
|
|
983
|
+
if (!isInlineEditableDescendant(descendants[i])) return false;
|
|
984
|
+
}
|
|
985
|
+
return true;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
function findTextEditTarget(hit) {
|
|
989
|
+
if (!hit || hit.nodeType !== 1 || hit === document.body || hit === document.documentElement) return null;
|
|
990
|
+
var selectedContainsHit = selectedEl && selectedEl.contains && selectedEl.contains(hit);
|
|
991
|
+
if (selectedContainsHit && hasOnlyInlineEditableChildren(selectedEl)) return selectedEl;
|
|
992
|
+
|
|
993
|
+
var candidate = null;
|
|
994
|
+
var node = hit;
|
|
995
|
+
while (node && node.nodeType === 1 && node !== document.body && node !== document.documentElement) {
|
|
996
|
+
if (hasOnlyInlineEditableChildren(node)) {
|
|
997
|
+
candidate = node;
|
|
998
|
+
}
|
|
999
|
+
if (selectedEl && node === selectedEl) break;
|
|
1000
|
+
node = node.parentElement;
|
|
1001
|
+
}
|
|
1002
|
+
return candidate || hit;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
function selectElementAtEvent(e) {
|
|
1006
|
+
stopNativeInteraction(e);
|
|
1007
|
+
blurActiveTextEditor();
|
|
1008
|
+
var target = elementFromEditorPoint(e.clientX, e.clientY);
|
|
1009
|
+
if (!target) return;
|
|
1010
|
+
selectedEl = target;
|
|
294
1011
|
var info = getElementInfo(selectedEl);
|
|
295
1012
|
positionOverlay(selectionOverlay, selectedEl);
|
|
296
1013
|
window.parent.postMessage({ type: 'element-select', payload: info }, '*');
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function openContextMenuAtEvent(e) {
|
|
1017
|
+
stopNativeInteraction(e);
|
|
1018
|
+
blurActiveTextEditor();
|
|
1019
|
+
var target = elementFromEditorPoint(e.clientX, e.clientY);
|
|
1020
|
+
var info = null;
|
|
1021
|
+
if (target) {
|
|
1022
|
+
selectedEl = target;
|
|
1023
|
+
info = getElementInfo(target);
|
|
1024
|
+
positionOverlay(selectionOverlay, target);
|
|
1025
|
+
window.parent.postMessage({ type: 'element-select', payload: info }, '*');
|
|
1026
|
+
}
|
|
1027
|
+
window.parent.postMessage({
|
|
1028
|
+
type: 'element-contextmenu',
|
|
1029
|
+
clientX: e.clientX,
|
|
1030
|
+
clientY: e.clientY,
|
|
1031
|
+
payload: info
|
|
1032
|
+
}, '*');
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
function findRuntimeTarget(selector, selectorCandidates) {
|
|
1036
|
+
var candidates = [];
|
|
1037
|
+
if (Array.isArray(selectorCandidates)) {
|
|
1038
|
+
selectorCandidates.forEach(function(candidate) {
|
|
1039
|
+
if (typeof candidate === 'string' && candidate && candidates.indexOf(candidate) === -1) {
|
|
1040
|
+
candidates.push(candidate);
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
if (selector && candidates.indexOf(selector) === -1) candidates.push(selector);
|
|
1045
|
+
if (
|
|
1046
|
+
selectedEl &&
|
|
1047
|
+
document.documentElement.contains(selectedEl) &&
|
|
1048
|
+
(candidates.length === 0 || matchesExactSelectorList(selectedEl, candidates))
|
|
1049
|
+
) {
|
|
1050
|
+
return selectedEl;
|
|
1051
|
+
}
|
|
1052
|
+
for (var i = 0; i < candidates.length; i += 1) {
|
|
1053
|
+
try {
|
|
1054
|
+
var match = document.querySelector(candidates[i]);
|
|
1055
|
+
if (match && !isLayerInteractionBlocked(match)) return match;
|
|
1056
|
+
} catch (_err) {}
|
|
1057
|
+
}
|
|
1058
|
+
return null;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
function removeRuntimeTarget(selector, selectorCandidates) {
|
|
1062
|
+
var target = findRuntimeTarget(selector, selectorCandidates);
|
|
1063
|
+
if (!target || target === document.body || target === document.documentElement) return false;
|
|
1064
|
+
if (target.parentElement) target.parentElement.removeChild(target);
|
|
1065
|
+
if (selectedEl === target || !document.documentElement.contains(selectedEl)) {
|
|
1066
|
+
selectedEl = null;
|
|
1067
|
+
selectionOverlay.style.display = 'none';
|
|
1068
|
+
}
|
|
1069
|
+
hoveredEl = null;
|
|
1070
|
+
highlightOverlay.style.display = 'none';
|
|
1071
|
+
hideMeasurements();
|
|
1072
|
+
refreshOverlays();
|
|
1073
|
+
return true;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function readPx(value) {
|
|
1077
|
+
var num = parseFloat(value);
|
|
1078
|
+
return Number.isFinite(num) ? num : 0;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
function currentRotation(el) {
|
|
1082
|
+
var transform = el.style.transform || window.getComputedStyle(el).transform || '';
|
|
1083
|
+
var match = transform.match(/rotate\\((-?\\d+(?:\\.\\d+)?)deg\\)/);
|
|
1084
|
+
if (match) return parseFloat(match[1]) || 0;
|
|
1085
|
+
if (transform && transform !== 'none' && window.DOMMatrixReadOnly) {
|
|
1086
|
+
try {
|
|
1087
|
+
var matrix = new DOMMatrixReadOnly(transform);
|
|
1088
|
+
return Math.round(Math.atan2(matrix.b, matrix.a) * 180 / Math.PI);
|
|
1089
|
+
} catch (err) {}
|
|
1090
|
+
}
|
|
1091
|
+
return 0;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
function mergeRotation(el, degrees) {
|
|
1095
|
+
var inline = el.style.transform || '';
|
|
1096
|
+
var next = inline.match(/rotate\\((-?\\d+(?:\\.\\d+)?)deg\\)/)
|
|
1097
|
+
? inline.replace(/rotate\\((-?\\d+(?:\\.\\d+)?)deg\\)/, 'rotate(' + degrees + 'deg)')
|
|
1098
|
+
: (inline && inline !== 'none' ? inline + ' ' : '') + 'rotate(' + degrees + 'deg)';
|
|
1099
|
+
return next.trim();
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
function ensurePositionable(el) {
|
|
1103
|
+
var cs = window.getComputedStyle(el);
|
|
1104
|
+
if (cs.position === 'static') {
|
|
1105
|
+
el.style.position = 'relative';
|
|
1106
|
+
if (!el.style.left) el.style.left = '0px';
|
|
1107
|
+
if (!el.style.top) el.style.top = '0px';
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function postVisualStyleChange(styles) {
|
|
1112
|
+
if (!selectedEl) return;
|
|
1113
|
+
window.parent.postMessage({
|
|
1114
|
+
type: 'visual-style-change',
|
|
1115
|
+
selector: getSelector(selectedEl),
|
|
1116
|
+
styles: styles,
|
|
1117
|
+
payload: getElementInfo(selectedEl),
|
|
1118
|
+
}, '*');
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
function postTextContentChange(el, value, html) {
|
|
1122
|
+
window.parent.postMessage({
|
|
1123
|
+
type: 'text-content-change',
|
|
1124
|
+
selector: getSelector(el),
|
|
1125
|
+
value: value,
|
|
1126
|
+
html: html,
|
|
1127
|
+
payload: getElementInfo(el),
|
|
1128
|
+
}, '*');
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function postTextEditingState(el, active) {
|
|
1132
|
+
var selection = window.getSelection && window.getSelection();
|
|
1133
|
+
window.parent.postMessage({
|
|
1134
|
+
type: 'text-editing-state',
|
|
1135
|
+
active: !!active,
|
|
1136
|
+
selector: el ? getSelector(el) : '',
|
|
1137
|
+
hasRange: !!(active && selection && selection.rangeCount > 0 && !selection.isCollapsed && selectionBelongsToElement(selection, el)),
|
|
1138
|
+
}, '*');
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
function insertPlainTextAtSelection(text) {
|
|
1142
|
+
if (!text) return;
|
|
1143
|
+
if (document.queryCommandSupported && document.queryCommandSupported('insertText')) {
|
|
1144
|
+
document.execCommand('insertText', false, text);
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
var selection = window.getSelection && window.getSelection();
|
|
1148
|
+
if (!selection || selection.rangeCount === 0) return;
|
|
1149
|
+
var range = selection.getRangeAt(0);
|
|
1150
|
+
range.deleteContents();
|
|
1151
|
+
var textNode = document.createTextNode(text);
|
|
1152
|
+
range.insertNode(textNode);
|
|
1153
|
+
range.setStartAfter(textNode);
|
|
1154
|
+
range.setEndAfter(textNode);
|
|
1155
|
+
selection.removeAllRanges();
|
|
1156
|
+
selection.addRange(range);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
function selectionBelongsToElement(selection, el) {
|
|
1160
|
+
if (!selection || !el || selection.rangeCount === 0) return false;
|
|
1161
|
+
var range = selection.getRangeAt(0);
|
|
1162
|
+
var ancestor = range.commonAncestorContainer;
|
|
1163
|
+
var ancestorEl = ancestor && ancestor.nodeType === 1 ? ancestor : ancestor && ancestor.parentElement;
|
|
1164
|
+
return !!(ancestorEl && (ancestorEl === el || el.contains(ancestorEl)));
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
function applyTextRangeStyle(property, value) {
|
|
1168
|
+
if (!activeTextEditEl || !property) return false;
|
|
1169
|
+
var selection = window.getSelection && window.getSelection();
|
|
1170
|
+
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return false;
|
|
1171
|
+
if (!selectionBelongsToElement(selection, activeTextEditEl)) return false;
|
|
1172
|
+
var range = selection.getRangeAt(0);
|
|
1173
|
+
var span = document.createElement('span');
|
|
1174
|
+
span.style[property] = value;
|
|
1175
|
+
if (!span.getAttribute('style')) return false;
|
|
1176
|
+
try {
|
|
1177
|
+
range.surroundContents(span);
|
|
1178
|
+
} catch (err) {
|
|
1179
|
+
var contents = range.extractContents();
|
|
1180
|
+
span.appendChild(contents);
|
|
1181
|
+
range.insertNode(span);
|
|
1182
|
+
}
|
|
1183
|
+
selection.removeAllRanges();
|
|
1184
|
+
var nextRange = document.createRange();
|
|
1185
|
+
nextRange.selectNodeContents(span);
|
|
1186
|
+
selection.addRange(nextRange);
|
|
1187
|
+
return true;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
function showTransformBadge(text, clientX, clientY) {
|
|
1191
|
+
transformBadge.textContent = text;
|
|
1192
|
+
transformBadge.style.display = 'block';
|
|
1193
|
+
transformBadge.style.left = clientX + 12 + 'px';
|
|
1194
|
+
transformBadge.style.top = clientY + 12 + 'px';
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function hideTransformBadge() {
|
|
1198
|
+
transformBadge.style.display = 'none';
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
function hideInsertionGuide() {
|
|
1202
|
+
insertionGuide.style.display = 'none';
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
function isOverlayElement(el) {
|
|
1206
|
+
return Boolean(el && el.getAttribute && el.getAttribute('data-agent-native-edit-overlay'));
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
function draggableElementChildren(parent) {
|
|
1210
|
+
return Array.prototype.slice.call(parent.children).filter(function(child) {
|
|
1211
|
+
return child.nodeType === 1 && !isOverlayElement(child) && !isLayerInteractionBlocked(child);
|
|
1212
|
+
});
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
function isFlowReorderCandidate(el) {
|
|
1216
|
+
if (!el || !el.parentElement) return false;
|
|
1217
|
+
if (el === document.body || el === document.documentElement) return false;
|
|
1218
|
+
var cs = window.getComputedStyle(el);
|
|
1219
|
+
if (cs.position === 'absolute' || cs.position === 'fixed') return false;
|
|
1220
|
+
var parent = el.parentElement;
|
|
1221
|
+
if (parent === document.body && draggableElementChildren(parent).length <= 2) return false;
|
|
1222
|
+
return draggableElementChildren(parent).filter(function(child) { return child !== el; }).length > 0;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
function parentFlowAxis(parent) {
|
|
1226
|
+
var cs = window.getComputedStyle(parent);
|
|
1227
|
+
if (cs.display === 'flex' || cs.display === 'inline-flex') {
|
|
1228
|
+
return cs.flexDirection && cs.flexDirection.indexOf('row') === 0 ? 'x' : 'y';
|
|
1229
|
+
}
|
|
1230
|
+
if (cs.display === 'grid' || cs.display === 'inline-grid') {
|
|
1231
|
+
var cols = (cs.gridTemplateColumns || '').split(' ').filter(Boolean).length;
|
|
1232
|
+
return cols > 1 ? 'x' : 'y';
|
|
1233
|
+
}
|
|
1234
|
+
return 'y';
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
function reorderTargetForPoint(el, clientX, clientY) {
|
|
1238
|
+
if (!el || !el.parentElement) return null;
|
|
1239
|
+
var hit = elementFromEditorPoint(clientX, clientY);
|
|
1240
|
+
if (
|
|
1241
|
+
hit &&
|
|
1242
|
+
hit !== document.body &&
|
|
1243
|
+
hit !== document.documentElement &&
|
|
1244
|
+
hit !== el &&
|
|
1245
|
+
!el.contains(hit) &&
|
|
1246
|
+
!isOverlayElement(hit)
|
|
1247
|
+
) {
|
|
1248
|
+
var hitChildren = draggableElementChildren(hit).filter(function(child) {
|
|
1249
|
+
return child !== el;
|
|
1250
|
+
});
|
|
1251
|
+
if (hitChildren.length === 0) {
|
|
1252
|
+
return { anchor: hit, placement: 'inside', axis: parentFlowAxis(hit) };
|
|
1253
|
+
}
|
|
1254
|
+
var hitParent = hit.parentElement;
|
|
1255
|
+
if (hitParent) {
|
|
1256
|
+
var hitAxis = parentFlowAxis(hitParent);
|
|
1257
|
+
var hitRect = hit.getBoundingClientRect();
|
|
1258
|
+
var hitCenter = hitAxis === 'x'
|
|
1259
|
+
? hitRect.left + hitRect.width / 2
|
|
1260
|
+
: hitRect.top + hitRect.height / 2;
|
|
1261
|
+
var hitPointer = hitAxis === 'x' ? clientX : clientY;
|
|
1262
|
+
return {
|
|
1263
|
+
anchor: hit,
|
|
1264
|
+
placement: hitPointer < hitCenter ? 'before' : 'after',
|
|
1265
|
+
axis: hitAxis,
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
var parent = el.parentElement;
|
|
1270
|
+
var axis = parentFlowAxis(parent);
|
|
1271
|
+
var siblings = draggableElementChildren(parent).filter(function(child) {
|
|
1272
|
+
return child !== el;
|
|
1273
|
+
});
|
|
1274
|
+
if (!siblings.length) return null;
|
|
1275
|
+
var beforeTarget = null;
|
|
1276
|
+
for (var i = 0; i < siblings.length; i += 1) {
|
|
1277
|
+
var rect = siblings[i].getBoundingClientRect();
|
|
1278
|
+
var center = axis === 'x' ? rect.left + rect.width / 2 : rect.top + rect.height / 2;
|
|
1279
|
+
var pointer = axis === 'x' ? clientX : clientY;
|
|
1280
|
+
if (pointer < center) {
|
|
1281
|
+
beforeTarget = siblings[i];
|
|
1282
|
+
break;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
var anchor = beforeTarget || siblings[siblings.length - 1];
|
|
1286
|
+
var placement = beforeTarget ? 'before' : 'after';
|
|
1287
|
+
return { anchor: anchor, placement: placement, axis: axis };
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function showInsertionGuideFor(target) {
|
|
1291
|
+
if (!target || !target.anchor) {
|
|
1292
|
+
hideInsertionGuide();
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
var rect = target.anchor.getBoundingClientRect();
|
|
1296
|
+
insertionGuide.style.display = 'block';
|
|
1297
|
+
insertionGuide.style.background = 'var(--design-editor-accent-color)';
|
|
1298
|
+
insertionGuide.style.border = '0';
|
|
1299
|
+
insertionGuide.style.boxShadow = '0 0 0 1px var(--design-editor-accent-color)';
|
|
1300
|
+
if (target.placement === 'inside') {
|
|
1301
|
+
insertionGuide.style.left = rect.left + 'px';
|
|
1302
|
+
insertionGuide.style.top = rect.top + 'px';
|
|
1303
|
+
insertionGuide.style.width = rect.width + 'px';
|
|
1304
|
+
insertionGuide.style.height = rect.height + 'px';
|
|
1305
|
+
insertionGuide.style.background = 'color-mix(in srgb, var(--design-editor-accent-color) 14%, transparent)';
|
|
1306
|
+
insertionGuide.style.border = '2px solid var(--design-editor-accent-color)';
|
|
1307
|
+
insertionGuide.style.boxShadow = 'none';
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
if (target.axis === 'x') {
|
|
1311
|
+
var x = target.placement === 'before' ? rect.left : rect.right;
|
|
1312
|
+
insertionGuide.style.left = x + 'px';
|
|
1313
|
+
insertionGuide.style.top = rect.top + 'px';
|
|
1314
|
+
insertionGuide.style.width = '2px';
|
|
1315
|
+
insertionGuide.style.height = rect.height + 'px';
|
|
1316
|
+
} else {
|
|
1317
|
+
var y = target.placement === 'before' ? rect.top : rect.bottom;
|
|
1318
|
+
insertionGuide.style.left = rect.left + 'px';
|
|
1319
|
+
insertionGuide.style.top = y + 'px';
|
|
1320
|
+
insertionGuide.style.width = rect.width + 'px';
|
|
1321
|
+
insertionGuide.style.height = '2px';
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
function applyRuntimeReorder(el, target) {
|
|
1326
|
+
if (!el || !target || !target.anchor || !target.anchor.parentElement) return;
|
|
1327
|
+
if (target.placement === 'inside') {
|
|
1328
|
+
target.anchor.appendChild(el);
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
var parent = target.anchor.parentElement;
|
|
1332
|
+
if (target.placement === 'before') {
|
|
1333
|
+
parent.insertBefore(el, target.anchor);
|
|
1334
|
+
} else {
|
|
1335
|
+
parent.insertBefore(el, target.anchor.nextSibling);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
function postVisualStructureChange(el, target) {
|
|
1340
|
+
if (!el || !target || !target.anchor) return;
|
|
1341
|
+
var requestId = 'move-' + Date.now() + '-' + Math.random().toString(16).slice(2);
|
|
1342
|
+
pendingStructureMove = { requestId: requestId, el: el, target: target };
|
|
1343
|
+
window.parent.postMessage({
|
|
1344
|
+
type: 'visual-structure-change',
|
|
1345
|
+
requestId: requestId,
|
|
1346
|
+
selector: getSelector(el),
|
|
1347
|
+
sourceId: getSourceId(el),
|
|
1348
|
+
anchorSelector: getSelector(target.anchor),
|
|
1349
|
+
anchorSourceId: getSourceId(target.anchor),
|
|
1350
|
+
placement: target.placement,
|
|
1351
|
+
payload: getElementInfo(el),
|
|
1352
|
+
}, '*');
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
function postVisualDuplicateChange(originalEl, cloneEl, target) {
|
|
1356
|
+
if (!originalEl || !cloneEl) return;
|
|
1357
|
+
window.parent.postMessage({
|
|
1358
|
+
type: 'visual-duplicate-change',
|
|
1359
|
+
selector: getSelector(originalEl),
|
|
1360
|
+
sourceId: getSourceId(originalEl),
|
|
1361
|
+
anchorSelector: target && target.anchor ? getSelector(target.anchor) : '',
|
|
1362
|
+
anchorSourceId: target && target.anchor ? getSourceId(target.anchor) : '',
|
|
1363
|
+
placement: target && target.placement ? target.placement : 'after',
|
|
1364
|
+
cloneHtml: cloneEl.outerHTML,
|
|
1365
|
+
payload: getElementInfo(cloneEl),
|
|
1366
|
+
}, '*');
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
function startMove(e) {
|
|
1370
|
+
if (!selectedEl) return;
|
|
1371
|
+
if (isLayerInteractionBlocked(selectedEl)) return;
|
|
1372
|
+
e.preventDefault();
|
|
1373
|
+
e.stopPropagation();
|
|
1374
|
+
var originalSelectedEl = selectedEl;
|
|
1375
|
+
var duplicatedForDrag = false;
|
|
1376
|
+
if (e.altKey && selectedEl !== document.body && selectedEl !== document.documentElement) {
|
|
1377
|
+
var clone = selectedEl.cloneNode(true);
|
|
1378
|
+
resetRuntimeStableIds(clone);
|
|
1379
|
+
selectedEl.parentElement.insertBefore(clone, selectedEl.nextSibling);
|
|
1380
|
+
selectedEl = clone;
|
|
1381
|
+
duplicatedForDrag = true;
|
|
1382
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
1383
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
|
|
1384
|
+
}
|
|
1385
|
+
if (isFlowReorderCandidate(selectedEl)) {
|
|
1386
|
+
var currentTarget = reorderTargetForPoint(selectedEl, e.clientX, e.clientY);
|
|
1387
|
+
showInsertionGuideFor(currentTarget);
|
|
1388
|
+
function onReorderMove(ev) {
|
|
1389
|
+
currentTarget = reorderTargetForPoint(selectedEl, ev.clientX, ev.clientY);
|
|
1390
|
+
showInsertionGuideFor(currentTarget);
|
|
1391
|
+
showTransformBadge(currentTarget ? 'Move layer' : 'Move', ev.clientX, ev.clientY);
|
|
1392
|
+
}
|
|
1393
|
+
function onReorderUp() {
|
|
1394
|
+
document.removeEventListener('mousemove', onReorderMove, true);
|
|
1395
|
+
document.removeEventListener('mouseup', onReorderUp, true);
|
|
1396
|
+
hideTransformBadge();
|
|
1397
|
+
hideInsertionGuide();
|
|
1398
|
+
if (!currentTarget) return;
|
|
1399
|
+
if (duplicatedForDrag) {
|
|
1400
|
+
applyRuntimeReorder(selectedEl, currentTarget);
|
|
1401
|
+
postVisualDuplicateChange(originalSelectedEl, selectedEl, currentTarget);
|
|
1402
|
+
}
|
|
1403
|
+
else postVisualStructureChange(selectedEl, currentTarget);
|
|
1404
|
+
}
|
|
1405
|
+
document.addEventListener('mousemove', onReorderMove, true);
|
|
1406
|
+
document.addEventListener('mouseup', onReorderUp, true);
|
|
1407
|
+
return;
|
|
1408
|
+
}
|
|
1409
|
+
ensurePositionable(selectedEl);
|
|
1410
|
+
var cs = window.getComputedStyle(selectedEl);
|
|
1411
|
+
var originLeft = readPx(selectedEl.style.left || cs.left);
|
|
1412
|
+
var originTop = readPx(selectedEl.style.top || cs.top);
|
|
1413
|
+
var startX = e.clientX;
|
|
1414
|
+
var startY = e.clientY;
|
|
1415
|
+
function onMove(ev) {
|
|
1416
|
+
var nextLeft = originLeft + ev.clientX - startX;
|
|
1417
|
+
var nextTop = originTop + ev.clientY - startY;
|
|
1418
|
+
selectedEl.style.left = Math.round(nextLeft) + 'px';
|
|
1419
|
+
selectedEl.style.top = Math.round(nextTop) + 'px';
|
|
1420
|
+
showTransformBadge('X ' + Math.round(nextLeft) + ' Y ' + Math.round(nextTop), ev.clientX, ev.clientY);
|
|
1421
|
+
refreshOverlays();
|
|
1422
|
+
}
|
|
1423
|
+
function onUp() {
|
|
1424
|
+
document.removeEventListener('mousemove', onMove, true);
|
|
1425
|
+
document.removeEventListener('mouseup', onUp, true);
|
|
1426
|
+
hideTransformBadge();
|
|
1427
|
+
if (duplicatedForDrag) {
|
|
1428
|
+
postVisualDuplicateChange(originalSelectedEl, selectedEl);
|
|
1429
|
+
} else {
|
|
1430
|
+
postVisualStyleChange({
|
|
1431
|
+
position: selectedEl.style.position,
|
|
1432
|
+
left: selectedEl.style.left,
|
|
1433
|
+
top: selectedEl.style.top,
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
document.addEventListener('mousemove', onMove, true);
|
|
1438
|
+
document.addEventListener('mouseup', onUp, true);
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
function startResize(handle, e) {
|
|
1442
|
+
if (!selectedEl) return;
|
|
1443
|
+
if (isLayerInteractionBlocked(selectedEl)) return;
|
|
1444
|
+
e.preventDefault();
|
|
1445
|
+
e.stopPropagation();
|
|
1446
|
+
ensurePositionable(selectedEl);
|
|
1447
|
+
var cs = window.getComputedStyle(selectedEl);
|
|
1448
|
+
var origin = {
|
|
1449
|
+
left: readPx(selectedEl.style.left || cs.left),
|
|
1450
|
+
top: readPx(selectedEl.style.top || cs.top),
|
|
1451
|
+
width: selectedEl.getBoundingClientRect().width,
|
|
1452
|
+
height: selectedEl.getBoundingClientRect().height,
|
|
1453
|
+
ratio: selectedEl.getBoundingClientRect().width / Math.max(1, selectedEl.getBoundingClientRect().height),
|
|
1454
|
+
};
|
|
1455
|
+
var startX = e.clientX;
|
|
1456
|
+
var startY = e.clientY;
|
|
1457
|
+
function nextRect(ev) {
|
|
1458
|
+
var dx = ev.clientX - startX;
|
|
1459
|
+
var dy = ev.clientY - startY;
|
|
1460
|
+
var left = origin.left;
|
|
1461
|
+
var top = origin.top;
|
|
1462
|
+
var width = origin.width;
|
|
1463
|
+
var height = origin.height;
|
|
1464
|
+
if (handle.indexOf('w') !== -1) {
|
|
1465
|
+
left = origin.left + dx;
|
|
1466
|
+
width = origin.width - dx;
|
|
1467
|
+
}
|
|
1468
|
+
if (handle.indexOf('e') !== -1) width = origin.width + dx;
|
|
1469
|
+
if (handle.indexOf('n') !== -1) {
|
|
1470
|
+
top = origin.top + dy;
|
|
1471
|
+
height = origin.height - dy;
|
|
1472
|
+
}
|
|
1473
|
+
if (handle.indexOf('s') !== -1) height = origin.height + dy;
|
|
1474
|
+
width = Math.max(8, width);
|
|
1475
|
+
height = Math.max(8, height);
|
|
1476
|
+
if ((ev.shiftKey || scaleToolEnabled) && handle.length === 2) {
|
|
1477
|
+
if (Math.abs(dx) > Math.abs(dy)) height = width / origin.ratio;
|
|
1478
|
+
else width = height * origin.ratio;
|
|
1479
|
+
}
|
|
1480
|
+
if (ev.altKey) {
|
|
1481
|
+
if (handle.indexOf('w') !== -1 || handle.indexOf('e') !== -1) left = origin.left - (width - origin.width) / 2;
|
|
1482
|
+
if (handle.indexOf('n') !== -1 || handle.indexOf('s') !== -1) top = origin.top - (height - origin.height) / 2;
|
|
1483
|
+
}
|
|
1484
|
+
return { left: left, top: top, width: width, height: height };
|
|
1485
|
+
}
|
|
1486
|
+
function onMove(ev) {
|
|
1487
|
+
var rect = nextRect(ev);
|
|
1488
|
+
selectedEl.style.left = Math.round(rect.left) + 'px';
|
|
1489
|
+
selectedEl.style.top = Math.round(rect.top) + 'px';
|
|
1490
|
+
selectedEl.style.width = Math.round(rect.width) + 'px';
|
|
1491
|
+
selectedEl.style.height = Math.round(rect.height) + 'px';
|
|
1492
|
+
showTransformBadge(Math.round(rect.width) + ' x ' + Math.round(rect.height), ev.clientX, ev.clientY);
|
|
1493
|
+
refreshOverlays();
|
|
1494
|
+
}
|
|
1495
|
+
function onUp() {
|
|
1496
|
+
document.removeEventListener('mousemove', onMove, true);
|
|
1497
|
+
document.removeEventListener('mouseup', onUp, true);
|
|
1498
|
+
hideTransformBadge();
|
|
1499
|
+
postVisualStyleChange({
|
|
1500
|
+
position: selectedEl.style.position,
|
|
1501
|
+
left: selectedEl.style.left,
|
|
1502
|
+
top: selectedEl.style.top,
|
|
1503
|
+
width: selectedEl.style.width,
|
|
1504
|
+
height: selectedEl.style.height,
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
document.addEventListener('mousemove', onMove, true);
|
|
1508
|
+
document.addEventListener('mouseup', onUp, true);
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
function startRotate(e) {
|
|
1512
|
+
if (!selectedEl) return;
|
|
1513
|
+
if (isLayerInteractionBlocked(selectedEl)) return;
|
|
1514
|
+
e.preventDefault();
|
|
1515
|
+
e.stopPropagation();
|
|
1516
|
+
var rect = selectedEl.getBoundingClientRect();
|
|
1517
|
+
var center = { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
1518
|
+
var originAngle = Math.atan2(e.clientY - center.y, e.clientX - center.x) * 180 / Math.PI;
|
|
1519
|
+
var originRotation = currentRotation(selectedEl);
|
|
1520
|
+
function onMove(ev) {
|
|
1521
|
+
var pointerAngle = Math.atan2(ev.clientY - center.y, ev.clientX - center.x) * 180 / Math.PI;
|
|
1522
|
+
var next = originRotation + pointerAngle - originAngle;
|
|
1523
|
+
if (ev.shiftKey) next = Math.round(next / 15) * 15;
|
|
1524
|
+
next = Math.round(next);
|
|
1525
|
+
selectedEl.style.transform = mergeRotation(selectedEl, next);
|
|
1526
|
+
showTransformBadge(next + 'deg', ev.clientX, ev.clientY);
|
|
1527
|
+
refreshOverlays();
|
|
1528
|
+
}
|
|
1529
|
+
function onUp() {
|
|
1530
|
+
document.removeEventListener('mousemove', onMove, true);
|
|
1531
|
+
document.removeEventListener('mouseup', onUp, true);
|
|
1532
|
+
hideTransformBadge();
|
|
1533
|
+
postVisualStyleChange({
|
|
1534
|
+
transform: selectedEl.style.transform,
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
document.addEventListener('mousemove', onMove, true);
|
|
1538
|
+
document.addEventListener('mouseup', onUp, true);
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
selectionOverlay.addEventListener('mousedown', function(e) {
|
|
1542
|
+
var resizeHandle = e.target && e.target.getAttribute && e.target.getAttribute('data-agent-native-edit-handle');
|
|
1543
|
+
if (!resizeHandle && e.target && e.target.getAttribute) {
|
|
1544
|
+
resizeHandle = e.target.getAttribute('data-agent-native-edge-handle');
|
|
1545
|
+
}
|
|
1546
|
+
if (resizeHandle) {
|
|
1547
|
+
startResize(resizeHandle, e);
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
var rotateHandle = e.target && e.target.getAttribute && e.target.getAttribute('data-agent-native-rotate-handle');
|
|
1551
|
+
if (rotateHandle) {
|
|
1552
|
+
startRotate(e);
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
startMove(e);
|
|
297
1556
|
}, true);
|
|
298
1557
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
1558
|
+
['pointerdown','pointerup','mousedown','mouseup','auxclick'].forEach(function(type) {
|
|
1559
|
+
shieldOverlay.addEventListener(type, stopNativeInteraction, true);
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
shieldOverlay.addEventListener('click', selectElementAtEvent, true);
|
|
1563
|
+
shieldOverlay.addEventListener('contextmenu', openContextMenuAtEvent, true);
|
|
1564
|
+
selectionOverlay.addEventListener('contextmenu', openContextMenuAtEvent, true);
|
|
1565
|
+
document.addEventListener('contextmenu', function(e) {
|
|
1566
|
+
if (isOverlayElement(e.target)) return;
|
|
1567
|
+
openContextMenuAtEvent(e);
|
|
1568
|
+
}, true);
|
|
1569
|
+
|
|
1570
|
+
document.addEventListener('keydown', function(e) {
|
|
1571
|
+
if (!shouldForwardDesignHotkey(e)) return;
|
|
1572
|
+
stopNativeInteraction(e);
|
|
1573
|
+
window.parent.postMessage({
|
|
1574
|
+
type: 'design-hotkey',
|
|
1575
|
+
key: e.key,
|
|
1576
|
+
code: e.code,
|
|
1577
|
+
metaKey: !!e.metaKey,
|
|
1578
|
+
ctrlKey: !!e.ctrlKey,
|
|
1579
|
+
shiftKey: !!e.shiftKey,
|
|
1580
|
+
altKey: !!e.altKey,
|
|
1581
|
+
repeat: !!e.repeat
|
|
1582
|
+
}, '*');
|
|
1583
|
+
}, true);
|
|
1584
|
+
|
|
1585
|
+
function placeTextCaretFromPoint(target, clientX, clientY) {
|
|
1586
|
+
try {
|
|
1587
|
+
var range = null;
|
|
1588
|
+
if (document.caretRangeFromPoint) {
|
|
1589
|
+
range = document.caretRangeFromPoint(clientX, clientY);
|
|
1590
|
+
} else if (document.caretPositionFromPoint) {
|
|
1591
|
+
var position = document.caretPositionFromPoint(clientX, clientY);
|
|
1592
|
+
if (position) {
|
|
1593
|
+
range = document.createRange();
|
|
1594
|
+
range.setStart(position.offsetNode, position.offset);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
if (!range) {
|
|
1598
|
+
range = document.createRange();
|
|
1599
|
+
range.selectNodeContents(target);
|
|
1600
|
+
range.collapse(false);
|
|
1601
|
+
}
|
|
1602
|
+
var selection = window.getSelection();
|
|
1603
|
+
selection.removeAllRanges();
|
|
1604
|
+
selection.addRange(range);
|
|
1605
|
+
} catch (err) {
|
|
1606
|
+
try {
|
|
1607
|
+
var fallbackRange = document.createRange();
|
|
1608
|
+
fallbackRange.selectNodeContents(target);
|
|
1609
|
+
fallbackRange.collapse(false);
|
|
1610
|
+
var fallbackSelection = window.getSelection();
|
|
1611
|
+
fallbackSelection.removeAllRanges();
|
|
1612
|
+
fallbackSelection.addRange(fallbackRange);
|
|
1613
|
+
} catch (_err) {}
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
function beginTextEditingFromEvent(e) {
|
|
1618
|
+
if (activeTextEditEl && e.target && activeTextEditEl.contains(e.target)) return;
|
|
1619
|
+
if (!textEditingEnabled) {
|
|
1620
|
+
stopNativeInteraction(e);
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
stopNativeInteraction(e);
|
|
1624
|
+
var target = findTextEditTarget(elementFromEditorPoint(e.clientX, e.clientY));
|
|
1625
|
+
if (!target || target.nodeType !== 1) return;
|
|
1626
|
+
selectedEl = target;
|
|
1627
|
+
var originalText = target.textContent || '';
|
|
1628
|
+
var originalHtml = target.innerHTML || '';
|
|
1629
|
+
var committed = false;
|
|
1630
|
+
activeTextEditEl = target;
|
|
1631
|
+
target.setAttribute('contenteditable', 'true');
|
|
1632
|
+
target.setAttribute('data-agent-native-text-editing', 'true');
|
|
1633
|
+
target.style.cursor = 'text';
|
|
1634
|
+
target.style.outline = '1.5px solid var(--design-editor-accent-color)';
|
|
1635
|
+
target.style.outlineOffset = '2px';
|
|
1636
|
+
setTextEditingPointerPassthrough(true);
|
|
1637
|
+
positionOverlay(selectionOverlay, target);
|
|
1638
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(target) }, '*');
|
|
1639
|
+
postTextEditingState(target, true);
|
|
1640
|
+
|
|
1641
|
+
function finish(commit) {
|
|
1642
|
+
if (committed) return;
|
|
1643
|
+
committed = true;
|
|
1644
|
+
target.removeEventListener('blur', onBlur, true);
|
|
1645
|
+
target.removeEventListener('keydown', onKeyDown, true);
|
|
1646
|
+
target.removeEventListener('paste', onPaste, true);
|
|
1647
|
+
target.removeEventListener('keyup', onSelectionChange, true);
|
|
1648
|
+
target.removeEventListener('mouseup', onSelectionChange, true);
|
|
1649
|
+
document.removeEventListener('selectionchange', onSelectionChange);
|
|
1650
|
+
target.removeAttribute('contenteditable');
|
|
1651
|
+
target.removeAttribute('data-agent-native-text-editing');
|
|
1652
|
+
target.style.cursor = '';
|
|
1653
|
+
target.style.outline = '';
|
|
1654
|
+
target.style.outlineOffset = '';
|
|
1655
|
+
setTextEditingPointerPassthrough(false);
|
|
1656
|
+
if (activeTextEditEl === target) activeTextEditEl = null;
|
|
1657
|
+
postTextEditingState(target, false);
|
|
1658
|
+
if (!commit) {
|
|
1659
|
+
target.innerHTML = originalHtml;
|
|
1660
|
+
refreshOverlays();
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
var next = target.textContent || '';
|
|
1664
|
+
var nextHtml = target.innerHTML || '';
|
|
1665
|
+
refreshOverlays();
|
|
1666
|
+
if (next !== originalText || nextHtml !== originalHtml) {
|
|
1667
|
+
postTextContentChange(target, next, nextHtml);
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
function onBlur() {
|
|
1672
|
+
finish(true);
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
function onKeyDown(ev) {
|
|
1676
|
+
if (ev.key === 'Escape') {
|
|
1677
|
+
ev.preventDefault();
|
|
1678
|
+
finish(true);
|
|
1679
|
+
target.blur();
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
if (ev.key === 'Enter' && !ev.shiftKey) {
|
|
1683
|
+
ev.preventDefault();
|
|
1684
|
+
finish(true);
|
|
1685
|
+
target.blur();
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
function onPaste(ev) {
|
|
1690
|
+
ev.preventDefault();
|
|
1691
|
+
insertPlainTextAtSelection((ev.clipboardData && ev.clipboardData.getData('text/plain')) || '');
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
function onSelectionChange() {
|
|
1695
|
+
postTextEditingState(target, true);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
target.addEventListener('blur', onBlur, true);
|
|
1699
|
+
target.addEventListener('keydown', onKeyDown, true);
|
|
1700
|
+
target.addEventListener('paste', onPaste, true);
|
|
1701
|
+
target.addEventListener('keyup', onSelectionChange, true);
|
|
1702
|
+
target.addEventListener('mouseup', onSelectionChange, true);
|
|
1703
|
+
document.addEventListener('selectionchange', onSelectionChange);
|
|
1704
|
+
target.focus();
|
|
1705
|
+
placeTextCaretFromPoint(target, e.clientX, e.clientY);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
shieldOverlay.addEventListener('dblclick', beginTextEditingFromEvent, true);
|
|
1709
|
+
selectionOverlay.addEventListener('dblclick', beginTextEditingFromEvent, true);
|
|
1710
|
+
document.addEventListener('dblclick', function(e) {
|
|
1711
|
+
if (isOverlayElement(e.target)) return;
|
|
1712
|
+
beginTextEditingFromEvent(e);
|
|
1713
|
+
}, true);
|
|
1714
|
+
|
|
1715
|
+
shieldOverlay.addEventListener('pointermove', function(e) {
|
|
1716
|
+
stopNativeInteraction(e);
|
|
1717
|
+
hoveredEl = elementFromEditorPoint(e.clientX, e.clientY);
|
|
1718
|
+
if (!hoveredEl) {
|
|
1719
|
+
highlightOverlay.style.display = 'none';
|
|
1720
|
+
hideMeasurements();
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
if (hoveredEl && hoveredEl.closest('[data-agent-native-text-editing]')) return;
|
|
302
1724
|
positionOverlay(highlightOverlay, hoveredEl);
|
|
1725
|
+
if (e.altKey && selectedEl && hoveredEl && selectedEl !== hoveredEl) {
|
|
1726
|
+
showMeasurements(selectedEl, hoveredEl);
|
|
1727
|
+
} else {
|
|
1728
|
+
hideMeasurements();
|
|
1729
|
+
}
|
|
303
1730
|
var info = getElementInfo(hoveredEl);
|
|
304
1731
|
window.parent.postMessage({ type: 'element-hover', payload: info }, '*');
|
|
305
1732
|
}, true);
|
|
306
1733
|
|
|
307
|
-
|
|
1734
|
+
shieldOverlay.addEventListener('pointerleave', function(e) {
|
|
1735
|
+
stopNativeInteraction(e);
|
|
308
1736
|
hoveredEl = null;
|
|
309
1737
|
highlightOverlay.style.display = 'none';
|
|
1738
|
+
hideMeasurements();
|
|
1739
|
+
}, true);
|
|
1740
|
+
|
|
1741
|
+
window.addEventListener('keyup', function(e) {
|
|
1742
|
+
if (e.key === 'Alt') hideMeasurements();
|
|
310
1743
|
}, true);
|
|
311
1744
|
|
|
312
1745
|
window.addEventListener('message', function(e) {
|
|
313
1746
|
if (e.source !== window.parent) return;
|
|
314
|
-
if (!e.data
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (
|
|
320
|
-
|
|
1747
|
+
if (!e.data) return;
|
|
1748
|
+
if (e.data.type === 'scale-tool-mode') {
|
|
1749
|
+
scaleToolEnabled = !!e.data.enabled;
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
if (e.data.type === 'clear-selection') {
|
|
1753
|
+
selectedEl = null;
|
|
1754
|
+
hoveredEl = null;
|
|
1755
|
+
selectionOverlay.style.display = 'none';
|
|
1756
|
+
highlightOverlay.style.display = 'none';
|
|
1757
|
+
hideMeasurements();
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
if (e.data.type === 'select-element') {
|
|
1761
|
+
var candidates = [];
|
|
1762
|
+
if (Array.isArray(e.data.selectorCandidates)) {
|
|
1763
|
+
e.data.selectorCandidates.forEach(function(selector) {
|
|
1764
|
+
if (typeof selector === 'string' && selector && candidates.indexOf(selector) === -1) {
|
|
1765
|
+
candidates.push(selector);
|
|
1766
|
+
}
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1769
|
+
if (e.data.selector && candidates.indexOf(String(e.data.selector)) === -1) {
|
|
1770
|
+
candidates.push(String(e.data.selector));
|
|
1771
|
+
}
|
|
1772
|
+
var target =
|
|
1773
|
+
selectedEl &&
|
|
1774
|
+
document.documentElement.contains(selectedEl) &&
|
|
1775
|
+
matchesExactSelectorList(selectedEl, candidates)
|
|
1776
|
+
? selectedEl
|
|
1777
|
+
: null;
|
|
1778
|
+
for (var i = 0; i < candidates.length && !target; i += 1) {
|
|
1779
|
+
try {
|
|
1780
|
+
var matches = document.querySelectorAll(candidates[i]);
|
|
1781
|
+
for (var j = 0; j < matches.length; j += 1) {
|
|
1782
|
+
if (!isLayerInteractionBlocked(matches[j])) {
|
|
1783
|
+
target = matches[j];
|
|
1784
|
+
break;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
} catch (_err) {}
|
|
1788
|
+
}
|
|
1789
|
+
if (!target) return;
|
|
1790
|
+
selectedEl = target;
|
|
1791
|
+
var selectedInfo = getElementInfo(target);
|
|
1792
|
+
positionOverlay(selectionOverlay, target);
|
|
1793
|
+
window.parent.postMessage({ type: 'element-select', payload: selectedInfo }, '*');
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
if (e.data.type === 'hover-element') {
|
|
1797
|
+
var hoverCandidates = [];
|
|
1798
|
+
if (Array.isArray(e.data.selectorCandidates)) {
|
|
1799
|
+
e.data.selectorCandidates.forEach(function(selector) {
|
|
1800
|
+
if (typeof selector === 'string' && selector && hoverCandidates.indexOf(selector) === -1) {
|
|
1801
|
+
hoverCandidates.push(selector);
|
|
1802
|
+
}
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
if (e.data.selector && hoverCandidates.indexOf(String(e.data.selector)) === -1) {
|
|
1806
|
+
hoverCandidates.push(String(e.data.selector));
|
|
1807
|
+
}
|
|
1808
|
+
if (hoverCandidates.length === 0) {
|
|
1809
|
+
hoveredEl = null;
|
|
1810
|
+
highlightOverlay.style.display = 'none';
|
|
1811
|
+
hideMeasurements();
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
var hoverTarget = findRuntimeTarget(String(e.data.selector || ''), hoverCandidates);
|
|
1815
|
+
hoveredEl = hoverTarget;
|
|
1816
|
+
if (hoveredEl && !isLayerInteractionBlocked(hoveredEl)) {
|
|
1817
|
+
positionOverlay(highlightOverlay, hoveredEl);
|
|
1818
|
+
} else {
|
|
1819
|
+
highlightOverlay.style.display = 'none';
|
|
1820
|
+
hideMeasurements();
|
|
1821
|
+
}
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
if (e.data.type === 'layer-states') {
|
|
1825
|
+
lockedSelectors = Array.isArray(e.data.lockedSelectors) ? e.data.lockedSelectors.filter(function(item) { return typeof item === 'string'; }) : [];
|
|
1826
|
+
hiddenSelectors = Array.isArray(e.data.hiddenSelectors) ? e.data.hiddenSelectors.filter(function(item) { return typeof item === 'string'; }) : [];
|
|
1827
|
+
if (selectedEl && isLayerInteractionBlocked(selectedEl)) {
|
|
1828
|
+
selectedEl = null;
|
|
1829
|
+
selectionOverlay.style.display = 'none';
|
|
1830
|
+
}
|
|
1831
|
+
if (hoveredEl && isLayerInteractionBlocked(hoveredEl)) {
|
|
1832
|
+
hoveredEl = null;
|
|
1833
|
+
highlightOverlay.style.display = 'none';
|
|
1834
|
+
}
|
|
1835
|
+
applyHiddenSelectors();
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
if (e.data.type === 'visual-structure-ack') {
|
|
1839
|
+
if (!pendingStructureMove || e.data.requestId !== pendingStructureMove.requestId) return;
|
|
1840
|
+
var move = pendingStructureMove;
|
|
1841
|
+
pendingStructureMove = null;
|
|
1842
|
+
if (e.data.applied) {
|
|
1843
|
+
applyRuntimeReorder(move.el, move.target);
|
|
1844
|
+
selectedEl = move.el;
|
|
1845
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
1846
|
+
window.parent.postMessage({ type: 'element-select', payload: getElementInfo(selectedEl) }, '*');
|
|
1847
|
+
}
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
if (e.data.type === 'replace-document-content') {
|
|
1851
|
+
replaceRuntimeDocument(e.data.content, e.data.selectedSelector, e.data.selectorCandidates);
|
|
1852
|
+
return;
|
|
1853
|
+
}
|
|
1854
|
+
if (e.data.type === 'delete-element') {
|
|
1855
|
+
removeRuntimeTarget(e.data.selector, e.data.selectorCandidates);
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
if (e.data.type !== 'style-change') return;
|
|
1859
|
+
var sel = e.data.selector;
|
|
1860
|
+
var prop = e.data.property;
|
|
1861
|
+
var val = e.data.value;
|
|
1862
|
+
var el = sel ? document.querySelector(sel) : null;
|
|
1863
|
+
if (activeTextEditEl && el === activeTextEditEl && applyTextRangeStyle(prop, val)) {
|
|
1864
|
+
postTextContentChange(activeTextEditEl, activeTextEditEl.textContent || '', activeTextEditEl.innerHTML || '');
|
|
1865
|
+
refreshOverlays();
|
|
1866
|
+
return;
|
|
1867
|
+
}
|
|
1868
|
+
if (el) el.style[prop] = val;
|
|
1869
|
+
});
|
|
321
1870
|
|
|
322
1871
|
window.addEventListener('scroll', refreshOverlays, true);
|
|
323
1872
|
window.addEventListener('resize', refreshOverlays);
|
|
@@ -327,12 +1876,61 @@ const EDIT_BRIDGE_SCRIPT = `
|
|
|
327
1876
|
|
|
328
1877
|
interface DesignCanvasProps {
|
|
329
1878
|
content: string;
|
|
1879
|
+
contentKey?: string;
|
|
330
1880
|
zoom: number;
|
|
331
1881
|
onZoomChange?: (zoom: number) => void;
|
|
332
1882
|
deviceFrame: DeviceFrameType;
|
|
1883
|
+
embeddedFrame?: {
|
|
1884
|
+
viewportWidth: number;
|
|
1885
|
+
viewportHeight: number;
|
|
1886
|
+
displayWidth: number;
|
|
1887
|
+
displayHeight: number;
|
|
1888
|
+
};
|
|
333
1889
|
editMode: boolean;
|
|
1890
|
+
interactMode: boolean;
|
|
1891
|
+
scaleMode?: boolean;
|
|
334
1892
|
onElementSelect: (info: ElementInfo) => void;
|
|
335
1893
|
onElementHover: (info: ElementInfo) => void;
|
|
1894
|
+
onVisualStyleChange?: (
|
|
1895
|
+
selector: string,
|
|
1896
|
+
styles: Record<string, string>,
|
|
1897
|
+
info?: ElementInfo,
|
|
1898
|
+
) => void;
|
|
1899
|
+
onTextContentChange?: (
|
|
1900
|
+
selector: string,
|
|
1901
|
+
value: string,
|
|
1902
|
+
info?: ElementInfo,
|
|
1903
|
+
details?: { html?: string },
|
|
1904
|
+
) => void;
|
|
1905
|
+
onTextEditingStateChange?: (state: {
|
|
1906
|
+
active: boolean;
|
|
1907
|
+
selector?: string;
|
|
1908
|
+
hasRange?: boolean;
|
|
1909
|
+
}) => void;
|
|
1910
|
+
onIframeHotkey?: (event: IframeHotkeyPayload) => void;
|
|
1911
|
+
onIframeContextMenu?: (event: IframeContextMenuPayload) => void;
|
|
1912
|
+
onVisualStructureChange?: (
|
|
1913
|
+
selector: string,
|
|
1914
|
+
anchorSelector: string,
|
|
1915
|
+
placement: "before" | "after" | "inside",
|
|
1916
|
+
info?: ElementInfo,
|
|
1917
|
+
details?: {
|
|
1918
|
+
sourceId?: string;
|
|
1919
|
+
anchorSourceId?: string;
|
|
1920
|
+
requestId?: string;
|
|
1921
|
+
},
|
|
1922
|
+
) => boolean | void;
|
|
1923
|
+
onVisualDuplicateChange?: (
|
|
1924
|
+
selector: string,
|
|
1925
|
+
cloneHtml: string,
|
|
1926
|
+
info?: ElementInfo,
|
|
1927
|
+
details?: {
|
|
1928
|
+
sourceId?: string;
|
|
1929
|
+
anchorSelector?: string;
|
|
1930
|
+
anchorSourceId?: string;
|
|
1931
|
+
placement?: "before" | "after" | "inside";
|
|
1932
|
+
},
|
|
1933
|
+
) => boolean | void;
|
|
336
1934
|
tweakValues: Record<string, string>;
|
|
337
1935
|
/** Whether draw-to-prompt mode is active (overlays the iframe). */
|
|
338
1936
|
drawMode?: boolean;
|
|
@@ -340,6 +1938,13 @@ interface DesignCanvasProps {
|
|
|
340
1938
|
onExitDrawMode?: () => void;
|
|
341
1939
|
/** Whether comment-pin drop mode is active. */
|
|
342
1940
|
pinMode?: boolean;
|
|
1941
|
+
selectedSelector?: string | null;
|
|
1942
|
+
selectedSelectorCandidates?: string[];
|
|
1943
|
+
hoveredSelector?: string | null;
|
|
1944
|
+
hoveredSelectorCandidates?: string[];
|
|
1945
|
+
lockedSelectors?: string[];
|
|
1946
|
+
hiddenSelectors?: string[];
|
|
1947
|
+
clearSelectionRequest?: number;
|
|
343
1948
|
/** Called when the user exits pin mode. */
|
|
344
1949
|
onExitPinMode?: () => void;
|
|
345
1950
|
/** Stable id of the open design (used for pin scoping + agent prompt). */
|
|
@@ -359,18 +1964,66 @@ interface DesignCanvasProps {
|
|
|
359
1964
|
onPrototypeNavigate?: (screen: string, href: string) => void;
|
|
360
1965
|
}
|
|
361
1966
|
|
|
1967
|
+
function getExternalPreviewUrl(content: string): string | null {
|
|
1968
|
+
const trimmed = content.trim();
|
|
1969
|
+
if (!/^https?:\/\//i.test(trimmed)) return null;
|
|
1970
|
+
try {
|
|
1971
|
+
const url = new URL(trimmed);
|
|
1972
|
+
url.hash = "";
|
|
1973
|
+
return url.toString();
|
|
1974
|
+
} catch {
|
|
1975
|
+
return null;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
export interface IframeHotkeyPayload {
|
|
1980
|
+
key: string;
|
|
1981
|
+
code: string;
|
|
1982
|
+
metaKey: boolean;
|
|
1983
|
+
ctrlKey: boolean;
|
|
1984
|
+
shiftKey: boolean;
|
|
1985
|
+
altKey: boolean;
|
|
1986
|
+
repeat: boolean;
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
export interface IframeContextMenuPayload {
|
|
1990
|
+
clientX: number;
|
|
1991
|
+
clientY: number;
|
|
1992
|
+
viewportClientX?: number;
|
|
1993
|
+
viewportClientY?: number;
|
|
1994
|
+
info?: ElementInfo | null;
|
|
1995
|
+
}
|
|
1996
|
+
|
|
362
1997
|
export function DesignCanvas({
|
|
363
1998
|
content,
|
|
1999
|
+
contentKey,
|
|
364
2000
|
zoom,
|
|
365
2001
|
onZoomChange,
|
|
366
2002
|
deviceFrame,
|
|
2003
|
+
embeddedFrame,
|
|
367
2004
|
editMode,
|
|
2005
|
+
interactMode,
|
|
2006
|
+
scaleMode = false,
|
|
2007
|
+
clearSelectionRequest,
|
|
368
2008
|
onElementSelect,
|
|
369
2009
|
onElementHover,
|
|
2010
|
+
onVisualStyleChange,
|
|
2011
|
+
onTextContentChange,
|
|
2012
|
+
onTextEditingStateChange,
|
|
2013
|
+
onIframeHotkey,
|
|
2014
|
+
onIframeContextMenu,
|
|
2015
|
+
onVisualStructureChange,
|
|
2016
|
+
onVisualDuplicateChange,
|
|
370
2017
|
tweakValues,
|
|
371
2018
|
drawMode,
|
|
372
2019
|
onExitDrawMode,
|
|
373
2020
|
pinMode,
|
|
2021
|
+
selectedSelector,
|
|
2022
|
+
selectedSelectorCandidates = [],
|
|
2023
|
+
hoveredSelector,
|
|
2024
|
+
hoveredSelectorCandidates = [],
|
|
2025
|
+
lockedSelectors = [],
|
|
2026
|
+
hiddenSelectors = [],
|
|
374
2027
|
onExitPinMode,
|
|
375
2028
|
designId,
|
|
376
2029
|
designTitle,
|
|
@@ -382,8 +2035,37 @@ export function DesignCanvas({
|
|
|
382
2035
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
|
383
2036
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
384
2037
|
const zoomRef = useRef(zoom);
|
|
2038
|
+
const previousContentKeyRef = useRef(contentKey);
|
|
2039
|
+
const [renderedContent, setRenderedContent] = useState(content);
|
|
2040
|
+
const [annotationPins, setAnnotationPins] = useState<CanvasPin[]>([]);
|
|
2041
|
+
const [pinSubmitSignal, setPinSubmitSignal] = useState(0);
|
|
2042
|
+
const isEmbeddedFrame = Boolean(embeddedFrame);
|
|
2043
|
+
const externalPreviewUrl = useMemo(
|
|
2044
|
+
() => getExternalPreviewUrl(renderedContent),
|
|
2045
|
+
[renderedContent],
|
|
2046
|
+
);
|
|
385
2047
|
zoomRef.current = zoom;
|
|
386
2048
|
|
|
2049
|
+
const queuedAnnotationPins = useMemo(
|
|
2050
|
+
() =>
|
|
2051
|
+
annotationPins.filter(
|
|
2052
|
+
(pin) => pin.queued && !pin.submitted && (pin.draft || "").trim(),
|
|
2053
|
+
),
|
|
2054
|
+
[annotationPins],
|
|
2055
|
+
);
|
|
2056
|
+
|
|
2057
|
+
useEffect(() => {
|
|
2058
|
+
if (previousContentKeyRef.current !== contentKey) {
|
|
2059
|
+
previousContentKeyRef.current = contentKey;
|
|
2060
|
+
setRenderedContent(content);
|
|
2061
|
+
}
|
|
2062
|
+
// Same-screen visual edits are already applied optimistically inside the
|
|
2063
|
+
// iframe before the source write is queued. Rebuilding srcdoc for that echo
|
|
2064
|
+
// reloads the iframe, flashes unstyled content, and drops selection. Only a
|
|
2065
|
+
// content-key change (screen switch / explicit remount) should replace the
|
|
2066
|
+
// iframe document here; the bridge replays inspector state after that load.
|
|
2067
|
+
}, [content, contentKey]);
|
|
2068
|
+
|
|
387
2069
|
usePinchZoom({
|
|
388
2070
|
containerRef: scrollContainerRef,
|
|
389
2071
|
zoom,
|
|
@@ -395,22 +2077,41 @@ export function DesignCanvas({
|
|
|
395
2077
|
});
|
|
396
2078
|
|
|
397
2079
|
// Build the srcdoc. The tweak bridge ALWAYS goes in so the panel works
|
|
398
|
-
// outside Edit mode. The
|
|
2080
|
+
// outside Edit mode. The editor chrome bridge is omitted only for Interact.
|
|
399
2081
|
const srcdoc = useMemo(() => {
|
|
2082
|
+
if (externalPreviewUrl) return undefined;
|
|
2083
|
+
const editorChromeBridge = interactMode
|
|
2084
|
+
? ""
|
|
2085
|
+
: createEditorBridgeThemeScript(readEditorBridgeThemeVars()) +
|
|
2086
|
+
EDITOR_CHROME_BRIDGE_SCRIPT.replace(
|
|
2087
|
+
"__TEXT_EDITING_ENABLED__",
|
|
2088
|
+
editMode ? "true" : "false",
|
|
2089
|
+
);
|
|
2090
|
+
const embeddedWheelBridge = EMBEDDED_WHEEL_BRIDGE_SCRIPT.replace(
|
|
2091
|
+
"__EMBEDDED_WHEEL_FORWARDING_ENABLED__",
|
|
2092
|
+
isEmbeddedFrame ? "true" : "false",
|
|
2093
|
+
);
|
|
400
2094
|
const bridgeToInject =
|
|
401
2095
|
TWEAK_BRIDGE_SCRIPT +
|
|
402
2096
|
ZOOM_BRIDGE_SCRIPT +
|
|
403
2097
|
NAV_BRIDGE_SCRIPT +
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
2098
|
+
embeddedWheelBridge +
|
|
2099
|
+
editorChromeBridge;
|
|
2100
|
+
if (renderedContent.includes("</body>")) {
|
|
2101
|
+
return renderedContent.replace("</body>", bridgeToInject + "</body>"); // i18n-ignore generated iframe HTML injection
|
|
407
2102
|
}
|
|
408
|
-
if (
|
|
409
|
-
return
|
|
2103
|
+
if (renderedContent.includes("</html>")) {
|
|
2104
|
+
return renderedContent.replace("</html>", bridgeToInject + "</html>"); // i18n-ignore generated iframe HTML injection
|
|
410
2105
|
}
|
|
411
2106
|
// 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
|
-
}, [
|
|
2107
|
+
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>`;
|
|
2108
|
+
}, [
|
|
2109
|
+
editMode,
|
|
2110
|
+
externalPreviewUrl,
|
|
2111
|
+
interactMode,
|
|
2112
|
+
isEmbeddedFrame,
|
|
2113
|
+
renderedContent,
|
|
2114
|
+
]);
|
|
414
2115
|
|
|
415
2116
|
// Listen for messages from the iframe
|
|
416
2117
|
useEffect(() => {
|
|
@@ -432,6 +2133,143 @@ export function DesignCanvas({
|
|
|
432
2133
|
if (e.data.type === "element-hover") {
|
|
433
2134
|
onElementHover(e.data.payload);
|
|
434
2135
|
}
|
|
2136
|
+
if (e.data.type === "visual-style-change") {
|
|
2137
|
+
const selector = String(e.data.selector || "");
|
|
2138
|
+
const styles =
|
|
2139
|
+
e.data.styles && typeof e.data.styles === "object"
|
|
2140
|
+
? (e.data.styles as Record<string, string>)
|
|
2141
|
+
: {};
|
|
2142
|
+
if (selector && Object.keys(styles).length > 0) {
|
|
2143
|
+
onVisualStyleChange?.(selector, styles, e.data.payload);
|
|
2144
|
+
}
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
if (e.data.type === "text-content-change") {
|
|
2148
|
+
const selector = String(e.data.selector || "");
|
|
2149
|
+
const value = String(e.data.value ?? "");
|
|
2150
|
+
const html =
|
|
2151
|
+
typeof e.data.html === "string" ? String(e.data.html) : undefined;
|
|
2152
|
+
if (selector) {
|
|
2153
|
+
onTextContentChange?.(selector, value, e.data.payload, { html });
|
|
2154
|
+
}
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
if (e.data.type === "visual-structure-change") {
|
|
2158
|
+
const selector = String(e.data.selector || "");
|
|
2159
|
+
const anchorSelector = String(e.data.anchorSelector || "");
|
|
2160
|
+
const placement = String(e.data.placement || "after");
|
|
2161
|
+
const requestId =
|
|
2162
|
+
typeof e.data.requestId === "string" ? e.data.requestId : undefined;
|
|
2163
|
+
if (
|
|
2164
|
+
selector &&
|
|
2165
|
+
anchorSelector &&
|
|
2166
|
+
(placement === "before" ||
|
|
2167
|
+
placement === "after" ||
|
|
2168
|
+
placement === "inside")
|
|
2169
|
+
) {
|
|
2170
|
+
const applied = onVisualStructureChange?.(
|
|
2171
|
+
selector,
|
|
2172
|
+
anchorSelector,
|
|
2173
|
+
placement,
|
|
2174
|
+
e.data.payload,
|
|
2175
|
+
{
|
|
2176
|
+
requestId,
|
|
2177
|
+
sourceId:
|
|
2178
|
+
typeof e.data.sourceId === "string"
|
|
2179
|
+
? e.data.sourceId
|
|
2180
|
+
: undefined,
|
|
2181
|
+
anchorSourceId:
|
|
2182
|
+
typeof e.data.anchorSourceId === "string"
|
|
2183
|
+
? e.data.anchorSourceId
|
|
2184
|
+
: undefined,
|
|
2185
|
+
},
|
|
2186
|
+
);
|
|
2187
|
+
if (requestId) {
|
|
2188
|
+
iframeRef.current?.contentWindow?.postMessage(
|
|
2189
|
+
{
|
|
2190
|
+
type: "visual-structure-ack",
|
|
2191
|
+
requestId,
|
|
2192
|
+
applied: applied !== false,
|
|
2193
|
+
},
|
|
2194
|
+
"*",
|
|
2195
|
+
);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
if (e.data.type === "visual-duplicate-change") {
|
|
2201
|
+
const selector = String(e.data.selector || "");
|
|
2202
|
+
const cloneHtml =
|
|
2203
|
+
typeof e.data.cloneHtml === "string" ? String(e.data.cloneHtml) : "";
|
|
2204
|
+
const placement = String(e.data.placement || "after");
|
|
2205
|
+
if (
|
|
2206
|
+
selector &&
|
|
2207
|
+
cloneHtml &&
|
|
2208
|
+
(placement === "before" ||
|
|
2209
|
+
placement === "after" ||
|
|
2210
|
+
placement === "inside")
|
|
2211
|
+
) {
|
|
2212
|
+
onVisualDuplicateChange?.(selector, cloneHtml, e.data.payload, {
|
|
2213
|
+
sourceId:
|
|
2214
|
+
typeof e.data.sourceId === "string" ? e.data.sourceId : undefined,
|
|
2215
|
+
anchorSelector:
|
|
2216
|
+
typeof e.data.anchorSelector === "string"
|
|
2217
|
+
? e.data.anchorSelector
|
|
2218
|
+
: undefined,
|
|
2219
|
+
anchorSourceId:
|
|
2220
|
+
typeof e.data.anchorSourceId === "string"
|
|
2221
|
+
? e.data.anchorSourceId
|
|
2222
|
+
: undefined,
|
|
2223
|
+
placement,
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
return;
|
|
2227
|
+
}
|
|
2228
|
+
if (e.data.type === "text-editing-state") {
|
|
2229
|
+
onTextEditingStateChange?.({
|
|
2230
|
+
active: Boolean(e.data.active),
|
|
2231
|
+
selector:
|
|
2232
|
+
typeof e.data.selector === "string" ? e.data.selector : undefined,
|
|
2233
|
+
hasRange: Boolean(e.data.hasRange),
|
|
2234
|
+
});
|
|
2235
|
+
return;
|
|
2236
|
+
}
|
|
2237
|
+
if (e.data.type === "design-hotkey") {
|
|
2238
|
+
onIframeHotkey?.({
|
|
2239
|
+
key: String(e.data.key || ""),
|
|
2240
|
+
code: String(e.data.code || ""),
|
|
2241
|
+
metaKey: Boolean(e.data.metaKey),
|
|
2242
|
+
ctrlKey: Boolean(e.data.ctrlKey),
|
|
2243
|
+
shiftKey: Boolean(e.data.shiftKey),
|
|
2244
|
+
altKey: Boolean(e.data.altKey),
|
|
2245
|
+
repeat: Boolean(e.data.repeat),
|
|
2246
|
+
});
|
|
2247
|
+
return;
|
|
2248
|
+
}
|
|
2249
|
+
if (e.data.type === "element-contextmenu") {
|
|
2250
|
+
const clientX = Number(e.data.clientX);
|
|
2251
|
+
const clientY = Number(e.data.clientY);
|
|
2252
|
+
if (Number.isFinite(clientX) && Number.isFinite(clientY)) {
|
|
2253
|
+
const iframe = iframeRef.current;
|
|
2254
|
+
const iframeRect = iframe?.getBoundingClientRect();
|
|
2255
|
+
const scaleX =
|
|
2256
|
+
iframe && iframeRect && iframe.clientWidth > 0
|
|
2257
|
+
? iframeRect.width / iframe.clientWidth
|
|
2258
|
+
: 1;
|
|
2259
|
+
const scaleY =
|
|
2260
|
+
iframe && iframeRect && iframe.clientHeight > 0
|
|
2261
|
+
? iframeRect.height / iframe.clientHeight
|
|
2262
|
+
: 1;
|
|
2263
|
+
onIframeContextMenu?.({
|
|
2264
|
+
clientX,
|
|
2265
|
+
clientY,
|
|
2266
|
+
viewportClientX: (iframeRect?.left ?? 0) + clientX * scaleX,
|
|
2267
|
+
viewportClientY: (iframeRect?.top ?? 0) + clientY * scaleY,
|
|
2268
|
+
info: e.data.payload ?? null,
|
|
2269
|
+
});
|
|
2270
|
+
}
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
435
2273
|
if (e.data.type === "prototype-navigate") {
|
|
436
2274
|
// External links are opened inside the iframe (sandbox allow-popups);
|
|
437
2275
|
// only internal screen switches reach the parent.
|
|
@@ -441,6 +2279,34 @@ export function DesignCanvas({
|
|
|
441
2279
|
);
|
|
442
2280
|
return;
|
|
443
2281
|
}
|
|
2282
|
+
if (e.data.type === "embedded-canvas-wheel") {
|
|
2283
|
+
if (!isEmbeddedFrame) return;
|
|
2284
|
+
const iframe = iframeRef.current;
|
|
2285
|
+
if (!iframe) return;
|
|
2286
|
+
const rect = iframe.getBoundingClientRect();
|
|
2287
|
+
const scaleX =
|
|
2288
|
+
iframe.clientWidth > 0 ? rect.width / iframe.clientWidth : 1;
|
|
2289
|
+
const scaleY =
|
|
2290
|
+
iframe.clientHeight > 0 ? rect.height / iframe.clientHeight : 1;
|
|
2291
|
+
const clientX = rect.left + Number(e.data.clientX || 0) * scaleX;
|
|
2292
|
+
const clientY = rect.top + Number(e.data.clientY || 0) * scaleY;
|
|
2293
|
+
const forwarded = new WheelEvent("wheel", {
|
|
2294
|
+
bubbles: true,
|
|
2295
|
+
cancelable: true,
|
|
2296
|
+
deltaX: Math.max(-240, Math.min(240, Number(e.data.deltaX) || 0)),
|
|
2297
|
+
deltaY: Math.max(-240, Math.min(240, Number(e.data.deltaY) || 0)),
|
|
2298
|
+
deltaZ: Math.max(-240, Math.min(240, Number(e.data.deltaZ) || 0)),
|
|
2299
|
+
deltaMode: Number(e.data.deltaMode) || WheelEvent.DOM_DELTA_PIXEL,
|
|
2300
|
+
clientX,
|
|
2301
|
+
clientY,
|
|
2302
|
+
ctrlKey: Boolean(e.data.ctrlKey),
|
|
2303
|
+
metaKey: Boolean(e.data.metaKey),
|
|
2304
|
+
shiftKey: Boolean(e.data.shiftKey),
|
|
2305
|
+
altKey: Boolean(e.data.altKey),
|
|
2306
|
+
});
|
|
2307
|
+
iframe.dispatchEvent(forwarded);
|
|
2308
|
+
return;
|
|
2309
|
+
}
|
|
444
2310
|
if (e.data.type === "pinch-zoom-wheel") {
|
|
445
2311
|
if (!onZoomChange) return;
|
|
446
2312
|
const iframe = iframeRef.current;
|
|
@@ -486,28 +2352,83 @@ export function DesignCanvas({
|
|
|
486
2352
|
}, [
|
|
487
2353
|
onElementSelect,
|
|
488
2354
|
onElementHover,
|
|
2355
|
+
onVisualStyleChange,
|
|
2356
|
+
onTextContentChange,
|
|
2357
|
+
onTextEditingStateChange,
|
|
2358
|
+
onIframeHotkey,
|
|
2359
|
+
onIframeContextMenu,
|
|
2360
|
+
onVisualStructureChange,
|
|
2361
|
+
onVisualDuplicateChange,
|
|
489
2362
|
onZoomChange,
|
|
490
2363
|
deviceFrame,
|
|
491
2364
|
onPrototypeNavigate,
|
|
2365
|
+
isEmbeddedFrame,
|
|
492
2366
|
]);
|
|
493
2367
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
2368
|
+
const replayIframeEditorState = useCallback(() => {
|
|
2369
|
+
const iframe = iframeRef.current;
|
|
2370
|
+
if (!iframe) return;
|
|
2371
|
+
iframe.contentWindow?.postMessage(
|
|
2372
|
+
{ type: "tweak-values", values: tweakValues },
|
|
2373
|
+
"*",
|
|
2374
|
+
);
|
|
2375
|
+
iframe.contentWindow?.postMessage(
|
|
2376
|
+
{ type: "layer-states", lockedSelectors, hiddenSelectors },
|
|
2377
|
+
"*",
|
|
2378
|
+
);
|
|
2379
|
+
iframe.contentWindow?.postMessage(
|
|
2380
|
+
{ type: "scale-tool-mode", enabled: scaleMode },
|
|
2381
|
+
"*",
|
|
2382
|
+
);
|
|
2383
|
+
iframe.contentWindow?.postMessage(
|
|
2384
|
+
selectedSelector
|
|
2385
|
+
? {
|
|
2386
|
+
type: "select-element",
|
|
2387
|
+
selector: selectedSelector,
|
|
2388
|
+
selectorCandidates: selectedSelectorCandidates,
|
|
2389
|
+
}
|
|
2390
|
+
: { type: "clear-selection" },
|
|
2391
|
+
"*",
|
|
2392
|
+
);
|
|
2393
|
+
iframe.contentWindow?.postMessage(
|
|
2394
|
+
hoveredSelector
|
|
2395
|
+
? {
|
|
2396
|
+
type: "hover-element",
|
|
2397
|
+
selector: hoveredSelector,
|
|
2398
|
+
selectorCandidates: hoveredSelectorCandidates,
|
|
2399
|
+
}
|
|
2400
|
+
: { type: "hover-element", selector: "", selectorCandidates: [] },
|
|
2401
|
+
"*",
|
|
2402
|
+
);
|
|
2403
|
+
}, [
|
|
2404
|
+
hoveredSelector,
|
|
2405
|
+
hoveredSelectorCandidates,
|
|
2406
|
+
hiddenSelectors,
|
|
2407
|
+
lockedSelectors,
|
|
2408
|
+
scaleMode,
|
|
2409
|
+
selectedSelector,
|
|
2410
|
+
selectedSelectorCandidates,
|
|
2411
|
+
tweakValues,
|
|
2412
|
+
]);
|
|
2413
|
+
|
|
2414
|
+
// Replay the editor state whenever it changes OR the iframe (re)loads. The
|
|
2415
|
+
// load case matters for screen switches and mode changes; without replaying
|
|
2416
|
+
// selection/layer state here, the freshly mounted document looks deselected.
|
|
498
2417
|
useEffect(() => {
|
|
499
2418
|
const iframe = iframeRef.current;
|
|
500
2419
|
if (!iframe) return;
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
2420
|
+
replayIframeEditorState();
|
|
2421
|
+
iframe.addEventListener("load", replayIframeEditorState);
|
|
2422
|
+
return () => iframe.removeEventListener("load", replayIframeEditorState);
|
|
2423
|
+
}, [replayIframeEditorState]);
|
|
2424
|
+
|
|
2425
|
+
useEffect(() => {
|
|
2426
|
+
if (clearSelectionRequest === undefined) return;
|
|
2427
|
+
iframeRef.current?.contentWindow?.postMessage(
|
|
2428
|
+
{ type: "clear-selection" },
|
|
2429
|
+
"*",
|
|
2430
|
+
);
|
|
2431
|
+
}, [clearSelectionRequest]);
|
|
511
2432
|
|
|
512
2433
|
const sendStyleChange = useCallback(
|
|
513
2434
|
(selector: string, property: string, value: string) => {
|
|
@@ -521,13 +2442,52 @@ export function DesignCanvas({
|
|
|
521
2442
|
[],
|
|
522
2443
|
);
|
|
523
2444
|
|
|
524
|
-
|
|
2445
|
+
const replacePreviewContent = useCallback(
|
|
2446
|
+
(nextContent: string, selector?: string | null, candidates?: string[]) => {
|
|
2447
|
+
const iframe = iframeRef.current;
|
|
2448
|
+
if (!iframe?.contentWindow) return false;
|
|
2449
|
+
iframe.contentWindow.postMessage(
|
|
2450
|
+
{
|
|
2451
|
+
type: "replace-document-content",
|
|
2452
|
+
content: nextContent,
|
|
2453
|
+
selectedSelector: selector ?? "",
|
|
2454
|
+
selectorCandidates: candidates ?? [],
|
|
2455
|
+
},
|
|
2456
|
+
"*",
|
|
2457
|
+
);
|
|
2458
|
+
return true;
|
|
2459
|
+
},
|
|
2460
|
+
[],
|
|
2461
|
+
);
|
|
2462
|
+
|
|
2463
|
+
const deleteRuntimeElement = useCallback(
|
|
2464
|
+
(selector?: string | null, candidates?: string[]) => {
|
|
2465
|
+
const iframe = iframeRef.current;
|
|
2466
|
+
if (!iframe?.contentWindow) return false;
|
|
2467
|
+
iframe.contentWindow.postMessage(
|
|
2468
|
+
{
|
|
2469
|
+
type: "delete-element",
|
|
2470
|
+
selector: selector ?? "",
|
|
2471
|
+
selectorCandidates: candidates ?? [],
|
|
2472
|
+
},
|
|
2473
|
+
"*",
|
|
2474
|
+
);
|
|
2475
|
+
return true;
|
|
2476
|
+
},
|
|
2477
|
+
[],
|
|
2478
|
+
);
|
|
2479
|
+
|
|
2480
|
+
// Expose iframe runtime mutations for the editor orchestrator.
|
|
525
2481
|
useEffect(() => {
|
|
526
2482
|
(window as any).__designCanvasSendStyle = sendStyleChange;
|
|
2483
|
+
(window as any).__designCanvasReplaceContent = replacePreviewContent;
|
|
2484
|
+
(window as any).__designCanvasDeleteElement = deleteRuntimeElement;
|
|
527
2485
|
return () => {
|
|
528
2486
|
delete (window as any).__designCanvasSendStyle;
|
|
2487
|
+
delete (window as any).__designCanvasReplaceContent;
|
|
2488
|
+
delete (window as any).__designCanvasDeleteElement;
|
|
529
2489
|
};
|
|
530
|
-
}, [sendStyleChange]);
|
|
2490
|
+
}, [deleteRuntimeElement, replacePreviewContent, sendStyleChange]);
|
|
531
2491
|
|
|
532
2492
|
// Device dimensions match real-world devices. iframes are replaced elements
|
|
533
2493
|
// with an intrinsic 300×150 size, so `aspect-ratio` + `height: auto` doesn't
|
|
@@ -550,28 +2510,38 @@ export function DesignCanvas({
|
|
|
550
2510
|
// iframe. The pin component anchors to `.design-canvas-iframe-wrapper`
|
|
551
2511
|
// via canvasSelector.
|
|
552
2512
|
//
|
|
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.
|
|
2513
|
+
// The wrapper carries a faint outline + soft shadow so the frame edge stays
|
|
2514
|
+
// visible when a design background matches the editor canvas.
|
|
556
2515
|
const iframeElement = (
|
|
557
2516
|
<div
|
|
558
2517
|
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
2518
|
style={{
|
|
560
|
-
width: iframeWidth,
|
|
561
|
-
height:
|
|
2519
|
+
width: embeddedFrame ? embeddedFrame.viewportWidth : iframeWidth,
|
|
2520
|
+
height: embeddedFrame
|
|
2521
|
+
? embeddedFrame.viewportHeight
|
|
2522
|
+
: deviceFrame === "none"
|
|
2523
|
+
? "100%"
|
|
2524
|
+
: (iframeHeight ?? undefined),
|
|
562
2525
|
}}
|
|
563
2526
|
>
|
|
564
2527
|
<iframe
|
|
565
2528
|
ref={iframeRef}
|
|
566
|
-
|
|
567
|
-
|
|
2529
|
+
src={externalPreviewUrl ?? undefined}
|
|
2530
|
+
srcDoc={externalPreviewUrl ? undefined : srcdoc}
|
|
2531
|
+
sandbox={
|
|
2532
|
+
externalPreviewUrl
|
|
2533
|
+
? "allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin"
|
|
2534
|
+
: "allow-scripts allow-popups allow-popups-to-escape-sandbox"
|
|
2535
|
+
}
|
|
568
2536
|
data-design-preview-iframe
|
|
569
|
-
className="
|
|
2537
|
+
className="block h-full w-full border-0 bg-transparent"
|
|
570
2538
|
title={t("designEditor.designPreview")}
|
|
571
2539
|
/>
|
|
572
2540
|
{/* Draw-to-prompt overlay — sits over the iframe, NOT inside it. */}
|
|
573
2541
|
<SharedDrawOverlay
|
|
574
2542
|
visible={!!drawMode}
|
|
2543
|
+
canvasInteractive={!pinMode}
|
|
2544
|
+
queuedAnnotationCount={queuedAnnotationPins.length}
|
|
575
2545
|
onClose={() => onExitDrawMode?.()}
|
|
576
2546
|
onSend={(annotations, instruction, canvasSize) => {
|
|
577
2547
|
const summary = annotations
|
|
@@ -581,24 +2551,77 @@ export function DesignCanvas({
|
|
|
581
2551
|
: `[label "${a.text}" at ${a.position.x.toFixed(0)},${a.position.y.toFixed(0)}]`,
|
|
582
2552
|
)
|
|
583
2553
|
.join("\n");
|
|
2554
|
+
const pinSummary = queuedAnnotationPins
|
|
2555
|
+
.flatMap((pin, index) => {
|
|
2556
|
+
const lines = [
|
|
2557
|
+
`[${index + 1}] Comment pin on ${commentContextLabel || designTitle || commentContextId || designId || "design"}`,
|
|
2558
|
+
`Position: ${pin.xPct.toFixed(1)}% from left, ${pin.yPct.toFixed(1)}% from top`,
|
|
2559
|
+
];
|
|
2560
|
+
if (pin.targetAnchorId)
|
|
2561
|
+
lines.push(`Anchor id: ${pin.targetAnchorId}`);
|
|
2562
|
+
if (pin.targetSelector)
|
|
2563
|
+
lines.push(`Element: ${pin.targetSelector}`);
|
|
2564
|
+
if (pin.targetText)
|
|
2565
|
+
lines.push(`Nearby text: "${pin.targetText}"`);
|
|
2566
|
+
lines.push("");
|
|
2567
|
+
lines.push((pin.draft || "").trim());
|
|
2568
|
+
return [...lines, ""];
|
|
2569
|
+
})
|
|
2570
|
+
.join("\n");
|
|
584
2571
|
const lines = [
|
|
585
|
-
`[
|
|
2572
|
+
`[Annotations on design ${designId || ""}${designTitle ? ` (${designTitle})` : ""}]`,
|
|
586
2573
|
`Canvas size: ${canvasSize.width.toFixed(0)}x${canvasSize.height.toFixed(0)}`,
|
|
587
|
-
summary,
|
|
2574
|
+
...(summary ? ["", "[Drawing]", summary] : []),
|
|
2575
|
+
...(pinSummary ? ["", "[Comment pins]", pinSummary] : []),
|
|
588
2576
|
"",
|
|
589
2577
|
instruction || "Apply these annotations to the design.",
|
|
590
2578
|
];
|
|
591
2579
|
try {
|
|
592
|
-
|
|
2580
|
+
sendToAgentChat({
|
|
2581
|
+
message: lines.join("\n"),
|
|
2582
|
+
submit: true,
|
|
2583
|
+
openSidebar: true,
|
|
2584
|
+
});
|
|
593
2585
|
} catch (err) {
|
|
594
2586
|
console.error("[DesignCanvas] failed to submit drawing:", err);
|
|
595
2587
|
}
|
|
2588
|
+
if (queuedAnnotationPins.length > 0) {
|
|
2589
|
+
setPinSubmitSignal((signal) => signal + 1);
|
|
2590
|
+
}
|
|
596
2591
|
onExitDrawMode?.();
|
|
597
2592
|
}}
|
|
598
2593
|
/>
|
|
599
2594
|
</div>
|
|
600
2595
|
);
|
|
601
2596
|
|
|
2597
|
+
if (embeddedFrame) {
|
|
2598
|
+
const scaleX =
|
|
2599
|
+
embeddedFrame.displayWidth / Math.max(1, embeddedFrame.viewportWidth);
|
|
2600
|
+
const scaleY =
|
|
2601
|
+
embeddedFrame.displayHeight / Math.max(1, embeddedFrame.viewportHeight);
|
|
2602
|
+
return (
|
|
2603
|
+
<div
|
|
2604
|
+
ref={scrollContainerRef}
|
|
2605
|
+
className="relative h-full w-full overflow-hidden"
|
|
2606
|
+
style={{
|
|
2607
|
+
width: embeddedFrame.displayWidth,
|
|
2608
|
+
height: embeddedFrame.displayHeight,
|
|
2609
|
+
}}
|
|
2610
|
+
>
|
|
2611
|
+
<div
|
|
2612
|
+
style={{
|
|
2613
|
+
width: embeddedFrame.viewportWidth,
|
|
2614
|
+
height: embeddedFrame.viewportHeight,
|
|
2615
|
+
transform: `scale(${scaleX}, ${scaleY})`,
|
|
2616
|
+
transformOrigin: "top left",
|
|
2617
|
+
}}
|
|
2618
|
+
>
|
|
2619
|
+
{iframeElement}
|
|
2620
|
+
</div>
|
|
2621
|
+
</div>
|
|
2622
|
+
);
|
|
2623
|
+
}
|
|
2624
|
+
|
|
602
2625
|
const wrappedContent =
|
|
603
2626
|
deviceFrame === "none" ? (
|
|
604
2627
|
iframeElement
|
|
@@ -611,32 +2634,22 @@ export function DesignCanvas({
|
|
|
611
2634
|
ref={scrollContainerRef}
|
|
612
2635
|
className="relative flex-1 h-full overflow-auto"
|
|
613
2636
|
>
|
|
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
2637
|
{/* Canvas area. "none" mode fills the canvas (responsive preview);
|
|
625
|
-
framed modes are centered inside the
|
|
2638
|
+
framed modes are centered inside the canvas with zoom applied. */}
|
|
626
2639
|
{deviceFrame === "none" ? (
|
|
627
|
-
<div className="relative h-full w-full
|
|
2640
|
+
<div className="relative flex h-full w-full items-center justify-center">
|
|
628
2641
|
<div
|
|
629
2642
|
className="h-full w-full"
|
|
630
2643
|
style={{
|
|
631
2644
|
transform: `scale(${zoom / 100})`,
|
|
632
|
-
transformOrigin: "
|
|
2645
|
+
transformOrigin: "center center",
|
|
633
2646
|
}}
|
|
634
2647
|
>
|
|
635
2648
|
{wrappedContent}
|
|
636
2649
|
</div>
|
|
637
2650
|
</div>
|
|
638
2651
|
) : (
|
|
639
|
-
<div className="relative flex items-center justify-center min-h-full
|
|
2652
|
+
<div className="relative flex items-center justify-center min-h-full">
|
|
640
2653
|
<div
|
|
641
2654
|
style={{
|
|
642
2655
|
transform: `scale(${zoom / 100})`,
|
|
@@ -653,6 +2666,10 @@ export function DesignCanvas({
|
|
|
653
2666
|
the zoom-transformed container to keep coordinates stable. */}
|
|
654
2667
|
<CanvasCommentPins
|
|
655
2668
|
active={!!pinMode}
|
|
2669
|
+
submitMode={drawMode ? "queue" : "direct"}
|
|
2670
|
+
onPinsChange={setAnnotationPins}
|
|
2671
|
+
submitQueuedSignal={pinSubmitSignal}
|
|
2672
|
+
clickPlaneUnderToolbar={!!drawMode}
|
|
656
2673
|
onClose={() => onExitPinMode?.()}
|
|
657
2674
|
canvasSelector=".design-canvas-iframe-wrapper"
|
|
658
2675
|
contextId={commentContextId || designId || "design"}
|