@agent-native/core 0.83.0 → 0.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +28 -0
- package/corpus/core/README.md +2 -2
- package/corpus/core/docs/content/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/ar-SA/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/de-DE/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/hi-IN/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/template-chat.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/cloneable-saas.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/faq.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/template-chat.mdx +1 -1
- package/corpus/core/docs/content/template-chat.mdx +1 -1
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/builtin.ts +1 -1
- package/corpus/core/src/agent/engine/output-tokens.ts +1 -1
- package/corpus/core/src/agent/harness/acp-adapter.ts +28 -4
- package/corpus/core/src/agent/harness/ai-sdk-adapter.ts +3 -0
- package/corpus/core/src/agent/harness/translate.ts +3 -0
- package/corpus/core/src/agent/harness/types.ts +1 -0
- package/corpus/core/src/agent/model-config.ts +34 -13
- package/corpus/core/src/agent/production-agent.ts +87 -10
- package/corpus/core/src/agent/thread-data-builder.ts +29 -4
- package/corpus/core/src/agent/tool-call-journal.ts +109 -48
- package/corpus/core/src/agent/types.ts +6 -1
- package/corpus/core/src/cli/design-connect.ts +106 -0
- package/corpus/core/src/cli/gateway-helpers.ts +17 -0
- package/corpus/core/src/cli/recap.ts +0 -2
- package/corpus/core/src/cli/skills.ts +54 -16
- package/corpus/core/src/cli/workspace-dev.ts +29 -5
- package/corpus/core/src/client/AssistantChat.tsx +89 -42
- package/corpus/core/src/client/DefaultSpinner.tsx +8 -1
- package/corpus/core/src/client/chat/message-components.tsx +49 -1
- package/corpus/core/src/client/chat/repo-helpers.ts +71 -0
- package/corpus/core/src/client/chat/run-recovery.tsx +15 -5
- package/corpus/core/src/client/components/ApiKeySettings.tsx +8 -8
- package/corpus/core/src/client/components/CodeRequiredDialog.tsx +26 -15
- package/corpus/core/src/client/components/MissingKeyCard.tsx +13 -6
- package/corpus/core/src/client/composer/TiptapComposer.tsx +4 -3
- package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +0 -1
- package/corpus/core/src/client/guided-questions.tsx +21 -27
- package/corpus/core/src/client/integrations/IntegrationCard.tsx +1 -1
- package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +1 -1
- package/corpus/core/src/client/onboarding/OnboardingBanner.tsx +2 -2
- package/corpus/core/src/client/onboarding/OnboardingPanel.tsx +23 -19
- package/corpus/core/src/client/resources/ResourceEditor.tsx +18 -10
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +5 -1
- package/corpus/core/src/client/settings/SettingsPanel.tsx +8 -3
- package/corpus/core/src/client/settings/useBuilderStatus.ts +8 -41
- package/corpus/core/src/client/useProductionAgent.ts +6 -2
- package/corpus/core/src/deploy/build.ts +1 -1
- package/corpus/core/src/observability/traces.ts +3 -3
- package/corpus/core/src/onboarding/default-steps.ts +1 -1
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +1 -1
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +1 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +177 -81
- package/corpus/core/src/server/core-routes-plugin.ts +7 -1
- package/corpus/core/src/server/open-route.ts +59 -40
- package/corpus/core/src/shared/reasoning-effort.ts +2 -0
- package/corpus/core/src/sharing/access.ts +22 -2
- package/corpus/core/src/sharing/registry.ts +18 -0
- package/corpus/core/src/styles/agent-native.css +104 -20
- package/corpus/core/src/styles/rich-markdown-editor.css +11 -10
- package/corpus/templates/analytics/app/components/dashboard/CumulativeNetChart.tsx +10 -10
- package/corpus/templates/analytics/app/components/dashboard/RevenueChart.tsx +10 -10
- package/corpus/templates/analytics/app/components/dashboard/RevenueComparisonChart.tsx +6 -5
- package/corpus/templates/analytics/app/components/dashboard/TimeSeriesChart.tsx +9 -9
- package/corpus/templates/analytics/app/global.css +4 -0
- package/corpus/templates/analytics/app/lib/chart-theme.ts +13 -0
- package/corpus/templates/analytics/app/pages/adhoc/_shared/KpiChart.tsx +22 -24
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-charts-now-use-theme-aware-tooltip-and-grid-colors.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-fixed-ask-composer-spacing-so-the-chat-field-has-a-comfortab.md +6 -0
- package/corpus/templates/calendar/AGENTS.md +4 -0
- package/corpus/templates/calendar/actions/connect-google-calendar.ts +80 -0
- package/corpus/templates/calendar/changelog/2026-06-30-calendar-now-gives-the-agent-a-safe-google-connection-link-i.md +6 -0
- package/corpus/templates/calendar/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +5 -5
- package/corpus/templates/content/app/global.css +6 -5
- package/corpus/templates/content/changelog/2026-06-30-editor-floating-toolbars-now-use-theme-aware-colors-in-light.md +6 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +6 -1
- package/corpus/templates/design/actions/add-localhost-screens.ts +63 -8
- package/corpus/templates/design/actions/apply-motion-edit.ts +12 -46
- package/corpus/templates/design/actions/list-design-native-assets.ts +2 -1
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +49 -1
- package/corpus/templates/design/actions/navigate.ts +3 -12
- package/corpus/templates/design/actions/open-visual-edit.ts +341 -0
- package/corpus/templates/design/actions/view-screen.ts +1 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +1 -1
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +345 -14
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +71 -91
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +25 -27
- package/corpus/templates/design/app/components/design/DeviceFrame.tsx +1 -1
- package/corpus/templates/design/app/components/design/DrawOverlay.tsx +2 -2
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1111 -570
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +162 -32
- package/corpus/templates/design/app/components/design/MotionDock.tsx +268 -240
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1038 -150
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +1 -1
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +81 -102
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +6 -6
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +11 -11
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +9 -7
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1129 -155
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +130 -37
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +24 -11
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +184 -162
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +137 -79
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +13 -13
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +8 -8
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +2 -2
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +4 -4
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +3 -3
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +13 -7
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +14 -11
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +4 -4
- package/corpus/templates/design/app/components/design/inspector/index.ts +2 -0
- package/corpus/templates/design/app/components/design/types.ts +19 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +61 -78
- package/corpus/templates/design/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/design/app/components/layout/Layout.tsx +3 -2
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +0 -2
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +5 -4
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +1 -1
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +2 -2
- package/corpus/templates/design/app/global.css +56 -0
- package/corpus/templates/design/app/hooks/use-agent-generating.ts +4 -5
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +6 -7
- package/corpus/templates/design/app/hooks/use-question-flow.ts +3 -2
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +6 -4
- package/corpus/templates/design/app/i18n/zh-TW.ts +5 -14
- package/corpus/templates/design/app/i18n-data.ts +63 -188
- package/corpus/templates/design/app/lib/agent-chat.ts +13 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +1428 -384
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +9 -5
- package/corpus/templates/design/app/pages/DesignSystems.tsx +2 -2
- package/corpus/templates/design/app/pages/Index.tsx +49 -26
- package/corpus/templates/design/app/pages/NotFound.tsx +25 -8
- package/corpus/templates/design/app/pages/VisualEdit.tsx +17 -5
- package/corpus/templates/design/app/routes/examples.tsx +1 -7
- package/corpus/templates/design/app/routes/templates.tsx +7 -4
- package/corpus/templates/design/changelog/2026-06-30-board-shapes-and-auto-layout-controls-are-clearer.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-component-selections-now-use-purple-component-chrome-and-sho.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-constraints-now-stay-tucked-into-a-compact-inspector-preview.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-creating-a-component-now-opens-in-a-compact-inspector-popove.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-cards-no-longer-show-legacy-project-type-badges.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-chat-now-keeps-new-turns-in-the-visible-design-agent-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-editor-panels-now-keep-their-compact-text-sizing-cons.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-no-longer-shows-the-connected-local-code-warning-bann.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-opens-to-the-files-panel-unless-a-generation-is-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-opens-with-a-cleaner-empty-canvas-and-loading-preview.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-tools-use-simpler-less-decorative-icons.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-dragging-absolute-elements-no-longer-shows-coordinate-labels.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-all-screens-opening-too-zoomed-in-by-default.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-the-public-design-assets-sidebar-so-native-components-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-the-screen-overview-board-so-empty-board-space-no-long.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-full-view-screens-can-now-scroll-while-remaining-editable.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-inspector-element-headers-show-the-selected-html-tag-and-the.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-inspector-inputs-stay-compact-across-desktop-breakpoints.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-layers-can-be-multi-selected-from-the-canvas-and-edited-toge.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-local-visual-edit-links-can-edit-localhost-frames-without-si.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-localhost-visual-edit-frames-can-now-be-selected-and-edited.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-marquee-selection-now-works-inside-screens-multi-selected-la.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-is-now-available-from-the-persistent-left-rail-and-th.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-timeline-collapse-and-reopen-animations-feel-smoother.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-new-board-shapes-now-start-with-neutral-gray-styling.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-new-designs-open-into-a-steadier-loading-state-before-the-ed.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-new-designs-show-a-loading-state-during-handoff-and-keep-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-public-visual-edit-design-links-now-open-directly-instead-of.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-removed-the-confusing-states-section-from-the-design-editor-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-removed-the-templates-gallery-from-design-navigation.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-share-stays-visible-as-soon-as-editable-designs-load.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-signed-out-save-and-share-controls-use-simpler-labels-and-op.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-text-added-on-the-canvas-now-focuses-cleanly-with-the-right-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-editor-loading-skeleton-is-simpler-and-its-bottom.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-editor-loading-skeleton-now-follows-the-current-t.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-no-longer-shows-child-layout-controls-o.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-no-longer-shows-the-ask-ai-section-abov.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-review-panel-no-longer-shows-a-separate-inline-au.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-empty-designs-page-now-focuses-on-a-single-primary-new-d.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-layers-panel-header-stays-as-layers-while-multiple-items.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-missing-design-screen-now-uses-a-neutral-back-button.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-motion-timeline-now-closes-without-squeezing-the-canvas-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-new-design-connect-ai-setup-now-lines-up-with-the-compos.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-screen-overview-board-now-blends-into-the-canvas-and-new.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-top-level-canvas-drops-now-show-screen-guides.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-top-level-canvas-layers-now-keep-a-transparent-backdrop-and-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-top-level-canvas-layers-now-select-reliably-from-all-screens.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-visual-edit-can-open-and-refresh-localhost-screens-in-one-au.md +6 -0
- package/corpus/templates/design/e2e/helpers.ts +15 -4
- package/corpus/templates/design/server/db/index.ts +52 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/design/server/plugins/auth.ts +1 -0
- package/corpus/templates/design/server/plugins/core-routes.ts +4 -0
- package/corpus/templates/design/shared/board-file.ts +380 -5
- package/corpus/templates/design/shared/board-objects.ts +2 -2
- package/corpus/templates/design/shared/code-layer.ts +50 -6
- package/corpus/templates/design/shared/motion-compiler.ts +27 -0
- package/corpus/templates/macros/app/components/DailyProgress.tsx +41 -37
- package/corpus/templates/macros/app/components/ExerciseCard.tsx +2 -2
- package/corpus/templates/macros/app/components/MealCard.tsx +2 -2
- package/corpus/templates/macros/app/components/VoiceDictation.tsx +1 -1
- package/corpus/templates/macros/app/components/WeeklyCaloriesChart.tsx +1 -1
- package/corpus/templates/macros/app/components/WeightCard.tsx +2 -2
- package/corpus/templates/macros/app/components/WeightTracker.tsx +1 -1
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/macros/app/routes/_index.tsx +5 -5
- package/corpus/templates/macros/app/routes/analytics.tsx +1 -1
- package/corpus/templates/macros/changelog/2026-06-30-dashboard-loading-and-chart-surfaces-now-use-theme-aware-col.md +6 -0
- package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +14 -11
- package/corpus/templates/mail/app/global.css +3 -2
- package/corpus/templates/mail/changelog/2026-06-30-compose-floating-toolbars-now-use-theme-aware-colors-in-ligh.md +6 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +2 -2
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +3 -3
- package/corpus/templates/slides/app/global.css +10 -0
- package/corpus/templates/slides/app/pages/Index.tsx +55 -51
- package/corpus/templates/slides/changelog/2026-06-30-deck-thumbnails-collapse-cleanly-to-one-column-when-the-chat.md +6 -0
- package/dist/agent/engine/anthropic-engine.d.ts +2 -2
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +2 -2
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builtin.js +1 -1
- package/dist/agent/engine/builtin.js.map +1 -1
- package/dist/agent/engine/output-tokens.js +1 -1
- package/dist/agent/engine/output-tokens.js.map +1 -1
- package/dist/agent/harness/acp-adapter.d.ts +4 -1
- package/dist/agent/harness/acp-adapter.d.ts.map +1 -1
- package/dist/agent/harness/acp-adapter.js +19 -2
- package/dist/agent/harness/acp-adapter.js.map +1 -1
- package/dist/agent/harness/ai-sdk-adapter.d.ts.map +1 -1
- package/dist/agent/harness/ai-sdk-adapter.js +3 -0
- package/dist/agent/harness/ai-sdk-adapter.js.map +1 -1
- package/dist/agent/harness/translate.d.ts.map +1 -1
- package/dist/agent/harness/translate.js +3 -0
- package/dist/agent/harness/translate.js.map +1 -1
- package/dist/agent/harness/types.d.ts +1 -0
- package/dist/agent/harness/types.d.ts.map +1 -1
- package/dist/agent/harness/types.js.map +1 -1
- package/dist/agent/model-config.d.ts +21 -20
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +31 -13
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +85 -10
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +27 -6
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/agent/tool-call-journal.d.ts +11 -9
- package/dist/agent/tool-call-journal.d.ts.map +1 -1
- package/dist/agent/tool-call-journal.js +92 -40
- package/dist/agent/tool-call-journal.js.map +1 -1
- package/dist/agent/types.d.ts +5 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +84 -0
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/gateway-helpers.d.ts +4 -0
- package/dist/cli/gateway-helpers.d.ts.map +1 -1
- package/dist/cli/gateway-helpers.js +9 -0
- package/dist/cli/gateway-helpers.js.map +1 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +0 -2
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +54 -16
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/workspace-dev.d.ts.map +1 -1
- package/dist/cli/workspace-dev.js +33 -6
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +72 -26
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/DefaultSpinner.d.ts.map +1 -1
- package/dist/client/DefaultSpinner.js +8 -1
- package/dist/client/DefaultSpinner.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +7 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +37 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/repo-helpers.d.ts +1 -0
- package/dist/client/chat/repo-helpers.d.ts.map +1 -1
- package/dist/client/chat/repo-helpers.js +59 -0
- package/dist/client/chat/repo-helpers.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +4 -4
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/components/ApiKeySettings.js +8 -8
- package/dist/client/components/ApiKeySettings.js.map +1 -1
- package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
- package/dist/client/components/CodeRequiredDialog.js +24 -15
- package/dist/client/components/CodeRequiredDialog.js.map +1 -1
- package/dist/client/components/MissingKeyCard.d.ts.map +1 -1
- package/dist/client/components/MissingKeyCard.js +11 -6
- package/dist/client/components/MissingKeyCard.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +4 -3
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +6 -6
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/integrations/IntegrationCard.js +1 -1
- package/dist/client/integrations/IntegrationCard.js.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.js +1 -1
- package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
- package/dist/client/onboarding/OnboardingBanner.js +2 -2
- package/dist/client/onboarding/OnboardingBanner.js.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.js +23 -19
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/resources/ResourceEditor.d.ts.map +1 -1
- package/dist/client/resources/ResourceEditor.js +13 -10
- package/dist/client/resources/ResourceEditor.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +2 -1
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +8 -3
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +6 -38
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/useProductionAgent.d.ts +2 -1
- package/dist/client/useProductionAgent.d.ts.map +1 -1
- package/dist/client/useProductionAgent.js.map +1 -1
- package/dist/deploy/build.js +1 -1
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js.map +1 -1
- package/dist/onboarding/default-steps.js +1 -1
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +3 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +134 -72
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +3 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +4 -1
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/open-route.d.ts +9 -0
- package/dist/server/open-route.d.ts.map +1 -1
- package/dist/server/open-route.js +48 -38
- package/dist/server/open-route.js.map +1 -1
- package/dist/shared/reasoning-effort.js +3 -0
- package/dist/shared/reasoning-effort.js.map +1 -1
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +18 -2
- package/dist/sharing/access.js.map +1 -1
- package/dist/sharing/registry.d.ts +10 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js.map +1 -1
- package/dist/styles/agent-native.css +104 -20
- package/dist/styles/rich-markdown-editor.css +11 -10
- package/docs/content/cloneable-saas.mdx +2 -2
- package/docs/content/faq.mdx +1 -1
- package/docs/content/locales/ar-SA/cloneable-saas.mdx +2 -2
- package/docs/content/locales/ar-SA/faq.mdx +1 -1
- package/docs/content/locales/ar-SA/template-chat.mdx +1 -1
- package/docs/content/locales/de-DE/cloneable-saas.mdx +2 -2
- package/docs/content/locales/de-DE/faq.mdx +1 -1
- package/docs/content/locales/de-DE/template-chat.mdx +1 -1
- package/docs/content/locales/es-ES/cloneable-saas.mdx +2 -2
- package/docs/content/locales/es-ES/faq.mdx +1 -1
- package/docs/content/locales/es-ES/template-chat.mdx +1 -1
- package/docs/content/locales/fr-FR/cloneable-saas.mdx +2 -2
- package/docs/content/locales/fr-FR/faq.mdx +1 -1
- package/docs/content/locales/fr-FR/template-chat.mdx +1 -1
- package/docs/content/locales/hi-IN/cloneable-saas.mdx +2 -2
- package/docs/content/locales/hi-IN/faq.mdx +1 -1
- package/docs/content/locales/hi-IN/template-chat.mdx +1 -1
- package/docs/content/locales/ja-JP/cloneable-saas.mdx +2 -2
- package/docs/content/locales/ja-JP/faq.mdx +1 -1
- package/docs/content/locales/ja-JP/template-chat.mdx +1 -1
- package/docs/content/locales/ko-KR/cloneable-saas.mdx +2 -2
- package/docs/content/locales/ko-KR/faq.mdx +1 -1
- package/docs/content/locales/ko-KR/template-chat.mdx +1 -1
- package/docs/content/locales/pt-BR/cloneable-saas.mdx +2 -2
- package/docs/content/locales/pt-BR/faq.mdx +1 -1
- package/docs/content/locales/pt-BR/template-chat.mdx +1 -1
- package/docs/content/locales/zh-CN/cloneable-saas.mdx +2 -2
- package/docs/content/locales/zh-CN/faq.mdx +1 -1
- package/docs/content/locales/zh-CN/template-chat.mdx +1 -1
- package/docs/content/locales/zh-TW/cloneable-saas.mdx +2 -2
- package/docs/content/locales/zh-TW/faq.mdx +1 -1
- package/docs/content/locales/zh-TW/template-chat.mdx +1 -1
- package/docs/content/template-chat.mdx +1 -1
- package/package.json +1 -1
- package/corpus/templates/design/app/components/design/LocalSourceEditBanner.tsx +0 -157
- package/corpus/templates/design/app/lib/design-templates.ts +0 -1305
- package/corpus/templates/design/app/pages/Templates.tsx +0 -279
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
appBasePath,
|
|
17
17
|
ensureEmbedAuthFetchInterceptor,
|
|
18
18
|
isEmbedAuthActive,
|
|
19
|
-
sendToAgentChat,
|
|
20
19
|
getBrowserTabId,
|
|
21
20
|
readClientAppState,
|
|
22
21
|
setClientAppState,
|
|
@@ -58,6 +57,10 @@ import {
|
|
|
58
57
|
hasCapability,
|
|
59
58
|
} from "@shared/design-source-capabilities";
|
|
60
59
|
import { shouldUseLiveFileContent } from "@shared/html-content";
|
|
60
|
+
import {
|
|
61
|
+
compile as compileMotionTimeline,
|
|
62
|
+
injectManagedMotionCss,
|
|
63
|
+
} from "@shared/motion-compiler";
|
|
61
64
|
import type { MotionTrack } from "@shared/motion-timeline";
|
|
62
65
|
import {
|
|
63
66
|
resolveTweaksToCssVars,
|
|
@@ -90,7 +93,7 @@ import {
|
|
|
90
93
|
IconPhoto,
|
|
91
94
|
IconRefresh,
|
|
92
95
|
IconChevronDown,
|
|
93
|
-
|
|
96
|
+
IconChevronUp,
|
|
94
97
|
IconCheck,
|
|
95
98
|
IconPointer,
|
|
96
99
|
IconTypography,
|
|
@@ -165,7 +168,6 @@ import {
|
|
|
165
168
|
LocalhostWriteConsentDialog,
|
|
166
169
|
type LocalhostWriteConsentPayload,
|
|
167
170
|
} from "@/components/design/LocalhostWriteConsentDialog";
|
|
168
|
-
import { LocalSourceEditBanner } from "@/components/design/LocalSourceEditBanner";
|
|
169
171
|
import {
|
|
170
172
|
MotionDock,
|
|
171
173
|
type MotionDockTrack,
|
|
@@ -173,13 +175,18 @@ import {
|
|
|
173
175
|
import {
|
|
174
176
|
MultiScreenCanvas,
|
|
175
177
|
OVERVIEW_FRAME_WIDTH,
|
|
178
|
+
type CanvasLayerMarqueeSelection,
|
|
176
179
|
type CanvasPrimitiveInsert,
|
|
177
180
|
type FrameGeometry,
|
|
178
181
|
} from "@/components/design/MultiScreenCanvas";
|
|
179
182
|
import { QuestionFlow } from "@/components/design/QuestionFlow";
|
|
180
183
|
import type { ReviewPanelProps } from "@/components/design/ReviewPanel";
|
|
181
184
|
import { TokensPanel } from "@/components/design/TokensPanel";
|
|
182
|
-
import type {
|
|
185
|
+
import type {
|
|
186
|
+
ElementInfo,
|
|
187
|
+
ElementSelectionIntent,
|
|
188
|
+
DeviceFrameType,
|
|
189
|
+
} from "@/components/design/types";
|
|
183
190
|
import {
|
|
184
191
|
DEVICE_FRAME_VIEWPORTS,
|
|
185
192
|
ZOOM_PRESETS,
|
|
@@ -232,7 +239,14 @@ import {
|
|
|
232
239
|
type DesignEditorCommand,
|
|
233
240
|
} from "@/hooks/use-navigation-state";
|
|
234
241
|
import { useQuestionFlow } from "@/hooks/use-question-flow";
|
|
235
|
-
import {
|
|
242
|
+
import {
|
|
243
|
+
isDesignHotkeyEditableTarget,
|
|
244
|
+
useDesignHotkeys,
|
|
245
|
+
} from "@/hooks/useDesignHotkeys";
|
|
246
|
+
import {
|
|
247
|
+
DESIGN_CHAT_STORAGE_KEY,
|
|
248
|
+
sendToDesignAgentChat,
|
|
249
|
+
} from "@/lib/agent-chat";
|
|
236
250
|
import {
|
|
237
251
|
clearPendingGeneration,
|
|
238
252
|
hasFreshPendingGeneration,
|
|
@@ -265,12 +279,16 @@ const STORED_RUN_LIVENESS_GRACE_MS = 20_000;
|
|
|
265
279
|
const MAX_DESIGN_UNDO_STACK = 50;
|
|
266
280
|
const OVERVIEW_ZOOM_THRESHOLD = 60;
|
|
267
281
|
const MOTION_DOCK_TRANSITION_MS = 200;
|
|
282
|
+
const MOTION_DOCK_EXIT_SETTLE_MS = 80;
|
|
283
|
+
const MOTION_DOCK_EXIT_FALLBACK_MS = MOTION_DOCK_TRANSITION_MS * 2 + 600;
|
|
268
284
|
const MOTION_AUTOSAVE_DELAY_MS = 500;
|
|
285
|
+
const BOARD_SURFACE_SIZE = 131_072;
|
|
269
286
|
/** Extensions that the localhost bridge allows to be written back to source. */
|
|
270
287
|
const LOCALHOST_WRITE_EXTENSIONS = new Set([".html", ".htm", ".css"]);
|
|
271
288
|
const NO_LOCALHOST_WRITE_CONTENT_MESSAGE =
|
|
272
289
|
"No content to write. Open the screen first." /* i18n-ignore */;
|
|
273
290
|
const FOCUSED_SCREEN_ZOOM = 100;
|
|
291
|
+
export const DEFAULT_OVERVIEW_ZOOM = 60;
|
|
274
292
|
const KEEPALIVE_FILE_SAVE_MAX_BYTES = 60_000;
|
|
275
293
|
const UNSUPPORTED_HTML2CANVAS_COLOR_RE =
|
|
276
294
|
/\b(?:color|color-mix|oklch|oklab|lab|lch)\(/i;
|
|
@@ -293,6 +311,14 @@ const HTML2CANVAS_UNSUPPORTED_VALUE_PROPERTIES = [
|
|
|
293
311
|
"list-style-image",
|
|
294
312
|
] as const;
|
|
295
313
|
|
|
314
|
+
function blurActiveDesignEditableTarget() {
|
|
315
|
+
if (typeof document === "undefined") return;
|
|
316
|
+
const active = document.activeElement;
|
|
317
|
+
if (active instanceof HTMLElement && isDesignHotkeyEditableTarget(active)) {
|
|
318
|
+
active.blur();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
296
322
|
function getContentSignature(content: string): string {
|
|
297
323
|
let hash = 2166136261;
|
|
298
324
|
for (let index = 0; index < content.length; index += 1) {
|
|
@@ -302,6 +328,10 @@ function getContentSignature(content: string): string {
|
|
|
302
328
|
return `${content.length}:${hash.toString(36)}`;
|
|
303
329
|
}
|
|
304
330
|
|
|
331
|
+
function dedupeStringIds(ids: string[]): string[] {
|
|
332
|
+
return Array.from(new Set(ids.filter(Boolean)));
|
|
333
|
+
}
|
|
334
|
+
|
|
305
335
|
export function isScreenRootElementInfo(info: ElementInfo | null | undefined) {
|
|
306
336
|
const tagName = info?.tagName?.toUpperCase();
|
|
307
337
|
return tagName === "BODY" || tagName === "HTML";
|
|
@@ -369,14 +399,26 @@ export function getSidebarCodeLayerSelectionState(args: {
|
|
|
369
399
|
currentViewMode: "single" | "overview";
|
|
370
400
|
ownerFileId?: string | null;
|
|
371
401
|
overviewSelectedScreenIds: string[];
|
|
402
|
+
screenFileIds?: string[];
|
|
372
403
|
}) {
|
|
373
|
-
const {
|
|
404
|
+
const {
|
|
405
|
+
currentViewMode,
|
|
406
|
+
ownerFileId,
|
|
407
|
+
overviewSelectedScreenIds,
|
|
408
|
+
screenFileIds,
|
|
409
|
+
} = args;
|
|
410
|
+
const ownerScreenId =
|
|
411
|
+
ownerFileId && (!screenFileIds || screenFileIds.includes(ownerFileId))
|
|
412
|
+
? ownerFileId
|
|
413
|
+
: null;
|
|
374
414
|
return {
|
|
375
415
|
viewMode: currentViewMode,
|
|
376
416
|
overviewSelectedScreenIds:
|
|
377
|
-
currentViewMode === "overview" &&
|
|
378
|
-
? [
|
|
379
|
-
:
|
|
417
|
+
currentViewMode === "overview" && ownerScreenId
|
|
418
|
+
? [ownerScreenId]
|
|
419
|
+
: currentViewMode === "overview" && ownerFileId
|
|
420
|
+
? []
|
|
421
|
+
: overviewSelectedScreenIds,
|
|
380
422
|
};
|
|
381
423
|
}
|
|
382
424
|
|
|
@@ -411,6 +453,10 @@ export function getOverviewCanvasZoom(
|
|
|
411
453
|
return displayZoom / scale;
|
|
412
454
|
}
|
|
413
455
|
|
|
456
|
+
export function getDefaultOverviewCanvasZoom(overviewZoomScale: number) {
|
|
457
|
+
return getOverviewCanvasZoom(DEFAULT_OVERVIEW_ZOOM, overviewZoomScale);
|
|
458
|
+
}
|
|
459
|
+
|
|
414
460
|
export function getDesignEditorShareUrl(
|
|
415
461
|
id: string,
|
|
416
462
|
origin: string,
|
|
@@ -423,6 +469,41 @@ export function getDesignEditorShareUrl(
|
|
|
423
469
|
return new URL(pathname, origin).toString();
|
|
424
470
|
}
|
|
425
471
|
|
|
472
|
+
function formatDesignEditorUrlZoom(zoom: number): string {
|
|
473
|
+
const rounded = Math.round(zoom * 100) / 100;
|
|
474
|
+
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(2);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export function getDesignEditorStateUrlSearch(args: {
|
|
478
|
+
currentSearch: string;
|
|
479
|
+
viewMode: "single" | "overview";
|
|
480
|
+
screenId?: string | null;
|
|
481
|
+
selectionId?: string | null;
|
|
482
|
+
zoom?: number | null;
|
|
483
|
+
}) {
|
|
484
|
+
const params = new URLSearchParams(args.currentSearch);
|
|
485
|
+
params.set("view", args.viewMode);
|
|
486
|
+
if (args.screenId) {
|
|
487
|
+
params.set("screen", args.screenId);
|
|
488
|
+
} else {
|
|
489
|
+
params.delete("screen");
|
|
490
|
+
}
|
|
491
|
+
params.delete("fileId");
|
|
492
|
+
params.delete("filename");
|
|
493
|
+
if (args.selectionId) {
|
|
494
|
+
params.set("selection", args.selectionId);
|
|
495
|
+
} else {
|
|
496
|
+
params.delete("selection");
|
|
497
|
+
}
|
|
498
|
+
if (typeof args.zoom === "number" && Number.isFinite(args.zoom)) {
|
|
499
|
+
params.set("zoom", formatDesignEditorUrlZoom(args.zoom));
|
|
500
|
+
} else {
|
|
501
|
+
params.delete("zoom");
|
|
502
|
+
}
|
|
503
|
+
const query = params.toString();
|
|
504
|
+
return query ? `?${query}` : "";
|
|
505
|
+
}
|
|
506
|
+
|
|
426
507
|
export function getLocalhostRouteSourceFile(args: {
|
|
427
508
|
sourceFile?: string;
|
|
428
509
|
source?: string;
|
|
@@ -473,14 +554,25 @@ export function getFreshActiveFileContent(args: {
|
|
|
473
554
|
export function getFreshScreenContent(args: {
|
|
474
555
|
screenId: string;
|
|
475
556
|
activeFileId?: string | null;
|
|
557
|
+
freshActiveContentFileId?: string | null;
|
|
476
558
|
freshActiveContent: string;
|
|
477
559
|
fileContentById: ReadonlyMap<string, string>;
|
|
478
560
|
}) {
|
|
479
|
-
|
|
561
|
+
const freshActiveContentFileId =
|
|
562
|
+
args.freshActiveContentFileId ?? args.activeFileId;
|
|
563
|
+
return args.screenId === args.activeFileId &&
|
|
564
|
+
args.screenId === freshActiveContentFileId
|
|
480
565
|
? args.freshActiveContent
|
|
481
566
|
: (args.fileContentById.get(args.screenId) ?? "");
|
|
482
567
|
}
|
|
483
568
|
|
|
569
|
+
export function shouldReplacePreviewAfterVisualStyleCommit(args: {
|
|
570
|
+
runtimeApplied?: boolean;
|
|
571
|
+
runtimeStyleApplied: boolean;
|
|
572
|
+
}) {
|
|
573
|
+
return !args.runtimeApplied && !args.runtimeStyleApplied;
|
|
574
|
+
}
|
|
575
|
+
|
|
484
576
|
export function getLayerMoveIterationOrder<T>(
|
|
485
577
|
orderedIds: readonly T[],
|
|
486
578
|
placement: "before" | "after" | "inside",
|
|
@@ -495,19 +587,37 @@ export function removeUndoRedoOrderKind<T extends string>(
|
|
|
495
587
|
return order.filter((entry) => entry !== kind);
|
|
496
588
|
}
|
|
497
589
|
|
|
498
|
-
type UndoRedoOrderKind = "content" | "file-content" | "geometry";
|
|
590
|
+
export type UndoRedoOrderKind = "content" | "file-content" | "geometry";
|
|
591
|
+
|
|
592
|
+
export function getUndoRedoPriorityOrder(
|
|
593
|
+
preferred: UndoRedoOrderKind | undefined,
|
|
594
|
+
): UndoRedoOrderKind[] {
|
|
595
|
+
if (preferred === "geometry") return ["geometry", "content", "file-content"];
|
|
596
|
+
if (preferred === "file-content")
|
|
597
|
+
return ["file-content", "content", "geometry"];
|
|
598
|
+
return ["content", "file-content", "geometry"];
|
|
599
|
+
}
|
|
499
600
|
|
|
500
601
|
function resolveZoomUpdate(update: SetStateAction<number>, current: number) {
|
|
501
602
|
return typeof update === "function" ? update(current) : update;
|
|
502
603
|
}
|
|
503
604
|
|
|
504
|
-
export function
|
|
605
|
+
export function shouldLimitEditorChromeUntilContentReady(args: {
|
|
505
606
|
fileCount: number;
|
|
607
|
+
hasActiveCanvasContent: boolean;
|
|
506
608
|
generating: boolean;
|
|
507
609
|
pendingGenerationActive: boolean;
|
|
508
610
|
}) {
|
|
509
|
-
const {
|
|
510
|
-
|
|
611
|
+
const {
|
|
612
|
+
fileCount,
|
|
613
|
+
generating,
|
|
614
|
+
hasActiveCanvasContent,
|
|
615
|
+
pendingGenerationActive,
|
|
616
|
+
} = args;
|
|
617
|
+
return (
|
|
618
|
+
(fileCount === 0 || !hasActiveCanvasContent) &&
|
|
619
|
+
(generating || pendingGenerationActive)
|
|
620
|
+
);
|
|
511
621
|
}
|
|
512
622
|
|
|
513
623
|
export function shouldEscapeToOverview(args: {
|
|
@@ -749,6 +859,13 @@ interface DesignFile {
|
|
|
749
859
|
updatedAt: string;
|
|
750
860
|
}
|
|
751
861
|
|
|
862
|
+
interface LiveScreenSnapshot {
|
|
863
|
+
url: string;
|
|
864
|
+
html: string;
|
|
865
|
+
status?: number;
|
|
866
|
+
contentType?: string;
|
|
867
|
+
}
|
|
868
|
+
|
|
752
869
|
interface DesignData {
|
|
753
870
|
id: string;
|
|
754
871
|
title: string;
|
|
@@ -1086,6 +1203,7 @@ function designEditorCommandFromSearchParams(
|
|
|
1086
1203
|
searchParams.get("screen") ??
|
|
1087
1204
|
searchParams.get("fileId") ??
|
|
1088
1205
|
searchParams.get("filename");
|
|
1206
|
+
const selection = searchParams.get("selection");
|
|
1089
1207
|
const rawZoom = searchParams.get("zoom");
|
|
1090
1208
|
const zoom = rawZoom !== null ? Number(rawZoom) : NaN;
|
|
1091
1209
|
const tool = normalizeDesignTool(searchParams.get("tool"));
|
|
@@ -1097,6 +1215,7 @@ function designEditorCommandFromSearchParams(
|
|
|
1097
1215
|
inspector !== "extensions" &&
|
|
1098
1216
|
!leftPanel &&
|
|
1099
1217
|
!screen &&
|
|
1218
|
+
!selection &&
|
|
1100
1219
|
!tool
|
|
1101
1220
|
) {
|
|
1102
1221
|
return null;
|
|
@@ -1117,6 +1236,7 @@ function designEditorCommandFromSearchParams(
|
|
|
1117
1236
|
}
|
|
1118
1237
|
if (leftPanel) command.leftPanel = leftPanel;
|
|
1119
1238
|
if (screen) command.screen = screen;
|
|
1239
|
+
if (selection) command.selection = selection;
|
|
1120
1240
|
if (Number.isFinite(zoom)) {
|
|
1121
1241
|
command.zoom = zoom;
|
|
1122
1242
|
} else if (editorView === "single") {
|
|
@@ -1307,6 +1427,35 @@ function removeAbsolutePositioningFromNodeInHtml(
|
|
|
1307
1427
|
}
|
|
1308
1428
|
}
|
|
1309
1429
|
|
|
1430
|
+
function setAbsolutePositioningForNodeInHtml(
|
|
1431
|
+
content: string,
|
|
1432
|
+
nodeAttrId: string,
|
|
1433
|
+
point: { x: number; y: number },
|
|
1434
|
+
pointerOffset?: { x: number; y: number },
|
|
1435
|
+
): string {
|
|
1436
|
+
if (typeof window === "undefined") return content;
|
|
1437
|
+
try {
|
|
1438
|
+
const doc = new DOMParser().parseFromString(content, "text/html");
|
|
1439
|
+
const element = doc.querySelector(
|
|
1440
|
+
`[data-agent-native-node-id="${CSS.escape(nodeAttrId)}"]`,
|
|
1441
|
+
) as HTMLElement | null;
|
|
1442
|
+
if (!element) return content;
|
|
1443
|
+
element.style.position = "absolute";
|
|
1444
|
+
element.style.left = `${Math.round(point.x - (pointerOffset?.x ?? 0))}px`;
|
|
1445
|
+
element.style.top = `${Math.round(point.y - (pointerOffset?.y ?? 0))}px`;
|
|
1446
|
+
element.style.removeProperty("right");
|
|
1447
|
+
element.style.removeProperty("bottom");
|
|
1448
|
+
return `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
|
|
1449
|
+
} catch {
|
|
1450
|
+
return content;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
function isAbsoluteCodeLayerNode(node: CodeLayerNode | null | undefined) {
|
|
1455
|
+
const position = String(node?.style.position ?? "").toLowerCase();
|
|
1456
|
+
return position === "absolute" || position === "fixed";
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1310
1459
|
function escapeHtmlText(value: string): string {
|
|
1311
1460
|
return value
|
|
1312
1461
|
.replace(/&/g, "&")
|
|
@@ -1574,6 +1723,9 @@ function appendCanvasPrimitiveToHtml(
|
|
|
1574
1723
|
}
|
|
1575
1724
|
svg.setAttribute("data-agent-native-node-id", nodeId);
|
|
1576
1725
|
svg.setAttribute("data-agent-native-layer-name", layerName);
|
|
1726
|
+
// Kind marker so the layers panel shows a true vector/line/arrow icon for
|
|
1727
|
+
// this SVG primitive instead of falling through to the rectangle glyph.
|
|
1728
|
+
// Read by treeTypeForNode in shared/code-layer.ts.
|
|
1577
1729
|
svg.setAttribute("data-an-primitive", primitive.kind);
|
|
1578
1730
|
svg.setAttribute(
|
|
1579
1731
|
"viewBox",
|
|
@@ -1619,6 +1771,10 @@ function appendCanvasPrimitiveToHtml(
|
|
|
1619
1771
|
polygon.setAttribute("stroke-linejoin", "round");
|
|
1620
1772
|
svg.setAttribute("data-agent-native-node-id", nodeId);
|
|
1621
1773
|
svg.setAttribute("data-agent-native-layer-name", layerName);
|
|
1774
|
+
// Kind marker so the layers panel shows a true polygon/star icon for this
|
|
1775
|
+
// SVG primitive instead of falling through to the rectangle glyph.
|
|
1776
|
+
// Read by treeTypeForNode in shared/code-layer.ts.
|
|
1777
|
+
svg.setAttribute("data-an-primitive", primitive.kind);
|
|
1622
1778
|
svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
|
|
1623
1779
|
svg.setAttribute(
|
|
1624
1780
|
"style",
|
|
@@ -1673,7 +1829,7 @@ function appendCanvasPrimitiveToHtml(
|
|
|
1673
1829
|
element.style.borderRadius = canonical.borderRadius;
|
|
1674
1830
|
element.style.overflow = "hidden";
|
|
1675
1831
|
} else if (primitive.kind === "text") {
|
|
1676
|
-
element.textContent = primitive.text ?? "
|
|
1832
|
+
element.textContent = primitive.text ?? "";
|
|
1677
1833
|
element.style.display = primitive.autoSize ? "inline-block" : "flex";
|
|
1678
1834
|
if (!primitive.autoSize) {
|
|
1679
1835
|
element.style.alignItems = "center";
|
|
@@ -2097,12 +2253,38 @@ function layerTypeForCodeLayer(
|
|
|
2097
2253
|
): LayersPanelNode["type"] {
|
|
2098
2254
|
if (node.type === "group") return "group";
|
|
2099
2255
|
if (node.type === "component") return "component";
|
|
2256
|
+
if (node.type === "ellipse") return "ellipse";
|
|
2100
2257
|
if (node.type === "shape") return "shape";
|
|
2258
|
+
if (node.type === "vector") return "vector";
|
|
2259
|
+
if (node.type === "line") return "line";
|
|
2260
|
+
if (node.type === "arrow") return "arrow";
|
|
2261
|
+
if (node.type === "polygon") return "polygon";
|
|
2262
|
+
if (node.type === "star") return "star";
|
|
2101
2263
|
if (node.type === "text") return "text";
|
|
2102
2264
|
if (node.type === "image") return "image";
|
|
2103
2265
|
return "element";
|
|
2104
2266
|
}
|
|
2105
2267
|
|
|
2268
|
+
function codeLayerNodeLooksLikeComponent(
|
|
2269
|
+
node: CodeLayerNode | null | undefined,
|
|
2270
|
+
): boolean {
|
|
2271
|
+
if (!node) return false;
|
|
2272
|
+
if (isComponentInstance(node)) return true;
|
|
2273
|
+
const tag = node.tag.toLowerCase();
|
|
2274
|
+
if (
|
|
2275
|
+
tag === "button" ||
|
|
2276
|
+
tag === "input" ||
|
|
2277
|
+
tag === "select" ||
|
|
2278
|
+
tag === "textarea"
|
|
2279
|
+
) {
|
|
2280
|
+
return true;
|
|
2281
|
+
}
|
|
2282
|
+
if (/component|card|button|control/i.test(node.layerName)) return true;
|
|
2283
|
+
return node.classes.some((item) =>
|
|
2284
|
+
/component|card|button|control/i.test(item),
|
|
2285
|
+
);
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2106
2288
|
function preferredCodeLayerSelector(node: CodeLayerNode): string {
|
|
2107
2289
|
return (
|
|
2108
2290
|
node.selectors.find((selector) =>
|
|
@@ -2272,6 +2454,7 @@ function codeLayerTreeToPanelNodes(
|
|
|
2272
2454
|
id: node.id,
|
|
2273
2455
|
name: resolvedLayerName(node),
|
|
2274
2456
|
type: layerTypeForCodeLayer(node),
|
|
2457
|
+
tagName: node.tag,
|
|
2275
2458
|
layout: node.layout,
|
|
2276
2459
|
detail: node.detail,
|
|
2277
2460
|
badge: node.badge,
|
|
@@ -2291,6 +2474,14 @@ interface EffectiveCodeLayerState {
|
|
|
2291
2474
|
hiddenIds: Set<string>;
|
|
2292
2475
|
}
|
|
2293
2476
|
|
|
2477
|
+
interface SelectedLayerTarget {
|
|
2478
|
+
layerId: string;
|
|
2479
|
+
fileId: string;
|
|
2480
|
+
node: CodeLayerNode;
|
|
2481
|
+
tree: CodeLayerTreeNode[];
|
|
2482
|
+
elementInfo: ElementInfo;
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2294
2485
|
function collectEffectiveCodeLayerState(
|
|
2295
2486
|
nodes: CodeLayerTreeNode[],
|
|
2296
2487
|
lockedIds: Set<string>,
|
|
@@ -2352,6 +2543,7 @@ function elementInfoFromCodeLayerNode(node: CodeLayerNode): ElementInfo {
|
|
|
2352
2543
|
),
|
|
2353
2544
|
boundingRect: { x: 0, y: 0, width: 0, height: 0 },
|
|
2354
2545
|
textContent: node.textSnippet ?? undefined,
|
|
2546
|
+
childElementCount: node.children.length,
|
|
2355
2547
|
isFlexChild: node.layout.parentDisplay?.includes("flex") ? true : false,
|
|
2356
2548
|
isFlexContainer: node.layout.isFlexContainer,
|
|
2357
2549
|
parentDisplay: node.layout.parentDisplay,
|
|
@@ -2480,6 +2672,7 @@ function canonicalElementInfoForCodeLayerNode(
|
|
|
2480
2672
|
selector: preferredCodeLayerSelector(node),
|
|
2481
2673
|
classes: node.classes,
|
|
2482
2674
|
confidence: node.confidence,
|
|
2675
|
+
childElementCount: node.children.length,
|
|
2483
2676
|
editCapabilities: info.editCapabilities?.some((capability) =>
|
|
2484
2677
|
capability.kind.startsWith("deterministic"),
|
|
2485
2678
|
)
|
|
@@ -2546,6 +2739,7 @@ export function refreshElementInfoFromContent(
|
|
|
2546
2739
|
sourceInfo.classes,
|
|
2547
2740
|
),
|
|
2548
2741
|
textContent: sourceInfo.textContent,
|
|
2742
|
+
childElementCount: sourceInfo.childElementCount,
|
|
2549
2743
|
isFlexChild: sourceInfo.isFlexChild,
|
|
2550
2744
|
isFlexContainer: sourceInfo.isFlexContainer,
|
|
2551
2745
|
parentDisplay: sourceInfo.parentDisplay,
|
|
@@ -2566,6 +2760,7 @@ export function refreshElementInfoFromContent(
|
|
|
2566
2760
|
classes,
|
|
2567
2761
|
),
|
|
2568
2762
|
textContent: element.textContent?.slice(0, 200) ?? info.textContent,
|
|
2763
|
+
childElementCount: element.children.length,
|
|
2569
2764
|
};
|
|
2570
2765
|
} catch {
|
|
2571
2766
|
return null;
|
|
@@ -2723,6 +2918,13 @@ function AgentNativeMenuMark({ className }: { className?: string }) {
|
|
|
2723
2918
|
|
|
2724
2919
|
type DesignLeftPanel = "file" | "agent" | "assets" | "tools" | "tokens";
|
|
2725
2920
|
|
|
2921
|
+
const INITIAL_GENERATION_DISABLED_LEFT_PANELS = new Set<DesignLeftPanel>([
|
|
2922
|
+
"file",
|
|
2923
|
+
"assets",
|
|
2924
|
+
"tools",
|
|
2925
|
+
"tokens",
|
|
2926
|
+
]);
|
|
2927
|
+
|
|
2726
2928
|
function normalizeDesignLeftPanel(value: unknown): DesignLeftPanel | undefined {
|
|
2727
2929
|
if (value === "extensions") return "tools";
|
|
2728
2930
|
return value === "file" ||
|
|
@@ -2736,11 +2938,19 @@ function normalizeDesignLeftPanel(value: unknown): DesignLeftPanel | undefined {
|
|
|
2736
2938
|
|
|
2737
2939
|
function DesignWorkspaceRail({
|
|
2738
2940
|
activePanel,
|
|
2941
|
+
disabledPanels,
|
|
2942
|
+
motionOpen,
|
|
2943
|
+
motionDisabled,
|
|
2739
2944
|
projectMenu,
|
|
2945
|
+
onMotionToggle,
|
|
2740
2946
|
onPanelChange,
|
|
2741
2947
|
}: {
|
|
2742
2948
|
activePanel: DesignLeftPanel;
|
|
2949
|
+
disabledPanels?: ReadonlySet<DesignLeftPanel>;
|
|
2950
|
+
motionOpen?: boolean;
|
|
2951
|
+
motionDisabled?: boolean;
|
|
2743
2952
|
projectMenu: ReactNode;
|
|
2953
|
+
onMotionToggle?: () => void;
|
|
2744
2954
|
onPanelChange: (panel: DesignLeftPanel) => void;
|
|
2745
2955
|
}) {
|
|
2746
2956
|
const t = useT();
|
|
@@ -2788,16 +2998,27 @@ function DesignWorkspaceRail({
|
|
|
2788
2998
|
<div className="flex min-h-0 flex-1 flex-col items-center gap-4">
|
|
2789
2999
|
{items.map((item) => {
|
|
2790
3000
|
const active = item.panel === activePanel;
|
|
3001
|
+
const disabled = disabledPanels?.has(item.panel) ?? false;
|
|
2791
3002
|
return (
|
|
2792
3003
|
<Tooltip key={item.panel}>
|
|
2793
3004
|
<TooltipTrigger asChild>
|
|
2794
3005
|
<button
|
|
2795
3006
|
type="button"
|
|
2796
3007
|
aria-label={item.label}
|
|
3008
|
+
aria-disabled={disabled || undefined}
|
|
2797
3009
|
aria-current={active ? "page" : undefined}
|
|
2798
|
-
|
|
3010
|
+
tabIndex={disabled ? -1 : undefined}
|
|
3011
|
+
onClick={(event) => {
|
|
3012
|
+
if (disabled) {
|
|
3013
|
+
event.preventDefault();
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
onPanelChange(item.panel);
|
|
3017
|
+
}}
|
|
2799
3018
|
className={cn(
|
|
2800
|
-
"group flex w-12 cursor-pointer flex-col items-center justify-start gap-1
|
|
3019
|
+
"group flex w-12 cursor-pointer flex-col items-center justify-start gap-1 rounded-none text-[10px] font-[450] leading-none text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
|
|
3020
|
+
disabled &&
|
|
3021
|
+
"cursor-default opacity-35 hover:text-muted-foreground",
|
|
2801
3022
|
active && "text-foreground",
|
|
2802
3023
|
)}
|
|
2803
3024
|
>
|
|
@@ -2807,6 +3028,8 @@ function DesignWorkspaceRail({
|
|
|
2807
3028
|
active
|
|
2808
3029
|
? "bg-[var(--design-editor-selection-color)] text-[var(--design-editor-accent-color)]"
|
|
2809
3030
|
: "text-muted-foreground group-hover:bg-[var(--design-editor-layer-hover-color)] group-hover:text-foreground",
|
|
3031
|
+
disabled &&
|
|
3032
|
+
"group-hover:bg-transparent group-hover:text-muted-foreground",
|
|
2810
3033
|
)}
|
|
2811
3034
|
>
|
|
2812
3035
|
{item.icon}
|
|
@@ -2821,6 +3044,53 @@ function DesignWorkspaceRail({
|
|
|
2821
3044
|
);
|
|
2822
3045
|
})}
|
|
2823
3046
|
</div>
|
|
3047
|
+
{onMotionToggle ? (
|
|
3048
|
+
<div className="mt-4 flex w-full flex-col items-center border-t border-border/70 pt-3">
|
|
3049
|
+
<Tooltip>
|
|
3050
|
+
<TooltipTrigger asChild>
|
|
3051
|
+
<button
|
|
3052
|
+
type="button"
|
|
3053
|
+
aria-label={"Motion" /* i18n-ignore */}
|
|
3054
|
+
aria-disabled={motionDisabled || undefined}
|
|
3055
|
+
aria-pressed={motionOpen || undefined}
|
|
3056
|
+
tabIndex={motionDisabled ? -1 : undefined}
|
|
3057
|
+
onClick={(event) => {
|
|
3058
|
+
if (motionDisabled) {
|
|
3059
|
+
event.preventDefault();
|
|
3060
|
+
return;
|
|
3061
|
+
}
|
|
3062
|
+
onMotionToggle();
|
|
3063
|
+
}}
|
|
3064
|
+
className={cn(
|
|
3065
|
+
"group flex w-12 cursor-pointer flex-col items-center justify-start gap-1 rounded-none !text-[10px] font-[450] leading-none text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-1 focus-visible:ring-[var(--design-editor-accent-color)]",
|
|
3066
|
+
motionDisabled &&
|
|
3067
|
+
"cursor-default opacity-35 hover:text-muted-foreground",
|
|
3068
|
+
motionOpen && "text-foreground",
|
|
3069
|
+
)}
|
|
3070
|
+
>
|
|
3071
|
+
<span
|
|
3072
|
+
className={cn(
|
|
3073
|
+
"flex size-8 items-center justify-center rounded-lg transition-colors",
|
|
3074
|
+
motionOpen
|
|
3075
|
+
? "bg-[var(--design-editor-selection-color)] text-[var(--design-editor-accent-color)]"
|
|
3076
|
+
: "text-muted-foreground group-hover:bg-[var(--design-editor-layer-hover-color)] group-hover:text-foreground",
|
|
3077
|
+
motionDisabled &&
|
|
3078
|
+
"group-hover:bg-transparent group-hover:text-muted-foreground",
|
|
3079
|
+
)}
|
|
3080
|
+
>
|
|
3081
|
+
<IconChevronUp className="size-[15px]" />
|
|
3082
|
+
</span>
|
|
3083
|
+
<span className="max-w-full truncate leading-none">
|
|
3084
|
+
{"Motion" /* i18n-ignore */}
|
|
3085
|
+
</span>
|
|
3086
|
+
</button>
|
|
3087
|
+
</TooltipTrigger>
|
|
3088
|
+
<TooltipContent side="right">
|
|
3089
|
+
{"Motion" /* i18n-ignore */}
|
|
3090
|
+
</TooltipContent>
|
|
3091
|
+
</Tooltip>
|
|
3092
|
+
</div>
|
|
3093
|
+
) : null}
|
|
2824
3094
|
</nav>
|
|
2825
3095
|
);
|
|
2826
3096
|
}
|
|
@@ -3634,6 +3904,25 @@ export default function DesignEditor() {
|
|
|
3634
3904
|
const { id } = useParams<{ id: string }>();
|
|
3635
3905
|
const navigate = useNavigate();
|
|
3636
3906
|
const location = useLocation();
|
|
3907
|
+
const initialEditorUrlRef = useRef<{
|
|
3908
|
+
designId: string | undefined;
|
|
3909
|
+
searchParams: URLSearchParams;
|
|
3910
|
+
} | null>(null);
|
|
3911
|
+
if (
|
|
3912
|
+
!initialEditorUrlRef.current ||
|
|
3913
|
+
initialEditorUrlRef.current.designId !== id
|
|
3914
|
+
) {
|
|
3915
|
+
initialEditorUrlRef.current = {
|
|
3916
|
+
designId: id,
|
|
3917
|
+
searchParams: new URLSearchParams(location.search),
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
const initialSearchParams = initialEditorUrlRef.current.searchParams;
|
|
3921
|
+
const initialRouteScreenTarget =
|
|
3922
|
+
initialSearchParams.get("screen") ??
|
|
3923
|
+
initialSearchParams.get("fileId") ??
|
|
3924
|
+
initialSearchParams.get("filename");
|
|
3925
|
+
const initialRouteSelectionId = initialSearchParams.get("selection") || null;
|
|
3637
3926
|
const searchParams = useMemo(
|
|
3638
3927
|
() => new URLSearchParams(location.search),
|
|
3639
3928
|
[location.search],
|
|
@@ -3668,7 +3957,9 @@ export default function DesignEditor() {
|
|
|
3668
3957
|
const [titleEditing, setTitleEditing] = useState(false);
|
|
3669
3958
|
const [titleDraft, setTitleDraft] = useState("");
|
|
3670
3959
|
const [screenZoom, setScreenZoom] = useState(FOCUSED_SCREEN_ZOOM);
|
|
3671
|
-
const [
|
|
3960
|
+
const [explicitOverviewCanvasZoom, setExplicitOverviewCanvasZoom] = useState<
|
|
3961
|
+
number | null
|
|
3962
|
+
>(null);
|
|
3672
3963
|
const [deviceFrame, setDeviceFrame] = useState<DeviceFrameType>("none");
|
|
3673
3964
|
const [viewMode, setViewMode] = useState<"single" | "overview">("overview");
|
|
3674
3965
|
const viewModeRef = useRef<"single" | "overview">("overview");
|
|
@@ -3696,6 +3987,8 @@ export default function DesignEditor() {
|
|
|
3696
3987
|
useState<InspectorTab>("design");
|
|
3697
3988
|
const [activeLeftPanel, setActiveLeftPanel] =
|
|
3698
3989
|
useState<DesignLeftPanel>("file");
|
|
3990
|
+
const initialSearchCommandAppliedForIdRef = useRef<string | null>(null);
|
|
3991
|
+
const initialUrlSelectionHydratedForIdRef = useRef<string | null>(null);
|
|
3699
3992
|
const [leftSidebarWidth, setLeftSidebarWidth] = useState(256);
|
|
3700
3993
|
const [rightSidebarWidth, setRightSidebarWidth] = useState(256);
|
|
3701
3994
|
const [layersSearchQuery, setLayersSearchQuery] = useState("");
|
|
@@ -3703,6 +3996,11 @@ export default function DesignEditor() {
|
|
|
3703
3996
|
const [selectedLayerIdsState, setSelectedLayerIdsState] = useState<string[]>(
|
|
3704
3997
|
[],
|
|
3705
3998
|
);
|
|
3999
|
+
const selectedLayerTargetsRef = useRef<SelectedLayerTarget[]>([]);
|
|
4000
|
+
const effectiveCodeLayerStateRef = useRef<EffectiveCodeLayerState>({
|
|
4001
|
+
lockedIds: new Set(),
|
|
4002
|
+
hiddenIds: new Set(),
|
|
4003
|
+
});
|
|
3706
4004
|
const [overviewSelectedScreenIds, setOverviewSelectedScreenIds] = useState<
|
|
3707
4005
|
string[]
|
|
3708
4006
|
>([]);
|
|
@@ -3853,10 +4151,22 @@ export default function DesignEditor() {
|
|
|
3853
4151
|
motionDockUnmountTimerRef.current = window.setTimeout(() => {
|
|
3854
4152
|
setMotionDockMounted(false);
|
|
3855
4153
|
motionDockUnmountTimerRef.current = null;
|
|
3856
|
-
},
|
|
4154
|
+
}, MOTION_DOCK_EXIT_FALLBACK_MS);
|
|
3857
4155
|
},
|
|
3858
4156
|
[clearMotionDockUnmountTimer],
|
|
3859
4157
|
);
|
|
4158
|
+
const handleMotionDockExitComplete = useCallback(() => {
|
|
4159
|
+
if (motionDockOpen) return;
|
|
4160
|
+
clearMotionDockUnmountTimer();
|
|
4161
|
+
if (typeof window === "undefined") {
|
|
4162
|
+
setMotionDockMounted(false);
|
|
4163
|
+
return;
|
|
4164
|
+
}
|
|
4165
|
+
motionDockUnmountTimerRef.current = window.setTimeout(() => {
|
|
4166
|
+
setMotionDockMounted(false);
|
|
4167
|
+
motionDockUnmountTimerRef.current = null;
|
|
4168
|
+
}, MOTION_DOCK_EXIT_SETTLE_MS);
|
|
4169
|
+
}, [clearMotionDockUnmountTimer, motionDockOpen]);
|
|
3860
4170
|
useEffect(
|
|
3861
4171
|
() => () => clearMotionDockUnmountTimer(),
|
|
3862
4172
|
[clearMotionDockUnmountTimer],
|
|
@@ -4123,9 +4433,6 @@ export default function DesignEditor() {
|
|
|
4123
4433
|
const designSelectionOwnerIdRef = useRef(`${TAB_ID}:${generateTabId()}`);
|
|
4124
4434
|
const frameGeometrySaveTimerRef = useRef<number | null>(null);
|
|
4125
4435
|
const [tweakSaveActive, setTweakSaveActive] = useState(false);
|
|
4126
|
-
// Dismissible localhost-source banner (reset per session).
|
|
4127
|
-
const [localSourceBannerDismissed, setLocalSourceBannerDismissed] =
|
|
4128
|
-
useState(false);
|
|
4129
4436
|
// Localhost write-consent dialog state. When the agent wants to write a local
|
|
4130
4437
|
// file and no valid grant exists for the active connection, we show the dialog
|
|
4131
4438
|
// with a pending payload; the user clicks "Allow writes" to mint a grant.
|
|
@@ -4318,7 +4625,9 @@ export default function DesignEditor() {
|
|
|
4318
4625
|
onComplete: handleGenerationComplete,
|
|
4319
4626
|
onStale: markGenerationStale,
|
|
4320
4627
|
shouldAdoptRunningTab: () =>
|
|
4321
|
-
Boolean(id) &&
|
|
4628
|
+
Boolean(id) &&
|
|
4629
|
+
!generationOutputReadyRef.current &&
|
|
4630
|
+
hasFreshPendingGeneration(id),
|
|
4322
4631
|
onAdoptRunningTab: (tabId) => {
|
|
4323
4632
|
generationRunConfirmedRef.current = true;
|
|
4324
4633
|
setGenerationChatTabId(tabId);
|
|
@@ -4371,8 +4680,9 @@ export default function DesignEditor() {
|
|
|
4371
4680
|
pendingQuestions && pendingQuestions.length > 0,
|
|
4372
4681
|
);
|
|
4373
4682
|
|
|
4374
|
-
const { session } = useSession();
|
|
4683
|
+
const { session, isLoading: sessionLoading } = useSession();
|
|
4375
4684
|
const isSignedIn = Boolean(session?.email);
|
|
4685
|
+
const sessionResolved = !sessionLoading;
|
|
4376
4686
|
|
|
4377
4687
|
useEffect(() => {
|
|
4378
4688
|
return () => clearGenerationCompleteTimer();
|
|
@@ -4410,12 +4720,11 @@ export default function DesignEditor() {
|
|
|
4410
4720
|
: undefined,
|
|
4411
4721
|
[session?.email, session?.name],
|
|
4412
4722
|
);
|
|
4723
|
+
const signInToSaveHref = buildSignInHrefForDesignIntent("save");
|
|
4724
|
+
const signInToShareHref = buildSignInHrefForDesignIntent("share");
|
|
4413
4725
|
const handleSignInToSave = useCallback(() => {
|
|
4414
4726
|
window.location.href = buildSignInHrefForDesignIntent("save");
|
|
4415
4727
|
}, []);
|
|
4416
|
-
const handleSignInToShare = useCallback(() => {
|
|
4417
|
-
window.location.href = buildSignInHrefForDesignIntent("share");
|
|
4418
|
-
}, []);
|
|
4419
4728
|
|
|
4420
4729
|
// Data fetching
|
|
4421
4730
|
useEffect(() => {
|
|
@@ -4462,6 +4771,7 @@ export default function DesignEditor() {
|
|
|
4462
4771
|
const canShareDesign =
|
|
4463
4772
|
designAccessRole === "owner" || designAccessRole === "admin";
|
|
4464
4773
|
const canEditDesign = canShareDesign || designAccessRole === "editor";
|
|
4774
|
+
const canRenderAuthenticatedShare = isSignedIn || canEditDesign;
|
|
4465
4775
|
const canEditDesignRef = useRef(canEditDesign);
|
|
4466
4776
|
const pendingLocalFileContentsRef = useRef<
|
|
4467
4777
|
Map<
|
|
@@ -5009,6 +5319,24 @@ export default function DesignEditor() {
|
|
|
5009
5319
|
return pending ? { ...file, content: pending.content } : file;
|
|
5010
5320
|
});
|
|
5011
5321
|
}, [pendingLocalFileContentsSnapshot, serverFiles]);
|
|
5322
|
+
const [liveScreenSnapshotsById, setLiveScreenSnapshotsById] = useState<
|
|
5323
|
+
Record<string, LiveScreenSnapshot>
|
|
5324
|
+
>({});
|
|
5325
|
+
useEffect(() => {
|
|
5326
|
+
const liveFileIds = new Set(serverFiles.map((file) => file.id));
|
|
5327
|
+
setLiveScreenSnapshotsById((current) => {
|
|
5328
|
+
let changed = false;
|
|
5329
|
+
const next: Record<string, LiveScreenSnapshot> = {};
|
|
5330
|
+
Object.entries(current).forEach(([fileId, snapshot]) => {
|
|
5331
|
+
if (!liveFileIds.has(fileId)) {
|
|
5332
|
+
changed = true;
|
|
5333
|
+
return;
|
|
5334
|
+
}
|
|
5335
|
+
next[fileId] = snapshot;
|
|
5336
|
+
});
|
|
5337
|
+
return changed ? next : current;
|
|
5338
|
+
});
|
|
5339
|
+
}, [serverFiles]);
|
|
5012
5340
|
const designDataJson = useMemo(
|
|
5013
5341
|
() => parseDesignDataJson(design?.data),
|
|
5014
5342
|
[design?.data],
|
|
@@ -5124,6 +5452,7 @@ export default function DesignEditor() {
|
|
|
5124
5452
|
height: numberValue("height"),
|
|
5125
5453
|
url: stringValue("url"),
|
|
5126
5454
|
previewUrl: stringValue("previewUrl"),
|
|
5455
|
+
bridgeUrl: stringValue("bridgeUrl"),
|
|
5127
5456
|
// Breakpoint preview widths (§6.4). When non-empty, MultiScreenCanvas
|
|
5128
5457
|
// renders one iframe per width to the right of the primary frame.
|
|
5129
5458
|
breakpointWidths: bpWidths,
|
|
@@ -5146,28 +5475,19 @@ export default function DesignEditor() {
|
|
|
5146
5475
|
return typeof boardFile?.content === "string" ? boardFile.content : "";
|
|
5147
5476
|
}, [boardFileId, files]);
|
|
5148
5477
|
|
|
5149
|
-
// Logical canvas-space bounding box of the board iframe.
|
|
5150
|
-
//
|
|
5151
|
-
//
|
|
5478
|
+
// Logical canvas-space bounding box of the board iframe. The board is an
|
|
5479
|
+
// invisible editing layer behind screen frames, not a finite artboard, so keep
|
|
5480
|
+
// it at the canvas-safe maximum instead of clipping it to the screen union.
|
|
5152
5481
|
const boardFrameGeometry = useMemo((): FrameGeometry | undefined => {
|
|
5153
5482
|
if (!boardFileId) return undefined;
|
|
5154
|
-
const
|
|
5155
|
-
if (entries.length === 0) return { x: 0, y: 0, width: 1920, height: 1080 };
|
|
5156
|
-
let maxRight = 0;
|
|
5157
|
-
let maxBottom = 0;
|
|
5158
|
-
for (const geo of entries) {
|
|
5159
|
-
const right = (geo.x ?? 0) + (geo.width ?? 0);
|
|
5160
|
-
const bottom = (geo.y ?? 0) + (geo.height ?? 0);
|
|
5161
|
-
if (right > maxRight) maxRight = right;
|
|
5162
|
-
if (bottom > maxBottom) maxBottom = bottom;
|
|
5163
|
-
}
|
|
5483
|
+
const origin = -BOARD_SURFACE_SIZE / 2;
|
|
5164
5484
|
return {
|
|
5165
|
-
x:
|
|
5166
|
-
y:
|
|
5167
|
-
width:
|
|
5168
|
-
height:
|
|
5485
|
+
x: origin,
|
|
5486
|
+
y: origin,
|
|
5487
|
+
width: BOARD_SURFACE_SIZE,
|
|
5488
|
+
height: BOARD_SURFACE_SIZE,
|
|
5169
5489
|
};
|
|
5170
|
-
}, [boardFileId
|
|
5490
|
+
}, [boardFileId]);
|
|
5171
5491
|
|
|
5172
5492
|
const queueFrameGeometrySave = useCallback(
|
|
5173
5493
|
(geometryById: CanvasFrameGeometryById) => {
|
|
@@ -5482,12 +5802,6 @@ export default function DesignEditor() {
|
|
|
5482
5802
|
setReviewAuditLoading(false);
|
|
5483
5803
|
}, [activeFile?.id, reviewFileId]);
|
|
5484
5804
|
|
|
5485
|
-
const initialGenerationReadOnly = shouldLockInspectorForInitialGeneration({
|
|
5486
|
-
fileCount: files.length,
|
|
5487
|
-
generating,
|
|
5488
|
-
pendingGenerationActive,
|
|
5489
|
-
});
|
|
5490
|
-
|
|
5491
5805
|
const selectedScreenIds = useMemo(
|
|
5492
5806
|
() =>
|
|
5493
5807
|
getSelectedScreenIdsForEditorState({
|
|
@@ -5506,6 +5820,10 @@ export default function DesignEditor() {
|
|
|
5506
5820
|
: undefined,
|
|
5507
5821
|
[activeOverviewScreenId, overviewScreens],
|
|
5508
5822
|
);
|
|
5823
|
+
const activeScreenBridgeUrl = activeOverviewScreen?.bridgeUrl;
|
|
5824
|
+
const activeScreenExternalSnapshotHtml = activeFile?.id
|
|
5825
|
+
? liveScreenSnapshotsById[activeFile.id]?.html
|
|
5826
|
+
: undefined;
|
|
5509
5827
|
const activeOverviewSourceWidth =
|
|
5510
5828
|
deviceFrame === "none"
|
|
5511
5829
|
? activeOverviewScreen?.width
|
|
@@ -5523,6 +5841,9 @@ export default function DesignEditor() {
|
|
|
5523
5841
|
overviewZoomScaleRef.current = overviewZoomScale;
|
|
5524
5842
|
}, [overviewZoomScale]);
|
|
5525
5843
|
|
|
5844
|
+
const overviewCanvasZoom =
|
|
5845
|
+
explicitOverviewCanvasZoom ??
|
|
5846
|
+
getDefaultOverviewCanvasZoom(overviewZoomScale);
|
|
5526
5847
|
const overviewZoom = getOverviewDisplayZoom(
|
|
5527
5848
|
overviewCanvasZoom,
|
|
5528
5849
|
overviewZoomScale,
|
|
@@ -5531,10 +5852,12 @@ export default function DesignEditor() {
|
|
|
5531
5852
|
const setZoomForView = useCallback(
|
|
5532
5853
|
(targetView: "single" | "overview", update: SetStateAction<number>) => {
|
|
5533
5854
|
if (targetView === "overview") {
|
|
5534
|
-
|
|
5855
|
+
setExplicitOverviewCanvasZoom((currentCanvasZoom) => {
|
|
5535
5856
|
const scale = overviewZoomScaleRef.current;
|
|
5857
|
+
const resolvedCanvasZoom =
|
|
5858
|
+
currentCanvasZoom ?? getDefaultOverviewCanvasZoom(scale);
|
|
5536
5859
|
const currentDisplayZoom = getOverviewDisplayZoom(
|
|
5537
|
-
|
|
5860
|
+
resolvedCanvasZoom,
|
|
5538
5861
|
scale,
|
|
5539
5862
|
);
|
|
5540
5863
|
const nextDisplayZoom = resolveZoomUpdate(update, currentDisplayZoom);
|
|
@@ -5561,6 +5884,7 @@ export default function DesignEditor() {
|
|
|
5561
5884
|
const applyDesignEditorCommand = useCallback(
|
|
5562
5885
|
(command: DesignEditorCommand | Record<string, unknown>) => {
|
|
5563
5886
|
if (!id || command.designId !== id) return true;
|
|
5887
|
+
const commandRecord = command as Record<string, unknown>;
|
|
5564
5888
|
const editorView =
|
|
5565
5889
|
command.editorView === "overview" || command.editorView === "single"
|
|
5566
5890
|
? command.editorView
|
|
@@ -5577,6 +5901,14 @@ export default function DesignEditor() {
|
|
|
5577
5901
|
: typeof command.screen === "string"
|
|
5578
5902
|
? command.screen
|
|
5579
5903
|
: null;
|
|
5904
|
+
const selectionId =
|
|
5905
|
+
typeof command.selection === "string"
|
|
5906
|
+
? command.selection
|
|
5907
|
+
: typeof commandRecord.nodeId === "string"
|
|
5908
|
+
? commandRecord.nodeId
|
|
5909
|
+
: typeof commandRecord.layerId === "string"
|
|
5910
|
+
? commandRecord.layerId
|
|
5911
|
+
: null;
|
|
5580
5912
|
const targetFile = findDesignFileByScreenTarget(files, target);
|
|
5581
5913
|
// A navigate command can name a screen the agent just created that the
|
|
5582
5914
|
// get-design query hasn't refetched yet. Treat any unresolved named target
|
|
@@ -5624,6 +5956,9 @@ export default function DesignEditor() {
|
|
|
5624
5956
|
if (targetFile) {
|
|
5625
5957
|
setActiveFileId(targetFile.id);
|
|
5626
5958
|
}
|
|
5959
|
+
if (selectionId) {
|
|
5960
|
+
setSelectedLayerIdsState([selectionId]);
|
|
5961
|
+
}
|
|
5627
5962
|
|
|
5628
5963
|
const commandZoom =
|
|
5629
5964
|
typeof command.zoom === "number" && Number.isFinite(command.zoom)
|
|
@@ -5635,12 +5970,12 @@ export default function DesignEditor() {
|
|
|
5635
5970
|
|
|
5636
5971
|
if (editorView === "overview") {
|
|
5637
5972
|
viewModeRef.current = "overview";
|
|
5638
|
-
setSelectedElement(null);
|
|
5973
|
+
if (!selectionId) setSelectedElement(null);
|
|
5639
5974
|
applyCommandTool("move");
|
|
5640
5975
|
setViewMode("overview");
|
|
5641
5976
|
} else if (editorView === "single") {
|
|
5642
5977
|
viewModeRef.current = "single";
|
|
5643
|
-
setSelectedElement(null);
|
|
5978
|
+
if (!selectionId) setSelectedElement(null);
|
|
5644
5979
|
applyCommandTool("move");
|
|
5645
5980
|
if (commandZoom === null) {
|
|
5646
5981
|
setScreenZoom(FOCUSED_SCREEN_ZOOM);
|
|
@@ -5657,10 +5992,20 @@ export default function DesignEditor() {
|
|
|
5657
5992
|
|
|
5658
5993
|
useEffect(() => {
|
|
5659
5994
|
if (!id) return;
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5995
|
+
if (initialSearchCommandAppliedForIdRef.current === id) return;
|
|
5996
|
+
const command = designEditorCommandFromSearchParams(
|
|
5997
|
+
id,
|
|
5998
|
+
initialSearchParams,
|
|
5999
|
+
);
|
|
6000
|
+
if (!command) {
|
|
6001
|
+
initialSearchCommandAppliedForIdRef.current = id;
|
|
6002
|
+
return;
|
|
6003
|
+
}
|
|
6004
|
+
const applied = applyDesignEditorCommand(command);
|
|
6005
|
+
if (applied) {
|
|
6006
|
+
initialSearchCommandAppliedForIdRef.current = id;
|
|
6007
|
+
}
|
|
6008
|
+
}, [applyDesignEditorCommand, id, initialSearchParams]);
|
|
5664
6009
|
|
|
5665
6010
|
useEffect(() => {
|
|
5666
6011
|
if (!id) return;
|
|
@@ -6590,9 +6935,20 @@ export default function DesignEditor() {
|
|
|
6590
6935
|
: (activeFile?.content ?? ""));
|
|
6591
6936
|
const activeContent =
|
|
6592
6937
|
typeof activeContentSource === "string" ? activeContentSource : "";
|
|
6938
|
+
const initialGenerationChromeLimited =
|
|
6939
|
+
shouldLimitEditorChromeUntilContentReady({
|
|
6940
|
+
fileCount: files.length,
|
|
6941
|
+
generating,
|
|
6942
|
+
hasActiveCanvasContent: Boolean(activeFile && activeContent.trim()),
|
|
6943
|
+
pendingGenerationActive,
|
|
6944
|
+
});
|
|
6593
6945
|
useLayoutEffect(() => {
|
|
6594
6946
|
latestActiveContentRef.current = activeContent;
|
|
6595
6947
|
}, [activeContent]);
|
|
6948
|
+
useEffect(() => {
|
|
6949
|
+
if (!initialGenerationChromeLimited || activeLeftPanel === "agent") return;
|
|
6950
|
+
setActiveLeftPanel("agent");
|
|
6951
|
+
}, [activeLeftPanel, initialGenerationChromeLimited]);
|
|
6596
6952
|
const fileContentById = useMemo(() => {
|
|
6597
6953
|
const map = new Map<string, string>();
|
|
6598
6954
|
for (const file of files) {
|
|
@@ -6605,22 +6961,58 @@ export default function DesignEditor() {
|
|
|
6605
6961
|
getFreshScreenContent({
|
|
6606
6962
|
screenId,
|
|
6607
6963
|
activeFileId: activeFile?.id,
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
latestContent: latestActiveContentRef.current,
|
|
6611
|
-
lastLocalContent: lastLocalContentRef.current,
|
|
6612
|
-
}),
|
|
6964
|
+
freshActiveContentFileId: activeFile?.id,
|
|
6965
|
+
freshActiveContent: activeContent,
|
|
6613
6966
|
fileContentById,
|
|
6614
6967
|
}),
|
|
6615
6968
|
[activeContent, activeFile?.id, fileContentById],
|
|
6616
6969
|
);
|
|
6970
|
+
const getProjectionContentForScreen = useCallback(
|
|
6971
|
+
(screenId: string) =>
|
|
6972
|
+
liveScreenSnapshotsById[screenId]?.html ?? getScreenContent(screenId),
|
|
6973
|
+
[getScreenContent, liveScreenSnapshotsById],
|
|
6974
|
+
);
|
|
6975
|
+
const handleScreenExternalContentSnapshot = useCallback(
|
|
6976
|
+
(screenId: string, snapshot: LiveScreenSnapshot) => {
|
|
6977
|
+
setLiveScreenSnapshotsById((current) => {
|
|
6978
|
+
const existing = current[screenId];
|
|
6979
|
+
if (
|
|
6980
|
+
existing?.url === snapshot.url &&
|
|
6981
|
+
existing.html === snapshot.html &&
|
|
6982
|
+
existing.status === snapshot.status &&
|
|
6983
|
+
existing.contentType === snapshot.contentType
|
|
6984
|
+
) {
|
|
6985
|
+
return current;
|
|
6986
|
+
}
|
|
6987
|
+
return { ...current, [screenId]: snapshot };
|
|
6988
|
+
});
|
|
6989
|
+
},
|
|
6990
|
+
[],
|
|
6991
|
+
);
|
|
6992
|
+
const updateLiveScreenSnapshotContent = useCallback(
|
|
6993
|
+
(screenId: string, html: string) => {
|
|
6994
|
+
const existing = liveScreenSnapshotsById[screenId];
|
|
6995
|
+
if (!existing) return false;
|
|
6996
|
+
if (existing.html === html) return true;
|
|
6997
|
+
setLiveScreenSnapshotsById((current) => ({
|
|
6998
|
+
...current,
|
|
6999
|
+
[screenId]: { ...existing, html },
|
|
7000
|
+
}));
|
|
7001
|
+
return true;
|
|
7002
|
+
},
|
|
7003
|
+
[liveScreenSnapshotsById],
|
|
7004
|
+
);
|
|
7005
|
+
const activeProjectionContent =
|
|
7006
|
+
activeFile?.id !== undefined
|
|
7007
|
+
? getProjectionContentForScreen(activeFile.id)
|
|
7008
|
+
: activeContent;
|
|
6617
7009
|
const pageStyles = useMemo(
|
|
6618
7010
|
() => getBodyInlineStyles(activeContent),
|
|
6619
7011
|
[activeContent],
|
|
6620
7012
|
);
|
|
6621
7013
|
const activeCodeLayerProjection = useMemo(
|
|
6622
|
-
() => buildCodeLayerProjection(
|
|
6623
|
-
[
|
|
7014
|
+
() => buildCodeLayerProjection(activeProjectionContent),
|
|
7015
|
+
[activeProjectionContent],
|
|
6624
7016
|
);
|
|
6625
7017
|
const activeMotionTimeline = motionTimelineResult?.timelines?.[0] ?? null;
|
|
6626
7018
|
const activeMotionHydrationFingerprint = activeFile?.id
|
|
@@ -6726,9 +7118,13 @@ export default function DesignEditor() {
|
|
|
6726
7118
|
const designSourceType = useMemo(
|
|
6727
7119
|
() =>
|
|
6728
7120
|
normalizeDesignSourceType(designDataJson.sourceType as unknown) ??
|
|
7121
|
+
normalizeDesignSourceType(designDataJson.sourceMode as unknown) ??
|
|
6729
7122
|
"inline",
|
|
6730
|
-
[designDataJson.sourceType],
|
|
7123
|
+
[designDataJson.sourceMode, designDataJson.sourceType],
|
|
6731
7124
|
);
|
|
7125
|
+
const activeCanvasSourceType =
|
|
7126
|
+
normalizeDesignSourceType(activeOverviewScreen?.sourceType) ??
|
|
7127
|
+
designSourceType;
|
|
6732
7128
|
const sourceCapabilities = useMemo(() => {
|
|
6733
7129
|
const caps = resolveSourceCapabilities(designSourceType);
|
|
6734
7130
|
return DESIGN_CAPABILITY_NAMES.filter((name) => hasCapability(caps, name));
|
|
@@ -6771,6 +7167,11 @@ export default function DesignEditor() {
|
|
|
6771
7167
|
? bridgeSourceIdForCodeLayerNode(selectedCodeLayerNode)
|
|
6772
7168
|
: undefined;
|
|
6773
7169
|
}, [selectedCodeLayerNode]);
|
|
7170
|
+
const selectedElementAlreadyComponent = useMemo(() => {
|
|
7171
|
+
if (!selectedElement) return false;
|
|
7172
|
+
if (selectedElement.componentName?.trim()) return true;
|
|
7173
|
+
return codeLayerNodeLooksLikeComponent(selectedCodeLayerNode);
|
|
7174
|
+
}, [selectedCodeLayerNode, selectedElement]);
|
|
6774
7175
|
|
|
6775
7176
|
useEffect(() => {
|
|
6776
7177
|
setShaderFillPreview(null);
|
|
@@ -6791,11 +7192,8 @@ export default function DesignEditor() {
|
|
|
6791
7192
|
// Outer HTML of the selection — backs the inline/Alpine "Inspect code" view.
|
|
6792
7193
|
const selectedElementOuterHtml = useMemo(() => {
|
|
6793
7194
|
if (!selectedElement?.selector) return null;
|
|
6794
|
-
return (
|
|
6795
|
-
|
|
6796
|
-
getElementOuterHtml(activeContent, selectedElement.selector)
|
|
6797
|
-
);
|
|
6798
|
-
}, [activeContent, selectedElement?.selector, selectedElement?.htmlContent]);
|
|
7195
|
+
return getElementOuterHtml(activeContent, selectedElement.selector);
|
|
7196
|
+
}, [activeContent, selectedElement?.selector]);
|
|
6799
7197
|
|
|
6800
7198
|
// §6.3 — the motion-dock target: the selected element's literal
|
|
6801
7199
|
// `data-agent-native-node-id` (the value the motion compiler + preview bridge
|
|
@@ -6862,7 +7260,13 @@ export default function DesignEditor() {
|
|
|
6862
7260
|
// resolveNodeToFile bridge op, which is wired through open-component-source
|
|
6863
7261
|
// when an external file path is available; until that round-trip is hooked
|
|
6864
7262
|
// up here the popover shows the projected HTML for all source types.
|
|
6865
|
-
return {
|
|
7263
|
+
return {
|
|
7264
|
+
html: selectedElementOuterHtml,
|
|
7265
|
+
tagName: selectedElement.tagName,
|
|
7266
|
+
id: selectedElement.id,
|
|
7267
|
+
classes: selectedElement.classes,
|
|
7268
|
+
sourceLocation: null,
|
|
7269
|
+
};
|
|
6866
7270
|
}, [selectedElement, selectedElementOuterHtml]);
|
|
6867
7271
|
|
|
6868
7272
|
const handleCreateComponent = useCallback(
|
|
@@ -6888,7 +7292,7 @@ export default function DesignEditor() {
|
|
|
6888
7292
|
// Follow-up: ask the Design agent to extract props and replace repeated
|
|
6889
7293
|
// instances with this component. The deterministic annotate above is the
|
|
6890
7294
|
// core; this is an enhancement that runs in the agent chat.
|
|
6891
|
-
|
|
7295
|
+
sendToDesignAgentChat({
|
|
6892
7296
|
message: `Extract props for the "${name}" component and replace repeated instances on this design with it.`,
|
|
6893
7297
|
context: [
|
|
6894
7298
|
`Design id: "${id}".`,
|
|
@@ -6943,18 +7347,22 @@ export default function DesignEditor() {
|
|
|
6943
7347
|
(screenId: string) => {
|
|
6944
7348
|
if (screenId === activeFile?.id) return activeCodeLayerProjection;
|
|
6945
7349
|
if (!fileContentById.has(screenId)) return null;
|
|
6946
|
-
return buildCodeLayerProjection(
|
|
7350
|
+
return buildCodeLayerProjection(getProjectionContentForScreen(screenId));
|
|
6947
7351
|
},
|
|
6948
7352
|
[
|
|
6949
7353
|
activeCodeLayerProjection,
|
|
6950
7354
|
activeFile?.id,
|
|
6951
7355
|
fileContentById,
|
|
6952
|
-
|
|
7356
|
+
getProjectionContentForScreen,
|
|
6953
7357
|
],
|
|
6954
7358
|
);
|
|
6955
7359
|
|
|
6956
7360
|
const replacePreviewContent = useCallback(
|
|
6957
|
-
(
|
|
7361
|
+
(
|
|
7362
|
+
nextContent: string,
|
|
7363
|
+
selector?: string | null,
|
|
7364
|
+
options: { forceFullDocument?: boolean } = {},
|
|
7365
|
+
) => {
|
|
6958
7366
|
const replaceContent = (window as any).__designCanvasReplaceContent;
|
|
6959
7367
|
if (typeof replaceContent !== "function") return false;
|
|
6960
7368
|
return Boolean(
|
|
@@ -6962,7 +7370,9 @@ export default function DesignEditor() {
|
|
|
6962
7370
|
nextContent,
|
|
6963
7371
|
selector ?? selectedCanvasSelector,
|
|
6964
7372
|
selectedCanvasSelectorCandidates,
|
|
6965
|
-
|
|
7373
|
+
{
|
|
7374
|
+
forceFullDocument: options.forceFullDocument === true,
|
|
7375
|
+
},
|
|
6966
7376
|
),
|
|
6967
7377
|
);
|
|
6968
7378
|
},
|
|
@@ -6989,6 +7399,7 @@ export default function DesignEditor() {
|
|
|
6989
7399
|
options: {
|
|
6990
7400
|
refreshPreview?: boolean;
|
|
6991
7401
|
skipPreview?: boolean;
|
|
7402
|
+
forcePreviewFullDocument?: boolean;
|
|
6992
7403
|
immediateSave?: boolean;
|
|
6993
7404
|
persist?: boolean;
|
|
6994
7405
|
recordHistory?: boolean;
|
|
@@ -7077,8 +7488,18 @@ export default function DesignEditor() {
|
|
|
7077
7488
|
? true
|
|
7078
7489
|
: forceRefresh
|
|
7079
7490
|
? false
|
|
7080
|
-
: replacePreviewContent(
|
|
7081
|
-
|
|
7491
|
+
: replacePreviewContent(
|
|
7492
|
+
nextContent,
|
|
7493
|
+
null,
|
|
7494
|
+
options.forcePreviewFullDocument
|
|
7495
|
+
? { forceFullDocument: true }
|
|
7496
|
+
: undefined,
|
|
7497
|
+
);
|
|
7498
|
+
if (
|
|
7499
|
+
forceRefresh ||
|
|
7500
|
+
options.forcePreviewFullDocument ||
|
|
7501
|
+
!replacedPreview
|
|
7502
|
+
) {
|
|
7082
7503
|
setContentRenderRevision((revision) => revision + 1);
|
|
7083
7504
|
}
|
|
7084
7505
|
if (ydoc && isSynced) {
|
|
@@ -7126,6 +7547,7 @@ export default function DesignEditor() {
|
|
|
7126
7547
|
options: {
|
|
7127
7548
|
refreshPreview?: boolean;
|
|
7128
7549
|
skipPreview?: boolean;
|
|
7550
|
+
forcePreviewFullDocument?: boolean;
|
|
7129
7551
|
persist?: boolean;
|
|
7130
7552
|
recordHistory?: boolean;
|
|
7131
7553
|
updatedAt?: string;
|
|
@@ -7225,27 +7647,47 @@ export default function DesignEditor() {
|
|
|
7225
7647
|
motionAutosaveTimerRef.current = window.setTimeout(() => {
|
|
7226
7648
|
motionAutosaveTimerRef.current = null;
|
|
7227
7649
|
if (motionAutosaveRevisionRef.current !== revisionAtSchedule) return;
|
|
7650
|
+
const tracksForSave = motionTracks.map(
|
|
7651
|
+
({ label: _label, ...track }) => track,
|
|
7652
|
+
);
|
|
7653
|
+
const currentContent = getFreshActiveFileContent({
|
|
7654
|
+
activeContent,
|
|
7655
|
+
latestContent: latestActiveContentRef.current,
|
|
7656
|
+
lastLocalContent: lastLocalContentRef.current,
|
|
7657
|
+
});
|
|
7658
|
+
const localMotionCss = compileMotionTimeline({
|
|
7659
|
+
id: motionTimelineId ?? "",
|
|
7660
|
+
designId: id,
|
|
7661
|
+
sourceRef: activeFile.id,
|
|
7662
|
+
filePath: null,
|
|
7663
|
+
tracks: tracksForSave,
|
|
7664
|
+
durationMs: motionDurationMs,
|
|
7665
|
+
defaultEase: "ease",
|
|
7666
|
+
compiledHash: null,
|
|
7667
|
+
createdAt: "",
|
|
7668
|
+
updatedAt: "",
|
|
7669
|
+
}).css;
|
|
7670
|
+
const localPatchedContent = injectManagedMotionCss(
|
|
7671
|
+
currentContent,
|
|
7672
|
+
localMotionCss,
|
|
7673
|
+
);
|
|
7228
7674
|
applyMotionEdit(
|
|
7229
7675
|
{
|
|
7230
7676
|
designId: id,
|
|
7231
7677
|
fileId: activeFile.id,
|
|
7232
7678
|
timelineId: motionTimelineId ?? undefined,
|
|
7233
7679
|
sourceRef: activeFile.id,
|
|
7234
|
-
tracks:
|
|
7680
|
+
tracks: tracksForSave,
|
|
7235
7681
|
durationMs: motionDurationMs,
|
|
7236
|
-
currentContent
|
|
7237
|
-
|
|
7238
|
-
latestContent: latestActiveContentRef.current,
|
|
7239
|
-
lastLocalContent: lastLocalContentRef.current,
|
|
7240
|
-
}),
|
|
7241
|
-
includeContent: true,
|
|
7682
|
+
currentContent,
|
|
7683
|
+
includeContent: false,
|
|
7242
7684
|
},
|
|
7243
7685
|
{
|
|
7244
7686
|
onSuccess: (result) => {
|
|
7245
7687
|
const response = result as {
|
|
7246
7688
|
fileId?: unknown;
|
|
7247
7689
|
timelineId?: unknown;
|
|
7248
|
-
|
|
7690
|
+
updatedAt?: unknown;
|
|
7249
7691
|
};
|
|
7250
7692
|
if (typeof response.timelineId === "string") {
|
|
7251
7693
|
setMotionTimelineId(response.timelineId);
|
|
@@ -7257,10 +7699,14 @@ export default function DesignEditor() {
|
|
|
7257
7699
|
}
|
|
7258
7700
|
if (
|
|
7259
7701
|
typeof response.fileId === "string" &&
|
|
7260
|
-
|
|
7702
|
+
response.fileId === activeFile.id
|
|
7261
7703
|
) {
|
|
7262
|
-
applyFileContentUpdate(response.fileId,
|
|
7263
|
-
refreshPreview:
|
|
7704
|
+
applyFileContentUpdate(response.fileId, localPatchedContent, {
|
|
7705
|
+
refreshPreview: true,
|
|
7706
|
+
persist: false,
|
|
7707
|
+
...(typeof response.updatedAt === "string"
|
|
7708
|
+
? { updatedAt: response.updatedAt }
|
|
7709
|
+
: {}),
|
|
7264
7710
|
});
|
|
7265
7711
|
}
|
|
7266
7712
|
void queryClient.invalidateQueries({
|
|
@@ -7408,6 +7854,7 @@ export default function DesignEditor() {
|
|
|
7408
7854
|
|
|
7409
7855
|
if (targetFile.id === activeFile?.id) {
|
|
7410
7856
|
applyLocalContentUpdate(nextContent, {
|
|
7857
|
+
forcePreviewFullDocument: true,
|
|
7411
7858
|
historyBeforeContent: baseContent,
|
|
7412
7859
|
immediateSave: true,
|
|
7413
7860
|
});
|
|
@@ -7472,7 +7919,7 @@ export default function DesignEditor() {
|
|
|
7472
7919
|
);
|
|
7473
7920
|
|
|
7474
7921
|
const handlePrimitiveCreated = useCallback(
|
|
7475
|
-
(screenId: string, nodeId: string) => {
|
|
7922
|
+
(screenId: string, nodeId: string, options?: { selectFrame?: boolean }) => {
|
|
7476
7923
|
// B2/B4 fix: stay in overview mode after drawing a primitive. The user
|
|
7477
7924
|
// drew a shape on the board — they should remain on the board with the
|
|
7478
7925
|
// new primitive selected, matching Figma behaviour. We activate the
|
|
@@ -7487,7 +7934,9 @@ export default function DesignEditor() {
|
|
|
7487
7934
|
setSelectedElement(null);
|
|
7488
7935
|
setHoveredElement(null);
|
|
7489
7936
|
setSelectedLayerIdsState([nodeId]);
|
|
7490
|
-
setOverviewSelectedScreenIds(
|
|
7937
|
+
setOverviewSelectedScreenIds(
|
|
7938
|
+
options?.selectFrame === false ? [] : [screenId],
|
|
7939
|
+
);
|
|
7491
7940
|
setActiveTool("move");
|
|
7492
7941
|
setMode("edit");
|
|
7493
7942
|
});
|
|
@@ -7504,7 +7953,7 @@ export default function DesignEditor() {
|
|
|
7504
7953
|
scheduleBeginTextEditForScreen(screenId, textNodeId);
|
|
7505
7954
|
}
|
|
7506
7955
|
},
|
|
7507
|
-
[clearPendingOverviewLayerSelectionTimer],
|
|
7956
|
+
[clearPendingOverviewLayerSelectionTimer, scheduleBeginTextEditForScreen],
|
|
7508
7957
|
);
|
|
7509
7958
|
|
|
7510
7959
|
/**
|
|
@@ -7518,22 +7967,14 @@ export default function DesignEditor() {
|
|
|
7518
7967
|
if (!boardFileId || !canEditDesign) return false;
|
|
7519
7968
|
const result = handleCreatePrimitive(boardFileId, primitive);
|
|
7520
7969
|
if (!result) return false;
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
|
|
7524
|
-
// so use the same iframe-targeted retry path as screen primitives.
|
|
7525
|
-
if (primitive.kind === "text") {
|
|
7526
|
-
const textNodeId =
|
|
7527
|
-
pendingTextEditNodeIdRef.current ?? primitive.nodeId ?? null;
|
|
7528
|
-
pendingTextEditNodeIdRef.current = null;
|
|
7529
|
-
if (textNodeId) {
|
|
7530
|
-
scheduleBeginTextEditForScreen(boardFileId, textNodeId);
|
|
7531
|
-
}
|
|
7970
|
+
const nodeId = typeof result === "string" ? result : primitive.nodeId;
|
|
7971
|
+
if (nodeId) {
|
|
7972
|
+
handlePrimitiveCreated(boardFileId, nodeId, { selectFrame: false });
|
|
7532
7973
|
}
|
|
7533
7974
|
|
|
7534
7975
|
return result;
|
|
7535
7976
|
},
|
|
7536
|
-
[boardFileId, canEditDesign, handleCreatePrimitive],
|
|
7977
|
+
[boardFileId, canEditDesign, handleCreatePrimitive, handlePrimitiveCreated],
|
|
7537
7978
|
);
|
|
7538
7979
|
|
|
7539
7980
|
const handleOverviewScreenSelectionChange = useCallback(
|
|
@@ -7571,6 +8012,7 @@ export default function DesignEditor() {
|
|
|
7571
8012
|
|
|
7572
8013
|
const handleMoveTool = useCallback(() => {
|
|
7573
8014
|
if (!canEditDesign) return;
|
|
8015
|
+
blurActiveDesignEditableTarget();
|
|
7574
8016
|
setActiveTool("move");
|
|
7575
8017
|
setMode("edit");
|
|
7576
8018
|
setDrawMode(false);
|
|
@@ -7579,6 +8021,7 @@ export default function DesignEditor() {
|
|
|
7579
8021
|
|
|
7580
8022
|
const handleFrameTool = useCallback(() => {
|
|
7581
8023
|
if (!canEditDesign) return;
|
|
8024
|
+
blurActiveDesignEditableTarget();
|
|
7582
8025
|
flushSync(() => {
|
|
7583
8026
|
setActiveTool("frame");
|
|
7584
8027
|
setMode("edit");
|
|
@@ -7592,6 +8035,7 @@ export default function DesignEditor() {
|
|
|
7592
8035
|
|
|
7593
8036
|
const handleTextTool = useCallback(() => {
|
|
7594
8037
|
if (!canEditDesign) return;
|
|
8038
|
+
blurActiveDesignEditableTarget();
|
|
7595
8039
|
flushSync(() => {
|
|
7596
8040
|
setActiveTool("text");
|
|
7597
8041
|
viewModeRef.current = "overview";
|
|
@@ -7606,6 +8050,7 @@ export default function DesignEditor() {
|
|
|
7606
8050
|
const handleShapeTool = useCallback(
|
|
7607
8051
|
(tool: ShapeTool) => {
|
|
7608
8052
|
if (!canEditDesign) return;
|
|
8053
|
+
blurActiveDesignEditableTarget();
|
|
7609
8054
|
flushSync(() => {
|
|
7610
8055
|
setActiveTool(tool);
|
|
7611
8056
|
viewModeRef.current = "overview";
|
|
@@ -7625,6 +8070,7 @@ export default function DesignEditor() {
|
|
|
7625
8070
|
|
|
7626
8071
|
const handlePenTool = useCallback(() => {
|
|
7627
8072
|
if (!canEditDesign) return;
|
|
8073
|
+
blurActiveDesignEditableTarget();
|
|
7628
8074
|
flushSync(() => {
|
|
7629
8075
|
setActiveTool("pen");
|
|
7630
8076
|
viewModeRef.current = "overview";
|
|
@@ -7638,6 +8084,7 @@ export default function DesignEditor() {
|
|
|
7638
8084
|
|
|
7639
8085
|
const handleHandTool = useCallback(() => {
|
|
7640
8086
|
if (!canEditDesign) return;
|
|
8087
|
+
blurActiveDesignEditableTarget();
|
|
7641
8088
|
setActiveTool("hand");
|
|
7642
8089
|
setMode("edit");
|
|
7643
8090
|
setDrawMode(false);
|
|
@@ -7648,6 +8095,7 @@ export default function DesignEditor() {
|
|
|
7648
8095
|
|
|
7649
8096
|
const handleScaleTool = useCallback(() => {
|
|
7650
8097
|
if (!activeFile || !canEditDesign) return;
|
|
8098
|
+
blurActiveDesignEditableTarget();
|
|
7651
8099
|
setActiveTool("scale");
|
|
7652
8100
|
setMode("edit");
|
|
7653
8101
|
setDrawMode(false);
|
|
@@ -7754,7 +8202,7 @@ export default function DesignEditor() {
|
|
|
7754
8202
|
"For tiny source changes, prefer `edit-design`, but make sure the tweak definitions are saved so the Tweaks panel updates.",
|
|
7755
8203
|
].join("\n");
|
|
7756
8204
|
|
|
7757
|
-
|
|
8205
|
+
sendToDesignAgentChat({
|
|
7758
8206
|
message: `Add tweak controls to "${design.title}": ${trimmed}`,
|
|
7759
8207
|
context,
|
|
7760
8208
|
submit: true,
|
|
@@ -8018,7 +8466,7 @@ export default function DesignEditor() {
|
|
|
8018
8466
|
);
|
|
8019
8467
|
|
|
8020
8468
|
const handleScreenElementSelect = useCallback(
|
|
8021
|
-
(screenId: string, info: ElementInfo) => {
|
|
8469
|
+
(screenId: string, info: ElementInfo, intent?: ElementSelectionIntent) => {
|
|
8022
8470
|
const pendingLayerId = pendingOverviewLayerSelectionRef.current;
|
|
8023
8471
|
const pendingScreenId = pendingOverviewScreenSelectionRef.current;
|
|
8024
8472
|
const createdSelection = createdOverviewLayerSelection;
|
|
@@ -8051,11 +8499,33 @@ export default function DesignEditor() {
|
|
|
8051
8499
|
) {
|
|
8052
8500
|
return;
|
|
8053
8501
|
}
|
|
8502
|
+
const additiveSelection = Boolean(
|
|
8503
|
+
node &&
|
|
8504
|
+
(intent?.additive ||
|
|
8505
|
+
intent?.range ||
|
|
8506
|
+
intent?.shiftKey ||
|
|
8507
|
+
intent?.metaKey ||
|
|
8508
|
+
intent?.ctrlKey),
|
|
8509
|
+
);
|
|
8054
8510
|
setActiveFileId(screenId);
|
|
8055
8511
|
setSelectedElement(canonical);
|
|
8056
8512
|
setHoveredElement(null);
|
|
8057
8513
|
setHoveredElementScreenId(null);
|
|
8058
|
-
|
|
8514
|
+
if (node && additiveSelection) {
|
|
8515
|
+
setSelectedLayerIdsState((current) => {
|
|
8516
|
+
const removeExisting =
|
|
8517
|
+
Boolean(intent?.metaKey || intent?.ctrlKey) &&
|
|
8518
|
+
!intent?.shiftKey &&
|
|
8519
|
+
current.includes(node.id);
|
|
8520
|
+
if (removeExisting) {
|
|
8521
|
+
const next = current.filter((layerId) => layerId !== node.id);
|
|
8522
|
+
return next.length > 0 ? next : [node.id];
|
|
8523
|
+
}
|
|
8524
|
+
return dedupeStringIds([...current, node.id]);
|
|
8525
|
+
});
|
|
8526
|
+
} else {
|
|
8527
|
+
setSelectedLayerIdsState(node ? [node.id] : []);
|
|
8528
|
+
}
|
|
8059
8529
|
if (viewModeRef.current === "overview") {
|
|
8060
8530
|
setOverviewSelectedScreenIds([]);
|
|
8061
8531
|
}
|
|
@@ -8108,10 +8578,10 @@ export default function DesignEditor() {
|
|
|
8108
8578
|
);
|
|
8109
8579
|
|
|
8110
8580
|
const handleElementSelect = useCallback(
|
|
8111
|
-
(info: ElementInfo) => {
|
|
8581
|
+
(info: ElementInfo, intent?: ElementSelectionIntent) => {
|
|
8112
8582
|
const screenId = activeFile?.id ?? activeFileId;
|
|
8113
8583
|
if (screenId) {
|
|
8114
|
-
handleScreenElementSelect(screenId, info);
|
|
8584
|
+
handleScreenElementSelect(screenId, info, intent);
|
|
8115
8585
|
return;
|
|
8116
8586
|
}
|
|
8117
8587
|
setSelectedElement(
|
|
@@ -8296,7 +8766,11 @@ export default function DesignEditor() {
|
|
|
8296
8766
|
// advance lastLocalContentRef.current to resolvedNextContent below, the
|
|
8297
8767
|
// next synchronous call reads the previous call's result and the patches
|
|
8298
8768
|
// compose. Falls back to activeContent when the ref is unset (file switch).
|
|
8769
|
+
const activeLiveSnapshot = activeFile
|
|
8770
|
+
? liveScreenSnapshotsById[activeFile.id]
|
|
8771
|
+
: undefined;
|
|
8299
8772
|
const baseContent =
|
|
8773
|
+
activeLiveSnapshot?.html ??
|
|
8300
8774
|
latestActiveContentRef.current ??
|
|
8301
8775
|
lastLocalContentRef.current ??
|
|
8302
8776
|
activeContent;
|
|
@@ -8493,62 +8967,77 @@ export default function DesignEditor() {
|
|
|
8493
8967
|
);
|
|
8494
8968
|
})
|
|
8495
8969
|
: null;
|
|
8496
|
-
const
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
!
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
const
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
|
|
8970
|
+
const liveSnapshotUpdated = activeLiveSnapshot
|
|
8971
|
+
? updateLiveScreenSnapshotContent(activeFile.id, resolvedNextContent)
|
|
8972
|
+
: false;
|
|
8973
|
+
if (liveSnapshotUpdated) {
|
|
8974
|
+
setPatchProof((prev) =>
|
|
8975
|
+
prev?.id === proofId ? { ...prev, status: "queued" } : prev,
|
|
8976
|
+
);
|
|
8977
|
+
if (!runtimeStyleApplied) {
|
|
8978
|
+
setContentRenderRevision((revision) => revision + 1);
|
|
8979
|
+
}
|
|
8980
|
+
} else {
|
|
8981
|
+
const yjsHistoryAvailable = Boolean(
|
|
8982
|
+
viewModeRef.current !== "overview" &&
|
|
8983
|
+
ydoc &&
|
|
8984
|
+
isSynced &&
|
|
8985
|
+
undoManagerRef.current,
|
|
8986
|
+
);
|
|
8987
|
+
if (
|
|
8988
|
+
!yjsHistoryAvailable &&
|
|
8989
|
+
!suppressContentHistoryRef.current &&
|
|
8990
|
+
baseContent !== resolvedNextContent
|
|
8991
|
+
) {
|
|
8992
|
+
const change = {
|
|
8993
|
+
fileId: activeFile.id,
|
|
8994
|
+
before: baseContent,
|
|
8995
|
+
after: resolvedNextContent,
|
|
8996
|
+
};
|
|
8997
|
+
if (viewModeRef.current === "overview") {
|
|
8998
|
+
recordContentHistoryEntry(change);
|
|
8999
|
+
} else {
|
|
9000
|
+
recordLocalContentHistoryEntry(change);
|
|
9001
|
+
}
|
|
8516
9002
|
}
|
|
8517
|
-
}
|
|
8518
9003
|
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
9004
|
+
setCollabContent(resolvedNextContent);
|
|
9005
|
+
setCollabContentFileId(activeFile.id);
|
|
9006
|
+
setPatchProof((prev) =>
|
|
9007
|
+
prev?.id === proofId ? { ...prev, status: "queued" } : prev,
|
|
9008
|
+
);
|
|
9009
|
+
// Mark as our own write so the get-design reconcile + Yjs observe don't
|
|
9010
|
+
// treat the echo as an external edit and fight the live value.
|
|
9011
|
+
lastLocalContentRef.current = resolvedNextContent;
|
|
9012
|
+
latestActiveContentRef.current = resolvedNextContent;
|
|
9013
|
+
// Write the edit into the shared Y.Doc so other open clients see it live
|
|
9014
|
+
// through Yjs (not only via the slower update-file → applyText round-trip).
|
|
9015
|
+
// Single-screen edits use the active-file UndoManager. Overview edits are
|
|
9016
|
+
// tracked in the global file-content stack so all screens share one order.
|
|
9017
|
+
if (ydoc && isSynced) {
|
|
9018
|
+
const ytext = ydoc.getText("content");
|
|
9019
|
+
if (ytext.toString() !== resolvedNextContent) {
|
|
9020
|
+
ydoc.transact(
|
|
9021
|
+
() => {
|
|
9022
|
+
ytext.delete(0, ytext.length);
|
|
9023
|
+
ytext.insert(0, resolvedNextContent);
|
|
9024
|
+
},
|
|
9025
|
+
yjsHistoryAvailable ? LOCAL_EDIT_ORIGIN : TAB_ID,
|
|
9026
|
+
);
|
|
9027
|
+
}
|
|
9028
|
+
}
|
|
9029
|
+
queueFileContentSave(activeFile.id, resolvedNextContent, {
|
|
9030
|
+
syncCollab: !(ydoc && isSynced),
|
|
9031
|
+
});
|
|
9032
|
+
if (
|
|
9033
|
+
shouldReplacePreviewAfterVisualStyleCommit({
|
|
9034
|
+
runtimeApplied: options.runtimeApplied,
|
|
9035
|
+
runtimeStyleApplied,
|
|
9036
|
+
}) &&
|
|
9037
|
+
!replacePreviewContent(resolvedNextContent, selector)
|
|
9038
|
+
) {
|
|
9039
|
+
setContentRenderRevision((revision) => revision + 1);
|
|
8542
9040
|
}
|
|
8543
|
-
}
|
|
8544
|
-
queueFileContentSave(activeFile.id, resolvedNextContent, {
|
|
8545
|
-
syncCollab: !(ydoc && isSynced),
|
|
8546
|
-
});
|
|
8547
|
-
if (
|
|
8548
|
-
!runtimeStyleApplied &&
|
|
8549
|
-
!replacePreviewContent(resolvedNextContent, selector)
|
|
8550
|
-
) {
|
|
8551
|
-
setContentRenderRevision((revision) => revision + 1);
|
|
8552
9041
|
}
|
|
8553
9042
|
if (resolvedNode) setSelectedLayerIdsState([resolvedNode.id]);
|
|
8554
9043
|
setSelectedElement((prev) => {
|
|
@@ -8576,40 +9065,153 @@ export default function DesignEditor() {
|
|
|
8576
9065
|
activeFile,
|
|
8577
9066
|
activeBreakpointWidthState,
|
|
8578
9067
|
canEditDesign,
|
|
9068
|
+
liveScreenSnapshotsById,
|
|
8579
9069
|
queueFileContentSave,
|
|
8580
9070
|
recordContentHistoryEntry,
|
|
8581
9071
|
recordLocalContentHistoryEntry,
|
|
8582
9072
|
replacePreviewContent,
|
|
8583
9073
|
selectedElement,
|
|
8584
9074
|
t,
|
|
9075
|
+
updateLiveScreenSnapshotContent,
|
|
8585
9076
|
ydoc,
|
|
8586
9077
|
isSynced,
|
|
8587
9078
|
],
|
|
8588
9079
|
);
|
|
8589
9080
|
|
|
8590
|
-
const
|
|
8591
|
-
(
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
9081
|
+
const commitStylesToSelectedLayers = useCallback(
|
|
9082
|
+
(styles: Record<string, string>) => {
|
|
9083
|
+
if (!canEditDesign) return false;
|
|
9084
|
+
const entries = Object.entries(styles).filter(
|
|
9085
|
+
([, value]) => value !== undefined,
|
|
9086
|
+
);
|
|
9087
|
+
if (entries.length === 0) return false;
|
|
9088
|
+
const effectiveLayerState = effectiveCodeLayerStateRef.current;
|
|
9089
|
+
const targets = selectedLayerTargetsRef.current.filter(
|
|
9090
|
+
(target) =>
|
|
9091
|
+
!effectiveLayerState.lockedIds.has(target.fileId) &&
|
|
9092
|
+
!effectiveLayerState.hiddenIds.has(target.fileId) &&
|
|
9093
|
+
!effectiveLayerState.lockedIds.has(target.layerId) &&
|
|
9094
|
+
!effectiveLayerState.hiddenIds.has(target.layerId),
|
|
9095
|
+
);
|
|
9096
|
+
if (targets.length <= 1) return false;
|
|
9097
|
+
|
|
9098
|
+
const targetsByFile = new Map<string, SelectedLayerTarget[]>();
|
|
9099
|
+
targets.forEach((target) => {
|
|
9100
|
+
targetsByFile.set(target.fileId, [
|
|
9101
|
+
...(targetsByFile.get(target.fileId) ?? []),
|
|
9102
|
+
target,
|
|
9103
|
+
]);
|
|
9104
|
+
});
|
|
9105
|
+
|
|
9106
|
+
let appliedAny = false;
|
|
9107
|
+
targetsByFile.forEach((fileTargets, fileId) => {
|
|
9108
|
+
const baseContent =
|
|
9109
|
+
fileId === activeFile?.id
|
|
9110
|
+
? getFreshActiveFileContent({
|
|
9111
|
+
activeContent,
|
|
9112
|
+
latestContent: latestActiveContentRef.current,
|
|
9113
|
+
lastLocalContent: lastLocalContentRef.current,
|
|
9114
|
+
})
|
|
9115
|
+
: getScreenContent(fileId);
|
|
9116
|
+
if (!baseContent) return;
|
|
9117
|
+
let nextContent = baseContent;
|
|
9118
|
+
let projection = buildCodeLayerProjection(nextContent);
|
|
9119
|
+
fileTargets.forEach((target) => {
|
|
9120
|
+
const sourceId = bridgeSourceIdForCodeLayerNode(target.node);
|
|
9121
|
+
const selector = preferredCodeLayerSelector(target.node);
|
|
9122
|
+
const node =
|
|
9123
|
+
projection.nodes.find((candidate) =>
|
|
9124
|
+
codeLayerNodeMatchesBridgeTarget(candidate, selector, sourceId),
|
|
9125
|
+
) ??
|
|
9126
|
+
projection.nodes.find(
|
|
9127
|
+
(candidate) => candidate.id === target.node.id,
|
|
9128
|
+
);
|
|
9129
|
+
if (!node) return;
|
|
9130
|
+
|
|
9131
|
+
entries.forEach(([property, value]) => {
|
|
9132
|
+
const patch = applyVisualEdit(nextContent, {
|
|
9133
|
+
kind: "style",
|
|
9134
|
+
target: { nodeId: node.id },
|
|
9135
|
+
property,
|
|
9136
|
+
value,
|
|
9137
|
+
});
|
|
9138
|
+
if (patch.result.status !== "applied") return;
|
|
9139
|
+
nextContent = patch.content;
|
|
9140
|
+
projection = patch.projection;
|
|
9141
|
+
});
|
|
9142
|
+
});
|
|
9143
|
+
if (nextContent === baseContent) return;
|
|
9144
|
+
appliedAny = true;
|
|
9145
|
+
applyFileContentUpdate(fileId, nextContent, {
|
|
9146
|
+
refreshPreview: fileId === activeFile?.id,
|
|
9147
|
+
});
|
|
9148
|
+
});
|
|
9149
|
+
|
|
9150
|
+
if (appliedAny) {
|
|
9151
|
+
const stylePatch = Object.fromEntries(entries);
|
|
9152
|
+
const primaryTarget = targets[targets.length - 1];
|
|
9153
|
+
if (primaryTarget) {
|
|
9154
|
+
setSelectedElement((previous) => {
|
|
9155
|
+
const previousMatches =
|
|
9156
|
+
previous &&
|
|
9157
|
+
codeLayerNodeMatchesBridgeTarget(
|
|
9158
|
+
primaryTarget.node,
|
|
9159
|
+
previous.selector,
|
|
9160
|
+
previous.sourceId ?? previous.id,
|
|
9161
|
+
);
|
|
9162
|
+
const base = previousMatches
|
|
9163
|
+
? canonicalElementInfoForCodeLayerNode(
|
|
9164
|
+
previous,
|
|
9165
|
+
primaryTarget.node,
|
|
9166
|
+
)
|
|
9167
|
+
: primaryTarget.elementInfo;
|
|
9168
|
+
return {
|
|
9169
|
+
...base,
|
|
9170
|
+
computedStyles: {
|
|
9171
|
+
...base.computedStyles,
|
|
9172
|
+
...stylePatch,
|
|
9173
|
+
},
|
|
9174
|
+
};
|
|
9175
|
+
});
|
|
9176
|
+
}
|
|
9177
|
+
}
|
|
9178
|
+
|
|
9179
|
+
return true;
|
|
9180
|
+
},
|
|
9181
|
+
[
|
|
9182
|
+
activeContent,
|
|
9183
|
+
activeFile?.id,
|
|
9184
|
+
applyFileContentUpdate,
|
|
9185
|
+
canEditDesign,
|
|
9186
|
+
getScreenContent,
|
|
9187
|
+
],
|
|
9188
|
+
);
|
|
9189
|
+
|
|
9190
|
+
const handleStyleChange = useCallback(
|
|
9191
|
+
(property: string, value: string) => {
|
|
9192
|
+
const selector = selectedElement?.selector ?? "body";
|
|
9193
|
+
if (
|
|
9194
|
+
textEditingState.active &&
|
|
9195
|
+
textEditingState.hasRange &&
|
|
9196
|
+
textEditingState.selector === selector
|
|
9197
|
+
) {
|
|
9198
|
+
const sendStyleChange = (window as any).__designCanvasSendStyle;
|
|
9199
|
+
if (typeof sendStyleChange === "function") {
|
|
9200
|
+
sendStyleChange(selector, property, value, {
|
|
9201
|
+
selectorCandidates: selectedCanvasSelectorCandidates,
|
|
9202
|
+
nodeId: selectedElement?.sourceId,
|
|
9203
|
+
});
|
|
9204
|
+
return;
|
|
9205
|
+
}
|
|
9206
|
+
}
|
|
9207
|
+
if (commitStylesToSelectedLayers({ [property]: value })) return;
|
|
9208
|
+
commitVisualStyles(selector, { [property]: value });
|
|
9209
|
+
},
|
|
9210
|
+
[
|
|
9211
|
+
commitStylesToSelectedLayers,
|
|
9212
|
+
commitVisualStyles,
|
|
9213
|
+
selectedElement?.selector,
|
|
9214
|
+
selectedElement?.sourceId,
|
|
8613
9215
|
selectedCanvasSelectorCandidates,
|
|
8614
9216
|
textEditingState.active,
|
|
8615
9217
|
textEditingState.hasRange,
|
|
@@ -8624,9 +9226,14 @@ export default function DesignEditor() {
|
|
|
8624
9226
|
Boolean(value),
|
|
8625
9227
|
);
|
|
8626
9228
|
if (entries.length === 0) return;
|
|
9229
|
+
if (commitStylesToSelectedLayers(Object.fromEntries(entries))) return;
|
|
8627
9230
|
commitVisualStyles(selector, Object.fromEntries(entries));
|
|
8628
9231
|
},
|
|
8629
|
-
[
|
|
9232
|
+
[
|
|
9233
|
+
commitStylesToSelectedLayers,
|
|
9234
|
+
commitVisualStyles,
|
|
9235
|
+
selectedElement?.selector,
|
|
9236
|
+
],
|
|
8630
9237
|
);
|
|
8631
9238
|
|
|
8632
9239
|
const getFreshActiveContent = useCallback(
|
|
@@ -8703,14 +9310,38 @@ export default function DesignEditor() {
|
|
|
8703
9310
|
);
|
|
8704
9311
|
return false;
|
|
8705
9312
|
}
|
|
8706
|
-
const
|
|
9313
|
+
const movedNodeAttrId =
|
|
9314
|
+
targetNode?.dataAttributes["data-agent-native-node-id"] ??
|
|
9315
|
+
details?.sourceId ??
|
|
9316
|
+
elementInfo?.sourceId ??
|
|
8707
9317
|
(patch.result.after?.nodeId
|
|
8708
9318
|
? patch.projection.nodes.find(
|
|
8709
9319
|
(node) => node.id === patch.result.after?.nodeId,
|
|
9320
|
+
)?.dataAttributes["data-agent-native-node-id"]
|
|
9321
|
+
: undefined);
|
|
9322
|
+
const nextContent =
|
|
9323
|
+
isAbsoluteCodeLayerNode(targetNode) && movedNodeAttrId
|
|
9324
|
+
? removeAbsolutePositioningFromNodeInHtml(
|
|
9325
|
+
patch.content,
|
|
9326
|
+
movedNodeAttrId,
|
|
9327
|
+
)
|
|
9328
|
+
: patch.content;
|
|
9329
|
+
const nextProjection = buildCodeLayerProjection(nextContent);
|
|
9330
|
+
const movedNode =
|
|
9331
|
+
(movedNodeAttrId
|
|
9332
|
+
? nextProjection.nodes.find(
|
|
9333
|
+
(node) =>
|
|
9334
|
+
node.dataAttributes["data-agent-native-node-id"] ===
|
|
9335
|
+
movedNodeAttrId,
|
|
9336
|
+
)
|
|
9337
|
+
: null) ??
|
|
9338
|
+
(patch.result.after?.nodeId
|
|
9339
|
+
? nextProjection.nodes.find(
|
|
9340
|
+
(node) => node.id === patch.result.after?.nodeId,
|
|
8710
9341
|
)
|
|
8711
9342
|
: null) ??
|
|
8712
9343
|
resolveCodeLayerNodeFromBridge(
|
|
8713
|
-
|
|
9344
|
+
nextProjection,
|
|
8714
9345
|
selector,
|
|
8715
9346
|
details?.sourceId ??
|
|
8716
9347
|
elementInfo?.sourceId ??
|
|
@@ -8718,7 +9349,7 @@ export default function DesignEditor() {
|
|
|
8718
9349
|
? bridgeSourceIdForCodeLayerNode(targetNode)
|
|
8719
9350
|
: undefined),
|
|
8720
9351
|
);
|
|
8721
|
-
applyLocalContentUpdate(
|
|
9352
|
+
applyLocalContentUpdate(nextContent, { skipPreview: true });
|
|
8722
9353
|
if (movedNode) setSelectedLayerIdsState([movedNode.id]);
|
|
8723
9354
|
if (elementInfo) {
|
|
8724
9355
|
setSelectedElement({
|
|
@@ -8829,7 +9460,8 @@ export default function DesignEditor() {
|
|
|
8829
9460
|
) => {
|
|
8830
9461
|
if (!canEditDesign) return;
|
|
8831
9462
|
if (!activeFile) return;
|
|
8832
|
-
const
|
|
9463
|
+
const activeLiveSnapshot = liveScreenSnapshotsById[activeFile.id];
|
|
9464
|
+
const baseContent = activeLiveSnapshot?.html ?? getFreshActiveContent();
|
|
8833
9465
|
const projection = buildCodeLayerProjection(baseContent);
|
|
8834
9466
|
const targetInfo = elementInfo ? { ...elementInfo, selector } : null;
|
|
8835
9467
|
const targetNode = targetInfo
|
|
@@ -8862,7 +9494,11 @@ export default function DesignEditor() {
|
|
|
8862
9494
|
);
|
|
8863
9495
|
return;
|
|
8864
9496
|
}
|
|
8865
|
-
|
|
9497
|
+
if (activeLiveSnapshot) {
|
|
9498
|
+
updateLiveScreenSnapshotContent(activeFile.id, nextContent);
|
|
9499
|
+
} else {
|
|
9500
|
+
applyLocalContentUpdate(nextContent, { skipPreview: true });
|
|
9501
|
+
}
|
|
8866
9502
|
setActiveTool("text");
|
|
8867
9503
|
setMode("edit");
|
|
8868
9504
|
if (removedContent) {
|
|
@@ -8905,7 +9541,9 @@ export default function DesignEditor() {
|
|
|
8905
9541
|
applyLocalContentUpdate,
|
|
8906
9542
|
canEditDesign,
|
|
8907
9543
|
getFreshActiveContent,
|
|
9544
|
+
liveScreenSnapshotsById,
|
|
8908
9545
|
t,
|
|
9546
|
+
updateLiveScreenSnapshotContent,
|
|
8909
9547
|
],
|
|
8910
9548
|
);
|
|
8911
9549
|
|
|
@@ -8925,7 +9563,8 @@ export default function DesignEditor() {
|
|
|
8925
9563
|
([, value]) => value !== undefined,
|
|
8926
9564
|
);
|
|
8927
9565
|
if (entries.length === 0) return;
|
|
8928
|
-
const
|
|
9566
|
+
const liveSnapshot = liveScreenSnapshotsById[screenId];
|
|
9567
|
+
const baseContent = liveSnapshot?.html ?? getScreenContent(screenId);
|
|
8929
9568
|
const projection = buildCodeLayerProjection(baseContent);
|
|
8930
9569
|
const targetInfo = elementInfo ? { ...elementInfo, selector } : null;
|
|
8931
9570
|
const targetNode = targetInfo
|
|
@@ -8969,6 +9608,10 @@ export default function DesignEditor() {
|
|
|
8969
9608
|
);
|
|
8970
9609
|
return;
|
|
8971
9610
|
}
|
|
9611
|
+
if (liveSnapshot) {
|
|
9612
|
+
updateLiveScreenSnapshotContent(screenId, nextContent);
|
|
9613
|
+
return;
|
|
9614
|
+
}
|
|
8972
9615
|
applyFileContentUpdate(screenId, nextContent, { skipPreview: true });
|
|
8973
9616
|
},
|
|
8974
9617
|
[
|
|
@@ -8977,7 +9620,9 @@ export default function DesignEditor() {
|
|
|
8977
9620
|
canEditDesign,
|
|
8978
9621
|
getScreenContent,
|
|
8979
9622
|
handleVisualStyleChange,
|
|
9623
|
+
liveScreenSnapshotsById,
|
|
8980
9624
|
t,
|
|
9625
|
+
updateLiveScreenSnapshotContent,
|
|
8981
9626
|
],
|
|
8982
9627
|
);
|
|
8983
9628
|
|
|
@@ -9165,7 +9810,8 @@ export default function DesignEditor() {
|
|
|
9165
9810
|
return;
|
|
9166
9811
|
}
|
|
9167
9812
|
if (!canEditDesign) return;
|
|
9168
|
-
const
|
|
9813
|
+
const liveSnapshot = liveScreenSnapshotsById[screenId];
|
|
9814
|
+
const baseContent = liveSnapshot?.html ?? getScreenContent(screenId);
|
|
9169
9815
|
const projection = buildCodeLayerProjection(baseContent);
|
|
9170
9816
|
const targetInfo = elementInfo ? { ...elementInfo, selector } : null;
|
|
9171
9817
|
const targetNode = targetInfo
|
|
@@ -9198,7 +9844,11 @@ export default function DesignEditor() {
|
|
|
9198
9844
|
);
|
|
9199
9845
|
return;
|
|
9200
9846
|
}
|
|
9201
|
-
|
|
9847
|
+
if (liveSnapshot) {
|
|
9848
|
+
updateLiveScreenSnapshotContent(screenId, nextContent);
|
|
9849
|
+
} else {
|
|
9850
|
+
applyFileContentUpdate(screenId, nextContent, { skipPreview: true });
|
|
9851
|
+
}
|
|
9202
9852
|
setActiveFileId(screenId);
|
|
9203
9853
|
setActiveTool("text");
|
|
9204
9854
|
setMode("edit");
|
|
@@ -9243,7 +9893,9 @@ export default function DesignEditor() {
|
|
|
9243
9893
|
canEditDesign,
|
|
9244
9894
|
getScreenContent,
|
|
9245
9895
|
handleTextContentChange,
|
|
9896
|
+
liveScreenSnapshotsById,
|
|
9246
9897
|
t,
|
|
9898
|
+
updateLiveScreenSnapshotContent,
|
|
9247
9899
|
],
|
|
9248
9900
|
);
|
|
9249
9901
|
|
|
@@ -9424,7 +10076,9 @@ export default function DesignEditor() {
|
|
|
9424
10076
|
});
|
|
9425
10077
|
if (result) {
|
|
9426
10078
|
pasteCascadeRef.current += 1;
|
|
9427
|
-
applyLocalContentUpdate(result.content
|
|
10079
|
+
applyLocalContentUpdate(result.content, {
|
|
10080
|
+
forcePreviewFullDocument: true,
|
|
10081
|
+
});
|
|
9428
10082
|
selectInsertedLayers(
|
|
9429
10083
|
activeFile.id,
|
|
9430
10084
|
result.content,
|
|
@@ -9476,7 +10130,9 @@ export default function DesignEditor() {
|
|
|
9476
10130
|
});
|
|
9477
10131
|
if (!result) return;
|
|
9478
10132
|
if (!position) pasteCascadeRef.current += 1;
|
|
9479
|
-
applyLocalContentUpdate(result.content
|
|
10133
|
+
applyLocalContentUpdate(result.content, {
|
|
10134
|
+
forcePreviewFullDocument: true,
|
|
10135
|
+
});
|
|
9480
10136
|
selectInsertedLayers(activeFile.id, result.content, result.rootNodeIds);
|
|
9481
10137
|
},
|
|
9482
10138
|
[
|
|
@@ -9508,7 +10164,9 @@ export default function DesignEditor() {
|
|
|
9508
10164
|
},
|
|
9509
10165
|
);
|
|
9510
10166
|
if (!result) return;
|
|
9511
|
-
applyLocalContentUpdate(result.content
|
|
10167
|
+
applyLocalContentUpdate(result.content, {
|
|
10168
|
+
forcePreviewFullDocument: true,
|
|
10169
|
+
});
|
|
9512
10170
|
selectInsertedLayers(activeFile.id, result.content, result.rootNodeIds);
|
|
9513
10171
|
} else {
|
|
9514
10172
|
handlePasteSelection();
|
|
@@ -9559,7 +10217,10 @@ export default function DesignEditor() {
|
|
|
9559
10217
|
insertedRootNodeIds.unshift(...result.rootNodeIds);
|
|
9560
10218
|
}
|
|
9561
10219
|
if (insertedRootNodeIds.length === 0) continue;
|
|
9562
|
-
applyFileContentUpdate(file.id, content, {
|
|
10220
|
+
applyFileContentUpdate(file.id, content, {
|
|
10221
|
+
forcePreviewFullDocument: true,
|
|
10222
|
+
refreshPreview: false,
|
|
10223
|
+
});
|
|
9563
10224
|
selectedScreenIds.push(file.id);
|
|
9564
10225
|
const finalProjection = buildCodeLayerProjection(content);
|
|
9565
10226
|
insertedRootNodeIds.forEach((rootNodeId) => {
|
|
@@ -9624,7 +10285,9 @@ export default function DesignEditor() {
|
|
|
9624
10285
|
placement: "after",
|
|
9625
10286
|
});
|
|
9626
10287
|
if (nextContent) {
|
|
9627
|
-
applyLocalContentUpdate(nextContent
|
|
10288
|
+
applyLocalContentUpdate(nextContent, {
|
|
10289
|
+
forcePreviewFullDocument: true,
|
|
10290
|
+
});
|
|
9628
10291
|
} else {
|
|
9629
10292
|
toast.error(t("designEditor.toasts.duplicateElementFailed"));
|
|
9630
10293
|
}
|
|
@@ -9835,57 +10498,6 @@ export default function DesignEditor() {
|
|
|
9835
10498
|
t,
|
|
9836
10499
|
]);
|
|
9837
10500
|
|
|
9838
|
-
/**
|
|
9839
|
-
* Convert the selected container to full auto-layout. Applies the
|
|
9840
|
-
* { kind: "autoLayout", enabled: true } substrate intent which sets
|
|
9841
|
-
* display:flex on the target AND strips position:absolute/left/top/right/bottom
|
|
9842
|
-
* from its direct children so they become flow children.
|
|
9843
|
-
*/
|
|
9844
|
-
const handleAutoLayoutConvert = useCallback(
|
|
9845
|
-
(
|
|
9846
|
-
targetNodeId: string,
|
|
9847
|
-
opts?: { direction?: "row" | "column"; gap?: string },
|
|
9848
|
-
) => {
|
|
9849
|
-
if (!canEditDesign || !activeFile) return;
|
|
9850
|
-
const baseContent = getFreshActiveContent();
|
|
9851
|
-
const patch = applyVisualEdit(baseContent, {
|
|
9852
|
-
kind: "autoLayout",
|
|
9853
|
-
targetId: targetNodeId,
|
|
9854
|
-
enabled: true,
|
|
9855
|
-
direction: opts?.direction ?? "row",
|
|
9856
|
-
gap: opts?.gap ?? "8px",
|
|
9857
|
-
});
|
|
9858
|
-
if (patch.result.status !== "applied") {
|
|
9859
|
-
toast.error(
|
|
9860
|
-
codeLayerPatchMessage(
|
|
9861
|
-
patch.result.message,
|
|
9862
|
-
t("designEditor.toasts.layerMoveFailed"),
|
|
9863
|
-
),
|
|
9864
|
-
{ duration: 4000 },
|
|
9865
|
-
);
|
|
9866
|
-
return;
|
|
9867
|
-
}
|
|
9868
|
-
applyLocalContentUpdate(patch.content, { skipPreview: true });
|
|
9869
|
-
// Re-select the container so the inspector refreshes its layout state.
|
|
9870
|
-
const containerNode = patch.projection.nodes.find(
|
|
9871
|
-
(n) =>
|
|
9872
|
-
n.dataAttributes["data-agent-native-node-id"] === targetNodeId ||
|
|
9873
|
-
n.id === targetNodeId,
|
|
9874
|
-
);
|
|
9875
|
-
if (containerNode) {
|
|
9876
|
-
setSelectedLayerIdsState([containerNode.id]);
|
|
9877
|
-
setSelectedElement(elementInfoFromCodeLayerNode(containerNode));
|
|
9878
|
-
}
|
|
9879
|
-
},
|
|
9880
|
-
[
|
|
9881
|
-
activeFile,
|
|
9882
|
-
applyLocalContentUpdate,
|
|
9883
|
-
canEditDesign,
|
|
9884
|
-
getFreshActiveContent,
|
|
9885
|
-
t,
|
|
9886
|
-
],
|
|
9887
|
-
);
|
|
9888
|
-
|
|
9889
10501
|
/**
|
|
9890
10502
|
* Handle a primitive being drag-dropped onto another primitive in the
|
|
9891
10503
|
* MultiScreenCanvas overview (CONTRACT: onPrimitiveReparent prop).
|
|
@@ -10083,6 +10695,8 @@ export default function DesignEditor() {
|
|
|
10083
10695
|
targetScreenId,
|
|
10084
10696
|
targetAnchorNodeId,
|
|
10085
10697
|
targetAnchorPlacement,
|
|
10698
|
+
targetLocalPoint,
|
|
10699
|
+
sourcePointerOffset,
|
|
10086
10700
|
}: {
|
|
10087
10701
|
sourceSelector: string;
|
|
10088
10702
|
sourceNodeId?: string;
|
|
@@ -10090,6 +10704,9 @@ export default function DesignEditor() {
|
|
|
10090
10704
|
targetScreenId: string;
|
|
10091
10705
|
targetAnchorNodeId?: string;
|
|
10092
10706
|
targetAnchorPlacement?: "before" | "after" | "inside";
|
|
10707
|
+
targetCanvasPoint?: { x: number; y: number };
|
|
10708
|
+
targetLocalPoint?: { x: number; y: number };
|
|
10709
|
+
sourcePointerOffset?: { x: number; y: number };
|
|
10093
10710
|
}) => {
|
|
10094
10711
|
if (!canEditDesign) return;
|
|
10095
10712
|
if (sourceScreenId === targetScreenId) return;
|
|
@@ -10151,16 +10768,23 @@ export default function DesignEditor() {
|
|
|
10151
10768
|
return;
|
|
10152
10769
|
}
|
|
10153
10770
|
|
|
10154
|
-
//
|
|
10155
|
-
//
|
|
10156
|
-
//
|
|
10157
|
-
// nodeAttrId when a collision triggered an id re-stamp) so the strip and
|
|
10158
|
-
// re-selection always find the correct element.
|
|
10771
|
+
// Hit-test anchors are emitted only for auto-layout insertion targets. If
|
|
10772
|
+
// there is no anchor, preserve absolute mode and rebase left/top to the
|
|
10773
|
+
// release point so screen↔board moves behave like Figma absolute layers.
|
|
10159
10774
|
const destNodeAttrId = result.movedNodeId ?? nodeAttrId;
|
|
10160
|
-
const
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10775
|
+
const nextDestContent = targetAnchorAttrId
|
|
10776
|
+
? removeAbsolutePositioningFromNodeInHtml(
|
|
10777
|
+
result.destHtml,
|
|
10778
|
+
destNodeAttrId,
|
|
10779
|
+
)
|
|
10780
|
+
: targetLocalPoint
|
|
10781
|
+
? setAbsolutePositioningForNodeInHtml(
|
|
10782
|
+
result.destHtml,
|
|
10783
|
+
destNodeAttrId,
|
|
10784
|
+
targetLocalPoint,
|
|
10785
|
+
sourcePointerOffset,
|
|
10786
|
+
)
|
|
10787
|
+
: result.destHtml;
|
|
10164
10788
|
|
|
10165
10789
|
recordContentHistoryEntry({
|
|
10166
10790
|
changes: [
|
|
@@ -10172,7 +10796,7 @@ export default function DesignEditor() {
|
|
|
10172
10796
|
{
|
|
10173
10797
|
fileId: targetScreenId,
|
|
10174
10798
|
before: destContent,
|
|
10175
|
-
after:
|
|
10799
|
+
after: nextDestContent,
|
|
10176
10800
|
},
|
|
10177
10801
|
],
|
|
10178
10802
|
});
|
|
@@ -10181,7 +10805,7 @@ export default function DesignEditor() {
|
|
|
10181
10805
|
recordHistory: false,
|
|
10182
10806
|
refreshPreview: true,
|
|
10183
10807
|
});
|
|
10184
|
-
applyFileContentUpdate(targetScreenId,
|
|
10808
|
+
applyFileContentUpdate(targetScreenId, nextDestContent, {
|
|
10185
10809
|
recordHistory: false,
|
|
10186
10810
|
refreshPreview: true,
|
|
10187
10811
|
});
|
|
@@ -10189,7 +10813,7 @@ export default function DesignEditor() {
|
|
|
10189
10813
|
// Switch active screen to the target and select the moved node; viewMode
|
|
10190
10814
|
// stays "overview" (no setViewMode call).
|
|
10191
10815
|
setActiveFileId(targetScreenId);
|
|
10192
|
-
const finalProjection = buildCodeLayerProjection(
|
|
10816
|
+
const finalProjection = buildCodeLayerProjection(nextDestContent);
|
|
10193
10817
|
const movedNodeFinal = finalProjection.nodes.find(
|
|
10194
10818
|
(n) => n.dataAttributes["data-agent-native-node-id"] === destNodeAttrId,
|
|
10195
10819
|
);
|
|
@@ -10429,9 +11053,8 @@ export default function DesignEditor() {
|
|
|
10429
11053
|
queueFileContentSave(activeFile.id, next, {
|
|
10430
11054
|
syncCollab: !(ydoc && isSynced),
|
|
10431
11055
|
});
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
}
|
|
11056
|
+
replacePreviewContent(next, null, { forceFullDocument: true });
|
|
11057
|
+
setContentRenderRevision((revision) => revision + 1);
|
|
10435
11058
|
// Clear stale selection if the undo removed the selected element.
|
|
10436
11059
|
setSelectedElement((prev) => {
|
|
10437
11060
|
if (!prev) return prev;
|
|
@@ -10468,6 +11091,7 @@ export default function DesignEditor() {
|
|
|
10468
11091
|
];
|
|
10469
11092
|
applyLocalContentUpdate(entry.before, {
|
|
10470
11093
|
refreshPreview: false,
|
|
11094
|
+
forcePreviewFullDocument: true,
|
|
10471
11095
|
immediateSave: true,
|
|
10472
11096
|
recordHistory: false,
|
|
10473
11097
|
});
|
|
@@ -10514,6 +11138,7 @@ export default function DesignEditor() {
|
|
|
10514
11138
|
if (change.fileId === activeFile?.id) {
|
|
10515
11139
|
applyLocalContentUpdate(change.before, {
|
|
10516
11140
|
refreshPreview: false,
|
|
11141
|
+
forcePreviewFullDocument: true,
|
|
10517
11142
|
immediateSave: true,
|
|
10518
11143
|
recordHistory: false,
|
|
10519
11144
|
});
|
|
@@ -10618,9 +11243,8 @@ export default function DesignEditor() {
|
|
|
10618
11243
|
queueFileContentSave(activeFile.id, next, {
|
|
10619
11244
|
syncCollab: !(ydoc && isSynced),
|
|
10620
11245
|
});
|
|
10621
|
-
|
|
10622
|
-
|
|
10623
|
-
}
|
|
11246
|
+
replacePreviewContent(next, null, { forceFullDocument: true });
|
|
11247
|
+
setContentRenderRevision((revision) => revision + 1);
|
|
10624
11248
|
// Clear stale selection if the redo removed the selected element.
|
|
10625
11249
|
setSelectedElement((prev) => {
|
|
10626
11250
|
if (!prev) return prev;
|
|
@@ -10657,6 +11281,7 @@ export default function DesignEditor() {
|
|
|
10657
11281
|
];
|
|
10658
11282
|
applyLocalContentUpdate(entry.after, {
|
|
10659
11283
|
refreshPreview: false,
|
|
11284
|
+
forcePreviewFullDocument: true,
|
|
10660
11285
|
immediateSave: true,
|
|
10661
11286
|
recordHistory: false,
|
|
10662
11287
|
});
|
|
@@ -10703,6 +11328,7 @@ export default function DesignEditor() {
|
|
|
10703
11328
|
if (change.fileId === activeFile?.id) {
|
|
10704
11329
|
applyLocalContentUpdate(change.after, {
|
|
10705
11330
|
refreshPreview: false,
|
|
11331
|
+
forcePreviewFullDocument: true,
|
|
10706
11332
|
immediateSave: true,
|
|
10707
11333
|
recordHistory: false,
|
|
10708
11334
|
});
|
|
@@ -10806,7 +11432,7 @@ export default function DesignEditor() {
|
|
|
10806
11432
|
viewModeRef.current = "overview";
|
|
10807
11433
|
setViewMode("overview");
|
|
10808
11434
|
setActiveTool("move");
|
|
10809
|
-
|
|
11435
|
+
setExplicitOverviewCanvasZoom(100);
|
|
10810
11436
|
}, []);
|
|
10811
11437
|
|
|
10812
11438
|
const runEditorViewTransition = useCallback((update: () => void) => {
|
|
@@ -11849,7 +12475,7 @@ ${serializedHtml}
|
|
|
11849
12475
|
: codingHandoffPreviewFallback);
|
|
11850
12476
|
const shareExportTab = (
|
|
11851
12477
|
<div className="space-y-3">
|
|
11852
|
-
<div className="text-[11px] font-semibold uppercase text-muted-foreground">
|
|
12478
|
+
<div className="!text-[11px] font-semibold uppercase text-muted-foreground">
|
|
11853
12479
|
{"Format" /* i18n-ignore share export section label */}
|
|
11854
12480
|
</div>
|
|
11855
12481
|
<div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2">
|
|
@@ -11874,11 +12500,11 @@ ${serializedHtml}
|
|
|
11874
12500
|
<span className="min-w-0 flex-1">
|
|
11875
12501
|
<span className="block truncate text-[12px] font-semibold text-foreground">
|
|
11876
12502
|
{option.title}{" "}
|
|
11877
|
-
<span className="text-[11px] font-medium text-muted-foreground">
|
|
12503
|
+
<span className="!text-[11px] font-medium text-muted-foreground">
|
|
11878
12504
|
{option.extension}
|
|
11879
12505
|
</span>
|
|
11880
12506
|
</span>
|
|
11881
|
-
<span className="mt-0.5 block text-[11px] leading-4 text-muted-foreground">
|
|
12507
|
+
<span className="mt-0.5 block !text-[11px] leading-4 text-muted-foreground">
|
|
11882
12508
|
{option.description}
|
|
11883
12509
|
</span>
|
|
11884
12510
|
</span>
|
|
@@ -11902,7 +12528,7 @@ ${serializedHtml}
|
|
|
11902
12528
|
<div className="text-[12px] font-medium text-foreground">
|
|
11903
12529
|
{selectedShareExportOption.title}
|
|
11904
12530
|
</div>
|
|
11905
|
-
<div className="text-[11px] text-muted-foreground">
|
|
12531
|
+
<div className="!text-[11px] text-muted-foreground">
|
|
11906
12532
|
{selectedShareExportOption.description}
|
|
11907
12533
|
</div>
|
|
11908
12534
|
</div>
|
|
@@ -11974,7 +12600,7 @@ ${serializedHtml}
|
|
|
11974
12600
|
<div className="text-[12px] font-medium text-foreground">
|
|
11975
12601
|
{"Download zip instead" /* i18n-ignore share send option */}
|
|
11976
12602
|
</div>
|
|
11977
|
-
<div className="mt-0.5 text-[11px] leading-4 text-muted-foreground">
|
|
12603
|
+
<div className="mt-0.5 !text-[11px] leading-4 text-muted-foreground">
|
|
11978
12604
|
{
|
|
11979
12605
|
"For agents without the Design connector, drop the bundle into your agent's chat manually." /* i18n-ignore share send option description */
|
|
11980
12606
|
}
|
|
@@ -12004,7 +12630,7 @@ ${serializedHtml}
|
|
|
12004
12630
|
const designSharePopoverClassName =
|
|
12005
12631
|
"z-[100010] !w-[min(620px,calc(100vw-32px))] !p-3 " +
|
|
12006
12632
|
"[&_[role=tablist]]:!inline-flex [&_[role=tablist]]:!w-fit [&_[role=tablist]]:!self-start [&_[role=tablist]]:justify-start [&_[role=tablist]]:gap-0.5 [&_[role=tablist]]:rounded-none [&_[role=tablist]]:bg-transparent [&_[role=tablist]]:p-0 " +
|
|
12007
|
-
"[&_[role=tab]]:!h-6 [&_[role=tab]]:!flex-none [&_[role=tab]]:rounded-md [&_[role=tab]]:px-2 [&_[role=tab]]
|
|
12633
|
+
"[&_[role=tab]]:!h-6 [&_[role=tab]]:!flex-none [&_[role=tab]]:rounded-md [&_[role=tab]]:px-2 [&_[role=tab]]:!text-[11px] [&_[role=tab]]:font-semibold [&_[role=tab]]:shadow-none [&_[role=tab]]:ring-0 " +
|
|
12008
12634
|
"[&_[role=tab]:hover]:bg-[var(--design-editor-panel-raised-bg)] [&_[role=tab]:hover]:text-foreground [&_[role=tab][aria-selected=true]]:bg-[var(--design-editor-panel-raised-bg)] [&_[role=tab][aria-selected=true]]:text-foreground [&_[role=tab][aria-selected=true]]:ring-0";
|
|
12009
12635
|
const designShareTabs = {
|
|
12010
12636
|
shareLabel: "Share link" /* i18n-ignore share tab label */,
|
|
@@ -12050,7 +12676,9 @@ ${serializedHtml}
|
|
|
12050
12676
|
() =>
|
|
12051
12677
|
files.map((file) => {
|
|
12052
12678
|
const content =
|
|
12053
|
-
file.id === activeFile?.id
|
|
12679
|
+
file.id === activeFile?.id
|
|
12680
|
+
? activeProjectionContent
|
|
12681
|
+
: getProjectionContentForScreen(file.id);
|
|
12054
12682
|
const projection =
|
|
12055
12683
|
file.id === activeFile?.id
|
|
12056
12684
|
? activeCodeLayerProjection
|
|
@@ -12069,9 +12697,10 @@ ${serializedHtml}
|
|
|
12069
12697
|
[
|
|
12070
12698
|
activeCodeLayerProjection,
|
|
12071
12699
|
activeCodeLayerTree,
|
|
12072
|
-
|
|
12700
|
+
activeProjectionContent,
|
|
12073
12701
|
activeFile?.id,
|
|
12074
12702
|
files,
|
|
12703
|
+
getProjectionContentForScreen,
|
|
12075
12704
|
],
|
|
12076
12705
|
);
|
|
12077
12706
|
const codeLayerModelByFileId = useMemo(
|
|
@@ -12115,6 +12744,7 @@ ${serializedHtml}
|
|
|
12115
12744
|
});
|
|
12116
12745
|
return state;
|
|
12117
12746
|
}, [codeLayerModelsByFile, hiddenLayerIds, lockedLayerIds]);
|
|
12747
|
+
effectiveCodeLayerStateRef.current = effectiveCodeLayerState;
|
|
12118
12748
|
useEffect(() => {
|
|
12119
12749
|
shouldPreserveBlockedOverviewLayerSelectionRef.current = (
|
|
12120
12750
|
screenId: string,
|
|
@@ -12358,12 +12988,191 @@ ${serializedHtml}
|
|
|
12358
12988
|
selectedLayerIdsState,
|
|
12359
12989
|
viewMode,
|
|
12360
12990
|
]);
|
|
12991
|
+
const selectedUrlSelectionId = useMemo(
|
|
12992
|
+
() =>
|
|
12993
|
+
selectedElementLayerId ??
|
|
12994
|
+
[...selectedLayerIds]
|
|
12995
|
+
.reverse()
|
|
12996
|
+
.find((layerId) => codeLayerOwnerByNodeId.has(layerId)) ??
|
|
12997
|
+
null,
|
|
12998
|
+
[codeLayerOwnerByNodeId, selectedElementLayerId, selectedLayerIds],
|
|
12999
|
+
);
|
|
12361
13000
|
const selectedLayerIdsRef = useRef<string[]>(selectedLayerIds);
|
|
12362
13001
|
|
|
12363
13002
|
useLayoutEffect(() => {
|
|
12364
13003
|
selectedLayerIdsRef.current = selectedLayerIds;
|
|
12365
13004
|
}, [selectedLayerIds]);
|
|
12366
13005
|
|
|
13006
|
+
useEffect(() => {
|
|
13007
|
+
if (!id) return;
|
|
13008
|
+
if (initialUrlSelectionHydratedForIdRef.current === id) return;
|
|
13009
|
+
if (!initialRouteSelectionId) {
|
|
13010
|
+
initialUrlSelectionHydratedForIdRef.current = id;
|
|
13011
|
+
return;
|
|
13012
|
+
}
|
|
13013
|
+
if (
|
|
13014
|
+
selectedUrlSelectionId &&
|
|
13015
|
+
selectedUrlSelectionId !== initialRouteSelectionId
|
|
13016
|
+
) {
|
|
13017
|
+
initialUrlSelectionHydratedForIdRef.current = id;
|
|
13018
|
+
return;
|
|
13019
|
+
}
|
|
13020
|
+
const owner = codeLayerOwnerByNodeId.get(initialRouteSelectionId);
|
|
13021
|
+
if (!owner) return;
|
|
13022
|
+
const selectionBlocked =
|
|
13023
|
+
effectiveCodeLayerState.lockedIds.has(owner.fileId) ||
|
|
13024
|
+
effectiveCodeLayerState.hiddenIds.has(owner.fileId) ||
|
|
13025
|
+
effectiveCodeLayerState.lockedIds.has(initialRouteSelectionId) ||
|
|
13026
|
+
effectiveCodeLayerState.hiddenIds.has(initialRouteSelectionId);
|
|
13027
|
+
if (
|
|
13028
|
+
activeFileId === owner.fileId &&
|
|
13029
|
+
selectedLayerIds.includes(initialRouteSelectionId) &&
|
|
13030
|
+
(selectionBlocked || selectedElementLayerId === initialRouteSelectionId)
|
|
13031
|
+
) {
|
|
13032
|
+
initialUrlSelectionHydratedForIdRef.current = id;
|
|
13033
|
+
return;
|
|
13034
|
+
}
|
|
13035
|
+
|
|
13036
|
+
pendingOverviewScreenSelectionRef.current = null;
|
|
13037
|
+
pendingOverviewLayerSelectionRef.current = null;
|
|
13038
|
+
clearPendingOverviewLayerSelectionTimer();
|
|
13039
|
+
setCreatedOverviewLayerSelection(null);
|
|
13040
|
+
setActiveFileId(owner.fileId);
|
|
13041
|
+
setSelectedLayerIdsState([initialRouteSelectionId]);
|
|
13042
|
+
if (viewModeRef.current === "overview") {
|
|
13043
|
+
setOverviewSelectedScreenIds([]);
|
|
13044
|
+
}
|
|
13045
|
+
setSelectedElement(
|
|
13046
|
+
selectionBlocked ? null : elementInfoFromCodeLayerNode(owner.node),
|
|
13047
|
+
);
|
|
13048
|
+
setHoveredElement(null);
|
|
13049
|
+
setHoveredElementScreenId(null);
|
|
13050
|
+
setActiveTool("move");
|
|
13051
|
+
setMode("edit");
|
|
13052
|
+
if (!selectionBlocked) {
|
|
13053
|
+
focusDesignInspectorForSelection();
|
|
13054
|
+
}
|
|
13055
|
+
initialUrlSelectionHydratedForIdRef.current = id;
|
|
13056
|
+
}, [
|
|
13057
|
+
activeFileId,
|
|
13058
|
+
clearPendingOverviewLayerSelectionTimer,
|
|
13059
|
+
codeLayerOwnerByNodeId,
|
|
13060
|
+
effectiveCodeLayerState,
|
|
13061
|
+
focusDesignInspectorForSelection,
|
|
13062
|
+
id,
|
|
13063
|
+
initialRouteSelectionId,
|
|
13064
|
+
selectedElementLayerId,
|
|
13065
|
+
selectedLayerIds,
|
|
13066
|
+
selectedUrlSelectionId,
|
|
13067
|
+
]);
|
|
13068
|
+
|
|
13069
|
+
useEffect(() => {
|
|
13070
|
+
if (!id || files.length === 0) return;
|
|
13071
|
+
if (
|
|
13072
|
+
initialRouteScreenTarget &&
|
|
13073
|
+
!findDesignFileByScreenTarget(files, initialRouteScreenTarget) &&
|
|
13074
|
+
!activeFileId
|
|
13075
|
+
) {
|
|
13076
|
+
return;
|
|
13077
|
+
}
|
|
13078
|
+
const preserveInitialRouteSelection = Boolean(
|
|
13079
|
+
initialRouteSelectionId &&
|
|
13080
|
+
initialUrlSelectionHydratedForIdRef.current !== id &&
|
|
13081
|
+
initialRouteSelectionId !== selectedUrlSelectionId &&
|
|
13082
|
+
codeLayerOwnerByNodeId.size === 0,
|
|
13083
|
+
);
|
|
13084
|
+
const nextSearch = getDesignEditorStateUrlSearch({
|
|
13085
|
+
currentSearch: location.search,
|
|
13086
|
+
viewMode,
|
|
13087
|
+
screenId: activeFile?.id ?? activeFileId,
|
|
13088
|
+
selectionId:
|
|
13089
|
+
selectedUrlSelectionId ??
|
|
13090
|
+
(preserveInitialRouteSelection ? initialRouteSelectionId : null),
|
|
13091
|
+
zoom,
|
|
13092
|
+
});
|
|
13093
|
+
if (nextSearch === location.search) return;
|
|
13094
|
+
navigate(
|
|
13095
|
+
{
|
|
13096
|
+
pathname: location.pathname,
|
|
13097
|
+
search: nextSearch,
|
|
13098
|
+
hash: location.hash,
|
|
13099
|
+
},
|
|
13100
|
+
{ replace: true, preventScrollReset: true },
|
|
13101
|
+
);
|
|
13102
|
+
}, [
|
|
13103
|
+
activeFile?.id,
|
|
13104
|
+
activeFileId,
|
|
13105
|
+
codeLayerOwnerByNodeId.size,
|
|
13106
|
+
files,
|
|
13107
|
+
id,
|
|
13108
|
+
location.hash,
|
|
13109
|
+
location.pathname,
|
|
13110
|
+
location.search,
|
|
13111
|
+
navigate,
|
|
13112
|
+
initialRouteScreenTarget,
|
|
13113
|
+
initialRouteSelectionId,
|
|
13114
|
+
selectedUrlSelectionId,
|
|
13115
|
+
viewMode,
|
|
13116
|
+
zoom,
|
|
13117
|
+
]);
|
|
13118
|
+
|
|
13119
|
+
const selectedLayerTargets = useMemo<SelectedLayerTarget[]>(
|
|
13120
|
+
() =>
|
|
13121
|
+
selectedLayerIds
|
|
13122
|
+
.map((layerId) => {
|
|
13123
|
+
const owner = codeLayerOwnerByNodeId.get(layerId);
|
|
13124
|
+
if (!owner) return null;
|
|
13125
|
+
const selectedMatches =
|
|
13126
|
+
selectedElement &&
|
|
13127
|
+
codeLayerNodeMatchesBridgeTarget(
|
|
13128
|
+
owner.node,
|
|
13129
|
+
selectedElement.selector,
|
|
13130
|
+
selectedElement.sourceId ?? selectedElement.id,
|
|
13131
|
+
);
|
|
13132
|
+
return {
|
|
13133
|
+
layerId,
|
|
13134
|
+
fileId: owner.fileId,
|
|
13135
|
+
node: owner.node,
|
|
13136
|
+
tree: owner.tree,
|
|
13137
|
+
elementInfo: selectedMatches
|
|
13138
|
+
? canonicalElementInfoForCodeLayerNode(
|
|
13139
|
+
selectedElement,
|
|
13140
|
+
owner.node,
|
|
13141
|
+
)
|
|
13142
|
+
: elementInfoFromCodeLayerNode(owner.node),
|
|
13143
|
+
};
|
|
13144
|
+
})
|
|
13145
|
+
.filter((target): target is SelectedLayerTarget => Boolean(target)),
|
|
13146
|
+
[codeLayerOwnerByNodeId, selectedElement, selectedLayerIds],
|
|
13147
|
+
);
|
|
13148
|
+
|
|
13149
|
+
useLayoutEffect(() => {
|
|
13150
|
+
selectedLayerTargetsRef.current = selectedLayerTargets;
|
|
13151
|
+
}, [selectedLayerTargets]);
|
|
13152
|
+
|
|
13153
|
+
const selectedLayerSelectorGroupsByScreen = useMemo(() => {
|
|
13154
|
+
const groupsByScreen: Record<string, string[][]> = {};
|
|
13155
|
+
selectedLayerTargets.forEach((target) => {
|
|
13156
|
+
const selectorGroup = codeLayerSelectorAliases(target.node);
|
|
13157
|
+
if (selectorGroup.length === 0) return;
|
|
13158
|
+
groupsByScreen[target.fileId] = [
|
|
13159
|
+
...(groupsByScreen[target.fileId] ?? []),
|
|
13160
|
+
selectorGroup,
|
|
13161
|
+
];
|
|
13162
|
+
});
|
|
13163
|
+
return groupsByScreen;
|
|
13164
|
+
}, [selectedLayerTargets]);
|
|
13165
|
+
|
|
13166
|
+
const selectedInspectorElements = useMemo(
|
|
13167
|
+
() =>
|
|
13168
|
+
selectedLayerTargets.length > 0
|
|
13169
|
+
? selectedLayerTargets.map((target) => target.elementInfo)
|
|
13170
|
+
: selectedElement
|
|
13171
|
+
? [selectedElement]
|
|
13172
|
+
: [],
|
|
13173
|
+
[selectedElement, selectedLayerTargets],
|
|
13174
|
+
);
|
|
13175
|
+
|
|
12367
13176
|
const layerPanelSelectedIds = useMemo(
|
|
12368
13177
|
() =>
|
|
12369
13178
|
viewMode === "overview" && createdOverviewLayerSelection
|
|
@@ -13158,7 +13967,7 @@ ${serializedHtml}
|
|
|
13158
13967
|
const handleLayerSelectionChange = useCallback(
|
|
13159
13968
|
(
|
|
13160
13969
|
ids: string[],
|
|
13161
|
-
|
|
13970
|
+
_intent: {
|
|
13162
13971
|
additive: boolean;
|
|
13163
13972
|
currentSelectedIds?: string[];
|
|
13164
13973
|
id: string;
|
|
@@ -13170,40 +13979,30 @@ ${serializedHtml}
|
|
|
13170
13979
|
pendingOverviewLayerSelectionRef.current = null;
|
|
13171
13980
|
clearPendingOverviewLayerSelectionTimer();
|
|
13172
13981
|
setCreatedOverviewLayerSelection(null);
|
|
13173
|
-
if (intent.additive && !intent.range) {
|
|
13174
|
-
const currentLayerIds = (
|
|
13175
|
-
intent.currentSelectedIds && intent.currentSelectedIds.length > 0
|
|
13176
|
-
? intent.currentSelectedIds
|
|
13177
|
-
: selectedLayerIdsRef.current
|
|
13178
|
-
).filter((layerId) => !layerId.startsWith("__"));
|
|
13179
|
-
const additiveLayerIds = currentLayerIds.includes(intent.id)
|
|
13180
|
-
? currentLayerIds.filter((layerId) => layerId !== intent.id)
|
|
13181
|
-
: [...currentLayerIds, intent.id];
|
|
13182
|
-
setSelectedLayerIdsState(additiveLayerIds);
|
|
13183
|
-
if (viewModeRef.current === "overview") {
|
|
13184
|
-
const fileIds = files.map((file) => file.id);
|
|
13185
|
-
const selectedScreenIds = getOverviewScreenIdsFromLayerSelection({
|
|
13186
|
-
fileIds,
|
|
13187
|
-
layerIds: additiveLayerIds,
|
|
13188
|
-
});
|
|
13189
|
-
setOverviewSelectedScreenIds(selectedScreenIds);
|
|
13190
|
-
}
|
|
13191
|
-
setSelectedElement(null);
|
|
13192
|
-
focusDesignInspectorForSelection();
|
|
13193
|
-
setActiveTool("move");
|
|
13194
|
-
setMode("edit");
|
|
13195
|
-
return;
|
|
13196
|
-
}
|
|
13197
13982
|
setSelectedLayerIdsState(nextLayerIds);
|
|
13198
|
-
const
|
|
13983
|
+
const screenFileIds = files
|
|
13984
|
+
.filter((file) => !isBoardFile(file.filename))
|
|
13985
|
+
.map((file) => file.id);
|
|
13986
|
+
if (viewModeRef.current === "overview") {
|
|
13987
|
+
setOverviewSelectedScreenIds(
|
|
13988
|
+
getOverviewScreenIdsFromLayerSelection({
|
|
13989
|
+
fileIds: screenFileIds,
|
|
13990
|
+
layerIds: nextLayerIds,
|
|
13991
|
+
}),
|
|
13992
|
+
);
|
|
13993
|
+
}
|
|
13994
|
+
const selectedId = nextLayerIds[nextLayerIds.length - 1];
|
|
13199
13995
|
if (!selectedId) {
|
|
13200
13996
|
setSelectedElement(null);
|
|
13201
13997
|
return;
|
|
13202
13998
|
}
|
|
13203
13999
|
const codeLayerOwner = codeLayerOwnerByNodeId.get(selectedId);
|
|
13204
14000
|
if (codeLayerOwner) {
|
|
14001
|
+
const ownerIsScreenFile = screenFileIds.includes(codeLayerOwner.fileId);
|
|
13205
14002
|
if (viewModeRef.current === "overview") {
|
|
13206
|
-
pendingOverviewScreenSelectionRef.current =
|
|
14003
|
+
pendingOverviewScreenSelectionRef.current = ownerIsScreenFile
|
|
14004
|
+
? codeLayerOwner.fileId
|
|
14005
|
+
: null;
|
|
13207
14006
|
pendingOverviewLayerSelectionRef.current = selectedId;
|
|
13208
14007
|
}
|
|
13209
14008
|
if (codeLayerOwner.fileId !== activeFile?.id) {
|
|
@@ -13213,6 +14012,7 @@ ${serializedHtml}
|
|
|
13213
14012
|
currentViewMode: viewModeRef.current,
|
|
13214
14013
|
ownerFileId: codeLayerOwner.fileId,
|
|
13215
14014
|
overviewSelectedScreenIds,
|
|
14015
|
+
screenFileIds,
|
|
13216
14016
|
});
|
|
13217
14017
|
viewModeRef.current = nextSelectionState.viewMode;
|
|
13218
14018
|
setViewMode(nextSelectionState.viewMode);
|
|
@@ -13243,11 +14043,19 @@ ${serializedHtml}
|
|
|
13243
14043
|
const fileId = selectedId.startsWith("code:")
|
|
13244
14044
|
? selectedId.slice("code:".length)
|
|
13245
14045
|
: selectedId;
|
|
13246
|
-
if (
|
|
14046
|
+
if (
|
|
14047
|
+
files.some((file) => file.id === fileId && !isBoardFile(file.filename))
|
|
14048
|
+
) {
|
|
13247
14049
|
setOverviewSelectedScreenIds([fileId]);
|
|
13248
14050
|
setActiveFileId(fileId);
|
|
13249
14051
|
setSelectedElement(null);
|
|
13250
|
-
setSelectedLayerIdsState(
|
|
14052
|
+
setSelectedLayerIdsState(
|
|
14053
|
+
nextLayerIds.some((layerId) =>
|
|
14054
|
+
files.some((file) => file.id === layerId),
|
|
14055
|
+
)
|
|
14056
|
+
? nextLayerIds
|
|
14057
|
+
: [fileId],
|
|
14058
|
+
);
|
|
13251
14059
|
setActiveTool("move");
|
|
13252
14060
|
setMode("edit");
|
|
13253
14061
|
viewModeRef.current = "overview";
|
|
@@ -13265,6 +14073,113 @@ ${serializedHtml}
|
|
|
13265
14073
|
],
|
|
13266
14074
|
);
|
|
13267
14075
|
|
|
14076
|
+
const handleLayerMarqueeSelectionChange = useCallback(
|
|
14077
|
+
(
|
|
14078
|
+
selection: CanvasLayerMarqueeSelection[],
|
|
14079
|
+
intent: ElementSelectionIntent,
|
|
14080
|
+
) => {
|
|
14081
|
+
pendingOverviewScreenSelectionRef.current = null;
|
|
14082
|
+
pendingOverviewLayerSelectionRef.current = null;
|
|
14083
|
+
clearPendingOverviewLayerSelectionTimer();
|
|
14084
|
+
setCreatedOverviewLayerSelection(null);
|
|
14085
|
+
|
|
14086
|
+
const resolved = selection
|
|
14087
|
+
.map((item) => {
|
|
14088
|
+
const projection = getCodeLayerProjectionForScreen(item.screenId);
|
|
14089
|
+
if (!projection) return null;
|
|
14090
|
+
const canonical = canonicalizeElementInfoFromProjection(
|
|
14091
|
+
projection,
|
|
14092
|
+
item.info,
|
|
14093
|
+
);
|
|
14094
|
+
const node = resolveCodeLayerNodeFromElementInfo(
|
|
14095
|
+
projection,
|
|
14096
|
+
canonical,
|
|
14097
|
+
);
|
|
14098
|
+
if (!node || isScreenRootElementInfo(canonical)) return null;
|
|
14099
|
+
return {
|
|
14100
|
+
screenId: item.screenId,
|
|
14101
|
+
node,
|
|
14102
|
+
elementInfo: canonical,
|
|
14103
|
+
};
|
|
14104
|
+
})
|
|
14105
|
+
.filter(
|
|
14106
|
+
(
|
|
14107
|
+
item,
|
|
14108
|
+
): item is {
|
|
14109
|
+
screenId: string;
|
|
14110
|
+
node: CodeLayerNode;
|
|
14111
|
+
elementInfo: ElementInfo;
|
|
14112
|
+
} => Boolean(item),
|
|
14113
|
+
);
|
|
14114
|
+
|
|
14115
|
+
const hitLayerIds = dedupeStringIds(resolved.map((item) => item.node.id));
|
|
14116
|
+
setSelectedLayerIdsState((current) =>
|
|
14117
|
+
intent.additive
|
|
14118
|
+
? dedupeStringIds([
|
|
14119
|
+
...current.filter((layerId) => !layerId.startsWith("__")),
|
|
14120
|
+
...hitLayerIds,
|
|
14121
|
+
])
|
|
14122
|
+
: hitLayerIds,
|
|
14123
|
+
);
|
|
14124
|
+
if (viewModeRef.current === "overview") {
|
|
14125
|
+
setOverviewSelectedScreenIds([]);
|
|
14126
|
+
}
|
|
14127
|
+
|
|
14128
|
+
const primary = resolved[resolved.length - 1];
|
|
14129
|
+
if (primary) {
|
|
14130
|
+
setActiveFileId(primary.screenId);
|
|
14131
|
+
setSelectedElement(primary.elementInfo);
|
|
14132
|
+
focusDesignInspectorForSelection();
|
|
14133
|
+
} else if (!intent.additive) {
|
|
14134
|
+
setSelectedElement(null);
|
|
14135
|
+
}
|
|
14136
|
+
|
|
14137
|
+
setActiveTool("move");
|
|
14138
|
+
setMode("edit");
|
|
14139
|
+
},
|
|
14140
|
+
[
|
|
14141
|
+
clearPendingOverviewLayerSelectionTimer,
|
|
14142
|
+
focusDesignInspectorForSelection,
|
|
14143
|
+
getCodeLayerProjectionForScreen,
|
|
14144
|
+
],
|
|
14145
|
+
);
|
|
14146
|
+
|
|
14147
|
+
const handleScreenElementMarqueeSelect = useCallback(
|
|
14148
|
+
(
|
|
14149
|
+
screenId: string,
|
|
14150
|
+
infos: ElementInfo[],
|
|
14151
|
+
intent?: ElementSelectionIntent,
|
|
14152
|
+
) => {
|
|
14153
|
+
handleLayerMarqueeSelectionChange(
|
|
14154
|
+
infos.map((info) => ({ screenId, info })),
|
|
14155
|
+
{
|
|
14156
|
+
additive: Boolean(
|
|
14157
|
+
intent?.additive ||
|
|
14158
|
+
intent?.range ||
|
|
14159
|
+
intent?.shiftKey ||
|
|
14160
|
+
intent?.metaKey ||
|
|
14161
|
+
intent?.ctrlKey,
|
|
14162
|
+
),
|
|
14163
|
+
range: Boolean(intent?.range || intent?.shiftKey),
|
|
14164
|
+
source: "marquee",
|
|
14165
|
+
shiftKey: Boolean(intent?.shiftKey),
|
|
14166
|
+
metaKey: Boolean(intent?.metaKey),
|
|
14167
|
+
ctrlKey: Boolean(intent?.ctrlKey),
|
|
14168
|
+
},
|
|
14169
|
+
);
|
|
14170
|
+
},
|
|
14171
|
+
[handleLayerMarqueeSelectionChange],
|
|
14172
|
+
);
|
|
14173
|
+
|
|
14174
|
+
const handleElementMarqueeSelect = useCallback(
|
|
14175
|
+
(infos: ElementInfo[], intent?: ElementSelectionIntent) => {
|
|
14176
|
+
const screenId = activeFile?.id ?? activeFileId;
|
|
14177
|
+
if (!screenId) return;
|
|
14178
|
+
handleScreenElementMarqueeSelect(screenId, infos, intent);
|
|
14179
|
+
},
|
|
14180
|
+
[activeFile?.id, activeFileId, handleScreenElementMarqueeSelect],
|
|
14181
|
+
);
|
|
14182
|
+
|
|
13268
14183
|
const handleLayerRename = useCallback(
|
|
13269
14184
|
(layerId: string, name: string) => {
|
|
13270
14185
|
if (!canEditDesign) return;
|
|
@@ -13518,7 +14433,12 @@ ${serializedHtml}
|
|
|
13518
14433
|
if (!id) return null;
|
|
13519
14434
|
|
|
13520
14435
|
if (designLoading || (!design && pendingGenerationActive)) {
|
|
13521
|
-
return
|
|
14436
|
+
return (
|
|
14437
|
+
<DesignEditorSkeleton
|
|
14438
|
+
embedded={embedded}
|
|
14439
|
+
pendingGeneration={pendingGenerationActive}
|
|
14440
|
+
/>
|
|
14441
|
+
);
|
|
13522
14442
|
}
|
|
13523
14443
|
|
|
13524
14444
|
if (!design) {
|
|
@@ -13533,7 +14453,7 @@ ${serializedHtml}
|
|
|
13533
14453
|
className="absolute inset-x-0 top-0 h-px bg-[var(--design-editor-panel-divider-color)]"
|
|
13534
14454
|
/>
|
|
13535
14455
|
<div className="relative flex w-full max-w-sm flex-col items-center text-center">
|
|
13536
|
-
<div className="mb-2 text-[11px] font-medium uppercase text-muted-foreground">
|
|
14456
|
+
<div className="mb-2 !text-[11px] font-medium uppercase text-muted-foreground">
|
|
13537
14457
|
404
|
|
13538
14458
|
</div>
|
|
13539
14459
|
<h1 className="text-xl font-semibold text-foreground">
|
|
@@ -13542,7 +14462,7 @@ ${serializedHtml}
|
|
|
13542
14462
|
<Button
|
|
13543
14463
|
variant="default"
|
|
13544
14464
|
onClick={() => navigate("/")}
|
|
13545
|
-
className="mt-7 h-9 cursor-pointer gap-2 rounded-md border border-
|
|
14465
|
+
className="mt-7 h-9 cursor-pointer gap-2 rounded-md border border-foreground bg-foreground px-3.5 text-background shadow-sm hover:border-foreground/90 hover:bg-foreground/90 hover:text-background focus-visible:ring-foreground"
|
|
13546
14466
|
>
|
|
13547
14467
|
<IconArrowLeft className="size-4 rtl:-scale-x-100" />
|
|
13548
14468
|
{t("designEditor.backToDesigns")}
|
|
@@ -13847,30 +14767,39 @@ ${serializedHtml}
|
|
|
13847
14767
|
<Tooltip>
|
|
13848
14768
|
<TooltipTrigger asChild>
|
|
13849
14769
|
<Button
|
|
14770
|
+
asChild
|
|
13850
14771
|
variant="outline"
|
|
13851
14772
|
size="sm"
|
|
13852
|
-
|
|
13853
|
-
className="h-8 max-w-[13rem] cursor-pointer gap-1.5 truncate rounded-md bg-[var(--design-editor-panel-raised-bg)] px-2 text-xs shadow-none"
|
|
14773
|
+
className="h-8 cursor-pointer gap-1.5 rounded-md bg-[var(--design-editor-panel-raised-bg)] px-3 text-sm shadow-none"
|
|
13854
14774
|
aria-label={t("designEditor.signUpToSave")}
|
|
13855
14775
|
>
|
|
13856
|
-
<
|
|
13857
|
-
|
|
14776
|
+
<a
|
|
14777
|
+
href={signInToSaveHref}
|
|
14778
|
+
target="_blank"
|
|
14779
|
+
rel="noopener noreferrer"
|
|
14780
|
+
>
|
|
14781
|
+
<span>{t("home.save")}</span>
|
|
14782
|
+
</a>
|
|
13858
14783
|
</Button>
|
|
13859
14784
|
</TooltipTrigger>
|
|
13860
|
-
<TooltipContent>
|
|
13861
|
-
{t("designEditor.signUpToSaveDescription")}
|
|
13862
|
-
</TooltipContent>
|
|
14785
|
+
<TooltipContent>{t("designEditor.signUpToSave")}</TooltipContent>
|
|
13863
14786
|
</Tooltip>
|
|
13864
14787
|
<Tooltip>
|
|
13865
14788
|
<TooltipTrigger asChild>
|
|
13866
14789
|
<Button
|
|
14790
|
+
asChild
|
|
13867
14791
|
variant="default"
|
|
13868
14792
|
size="sm"
|
|
13869
|
-
onClick={handleSignInToShare}
|
|
13870
14793
|
className="h-8 cursor-pointer gap-1.5 rounded-md !border-[var(--design-editor-accent-color)] !bg-[var(--design-editor-accent-color)] px-3 text-sm !text-[var(--design-editor-accent-contrast-color)] shadow-none hover:!border-[var(--design-editor-accent-hover-color)] hover:!bg-[var(--design-editor-accent-hover-color)] hover:!text-[var(--design-editor-accent-contrast-color)] focus-visible:ring-[var(--design-editor-accent-color)]"
|
|
14794
|
+
aria-label={t("designEditor.signUpToShare")}
|
|
13871
14795
|
>
|
|
13872
|
-
<
|
|
13873
|
-
|
|
14796
|
+
<a
|
|
14797
|
+
href={signInToShareHref}
|
|
14798
|
+
target="_blank"
|
|
14799
|
+
rel="noopener noreferrer"
|
|
14800
|
+
>
|
|
14801
|
+
<span>{t("designEditor.share")}</span>
|
|
14802
|
+
</a>
|
|
13874
14803
|
</Button>
|
|
13875
14804
|
</TooltipTrigger>
|
|
13876
14805
|
<TooltipContent>{t("designEditor.signUpToShare")}</TooltipContent>
|
|
@@ -14011,7 +14940,7 @@ ${serializedHtml}
|
|
|
14011
14940
|
</PopoverContent>
|
|
14012
14941
|
</Popover>
|
|
14013
14942
|
|
|
14014
|
-
{
|
|
14943
|
+
{canRenderAuthenticatedShare ? (
|
|
14015
14944
|
<ShareButton
|
|
14016
14945
|
resourceType="design"
|
|
14017
14946
|
resourceId={id}
|
|
@@ -14025,40 +14954,15 @@ ${serializedHtml}
|
|
|
14025
14954
|
popoverClassName={designSharePopoverClassName}
|
|
14026
14955
|
triggerClassName="h-8 rounded-md !border-[var(--design-editor-accent-color)] !bg-[var(--design-editor-accent-color)] px-3 text-sm !text-[var(--design-editor-accent-contrast-color)] shadow-none hover:!border-[var(--design-editor-accent-hover-color)] hover:!bg-[var(--design-editor-accent-hover-color)] hover:!text-[var(--design-editor-accent-contrast-color)] focus-visible:ring-[var(--design-editor-accent-color)] [&_svg]:!text-[var(--design-editor-accent-contrast-color)]"
|
|
14027
14956
|
/>
|
|
14028
|
-
) : (
|
|
14957
|
+
) : sessionResolved ? (
|
|
14029
14958
|
signedOutPersistenceActions
|
|
14030
|
-
)}
|
|
14959
|
+
) : null}
|
|
14031
14960
|
</div>
|
|
14032
14961
|
</div>
|
|
14033
14962
|
</div>
|
|
14034
14963
|
);
|
|
14035
14964
|
|
|
14036
14965
|
const leftContentWidth = Math.max(leftSidebarWidth, 320);
|
|
14037
|
-
const motionDockLauncherHidden = motionDockMounted || motionDockOpen;
|
|
14038
|
-
const motionDockLauncher =
|
|
14039
|
-
!embedded && activeFile ? (
|
|
14040
|
-
<div
|
|
14041
|
-
aria-hidden={motionDockLauncherHidden ? true : undefined}
|
|
14042
|
-
className={cn(
|
|
14043
|
-
"overflow-hidden border-t border-[var(--design-editor-panel-divider-color)] transition-[max-height,opacity,border-color] duration-200 ease-out",
|
|
14044
|
-
motionDockLauncherHidden
|
|
14045
|
-
? "max-h-0 border-transparent opacity-0"
|
|
14046
|
-
: "max-h-9 opacity-100",
|
|
14047
|
-
)}
|
|
14048
|
-
>
|
|
14049
|
-
<button
|
|
14050
|
-
type="button"
|
|
14051
|
-
aria-label={"Expand motion dock" /* i18n-ignore */}
|
|
14052
|
-
className="flex h-8 w-full cursor-pointer items-center gap-2 bg-[var(--design-editor-panel-bg)] px-3 text-left text-[11px] font-medium uppercase tracking-wide text-muted-foreground outline-none transition-colors hover:bg-[var(--design-editor-panel-raised-bg)] hover:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-[var(--design-editor-accent-color)] disabled:cursor-default"
|
|
14053
|
-
disabled={motionDockLauncherHidden}
|
|
14054
|
-
onClick={() => setMotionDockOpenAnimated(true)}
|
|
14055
|
-
>
|
|
14056
|
-
<IconChevronRight className="size-3.5 shrink-0" />
|
|
14057
|
-
<span className="min-w-0 flex-1 truncate">Motion</span>
|
|
14058
|
-
</button>
|
|
14059
|
-
</div>
|
|
14060
|
-
) : null;
|
|
14061
|
-
|
|
14062
14966
|
return (
|
|
14063
14967
|
// h-full not flex-1: the parent <main> uses overflow-y-auto, not flex,
|
|
14064
14968
|
// so flex-1 on the child doesn't resolve to the available height. h-full
|
|
@@ -14434,7 +15338,7 @@ ${serializedHtml}
|
|
|
14434
15338
|
/>
|
|
14435
15339
|
)}
|
|
14436
15340
|
|
|
14437
|
-
{!embedded &&
|
|
15341
|
+
{!embedded && canRenderAuthenticatedShare ? (
|
|
14438
15342
|
<ShareButton
|
|
14439
15343
|
resourceType="design"
|
|
14440
15344
|
resourceId={id}
|
|
@@ -14449,7 +15353,7 @@ ${serializedHtml}
|
|
|
14449
15353
|
popoverClassName={designSharePopoverClassName}
|
|
14450
15354
|
triggerClassName="h-8 rounded-md !border-[var(--design-editor-accent-color)] !bg-[var(--design-editor-accent-color)] px-3 !text-[var(--design-editor-accent-contrast-color)] shadow-none hover:!border-[var(--design-editor-accent-hover-color)] hover:!bg-[var(--design-editor-accent-hover-color)] hover:!text-[var(--design-editor-accent-contrast-color)] focus-visible:ring-[var(--design-editor-accent-color)] [&_svg]:!text-[var(--design-editor-accent-contrast-color)]"
|
|
14451
15355
|
/>
|
|
14452
|
-
) : !embedded ? (
|
|
15356
|
+
) : !embedded && sessionResolved ? (
|
|
14453
15357
|
signedOutPersistenceActions
|
|
14454
15358
|
) : null}
|
|
14455
15359
|
</div>
|
|
@@ -14462,7 +15366,15 @@ ${serializedHtml}
|
|
|
14462
15366
|
<div className="relative flex min-h-0 shrink-0 border-r border-[var(--design-editor-panel-divider-color)] bg-[var(--design-editor-panel-bg)]">
|
|
14463
15367
|
<DesignWorkspaceRail
|
|
14464
15368
|
activePanel={activeLeftPanel}
|
|
15369
|
+
disabledPanels={
|
|
15370
|
+
initialGenerationChromeLimited
|
|
15371
|
+
? INITIAL_GENERATION_DISABLED_LEFT_PANELS
|
|
15372
|
+
: undefined
|
|
15373
|
+
}
|
|
15374
|
+
motionOpen={motionDockOpen}
|
|
15375
|
+
motionDisabled={!activeFile}
|
|
14465
15376
|
projectMenu={projectMenu}
|
|
15377
|
+
onMotionToggle={() => setMotionDockOpenAnimated(!motionDockOpen)}
|
|
14466
15378
|
onPanelChange={setActiveLeftPanel}
|
|
14467
15379
|
/>
|
|
14468
15380
|
<div
|
|
@@ -14496,7 +15408,6 @@ ${serializedHtml}
|
|
|
14496
15408
|
selectedIds={layerPanelSelectedIds}
|
|
14497
15409
|
expandedIds={layerPanelExpandedIds}
|
|
14498
15410
|
searchQuery={layersSearchQuery}
|
|
14499
|
-
footer={motionDockLauncher}
|
|
14500
15411
|
onScreenSelect={handleSidebarScreenSelect}
|
|
14501
15412
|
onScreenOverview={handleSidebarScreenOverview}
|
|
14502
15413
|
onAddScreen={handleAddScreen}
|
|
@@ -14526,6 +15437,7 @@ ${serializedHtml}
|
|
|
14526
15437
|
<AgentChatSurface
|
|
14527
15438
|
mode="panel"
|
|
14528
15439
|
className="min-h-0 flex-1 border-0 bg-transparent shadow-none"
|
|
15440
|
+
storageKey={DESIGN_CHAT_STORAGE_KEY}
|
|
14529
15441
|
emptyStateText={t("chat.emptyState")}
|
|
14530
15442
|
suggestions={[
|
|
14531
15443
|
t("chat.suggestionLandingPage"),
|
|
@@ -14708,18 +15620,6 @@ ${serializedHtml}
|
|
|
14708
15620
|
>
|
|
14709
15621
|
{activeFile ? (
|
|
14710
15622
|
<div className="flex min-w-0 flex-1 flex-col overflow-hidden">
|
|
14711
|
-
{/* Banner for screens connected to a local dev server: edits
|
|
14712
|
-
route through the agent rather than being applied inline. */}
|
|
14713
|
-
{activeScreenIsLocalSource &&
|
|
14714
|
-
!localSourceBannerDismissed &&
|
|
14715
|
-
id && (
|
|
14716
|
-
<LocalSourceEditBanner
|
|
14717
|
-
designId={id}
|
|
14718
|
-
fileId={activeFile.id}
|
|
14719
|
-
routeSourceFile={activeScreenRouteSourceFile}
|
|
14720
|
-
onDismiss={() => setLocalSourceBannerDismissed(true)}
|
|
14721
|
-
/>
|
|
14722
|
-
)}
|
|
14723
15623
|
{/* "Apply to source" affordance: write the current editor
|
|
14724
15624
|
content back to the local HTML/CSS file via the bridge.
|
|
14725
15625
|
Only shown for localhost-backed screens where the route
|
|
@@ -14730,7 +15630,7 @@ ${serializedHtml}
|
|
|
14730
15630
|
<Button
|
|
14731
15631
|
size="sm"
|
|
14732
15632
|
variant="outline"
|
|
14733
|
-
className="h-6 gap-1.5 px-2 text-[11px]"
|
|
15633
|
+
className="h-6 gap-1.5 px-2 !text-[11px]"
|
|
14734
15634
|
disabled={applyToSourcePending}
|
|
14735
15635
|
onClick={handleApplyToSource}
|
|
14736
15636
|
>
|
|
@@ -14769,7 +15669,7 @@ ${serializedHtml}
|
|
|
14769
15669
|
<MultiScreenCanvas
|
|
14770
15670
|
screens={overviewScreens}
|
|
14771
15671
|
zoom={overviewCanvasZoom}
|
|
14772
|
-
onZoomChange={
|
|
15672
|
+
onZoomChange={setExplicitOverviewCanvasZoom}
|
|
14773
15673
|
activeId={activeFileId}
|
|
14774
15674
|
selectedScreenIds={overviewSelectedScreenIds}
|
|
14775
15675
|
fullViewScreenIds={fullViewScreenIds}
|
|
@@ -14798,16 +15698,68 @@ ${serializedHtml}
|
|
|
14798
15698
|
onPrimitiveReparent={handleOverviewPrimitiveReparent}
|
|
14799
15699
|
onCrossScreenElementDrop={handleCrossScreenElementDrop}
|
|
14800
15700
|
boardFileId={boardFileId}
|
|
15701
|
+
boardIsActive={activeFileId === boardFileId}
|
|
14801
15702
|
boardFileContent={boardFileContent}
|
|
14802
15703
|
boardFrameGeometry={boardFrameGeometry}
|
|
15704
|
+
boardClearSelectionRequest={overviewClearSelectionRequest}
|
|
15705
|
+
boardSelectedSelector={
|
|
15706
|
+
activeFileId === boardFileId
|
|
15707
|
+
? selectedCanvasSelector
|
|
15708
|
+
: null
|
|
15709
|
+
}
|
|
15710
|
+
boardSelectedSelectorCandidates={
|
|
15711
|
+
activeFileId === boardFileId
|
|
15712
|
+
? selectedCanvasSelectorCandidates
|
|
15713
|
+
: []
|
|
15714
|
+
}
|
|
15715
|
+
boardHoveredSelector={
|
|
15716
|
+
hoveredElementScreenId === boardFileId
|
|
15717
|
+
? hoveredCanvasSelector
|
|
15718
|
+
: null
|
|
15719
|
+
}
|
|
15720
|
+
boardHoveredSelectorCandidates={
|
|
15721
|
+
hoveredElementScreenId === boardFileId
|
|
15722
|
+
? hoveredCanvasSelectorCandidates
|
|
15723
|
+
: []
|
|
15724
|
+
}
|
|
15725
|
+
boardLockedSelectors={
|
|
15726
|
+
boardFileId
|
|
15727
|
+
? getLayerSelectorsForFile(
|
|
15728
|
+
boardFileId,
|
|
15729
|
+
lockedLayerIds,
|
|
15730
|
+
)
|
|
15731
|
+
: []
|
|
15732
|
+
}
|
|
15733
|
+
boardHiddenSelectors={
|
|
15734
|
+
boardFileId
|
|
15735
|
+
? getLayerSelectorsForFile(
|
|
15736
|
+
boardFileId,
|
|
15737
|
+
hiddenLayerIds,
|
|
15738
|
+
)
|
|
15739
|
+
: []
|
|
15740
|
+
}
|
|
14803
15741
|
onBoardDrawPrimitive={
|
|
14804
15742
|
canEditDesign ? handleBoardDrawPrimitive : undefined
|
|
14805
15743
|
}
|
|
14806
15744
|
boardEditMode={canEditDesign}
|
|
14807
15745
|
onBoardElementSelect={
|
|
14808
15746
|
boardFileId
|
|
14809
|
-
? (info) =>
|
|
14810
|
-
handleScreenElementSelect(
|
|
15747
|
+
? (info, intent) =>
|
|
15748
|
+
handleScreenElementSelect(
|
|
15749
|
+
boardFileId,
|
|
15750
|
+
info,
|
|
15751
|
+
intent,
|
|
15752
|
+
)
|
|
15753
|
+
: undefined
|
|
15754
|
+
}
|
|
15755
|
+
onBoardElementMarqueeSelect={
|
|
15756
|
+
boardFileId
|
|
15757
|
+
? (infos, intent) =>
|
|
15758
|
+
handleScreenElementMarqueeSelect(
|
|
15759
|
+
boardFileId,
|
|
15760
|
+
infos,
|
|
15761
|
+
intent,
|
|
15762
|
+
)
|
|
14811
15763
|
: undefined
|
|
14812
15764
|
}
|
|
14813
15765
|
onBoardElementHover={
|
|
@@ -14816,6 +15768,20 @@ ${serializedHtml}
|
|
|
14816
15768
|
handleScreenElementHover(boardFileId, info)
|
|
14817
15769
|
: undefined
|
|
14818
15770
|
}
|
|
15771
|
+
onBoardElementClear={
|
|
15772
|
+
boardFileId
|
|
15773
|
+
? () => handleScreenElementClear(boardFileId)
|
|
15774
|
+
: undefined
|
|
15775
|
+
}
|
|
15776
|
+
onBoardIframeHotkey={handleIframeHotkey}
|
|
15777
|
+
onBoardIframeContextMenu={handleIframeContextMenu}
|
|
15778
|
+
onBoardTextEditingStateChange={setTextEditingState}
|
|
15779
|
+
onBoardElementDblClickText={
|
|
15780
|
+
boardFileId
|
|
15781
|
+
? (info) =>
|
|
15782
|
+
handleScreenElementDblClickText(boardFileId, info)
|
|
15783
|
+
: undefined
|
|
15784
|
+
}
|
|
14819
15785
|
onBoardVisualStyleChange={
|
|
14820
15786
|
boardFileId
|
|
14821
15787
|
? (selector, styles, info) =>
|
|
@@ -14873,6 +15839,12 @@ ${serializedHtml}
|
|
|
14873
15839
|
onCreateScreenFrame={handleCreateScreenFrame}
|
|
14874
15840
|
onDeleteSelection={handleDeleteOverviewSelection}
|
|
14875
15841
|
onSelectionChange={handleOverviewScreenSelectionChange}
|
|
15842
|
+
onLayerMarqueeSelectionChange={
|
|
15843
|
+
handleLayerMarqueeSelectionChange
|
|
15844
|
+
}
|
|
15845
|
+
selectedLayerSelectorGroupsByScreen={
|
|
15846
|
+
selectedLayerSelectorGroupsByScreen
|
|
15847
|
+
}
|
|
14876
15848
|
onPick={(id) => {
|
|
14877
15849
|
pendingOverviewScreenSelectionRef.current = null;
|
|
14878
15850
|
pendingOverviewLayerSelectionRef.current = null;
|
|
@@ -14941,6 +15913,13 @@ ${serializedHtml}
|
|
|
14941
15913
|
renderScreenContent={(screen, metadata, geometry) => {
|
|
14942
15914
|
const screenIsActive = screen.id === activeFile?.id;
|
|
14943
15915
|
const screenContent = getScreenContent(screen.id);
|
|
15916
|
+
const screenSourceType =
|
|
15917
|
+
normalizeDesignSourceType(screen.sourceType) ??
|
|
15918
|
+
metadata.source ??
|
|
15919
|
+
designSourceType;
|
|
15920
|
+
const screenBridgeUrl = screen.bridgeUrl;
|
|
15921
|
+
const screenSnapshot =
|
|
15922
|
+
liveScreenSnapshotsById[screen.id]?.html;
|
|
14944
15923
|
const screenContentKey = screenIsActive
|
|
14945
15924
|
? [screen.id, contentRenderRevision].join(":")
|
|
14946
15925
|
: [
|
|
@@ -14957,7 +15936,15 @@ ${serializedHtml}
|
|
|
14957
15936
|
screenId={screen.id}
|
|
14958
15937
|
zoom={100}
|
|
14959
15938
|
deviceFrame="none"
|
|
14960
|
-
sourceType={
|
|
15939
|
+
sourceType={screenSourceType}
|
|
15940
|
+
bridgeUrl={screenBridgeUrl}
|
|
15941
|
+
externalSnapshotHtml={screenSnapshot}
|
|
15942
|
+
onExternalContentSnapshot={(snapshot) =>
|
|
15943
|
+
handleScreenExternalContentSnapshot(
|
|
15944
|
+
screen.id,
|
|
15945
|
+
snapshot,
|
|
15946
|
+
)
|
|
15947
|
+
}
|
|
14961
15948
|
fusionUrl={designFusionUrl}
|
|
14962
15949
|
onComponentSourceJump={handleComponentSourceJump}
|
|
14963
15950
|
embeddedFrame={{
|
|
@@ -14983,7 +15970,7 @@ ${serializedHtml}
|
|
|
14983
15970
|
editorChromeScaleY={overviewCanvasZoom / 100}
|
|
14984
15971
|
editMode={mode === "edit"}
|
|
14985
15972
|
interactMode={false}
|
|
14986
|
-
readOnly={!
|
|
15973
|
+
readOnly={!canEditDesign}
|
|
14987
15974
|
scaleMode={screenIsActive && activeTool === "scale"}
|
|
14988
15975
|
clearSelectionRequest={
|
|
14989
15976
|
overviewClearSelectionRequest
|
|
@@ -14997,6 +15984,10 @@ ${serializedHtml}
|
|
|
14997
15984
|
? selectedCanvasSelectorCandidates
|
|
14998
15985
|
: []
|
|
14999
15986
|
}
|
|
15987
|
+
selectedSelectorGroups={
|
|
15988
|
+
selectedLayerSelectorGroupsByScreen[screen.id] ??
|
|
15989
|
+
[]
|
|
15990
|
+
}
|
|
15000
15991
|
hoveredSelector={
|
|
15001
15992
|
hoveredElementScreenId === screen.id
|
|
15002
15993
|
? hoveredCanvasSelector
|
|
@@ -15015,8 +16006,15 @@ ${serializedHtml}
|
|
|
15015
16006
|
screen.id,
|
|
15016
16007
|
hiddenLayerIds,
|
|
15017
16008
|
)}
|
|
15018
|
-
onElementSelect={(info) =>
|
|
15019
|
-
handleScreenElementSelect(screen.id, info)
|
|
16009
|
+
onElementSelect={(info, intent) =>
|
|
16010
|
+
handleScreenElementSelect(screen.id, info, intent)
|
|
16011
|
+
}
|
|
16012
|
+
onElementMarqueeSelect={(infos, intent) =>
|
|
16013
|
+
handleScreenElementMarqueeSelect(
|
|
16014
|
+
screen.id,
|
|
16015
|
+
infos,
|
|
16016
|
+
intent,
|
|
16017
|
+
)
|
|
15020
16018
|
}
|
|
15021
16019
|
onElementHover={(info) =>
|
|
15022
16020
|
handleScreenElementHover(screen.id, info)
|
|
@@ -15101,7 +16099,16 @@ ${serializedHtml}
|
|
|
15101
16099
|
zoom={zoom}
|
|
15102
16100
|
onZoomChange={setZoom}
|
|
15103
16101
|
deviceFrame={deviceFrame}
|
|
15104
|
-
sourceType={
|
|
16102
|
+
sourceType={activeCanvasSourceType}
|
|
16103
|
+
bridgeUrl={activeScreenBridgeUrl}
|
|
16104
|
+
externalSnapshotHtml={activeScreenExternalSnapshotHtml}
|
|
16105
|
+
onExternalContentSnapshot={(snapshot) => {
|
|
16106
|
+
if (!activeFile?.id) return;
|
|
16107
|
+
handleScreenExternalContentSnapshot(
|
|
16108
|
+
activeFile.id,
|
|
16109
|
+
snapshot,
|
|
16110
|
+
);
|
|
16111
|
+
}}
|
|
15105
16112
|
fusionUrl={designFusionUrl}
|
|
15106
16113
|
previewWidthPx={activeBreakpointWidthState}
|
|
15107
16114
|
shaderFillPreview={shaderFillPreview}
|
|
@@ -15116,6 +16123,13 @@ ${serializedHtml}
|
|
|
15116
16123
|
selectedSelectorCandidates={
|
|
15117
16124
|
selectedCanvasSelectorCandidates
|
|
15118
16125
|
}
|
|
16126
|
+
selectedSelectorGroups={
|
|
16127
|
+
activeFile
|
|
16128
|
+
? (selectedLayerSelectorGroupsByScreen[
|
|
16129
|
+
activeFile.id
|
|
16130
|
+
] ?? [])
|
|
16131
|
+
: []
|
|
16132
|
+
}
|
|
15119
16133
|
hoveredSelector={hoveredCanvasSelector}
|
|
15120
16134
|
hoveredSelectorCandidates={
|
|
15121
16135
|
hoveredCanvasSelectorCandidates
|
|
@@ -15123,6 +16137,7 @@ ${serializedHtml}
|
|
|
15123
16137
|
lockedSelectors={lockedLayerSelectors}
|
|
15124
16138
|
hiddenSelectors={hiddenLayerSelectors}
|
|
15125
16139
|
onElementSelect={handleElementSelect}
|
|
16140
|
+
onElementMarqueeSelect={handleElementMarqueeSelect}
|
|
15126
16141
|
onElementHover={handleElementHover}
|
|
15127
16142
|
onClearSelection={() => {
|
|
15128
16143
|
setSelectedElement(null);
|
|
@@ -15191,18 +16206,43 @@ ${serializedHtml}
|
|
|
15191
16206
|
</div>
|
|
15192
16207
|
</div>
|
|
15193
16208
|
) : (
|
|
15194
|
-
<div className="flex flex-1 items-center justify-center">
|
|
15195
|
-
<div className="text-center">
|
|
16209
|
+
<div className="flex min-h-0 flex-1 items-center justify-center px-8 py-10">
|
|
16210
|
+
<div className="flex w-full max-w-md flex-col items-center text-center">
|
|
15196
16211
|
{generating || pendingGenerationActive ? (
|
|
15197
16212
|
<>
|
|
15198
|
-
<
|
|
16213
|
+
<div className="mb-4 flex size-12 items-center justify-center rounded-xl border border-[var(--design-editor-panel-divider-color)] bg-[var(--design-editor-panel-bg)] shadow-[0_18px_50px_-34px_rgba(0,0,0,0.8)]">
|
|
16214
|
+
<Spinner className="size-5 text-foreground/40" />
|
|
16215
|
+
</div>
|
|
15199
16216
|
<p className="text-sm text-muted-foreground">
|
|
15200
16217
|
{t("designEditor.generating")}
|
|
15201
16218
|
</p>
|
|
15202
16219
|
</>
|
|
15203
16220
|
) : (
|
|
15204
16221
|
<>
|
|
15205
|
-
<
|
|
16222
|
+
<div
|
|
16223
|
+
aria-hidden="true"
|
|
16224
|
+
className="mb-5 w-full max-w-sm rounded-xl bg-[#f7f8fb] p-3 dark:bg-[#f4f6f8]"
|
|
16225
|
+
>
|
|
16226
|
+
<div className="flex h-7 items-center justify-between px-1 pb-2">
|
|
16227
|
+
<div className="flex gap-1.5">
|
|
16228
|
+
<span className="size-2 rounded-full bg-slate-950/[0.12]" />
|
|
16229
|
+
<span className="size-2 rounded-full bg-slate-950/[0.1]" />
|
|
16230
|
+
<span className="size-2 rounded-full bg-slate-950/[0.08]" />
|
|
16231
|
+
</div>
|
|
16232
|
+
<span className="h-2 w-16 rounded bg-slate-950/[0.08]" />
|
|
16233
|
+
</div>
|
|
16234
|
+
<div className="space-y-3 pt-4">
|
|
16235
|
+
<span className="block h-5 w-2/3 rounded bg-slate-950/[0.085]" />
|
|
16236
|
+
<span className="block h-4 w-1/2 rounded bg-slate-950/[0.07]" />
|
|
16237
|
+
<div className="grid grid-cols-3 gap-2 pt-2">
|
|
16238
|
+
<span className="h-12 rounded-md bg-slate-950/[0.07]" />
|
|
16239
|
+
<span className="h-12 rounded-md bg-slate-950/[0.07]" />
|
|
16240
|
+
<span className="h-12 rounded-md bg-slate-950/[0.07]" />
|
|
16241
|
+
</div>
|
|
16242
|
+
<span className="block h-20 rounded-lg bg-slate-950/[0.07]" />
|
|
16243
|
+
</div>
|
|
16244
|
+
</div>
|
|
16245
|
+
<p className="mb-3 text-sm font-medium text-foreground/85">
|
|
15206
16246
|
{generationIssue ?? t("designEditor.noFiles")}
|
|
15207
16247
|
</p>
|
|
15208
16248
|
{retryablePrompt ? (
|
|
@@ -15214,7 +16254,7 @@ ${serializedHtml}
|
|
|
15214
16254
|
{retryablePrompt ? (
|
|
15215
16255
|
<Button
|
|
15216
16256
|
size="sm"
|
|
15217
|
-
className="cursor-pointer"
|
|
16257
|
+
className="h-8 cursor-pointer rounded-md"
|
|
15218
16258
|
onClick={handleRetryGeneration}
|
|
15219
16259
|
>
|
|
15220
16260
|
<IconRefresh className="h-3.5 w-3.5" />
|
|
@@ -15225,7 +16265,7 @@ ${serializedHtml}
|
|
|
15225
16265
|
ref={generateBtnRef}
|
|
15226
16266
|
variant={retryablePrompt ? "ghost" : "outline"}
|
|
15227
16267
|
size="sm"
|
|
15228
|
-
className="cursor-pointer"
|
|
16268
|
+
className="h-8 cursor-pointer rounded-md"
|
|
15229
16269
|
onClick={() => {
|
|
15230
16270
|
setRetryablePrompt(null);
|
|
15231
16271
|
handlePromptOpenChange(true);
|
|
@@ -15246,7 +16286,7 @@ ${serializedHtml}
|
|
|
15246
16286
|
)}
|
|
15247
16287
|
|
|
15248
16288
|
{/* Right rail */}
|
|
15249
|
-
{!embedded ? (
|
|
16289
|
+
{!embedded && !initialGenerationChromeLimited ? (
|
|
15250
16290
|
<div
|
|
15251
16291
|
className="relative flex h-full min-h-0 shrink-0 flex-col border-l border-[var(--design-editor-panel-divider-color)] bg-[var(--design-editor-panel-bg)]"
|
|
15252
16292
|
style={{ width: rightSidebarWidth }}
|
|
@@ -15263,6 +16303,7 @@ ${serializedHtml}
|
|
|
15263
16303
|
<div className="min-h-0 flex-1">
|
|
15264
16304
|
<EditPanel
|
|
15265
16305
|
selectedElement={selectedElement}
|
|
16306
|
+
selectedElements={selectedInspectorElements}
|
|
15266
16307
|
pageStyles={pageStyles}
|
|
15267
16308
|
zoom={zoom}
|
|
15268
16309
|
headerTrailing={zoomControl}
|
|
@@ -15271,7 +16312,6 @@ ${serializedHtml}
|
|
|
15271
16312
|
onActiveTabChange={setActiveInspectorTab}
|
|
15272
16313
|
tweaks={tweaks}
|
|
15273
16314
|
tweakValues={tweakSelections}
|
|
15274
|
-
readOnly={initialGenerationReadOnly}
|
|
15275
16315
|
activeContent={activeContent}
|
|
15276
16316
|
activeFileUpdatedAt={activeFile?.updatedAt ?? null}
|
|
15277
16317
|
onComponentPropApplied={handleComponentPropApplied}
|
|
@@ -15286,16 +16326,19 @@ ${serializedHtml}
|
|
|
15286
16326
|
onRequestTweaks={handleRequestTweaks}
|
|
15287
16327
|
onStyleChange={handleStyleChange}
|
|
15288
16328
|
onStylesChange={handleStylesChange}
|
|
15289
|
-
onAutoLayoutConvert={handleAutoLayoutConvert}
|
|
15290
16329
|
onExport={handleInspectorExport}
|
|
15291
16330
|
exporting={pngExporting || svgExporting}
|
|
15292
16331
|
designId={id}
|
|
15293
16332
|
fileId={activeFile?.id}
|
|
15294
|
-
filename={activeFile?.filename}
|
|
15295
16333
|
componentNodeId={selectedComponentNodeId}
|
|
15296
16334
|
sourceCapabilities={sourceCapabilities}
|
|
16335
|
+
selectedElementAlreadyComponent={
|
|
16336
|
+
selectedElementAlreadyComponent
|
|
16337
|
+
}
|
|
15297
16338
|
onCreateComponent={
|
|
15298
|
-
id && selectedElement
|
|
16339
|
+
id && selectedElement && !selectedElementAlreadyComponent
|
|
16340
|
+
? handleCreateComponent
|
|
16341
|
+
: undefined
|
|
15299
16342
|
}
|
|
15300
16343
|
defaultComponentName={defaultComponentName}
|
|
15301
16344
|
inspectCode={inspectCodeData}
|
|
@@ -15366,6 +16409,7 @@ ${serializedHtml}
|
|
|
15366
16409
|
durationMs={motionDurationMs}
|
|
15367
16410
|
open={motionDockOpen}
|
|
15368
16411
|
onOpenChange={setMotionDockOpenAnimated}
|
|
16412
|
+
onExitComplete={handleMotionDockExitComplete}
|
|
15369
16413
|
onTracksChange={handleMotionTracksChange}
|
|
15370
16414
|
onDurationChange={handleMotionDurationChange}
|
|
15371
16415
|
canvasIframeRef={canvasIframeRef}
|