@agent-native/core 0.85.7 → 0.86.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +133 -1
- package/corpus/core/README.md +0 -11
- package/corpus/core/docs/content/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/ar-SA/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/ar-SA/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/ar-SA/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/workspace-connections.mdx +11 -11
- package/corpus/core/docs/content/locales/de-DE/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/de-DE/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/de-DE/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/de-DE/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/es-ES/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/es-ES/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/es-ES/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/fr-FR/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/fr-FR/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/hi-IN/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/hi-IN/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/ja-JP/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/ja-JP/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/ko-KR/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/ko-KR/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/pt-BR/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/pt-BR/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/zh-CN/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/zh-CN/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/cloneable-saas.mdx +0 -1
- package/corpus/core/docs/content/locales/zh-TW/plan-plugin.mdx +10 -9
- package/corpus/core/docs/content/locales/zh-TW/sharing.mdx +1 -2
- package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +2 -3
- package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +23 -7
- package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/workspace-connections.mdx +1 -1
- package/corpus/core/docs/content/plan-plugin.mdx +12 -11
- package/corpus/core/docs/content/sharing.mdx +1 -2
- package/corpus/core/docs/content/skills-guide.mdx +4 -3
- package/corpus/core/docs/content/template-plan.mdx +37 -6
- package/corpus/core/docs/content/tracking.mdx +1 -1
- package/corpus/core/docs/content/workspace-connections.mdx +1 -1
- package/corpus/core/package.json +7 -6
- package/corpus/core/src/agent/run-store.ts +1 -1
- package/corpus/core/src/chat-threads/store.ts +73 -11
- package/corpus/core/src/checkpoints/store.ts +13 -2
- package/corpus/core/src/cli/create.ts +3 -4
- package/corpus/core/src/cli/design-connect.ts +469 -9
- package/corpus/core/src/cli/index.ts +13 -0
- package/corpus/core/src/cli/plan-local.ts +281 -24
- package/corpus/core/src/cli/skills.ts +177 -21
- package/corpus/core/src/cli/templates-meta.ts +0 -13
- package/corpus/core/src/cli/visualize-repo.ts +715 -0
- package/corpus/core/src/client/AgentPanel.tsx +10 -0
- package/corpus/core/src/client/AssistantChat.tsx +194 -21
- package/corpus/core/src/client/ErrorBoundary.tsx +67 -12
- package/corpus/core/src/client/ErrorReportActions.tsx +69 -0
- package/corpus/core/src/client/FeedbackButton.tsx +8 -4
- package/corpus/core/src/client/agent-chat-adapter.ts +6 -5
- package/corpus/core/src/client/app-providers.tsx +2 -2
- package/corpus/core/src/client/collab/index.ts +31 -0
- package/corpus/core/src/client/components/LiveCursorOverlay.tsx +16 -1
- package/corpus/core/src/client/components/PresenceBar.tsx +14 -1
- package/corpus/core/src/client/components/RecentEditHighlights.tsx +219 -0
- package/corpus/core/src/client/components/RemoteSelectionRings.tsx +40 -6
- package/corpus/core/src/client/db-admin/DbAdminPage.tsx +41 -13
- package/corpus/core/src/client/db-admin/SqlEditor.tsx +10 -3
- package/corpus/core/src/client/db-admin/TableEditor.tsx +12 -5
- package/corpus/core/src/client/db-admin/useAgentSync.ts +10 -3
- package/corpus/core/src/client/db-admin/useDbAdmin.ts +65 -16
- package/corpus/core/src/client/error-reporting.ts +100 -0
- package/corpus/core/src/client/index.ts +59 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +0 -4
- package/corpus/core/src/client/org/RequireActiveOrg.tsx +12 -0
- package/corpus/core/src/client/rich-markdown-editor/SharedRichEditor.tsx +49 -32
- package/corpus/core/src/client/rich-markdown-editor/index.ts +6 -0
- package/corpus/core/src/client/rich-markdown-editor/surgical-apply.ts +192 -0
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +59 -8
- package/corpus/core/src/client/session-replay.ts +181 -15
- package/corpus/core/src/client/sse-event-processor.ts +90 -1
- package/corpus/core/src/client/use-action.ts +123 -25
- package/corpus/core/src/client/use-agent-engine-configured.ts +7 -1
- package/corpus/core/src/client/use-chat-threads.ts +18 -7
- package/corpus/core/src/client/use-db-sync.ts +104 -19
- package/corpus/core/src/client/visual-style-controls.tsx +1088 -0
- package/corpus/core/src/collab/agent-presence.ts +191 -62
- package/corpus/core/src/collab/awareness.ts +83 -6
- package/corpus/core/src/collab/client.ts +89 -146
- package/corpus/core/src/collab/index.ts +34 -0
- package/corpus/core/src/collab/recent-edits.ts +171 -0
- package/corpus/core/src/collab/undo.ts +432 -0
- package/corpus/core/src/collab/ydoc-manager.ts +68 -0
- package/corpus/core/src/connections/catalog.ts +2 -3
- package/corpus/core/src/db/create-get-db.ts +86 -1
- package/corpus/core/src/db/ensure-additive-columns.ts +430 -0
- package/corpus/core/src/db/index.ts +6 -0
- package/corpus/core/src/db/migrations.ts +236 -35
- package/corpus/core/src/db-admin/index.ts +26 -0
- package/corpus/core/src/db-admin/operations.ts +87 -49
- package/corpus/core/src/jobs/scheduler.ts +17 -14
- package/corpus/core/src/localization/default-messages.ts +4 -0
- package/corpus/core/src/observability/traces.ts +182 -1
- package/corpus/core/src/org/context.ts +79 -2
- package/corpus/core/src/server/agent-chat-plugin.ts +10 -6
- package/corpus/core/src/server/auth-marketing.ts +0 -11
- package/corpus/core/src/server/auth.ts +11 -4
- package/corpus/core/src/server/builder-design-systems.ts +328 -9
- package/corpus/core/src/server/collab-plugin.ts +56 -13
- package/corpus/core/src/server/design-token-utils.ts +1 -1
- package/corpus/core/src/server/fusion-app.ts +447 -0
- package/corpus/core/src/server/index.ts +28 -0
- package/corpus/core/src/server/onboarding-html.ts +3 -4
- package/corpus/core/src/server/poll-events.ts +14 -2
- package/corpus/core/src/server/poll.ts +219 -12
- package/corpus/core/src/server/schema-prompt.ts +28 -9
- package/corpus/core/src/settings/store.ts +33 -2
- package/corpus/core/src/sharing/actions/set-resource-visibility.ts +2 -0
- package/corpus/core/src/sharing/actions/share-resource.ts +3 -0
- package/corpus/core/src/sharing/actions/unshare-resource.ts +2 -0
- package/corpus/core/src/styles/agent-native.css +0 -5
- package/corpus/core/src/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
- package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/core/src/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
- package/corpus/core/src/templates/default/app/root.tsx +47 -0
- package/corpus/core/src/templates/default/package.json +3 -3
- package/corpus/core/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
- package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
- package/corpus/core/src/tracking/providers.ts +22 -1
- package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +20 -5
- package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +8 -0
- package/corpus/templates/analytics/AGENTS.md +35 -6
- package/corpus/templates/analytics/actions/delete-db-admin-connection.ts +19 -0
- package/corpus/templates/analytics/actions/list-db-admin-connections.ts +18 -0
- package/corpus/templates/analytics/actions/navigate.ts +28 -4
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
- package/corpus/templates/analytics/actions/save-db-admin-connection.ts +33 -0
- package/corpus/templates/analytics/actions/view-screen.ts +65 -0
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +23 -1
- package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +16 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +36 -1
- package/corpus/templates/analytics/app/i18n-data.ts +361 -10
- package/corpus/templates/analytics/app/pages/Agents.tsx +512 -0
- package/corpus/templates/analytics/app/pages/DataSources.tsx +11 -1
- package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +76 -40
- package/corpus/templates/analytics/app/pages/sessions/session-replay-devtools.ts +5 -0
- package/corpus/templates/analytics/app/routes/agents.tsx +10 -0
- package/corpus/templates/analytics/changelog/2026-07-03-agent-admin-tools-page.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-03-agent-llm-observability-dashboard.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-03-connect-other-app-databases-from-analytics.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-03-session-recordings-now-include-response-bodies-for-5xx-error.md +6 -0
- package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +34 -0
- package/corpus/templates/analytics/docs/schemas/tracked-events.md +6 -0
- package/corpus/templates/analytics/package.json +3 -3
- package/corpus/templates/analytics/seeds/dashboards/agent-observability-llm.json +173 -0
- package/corpus/templates/analytics/server/db/schema.ts +27 -0
- package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +23 -5
- package/corpus/templates/analytics/server/lib/analytics-alerts.ts +58 -13
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +23 -0
- package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +5 -0
- package/corpus/templates/analytics/server/lib/db-admin-connections.ts +458 -0
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +120 -8
- package/corpus/templates/analytics/server/lib/session-replay.ts +3 -2
- package/corpus/templates/analytics/server/plugins/db.ts +128 -2
- package/corpus/templates/analytics/server/routes/[base]/_agent-native/analytics-db-admin/[...path].ts +1 -0
- package/corpus/templates/analytics/server/routes/_agent-native/analytics-db-admin/[...path].ts +160 -0
- package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +45 -1
- package/corpus/templates/analytics/shared/session-replay-diagnostics.ts +10 -0
- package/corpus/templates/assets/.agents/skills/real-time-collab/SKILL.md +82 -19
- package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +8 -0
- package/corpus/templates/assets/package.json +3 -3
- package/corpus/templates/assets/server/plugins/db.ts +63 -2
- package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/brain/package.json +3 -3
- package/corpus/templates/brain/server/plugins/db.ts +75 -2
- package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +8 -0
- package/corpus/templates/calendar/package.json +3 -3
- package/corpus/templates/calendar/server/plugins/db.ts +64 -2
- package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/chat/package.json +3 -3
- package/corpus/templates/clips/actions/finalize-recording.ts +50 -40
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +8 -6
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -3
- package/corpus/templates/clips/changelog/2026-07-03-mobile-recording-pages-keep-videos-in-a-16-9-player-with-act.md +6 -0
- package/corpus/templates/clips/package.json +3 -3
- package/corpus/templates/clips/server/lib/recording-upload-state.ts +56 -0
- package/corpus/templates/clips/server/plugins/db.ts +68 -1
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +27 -3
- package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +8 -0
- package/corpus/templates/content/actions/edit-document.ts +48 -1
- package/corpus/templates/content/actions/list-documents.ts +101 -94
- package/corpus/templates/content/actions/search-documents.ts +19 -3
- package/corpus/templates/content/actions/update-document.ts +54 -1
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +127 -7
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +97 -2
- package/corpus/templates/content/app/root.tsx +9 -0
- package/corpus/templates/content/changelog/2026-07-03-agent-document-edits-now-appear-as-a-live-collaborator-prese.md +6 -0
- package/corpus/templates/content/changelog/2026-07-03-document-search-and-list-views-load-faster-on-large-workspac.md +6 -0
- package/corpus/templates/content/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
- package/corpus/templates/content/package.json +3 -3
- package/corpus/templates/content/server/plugins/db.ts +57 -1
- package/corpus/templates/design/.agents/skills/full-app-build/SKILL.md +140 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +20 -0
- package/corpus/templates/design/AGENTS.md +63 -19
- package/corpus/templates/design/actions/add-fusion-screens.ts +91 -0
- package/corpus/templates/design/actions/apply-a11y-fix.ts +5 -1
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +10 -2
- package/corpus/templates/design/actions/apply-fusion-edits.ts +161 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +34 -1
- package/corpus/templates/design/actions/create-component.ts +5 -1
- package/corpus/templates/design/actions/create-fusion-app.ts +202 -0
- package/corpus/templates/design/actions/deploy-fusion-app.ts +123 -0
- package/corpus/templates/design/actions/edit-design.ts +13 -16
- package/corpus/templates/design/actions/get-design-system.ts +21 -0
- package/corpus/templates/design/actions/get-fusion-deploy-status.ts +86 -0
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +14 -37
- package/corpus/templates/design/actions/list-fusion-edits.ts +96 -0
- package/corpus/templates/design/actions/list-local-files.ts +133 -0
- package/corpus/templates/design/actions/push-fusion-app.ts +62 -0
- package/corpus/templates/design/actions/queue-fusion-edit.ts +112 -0
- package/corpus/templates/design/actions/read-local-file.ts +139 -0
- package/corpus/templates/design/actions/send-fusion-message.ts +69 -0
- package/corpus/templates/design/actions/sync-fusion-app.ts +196 -0
- package/corpus/templates/design/actions/write-local-file.ts +115 -14
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +12 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +379 -0
- package/corpus/templates/design/app/components/design/EditPanel.tsx +149 -73
- package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +328 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +148 -30
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +1 -1
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +567 -6
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -98
- package/corpus/templates/design/app/components/design/code-workbench/ActivityBar.tsx +85 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +466 -0
- package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +114 -0
- package/corpus/templates/design/app/components/design/code-workbench/commands.ts +339 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/Breadcrumbs.tsx +42 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/BufferSync.tsx +128 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/EditorTabs.tsx +372 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +268 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/StatusBar.tsx +208 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-setup.ts +61 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-theme-palette.ts +191 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-theme.ts +29 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/status-bar-lang.ts +21 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/tab-drag.ts +27 -0
- package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +161 -0
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +555 -0
- package/corpus/templates/design/app/components/design/code-workbench/explorer/file-icons.tsx +115 -0
- package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +65 -0
- package/corpus/templates/design/app/components/design/code-workbench/format/prettier-format.ts +182 -0
- package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +133 -0
- package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +563 -0
- package/corpus/templates/design/app/components/design/code-workbench/quickinput/fuzzy.ts +170 -0
- package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +508 -0
- package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +280 -0
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +782 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/create-providers.ts +39 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/inline-provider.ts +196 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +177 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/tree.ts +110 -0
- package/corpus/templates/design/app/components/design/code-workbench/workspace/types.ts +103 -0
- package/corpus/templates/design/app/components/design/code-workbench-theme.ts +68 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +127 -10
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +52 -29
- package/corpus/templates/design/app/components/design/inspector/index.ts +9 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +56 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +80 -1
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +6 -16
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +6 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +17 -15
- package/corpus/templates/design/app/i18n-data.ts +185 -160
- package/corpus/templates/design/app/pages/DesignEditor.tsx +1474 -175
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +32 -14
- package/corpus/templates/design/app/pages/Index.tsx +90 -17
- package/corpus/templates/design/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-the-ai-now-edits-like-a-collaborator-on-canvas-its-cursor-gl.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-the-code-editor-is-easier-to-use-with-top-sidebar-tabs-clear.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-the-code-panel-is-now-a-full-vs-code-style-editor-with-a-fil.md +6 -0
- package/corpus/templates/design/package.json +4 -3
- package/corpus/templates/design/server/db/schema.ts +31 -0
- package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +16 -106
- package/corpus/templates/design/server/lib/fusion-screens.ts +246 -0
- package/corpus/templates/design/server/plugins/db.ts +89 -2
- package/corpus/templates/design/shared/collab-selection.ts +52 -0
- package/corpus/templates/design/shared/full-app.ts +126 -0
- package/corpus/templates/design/shared/pen-path.ts +296 -0
- package/corpus/templates/dispatch/package.json +3 -3
- package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +8 -0
- package/corpus/templates/forms/package.json +3 -3
- package/corpus/templates/forms/server/plugins/db.ts +62 -2
- package/corpus/templates/macros/package.json +3 -3
- package/corpus/templates/macros/server/plugins/db.ts +63 -2
- package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +8 -0
- package/corpus/templates/mail/app/root.tsx +60 -1
- package/corpus/templates/mail/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
- package/corpus/templates/mail/package.json +3 -3
- package/corpus/templates/mail/server/lib/contact-frequency.ts +42 -15
- package/corpus/templates/mail/server/plugins/db.ts +63 -2
- package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +9 -3
- package/corpus/templates/plan/.agents/skills/visualize-repo/SKILL.md +92 -0
- package/corpus/templates/plan/actions/export-visual-plan.ts +5 -0
- package/corpus/templates/plan/actions/get-local-plan-folder.ts +4 -22
- package/corpus/templates/plan/actions/get-plan-version.ts +6 -16
- package/corpus/templates/plan/actions/list-plan-versions.ts +48 -20
- package/corpus/templates/plan/actions/patch-visual-plan-source.ts +41 -1
- package/corpus/templates/plan/actions/promote-local-plan-folder.ts +4 -22
- package/corpus/templates/plan/actions/update-local-plan-comments.ts +4 -2
- package/corpus/templates/plan/actions/update-local-plan-folder.ts +4 -22
- package/corpus/templates/plan/actions/update-visual-plan.ts +60 -1
- package/corpus/templates/plan/agent-native.app-skill.json +10 -0
- package/corpus/templates/plan/app/components/editor/PlanDocumentEditor.tsx +138 -39
- package/corpus/templates/plan/app/components/editor/usePlanUndoStack.ts +165 -23
- package/corpus/templates/plan/app/components/plan/PlanContentRenderer.tsx +80 -4
- package/corpus/templates/plan/app/global.css +11 -9
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +10 -1
- package/corpus/templates/plan/app/hooks/use-plan-presence.ts +66 -0
- package/corpus/templates/plan/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/plan/app/i18n/de-DE.ts +4 -0
- package/corpus/templates/plan/app/i18n/en-US.ts +4 -0
- package/corpus/templates/plan/app/i18n/es-ES.ts +4 -0
- package/corpus/templates/plan/app/i18n/fr-FR.ts +4 -0
- package/corpus/templates/plan/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/plan/app/i18n/ja-JP.ts +4 -0
- package/corpus/templates/plan/app/i18n/ko-KR.ts +4 -0
- package/corpus/templates/plan/app/i18n/pt-BR.ts +4 -0
- package/corpus/templates/plan/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/plan/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +289 -66
- package/corpus/templates/plan/changelog/2026-07-03-error-screens-now-include-a-feedback-button-with-debug-info.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-03-local-visual-plans-and-recaps-can-save-comments-without-sign.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-03-plan-tabs-render-nested-text-instead-of-raw-json.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-03-plans-are-now-fully-real-time-collaborative-multiple-people-.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-03-plans-open-and-refresh-faster-especially-long-lived-plans-wi.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-03-undo-now-survives-agent-edits-cmd-z-keeps-working-on-your-ow.md +6 -0
- package/corpus/templates/plan/package.json +5 -3
- package/corpus/templates/plan/server/db/schema.ts +12 -0
- package/corpus/templates/plan/server/lib/local-plan-kind.ts +25 -0
- package/corpus/templates/plan/server/lib/plan-versions.ts +83 -1
- package/corpus/templates/plan/server/lib/public-action-paths.ts +0 -2
- package/corpus/templates/plan/server/plans.ts +26 -3
- package/corpus/templates/plan/server/plugins/db.ts +105 -2
- package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +8 -4
- package/corpus/templates/slides/AGENTS.md +11 -3
- package/corpus/templates/slides/actions/_agent-presence.ts +48 -0
- package/corpus/templates/slides/actions/add-slide.ts +16 -18
- package/corpus/templates/slides/actions/get-design-system.ts +21 -0
- package/corpus/templates/slides/actions/import-file.ts +15 -7
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +14 -37
- package/corpus/templates/slides/actions/patch-deck.ts +4 -2
- package/corpus/templates/slides/actions/update-slide.ts +114 -77
- package/corpus/templates/slides/actions/view-screen.ts +39 -0
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +32 -8
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +47 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +34 -39
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +507 -72
- package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +346 -0
- package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +56 -6
- package/corpus/templates/slides/app/components/editor/TweaksPanel.tsx +46 -96
- package/corpus/templates/slides/app/components/layout/Layout.tsx +6 -1
- package/corpus/templates/slides/app/components/visual-editor/SaveStatusIndicator.tsx +8 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +589 -176
- package/corpus/templates/slides/app/hooks/use-deck-presence.ts +84 -42
- package/corpus/templates/slides/app/i18n/ar-SA.ts +54 -7
- package/corpus/templates/slides/app/i18n/de-DE.ts +53 -8
- package/corpus/templates/slides/app/i18n/en-US.ts +54 -7
- package/corpus/templates/slides/app/i18n/es-ES.ts +53 -8
- package/corpus/templates/slides/app/i18n/fr-FR.ts +53 -8
- package/corpus/templates/slides/app/i18n/hi-IN.ts +54 -7
- package/corpus/templates/slides/app/i18n/ja-JP.ts +53 -8
- package/corpus/templates/slides/app/i18n/ko-KR.ts +53 -7
- package/corpus/templates/slides/app/i18n/pt-BR.ts +53 -8
- package/corpus/templates/slides/app/i18n/zh-CN.ts +54 -7
- package/corpus/templates/slides/app/i18n/zh-TW.ts +54 -7
- package/corpus/templates/slides/app/lib/canvas-zoom.ts +16 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +28 -16
- package/corpus/templates/slides/changelog/2026-07-03-design-system-setup-now-indexes-figma-code-and-design-md-sou.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-03-figma-style-slide-editing.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-03-see-the-ai-working-with-you-when-the-agent-edits-a-slide-it-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-03-slide-editing-now-keeps-thumbnails-speaker-notes-styling-and.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-03-undo-redo-is-now-precise-and-safe-with-collaborators-it-only.md +6 -0
- package/corpus/templates/slides/package.json +3 -3
- package/corpus/templates/slides/server/handlers/decks.ts +31 -3
- package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +16 -104
- package/corpus/templates/slides/server/plugins/db.ts +65 -2
- package/corpus/templates/videos/netlify.toml +1 -12
- package/dist/agent/run-store.js +1 -1
- package/dist/agent/run-store.js.map +1 -1
- package/dist/chat-threads/store.d.ts +9 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +63 -11
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/checkpoints/store.d.ts.map +1 -1
- package/dist/checkpoints/store.js +10 -2
- package/dist/checkpoints/store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +3 -5
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/design-connect.d.ts +49 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +414 -9
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/index.js +12 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plan-local.d.ts +24 -0
- package/dist/cli/plan-local.d.ts.map +1 -1
- package/dist/cli/plan-local.js +188 -20
- package/dist/cli/plan-local.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +173 -21
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/templates-meta.d.ts.map +1 -1
- package/dist/cli/templates-meta.js +0 -14
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/cli/visualize-repo.d.ts +49 -0
- package/dist/cli/visualize-repo.d.ts.map +1 -0
- package/dist/cli/visualize-repo.js +561 -0
- package/dist/cli/visualize-repo.js.map +1 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +2 -1
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +175 -18
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ErrorBoundary.d.ts.map +1 -1
- package/dist/client/ErrorBoundary.js +37 -2
- package/dist/client/ErrorBoundary.js.map +1 -1
- package/dist/client/ErrorReportActions.d.ts +13 -0
- package/dist/client/ErrorReportActions.d.ts.map +1 -0
- package/dist/client/ErrorReportActions.js +14 -0
- package/dist/client/ErrorReportActions.js.map +1 -0
- package/dist/client/FeedbackButton.d.ts +3 -1
- package/dist/client/FeedbackButton.d.ts.map +1 -1
- package/dist/client/FeedbackButton.js +6 -5
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +6 -5
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/app-providers.d.ts +2 -2
- package/dist/client/app-providers.js.map +1 -1
- package/dist/client/collab/index.d.ts +4 -1
- package/dist/client/collab/index.d.ts.map +1 -1
- package/dist/client/collab/index.js +3 -0
- package/dist/client/collab/index.js.map +1 -1
- package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
- package/dist/client/components/LiveCursorOverlay.js +11 -3
- package/dist/client/components/LiveCursorOverlay.js.map +1 -1
- package/dist/client/components/PresenceBar.d.ts.map +1 -1
- package/dist/client/components/PresenceBar.js +6 -1
- package/dist/client/components/PresenceBar.js.map +1 -1
- package/dist/client/components/RecentEditHighlights.d.ts +43 -0
- package/dist/client/components/RecentEditHighlights.d.ts.map +1 -0
- package/dist/client/components/RecentEditHighlights.js +146 -0
- package/dist/client/components/RecentEditHighlights.js.map +1 -0
- package/dist/client/components/RemoteSelectionRings.d.ts +9 -0
- package/dist/client/components/RemoteSelectionRings.d.ts.map +1 -1
- package/dist/client/components/RemoteSelectionRings.js +22 -8
- package/dist/client/components/RemoteSelectionRings.js.map +1 -1
- package/dist/client/db-admin/DbAdminPage.d.ts +9 -1
- package/dist/client/db-admin/DbAdminPage.d.ts.map +1 -1
- package/dist/client/db-admin/DbAdminPage.js +17 -13
- package/dist/client/db-admin/DbAdminPage.js.map +1 -1
- package/dist/client/db-admin/SqlEditor.d.ts +3 -1
- package/dist/client/db-admin/SqlEditor.d.ts.map +1 -1
- package/dist/client/db-admin/SqlEditor.js +3 -3
- package/dist/client/db-admin/SqlEditor.js.map +1 -1
- package/dist/client/db-admin/TableEditor.d.ts +3 -1
- package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
- package/dist/client/db-admin/TableEditor.js +6 -6
- package/dist/client/db-admin/TableEditor.js.map +1 -1
- package/dist/client/db-admin/useAgentSync.d.ts +3 -2
- package/dist/client/db-admin/useAgentSync.d.ts.map +1 -1
- package/dist/client/db-admin/useAgentSync.js +8 -4
- package/dist/client/db-admin/useAgentSync.js.map +1 -1
- package/dist/client/db-admin/useDbAdmin.d.ts +11 -7
- package/dist/client/db-admin/useDbAdmin.d.ts.map +1 -1
- package/dist/client/db-admin/useDbAdmin.js +45 -20
- package/dist/client/db-admin/useDbAdmin.js.map +1 -1
- package/dist/client/error-reporting.d.ts +19 -0
- package/dist/client/error-reporting.d.ts.map +1 -0
- package/dist/client/error-reporting.js +71 -0
- package/dist/client/error-reporting.js.map +1 -0
- package/dist/client/index.d.ts +8 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +9 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +1 -3
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/RequireActiveOrg.d.ts.map +1 -1
- package/dist/client/org/RequireActiveOrg.js +2 -1
- package/dist/client/org/RequireActiveOrg.js.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +8 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +8 -2
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/client/rich-markdown-editor/index.d.ts +1 -0
- package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/index.js +1 -0
- package/dist/client/rich-markdown-editor/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/surgical-apply.d.ts +66 -0
- package/dist/client/rich-markdown-editor/surgical-apply.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/surgical-apply.js +147 -0
- package/dist/client/rich-markdown-editor/surgical-apply.js.map +1 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +18 -2
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +39 -8
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/client/session-replay.d.ts +10 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +134 -7
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +81 -1
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-action.d.ts +24 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +109 -27
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +7 -1
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +11 -3
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +36 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +68 -12
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/visual-style-controls.d.ts +90 -0
- package/dist/client/visual-style-controls.d.ts.map +1 -0
- package/dist/client/visual-style-controls.js +499 -0
- package/dist/client/visual-style-controls.js.map +1 -0
- package/dist/collab/agent-presence.d.ts +51 -5
- package/dist/collab/agent-presence.d.ts.map +1 -1
- package/dist/collab/agent-presence.js +144 -58
- package/dist/collab/agent-presence.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +32 -0
- package/dist/collab/awareness-store.d.ts.map +1 -0
- package/dist/collab/awareness-store.js +0 -0
- package/dist/collab/awareness-store.js.map +1 -0
- package/dist/collab/awareness.d.ts +12 -1
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +53 -5
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/client.d.ts +2 -0
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +79 -119
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/index.d.ts +3 -1
- package/dist/collab/index.d.ts.map +1 -1
- package/dist/collab/index.js +5 -1
- package/dist/collab/index.js.map +1 -1
- package/dist/collab/recent-edits.d.ts +87 -0
- package/dist/collab/recent-edits.d.ts.map +1 -0
- package/dist/collab/recent-edits.js +116 -0
- package/dist/collab/recent-edits.js.map +1 -0
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/undo.d.ts +121 -0
- package/dist/collab/undo.d.ts.map +1 -0
- package/dist/collab/undo.js +244 -0
- package/dist/collab/undo.js.map +1 -0
- package/dist/collab/ydoc-manager.d.ts +10 -0
- package/dist/collab/ydoc-manager.d.ts.map +1 -1
- package/dist/collab/ydoc-manager.js +57 -0
- package/dist/collab/ydoc-manager.js.map +1 -1
- package/dist/connections/catalog.d.ts +2 -2
- package/dist/connections/catalog.d.ts.map +1 -1
- package/dist/connections/catalog.js +1 -1
- package/dist/connections/catalog.js.map +1 -1
- package/dist/db/create-get-db.d.ts +22 -0
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +73 -1
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/db/ensure-additive-columns.d.ts +93 -0
- package/dist/db/ensure-additive-columns.d.ts.map +1 -0
- package/dist/db/ensure-additive-columns.js +355 -0
- package/dist/db/ensure-additive-columns.js.map +1 -0
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +1 -0
- package/dist/db/index.js.map +1 -1
- package/dist/db/migrations.d.ts +70 -0
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +196 -33
- package/dist/db/migrations.js.map +1 -1
- package/dist/db-admin/index.d.ts +3 -0
- package/dist/db-admin/index.d.ts.map +1 -0
- package/dist/db-admin/index.js +2 -0
- package/dist/db-admin/index.js.map +1 -0
- package/dist/db-admin/operations.d.ts +26 -5
- package/dist/db-admin/operations.d.ts.map +1 -1
- package/dist/db-admin/operations.js +66 -55
- package/dist/db-admin/operations.js.map +1 -1
- package/dist/jobs/scheduler.js +17 -10
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/localization/default-messages.d.ts +3 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +3 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +2 -2
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +155 -1
- package/dist/observability/traces.js.map +1 -1
- package/dist/org/context.d.ts +7 -0
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +54 -2
- package/dist/org/context.js.map +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +10 -6
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/auth-marketing.d.ts.map +1 -1
- package/dist/server/auth-marketing.js +0 -10
- package/dist/server/auth-marketing.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +6 -4
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts +61 -0
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +219 -14
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/collab-plugin.d.ts.map +1 -1
- package/dist/server/collab-plugin.js +45 -13
- package/dist/server/collab-plugin.js.map +1 -1
- package/dist/server/design-token-utils.d.ts +1 -1
- package/dist/server/design-token-utils.js +1 -1
- package/dist/server/design-token-utils.js.map +1 -1
- package/dist/server/fusion-app.d.ts +107 -0
- package/dist/server/fusion-app.d.ts.map +1 -0
- package/dist/server/fusion-app.js +371 -0
- package/dist/server/fusion-app.js.map +1 -0
- package/dist/server/index.d.ts +2 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +3 -4
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/poll-events.d.ts +2 -0
- package/dist/server/poll-events.d.ts.map +1 -1
- package/dist/server/poll-events.js +7 -2
- package/dist/server/poll-events.js.map +1 -1
- package/dist/server/poll.d.ts +47 -3
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +176 -10
- package/dist/server/poll.js.map +1 -1
- package/dist/server/schema-prompt.d.ts.map +1 -1
- package/dist/server/schema-prompt.js +24 -7
- package/dist/server/schema-prompt.js.map +1 -1
- package/dist/settings/store.d.ts.map +1 -1
- package/dist/settings/store.js +32 -3
- package/dist/settings/store.js.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.js +2 -0
- package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
- package/dist/sharing/actions/share-resource.d.ts.map +1 -1
- package/dist/sharing/actions/share-resource.js +3 -0
- package/dist/sharing/actions/share-resource.js.map +1 -1
- package/dist/sharing/actions/unshare-resource.js +2 -0
- package/dist/sharing/actions/unshare-resource.js.map +1 -1
- package/dist/styles/agent-native.css +0 -5
- package/dist/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/dist/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
- package/dist/templates/default/app/root.tsx +47 -0
- package/dist/templates/default/package.json +3 -3
- package/dist/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
- package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +17 -1
- package/dist/tracking/providers.js.map +1 -1
- package/docs/content/cloneable-saas.mdx +0 -1
- package/docs/content/locales/ar-SA/cloneable-saas.mdx +0 -1
- package/docs/content/locales/ar-SA/plan-plugin.mdx +10 -9
- package/docs/content/locales/ar-SA/sharing.mdx +1 -2
- package/docs/content/locales/ar-SA/skills-guide.mdx +2 -3
- package/docs/content/locales/ar-SA/template-plan.mdx +23 -7
- package/docs/content/locales/ar-SA/tracking.mdx +1 -1
- package/docs/content/locales/ar-SA/workspace-connections.mdx +11 -11
- package/docs/content/locales/de-DE/cloneable-saas.mdx +0 -1
- package/docs/content/locales/de-DE/plan-plugin.mdx +10 -9
- package/docs/content/locales/de-DE/sharing.mdx +1 -2
- package/docs/content/locales/de-DE/skills-guide.mdx +2 -3
- package/docs/content/locales/de-DE/template-plan.mdx +23 -7
- package/docs/content/locales/de-DE/tracking.mdx +1 -1
- package/docs/content/locales/de-DE/workspace-connections.mdx +1 -1
- package/docs/content/locales/es-ES/cloneable-saas.mdx +0 -1
- package/docs/content/locales/es-ES/plan-plugin.mdx +10 -9
- package/docs/content/locales/es-ES/sharing.mdx +1 -2
- package/docs/content/locales/es-ES/skills-guide.mdx +2 -3
- package/docs/content/locales/es-ES/template-plan.mdx +23 -7
- package/docs/content/locales/es-ES/tracking.mdx +1 -1
- package/docs/content/locales/es-ES/workspace-connections.mdx +1 -1
- package/docs/content/locales/fr-FR/cloneable-saas.mdx +0 -1
- package/docs/content/locales/fr-FR/plan-plugin.mdx +10 -9
- package/docs/content/locales/fr-FR/sharing.mdx +1 -2
- package/docs/content/locales/fr-FR/skills-guide.mdx +2 -3
- package/docs/content/locales/fr-FR/template-plan.mdx +23 -7
- package/docs/content/locales/fr-FR/tracking.mdx +1 -1
- package/docs/content/locales/fr-FR/workspace-connections.mdx +1 -1
- package/docs/content/locales/hi-IN/cloneable-saas.mdx +0 -1
- package/docs/content/locales/hi-IN/plan-plugin.mdx +10 -9
- package/docs/content/locales/hi-IN/sharing.mdx +1 -2
- package/docs/content/locales/hi-IN/skills-guide.mdx +2 -3
- package/docs/content/locales/hi-IN/template-plan.mdx +23 -7
- package/docs/content/locales/hi-IN/tracking.mdx +1 -1
- package/docs/content/locales/hi-IN/workspace-connections.mdx +1 -1
- package/docs/content/locales/ja-JP/cloneable-saas.mdx +0 -1
- package/docs/content/locales/ja-JP/plan-plugin.mdx +10 -9
- package/docs/content/locales/ja-JP/sharing.mdx +1 -2
- package/docs/content/locales/ja-JP/skills-guide.mdx +2 -3
- package/docs/content/locales/ja-JP/template-plan.mdx +23 -7
- package/docs/content/locales/ja-JP/tracking.mdx +1 -1
- package/docs/content/locales/ja-JP/workspace-connections.mdx +1 -1
- package/docs/content/locales/ko-KR/cloneable-saas.mdx +0 -1
- package/docs/content/locales/ko-KR/plan-plugin.mdx +10 -9
- package/docs/content/locales/ko-KR/sharing.mdx +1 -2
- package/docs/content/locales/ko-KR/skills-guide.mdx +2 -3
- package/docs/content/locales/ko-KR/template-plan.mdx +23 -7
- package/docs/content/locales/ko-KR/tracking.mdx +1 -1
- package/docs/content/locales/ko-KR/workspace-connections.mdx +1 -1
- package/docs/content/locales/pt-BR/cloneable-saas.mdx +0 -1
- package/docs/content/locales/pt-BR/plan-plugin.mdx +10 -9
- package/docs/content/locales/pt-BR/sharing.mdx +1 -2
- package/docs/content/locales/pt-BR/skills-guide.mdx +2 -3
- package/docs/content/locales/pt-BR/template-plan.mdx +23 -7
- package/docs/content/locales/pt-BR/tracking.mdx +1 -1
- package/docs/content/locales/pt-BR/workspace-connections.mdx +1 -1
- package/docs/content/locales/zh-CN/cloneable-saas.mdx +0 -1
- package/docs/content/locales/zh-CN/plan-plugin.mdx +10 -9
- package/docs/content/locales/zh-CN/sharing.mdx +1 -2
- package/docs/content/locales/zh-CN/skills-guide.mdx +2 -3
- package/docs/content/locales/zh-CN/template-plan.mdx +23 -7
- package/docs/content/locales/zh-CN/tracking.mdx +1 -1
- package/docs/content/locales/zh-CN/workspace-connections.mdx +1 -1
- package/docs/content/locales/zh-TW/cloneable-saas.mdx +0 -1
- package/docs/content/locales/zh-TW/plan-plugin.mdx +10 -9
- package/docs/content/locales/zh-TW/sharing.mdx +1 -2
- package/docs/content/locales/zh-TW/skills-guide.mdx +2 -3
- package/docs/content/locales/zh-TW/template-plan.mdx +23 -7
- package/docs/content/locales/zh-TW/tracking.mdx +1 -1
- package/docs/content/locales/zh-TW/workspace-connections.mdx +1 -1
- package/docs/content/plan-plugin.mdx +12 -11
- package/docs/content/sharing.mdx +1 -2
- package/docs/content/skills-guide.mdx +4 -3
- package/docs/content/template-plan.mdx +37 -6
- package/docs/content/tracking.mdx +1 -1
- package/docs/content/workspace-connections.mdx +1 -1
- package/package.json +7 -6
- package/src/templates/default/.agents/skills/real-time-collab/SKILL.md +82 -19
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/src/templates/default/.agents/skills/storing-data/SKILL.md +8 -0
- package/src/templates/default/app/root.tsx +47 -0
- package/src/templates/default/package.json +3 -3
- package/src/templates/workspace-core/.agents/skills/client-methods/SKILL.md +8 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +22 -0
- package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +82 -19
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +15 -0
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +8 -0
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -0
- package/corpus/core/docs/content/locales/ar-SA/template-videos.mdx +0 -336
- package/corpus/core/docs/content/locales/de-DE/template-videos.mdx +0 -336
- package/corpus/core/docs/content/locales/es-ES/template-videos.mdx +0 -336
- package/corpus/core/docs/content/locales/fr-FR/template-videos.mdx +0 -336
- package/corpus/core/docs/content/locales/hi-IN/template-videos.mdx +0 -336
- package/corpus/core/docs/content/locales/ja-JP/template-videos.mdx +0 -336
- package/corpus/core/docs/content/locales/ko-KR/template-videos.mdx +0 -336
- package/corpus/core/docs/content/locales/pt-BR/template-videos.mdx +0 -336
- package/corpus/core/docs/content/locales/zh-CN/template-videos.mdx +0 -334
- package/corpus/core/docs/content/locales/zh-TW/template-videos.mdx +0 -334
- package/corpus/core/docs/content/template-videos.mdx +0 -332
- package/corpus/templates/design/app/components/design/CodeWorkbenchHost.tsx +0 -697
- package/corpus/templates/slides/app/components/editor/CodeEditor.tsx +0 -49
- package/corpus/templates/videos/.agents/skills/actions/SKILL.md +0 -432
- package/corpus/templates/videos/.agents/skills/animation-tracks/SKILL.md +0 -99
- package/corpus/templates/videos/.agents/skills/capture-learnings/SKILL.md +0 -76
- package/corpus/templates/videos/.agents/skills/composition-management/SKILL.md +0 -106
- package/corpus/templates/videos/.agents/skills/create-skill/SKILL.md +0 -221
- package/corpus/templates/videos/.agents/skills/delegate-to-agent/SKILL.md +0 -263
- package/corpus/templates/videos/.agents/skills/frontend-design/SKILL.md +0 -112
- package/corpus/templates/videos/.agents/skills/security/SKILL.md +0 -263
- package/corpus/templates/videos/.agents/skills/self-modifying-code/SKILL.md +0 -88
- package/corpus/templates/videos/.agents/skills/shadcn-ui/SKILL.md +0 -95
- package/corpus/templates/videos/.builder/ANIMATED_PROPERTIES.md +0 -335
- package/corpus/templates/videos/.builder/KEYFRAME_SYNC.md +0 -166
- package/corpus/templates/videos/.builder/SEAMLESS_SYNC.md +0 -211
- package/corpus/templates/videos/.builder/rules/deploy-app.mdc +0 -22
- package/corpus/templates/videos/.builder/rules/organize-ui.mdc +0 -9
- package/corpus/templates/videos/.builder/skills/remotion/SKILL.md +0 -29
- package/corpus/templates/videos/.builder/skills/remotion/rules/animations.md +0 -27
- package/corpus/templates/videos/.builder/skills/remotion/rules/assets.md +0 -43
- package/corpus/templates/videos/.builder/skills/remotion/rules/audio.md +0 -58
- package/corpus/templates/videos/.builder/skills/remotion/rules/compositions.md +0 -129
- package/corpus/templates/videos/.builder/skills/remotion/rules/fonts.md +0 -47
- package/corpus/templates/videos/.builder/skills/remotion/rules/images.md +0 -41
- package/corpus/templates/videos/.builder/skills/remotion/rules/parameters.md +0 -59
- package/corpus/templates/videos/.builder/skills/remotion/rules/sequencing.md +0 -54
- package/corpus/templates/videos/.builder/skills/remotion/rules/tailwind.md +0 -11
- package/corpus/templates/videos/.builder/skills/remotion/rules/text-animations.md +0 -18
- package/corpus/templates/videos/.builder/skills/remotion/rules/timing.md +0 -103
- package/corpus/templates/videos/.builder/skills/remotion/rules/transparent-videos.md +0 -35
- package/corpus/templates/videos/.builder/skills/remotion/rules/trimming.md +0 -38
- package/corpus/templates/videos/.builder/skills/remotion/rules/videos.md +0 -54
- package/corpus/templates/videos/.dockerignore +0 -62
- package/corpus/templates/videos/.env.example +0 -4
- package/corpus/templates/videos/.oxfmtrc.json +0 -8
- package/corpus/templates/videos/AGENTS.md +0 -46
- package/corpus/templates/videos/CHANGELOG.md +0 -10
- package/corpus/templates/videos/DEVELOPING.md +0 -143
- package/corpus/templates/videos/README.md +0 -33
- package/corpus/templates/videos/actions/analyze-brand-assets.ts +0 -151
- package/corpus/templates/videos/actions/apply-design-system.ts +0 -35
- package/corpus/templates/videos/actions/create-design-system.ts +0 -84
- package/corpus/templates/videos/actions/create-folder.ts +0 -48
- package/corpus/templates/videos/actions/db-connect.ts +0 -45
- package/corpus/templates/videos/actions/db-health.ts +0 -24
- package/corpus/templates/videos/actions/db-status.ts +0 -28
- package/corpus/templates/videos/actions/delete-composition.ts +0 -272
- package/corpus/templates/videos/actions/delete-folder.ts +0 -27
- package/corpus/templates/videos/actions/generate-animated-component.ts +0 -470
- package/corpus/templates/videos/actions/get-composition.ts +0 -36
- package/corpus/templates/videos/actions/get-design-system.ts +0 -35
- package/corpus/templates/videos/actions/import-code.ts +0 -67
- package/corpus/templates/videos/actions/import-design-project.ts +0 -42
- package/corpus/templates/videos/actions/import-document.ts +0 -125
- package/corpus/templates/videos/actions/import-from-url.ts +0 -18
- package/corpus/templates/videos/actions/import-github.ts +0 -182
- package/corpus/templates/videos/actions/list-compositions.ts +0 -32
- package/corpus/templates/videos/actions/list-design-systems.ts +0 -66
- package/corpus/templates/videos/actions/list-folders.ts +0 -61
- package/corpus/templates/videos/actions/move-composition-to-folder.ts +0 -59
- package/corpus/templates/videos/actions/navigate.ts +0 -40
- package/corpus/templates/videos/actions/rename-folder.ts +0 -41
- package/corpus/templates/videos/actions/run.ts +0 -2
- package/corpus/templates/videos/actions/save-composition.ts +0 -114
- package/corpus/templates/videos/actions/set-default-design-system.ts +0 -40
- package/corpus/templates/videos/actions/update-composition.ts +0 -81
- package/corpus/templates/videos/actions/update-design-system.ts +0 -68
- package/corpus/templates/videos/actions/validate-compositions.ts +0 -502
- package/corpus/templates/videos/actions/view-screen.ts +0 -48
- package/corpus/templates/videos/app/components/CameraControls.tsx +0 -442
- package/corpus/templates/videos/app/components/CameraToolbar.tsx +0 -437
- package/corpus/templates/videos/app/components/CloudUpgrade.tsx +0 -287
- package/corpus/templates/videos/app/components/CollabPresenceBar.tsx +0 -81
- package/corpus/templates/videos/app/components/CompSettingsEditor.tsx +0 -107
- package/corpus/templates/videos/app/components/ComponentLibrarySidebar.tsx +0 -457
- package/corpus/templates/videos/app/components/ComponentPropsPanel.tsx +0 -227
- package/corpus/templates/videos/app/components/ComponentsSidebar.tsx +0 -70
- package/corpus/templates/videos/app/components/CompositionCard.tsx +0 -147
- package/corpus/templates/videos/app/components/CurrentElementPanel.tsx +0 -835
- package/corpus/templates/videos/app/components/CursorControls.tsx +0 -664
- package/corpus/templates/videos/app/components/CursorPositioningOverlay.tsx +0 -238
- package/corpus/templates/videos/app/components/LibraryFolderRow.tsx +0 -235
- package/corpus/templates/videos/app/components/MotionCurveSelect.tsx +0 -61
- package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +0 -192
- package/corpus/templates/videos/app/components/PropsEditor.tsx +0 -210
- package/corpus/templates/videos/app/components/QuestionFlow.tsx +0 -40
- package/corpus/templates/videos/app/components/Sidebar.tsx +0 -609
- package/corpus/templates/videos/app/components/StudioHeader.tsx +0 -109
- package/corpus/templates/videos/app/components/Timeline.tsx +0 -2067
- package/corpus/templates/videos/app/components/TrackPropertiesPanel.tsx +0 -922
- package/corpus/templates/videos/app/components/TweaksPanel.tsx +0 -277
- package/corpus/templates/videos/app/components/VideoPlayer.tsx +0 -487
- package/corpus/templates/videos/app/components/design-system/DesignSystemCard.tsx +0 -134
- package/corpus/templates/videos/app/components/design-system/DesignSystemPreview.tsx +0 -176
- package/corpus/templates/videos/app/components/design-system/DesignSystemSetup.tsx +0 -707
- package/corpus/templates/videos/app/components/keyframes/KeyframeActionButtons.tsx +0 -78
- package/corpus/templates/videos/app/components/keyframes/KeyframeNavigation.tsx +0 -59
- package/corpus/templates/videos/app/components/keyframes/PropertyNumberInput.tsx +0 -56
- package/corpus/templates/videos/app/components/layout/Header.tsx +0 -64
- package/corpus/templates/videos/app/components/layout/HeaderActions.tsx +0 -84
- package/corpus/templates/videos/app/components/layout/Layout.tsx +0 -132
- package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +0 -180
- package/corpus/templates/videos/app/components/ui/accordion.tsx +0 -56
- package/corpus/templates/videos/app/components/ui/alert-dialog.tsx +0 -139
- package/corpus/templates/videos/app/components/ui/alert.tsx +0 -59
- package/corpus/templates/videos/app/components/ui/aspect-ratio.tsx +0 -5
- package/corpus/templates/videos/app/components/ui/avatar.tsx +0 -48
- package/corpus/templates/videos/app/components/ui/badge.tsx +0 -37
- package/corpus/templates/videos/app/components/ui/breadcrumb.tsx +0 -115
- package/corpus/templates/videos/app/components/ui/button.tsx +0 -57
- package/corpus/templates/videos/app/components/ui/calendar.tsx +0 -68
- package/corpus/templates/videos/app/components/ui/card.tsx +0 -86
- package/corpus/templates/videos/app/components/ui/carousel.tsx +0 -260
- package/corpus/templates/videos/app/components/ui/chart.tsx +0 -375
- package/corpus/templates/videos/app/components/ui/checkbox.tsx +0 -28
- package/corpus/templates/videos/app/components/ui/collapsible.tsx +0 -9
- package/corpus/templates/videos/app/components/ui/command.tsx +0 -154
- package/corpus/templates/videos/app/components/ui/context-menu.tsx +0 -202
- package/corpus/templates/videos/app/components/ui/dialog.tsx +0 -130
- package/corpus/templates/videos/app/components/ui/drawer.tsx +0 -116
- package/corpus/templates/videos/app/components/ui/dropdown-menu.tsx +0 -212
- package/corpus/templates/videos/app/components/ui/form.tsx +0 -177
- package/corpus/templates/videos/app/components/ui/hover-card.tsx +0 -27
- package/corpus/templates/videos/app/components/ui/input-otp.tsx +0 -69
- package/corpus/templates/videos/app/components/ui/input.tsx +0 -22
- package/corpus/templates/videos/app/components/ui/label.tsx +0 -24
- package/corpus/templates/videos/app/components/ui/menubar.tsx +0 -234
- package/corpus/templates/videos/app/components/ui/navigation-menu.tsx +0 -128
- package/corpus/templates/videos/app/components/ui/pagination.tsx +0 -121
- package/corpus/templates/videos/app/components/ui/popover.tsx +0 -44
- package/corpus/templates/videos/app/components/ui/progress.tsx +0 -26
- package/corpus/templates/videos/app/components/ui/radio-group.tsx +0 -42
- package/corpus/templates/videos/app/components/ui/resizable.tsx +0 -43
- package/corpus/templates/videos/app/components/ui/scroll-area.tsx +0 -46
- package/corpus/templates/videos/app/components/ui/select.tsx +0 -158
- package/corpus/templates/videos/app/components/ui/separator.tsx +0 -29
- package/corpus/templates/videos/app/components/ui/sheet.tsx +0 -160
- package/corpus/templates/videos/app/components/ui/sidebar.tsx +0 -782
- package/corpus/templates/videos/app/components/ui/skeleton.tsx +0 -15
- package/corpus/templates/videos/app/components/ui/slider.tsx +0 -26
- package/corpus/templates/videos/app/components/ui/sonner.tsx +0 -58
- package/corpus/templates/videos/app/components/ui/spinner.tsx +0 -17
- package/corpus/templates/videos/app/components/ui/switch.tsx +0 -27
- package/corpus/templates/videos/app/components/ui/table.tsx +0 -117
- package/corpus/templates/videos/app/components/ui/tabs.tsx +0 -53
- package/corpus/templates/videos/app/components/ui/textarea.tsx +0 -23
- package/corpus/templates/videos/app/components/ui/toast.tsx +0 -127
- package/corpus/templates/videos/app/components/ui/toaster.tsx +0 -33
- package/corpus/templates/videos/app/components/ui/toggle-group.tsx +0 -59
- package/corpus/templates/videos/app/components/ui/toggle.tsx +0 -43
- package/corpus/templates/videos/app/components/ui/tooltip.tsx +0 -30
- package/corpus/templates/videos/app/components/ui/use-toast.ts +0 -3
- package/corpus/templates/videos/app/config/constants.ts +0 -204
- package/corpus/templates/videos/app/contexts/CompositionContext.tsx +0 -437
- package/corpus/templates/videos/app/contexts/CurrentElementContext.tsx +0 -247
- package/corpus/templates/videos/app/contexts/PlaybackContext.tsx +0 -65
- package/corpus/templates/videos/app/contexts/TimelineContext.tsx +0 -433
- package/corpus/templates/videos/app/entry.client.tsx +0 -19
- package/corpus/templates/videos/app/entry.server.tsx +0 -10
- package/corpus/templates/videos/app/global.css +0 -171
- package/corpus/templates/videos/app/hooks/use-composition-collab.ts +0 -269
- package/corpus/templates/videos/app/hooks/use-composition-design-system.ts +0 -47
- package/corpus/templates/videos/app/hooks/use-database-compositions.ts +0 -144
- package/corpus/templates/videos/app/hooks/use-db-status.ts +0 -30
- package/corpus/templates/videos/app/hooks/use-design-systems.ts +0 -21
- package/corpus/templates/videos/app/hooks/use-folders.ts +0 -199
- package/corpus/templates/videos/app/hooks/use-mobile.tsx +0 -21
- package/corpus/templates/videos/app/hooks/use-navigation-state.ts +0 -52
- package/corpus/templates/videos/app/hooks/use-question-flow.ts +0 -14
- package/corpus/templates/videos/app/hooks/use-toast.ts +0 -188
- package/corpus/templates/videos/app/hooks/useUnsavedChanges.ts +0 -48
- package/corpus/templates/videos/app/i18n/ar-SA.ts +0 -446
- package/corpus/templates/videos/app/i18n/de-DE.ts +0 -470
- package/corpus/templates/videos/app/i18n/en-US.ts +0 -452
- package/corpus/templates/videos/app/i18n/es-ES.ts +0 -468
- package/corpus/templates/videos/app/i18n/fr-FR.ts +0 -469
- package/corpus/templates/videos/app/i18n/hi-IN.ts +0 -446
- package/corpus/templates/videos/app/i18n/index.ts +0 -34
- package/corpus/templates/videos/app/i18n/ja-JP.ts +0 -460
- package/corpus/templates/videos/app/i18n/ko-KR.ts +0 -453
- package/corpus/templates/videos/app/i18n/pt-BR.ts +0 -461
- package/corpus/templates/videos/app/i18n/zh-CN.ts +0 -437
- package/corpus/templates/videos/app/i18n/zh-TW.ts +0 -437
- package/corpus/templates/videos/app/lib/database-compositions.ts +0 -67
- package/corpus/templates/videos/app/lib/design-systems.ts +0 -234
- package/corpus/templates/videos/app/lib/tab-id.ts +0 -1
- package/corpus/templates/videos/app/lib/utils.ts +0 -1
- package/corpus/templates/videos/app/pages/ComponentLibrary.tsx +0 -134
- package/corpus/templates/videos/app/pages/ComponentLibraryView.tsx +0 -362
- package/corpus/templates/videos/app/pages/CompositionView.tsx +0 -715
- package/corpus/templates/videos/app/pages/DesignSystems.tsx +0 -180
- package/corpus/templates/videos/app/pages/Index.tsx +0 -258
- package/corpus/templates/videos/app/pages/NewComposition.tsx +0 -95
- package/corpus/templates/videos/app/pages/NotFound.tsx +0 -36
- package/corpus/templates/videos/app/remotion/CameraHost.tsx +0 -223
- package/corpus/templates/videos/app/remotion/animations/useElementAnimations.ts +0 -231
- package/corpus/templates/videos/app/remotion/componentRegistry.ts +0 -473
- package/corpus/templates/videos/app/remotion/components/AnimatedElement.tsx +0 -436
- package/corpus/templates/videos/app/remotion/components/InteractiveElement.tsx +0 -159
- package/corpus/templates/videos/app/remotion/components/InteractiveZoneEditor.tsx +0 -273
- package/corpus/templates/videos/app/remotion/compositions/BlankComposition.tsx +0 -204
- package/corpus/templates/videos/app/remotion/compositions/index.ts +0 -2
- package/corpus/templates/videos/app/remotion/createComponentCursorTrack.ts +0 -132
- package/corpus/templates/videos/app/remotion/docs/CURSOR_TRACKS.md +0 -285
- package/corpus/templates/videos/app/remotion/easingFunctions.ts +0 -255
- package/corpus/templates/videos/app/remotion/hooks/createInteractiveComposition.tsx +0 -283
- package/corpus/templates/videos/app/remotion/hooks/useCursorHistory.ts +0 -72
- package/corpus/templates/videos/app/remotion/hooks/useCursorTypeFromHover.ts +0 -35
- package/corpus/templates/videos/app/remotion/hooks/useEditableZones.tsx +0 -267
- package/corpus/templates/videos/app/remotion/hooks/useHoverAnimation.ts +0 -90
- package/corpus/templates/videos/app/remotion/hooks/useHoverAnimationSmooth.ts +0 -146
- package/corpus/templates/videos/app/remotion/hooks/useInteractiveComponent.ts +0 -711
- package/corpus/templates/videos/app/remotion/hooks/useRegisterInteractiveElement.ts +0 -56
- package/corpus/templates/videos/app/remotion/library-components/Button.tsx +0 -252
- package/corpus/templates/videos/app/remotion/library-components/Card.tsx +0 -241
- package/corpus/templates/videos/app/remotion/library-components/CodePanel.tsx +0 -1005
- package/corpus/templates/videos/app/remotion/library-components/FileItem.tsx +0 -196
- package/corpus/templates/videos/app/remotion/library-components/FolderItem.tsx +0 -199
- package/corpus/templates/videos/app/remotion/library-components/PrimaryButton.tsx +0 -177
- package/corpus/templates/videos/app/remotion/library-components/SecondaryButton.tsx +0 -167
- package/corpus/templates/videos/app/remotion/library-components/SectionHeader.tsx +0 -108
- package/corpus/templates/videos/app/remotion/library-components/TEMPLATE.tsx +0 -211
- package/corpus/templates/videos/app/remotion/library-components/index.ts +0 -10
- package/corpus/templates/videos/app/remotion/registry.ts +0 -91
- package/corpus/templates/videos/app/remotion/testing/animationTestUtils.ts +0 -366
- package/corpus/templates/videos/app/remotion/trackAnimation.ts +0 -179
- package/corpus/templates/videos/app/remotion/trackHelpers.ts +0 -110
- package/corpus/templates/videos/app/remotion/ui-components/Cursor.tsx +0 -182
- package/corpus/templates/videos/app/remotion/ui-components/InteractiveButton.tsx +0 -116
- package/corpus/templates/videos/app/remotion/ui-components/InteractiveCard.tsx +0 -126
- package/corpus/templates/videos/app/remotion/ui-components/index.ts +0 -16
- package/corpus/templates/videos/app/remotion/utils/animationHelpers.ts +0 -444
- package/corpus/templates/videos/app/remotion/utils/interactiveElements.ts +0 -235
- package/corpus/templates/videos/app/remotion/utils/springAnimation.ts +0 -76
- package/corpus/templates/videos/app/root.tsx +0 -165
- package/corpus/templates/videos/app/routes/$.tsx +0 -10
- package/corpus/templates/videos/app/routes/_index.tsx +0 -25
- package/corpus/templates/videos/app/routes/c.$compositionId.tsx +0 -10
- package/corpus/templates/videos/app/routes/components.tsx +0 -10
- package/corpus/templates/videos/app/routes/design-systems.tsx +0 -10
- package/corpus/templates/videos/app/routes/extensions.$id.$slug.tsx +0 -2
- package/corpus/templates/videos/app/routes/extensions.$id.tsx +0 -5
- package/corpus/templates/videos/app/routes/extensions._index.tsx +0 -5
- package/corpus/templates/videos/app/routes/extensions.tsx +0 -5
- package/corpus/templates/videos/app/routes/settings.tsx +0 -88
- package/corpus/templates/videos/app/routes/team.tsx +0 -11
- package/corpus/templates/videos/app/routes.ts +0 -4
- package/corpus/templates/videos/app/state.ts +0 -77
- package/corpus/templates/videos/app/types/elementAnimations.ts +0 -121
- package/corpus/templates/videos/app/types.ts +0 -200
- package/corpus/templates/videos/app/utils/compositionHelpers.ts +0 -445
- package/corpus/templates/videos/app/utils/debug.ts +0 -63
- package/corpus/templates/videos/app/utils/keyframeUtils.ts +0 -331
- package/corpus/templates/videos/app/utils/timelineCoordinates.ts +0 -79
- package/corpus/templates/videos/app/vite-env.d.ts +0 -6
- package/corpus/templates/videos/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +0 -6
- package/corpus/templates/videos/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +0 -6
- package/corpus/templates/videos/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +0 -6
- package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +0 -6
- package/corpus/templates/videos/changelog/2026-06-29-video-layouts-adapt-when-the-agent-sidebar-is-open.md +0 -6
- package/corpus/templates/videos/components.json +0 -20
- package/corpus/templates/videos/data/sync-config.json +0 -3
- package/corpus/templates/videos/docs/examples/create-composition.example.ts +0 -128
- package/corpus/templates/videos/drizzle.config.ts +0 -3
- package/corpus/templates/videos/learnings.defaults.md +0 -5
- package/corpus/templates/videos/package.json +0 -114
- package/corpus/templates/videos/public/agent-native-icon-dark.svg +0 -10
- package/corpus/templates/videos/public/agent-native-icon-light.svg +0 -10
- package/corpus/templates/videos/public/agent-native-logo-dark.svg +0 -21
- package/corpus/templates/videos/public/agent-native-logo-light.svg +0 -21
- package/corpus/templates/videos/public/favicon.svg +0 -1
- package/corpus/templates/videos/public/icon-180.svg +0 -1
- package/corpus/templates/videos/public/icon-192.svg +0 -1
- package/corpus/templates/videos/public/icon-512.svg +0 -1
- package/corpus/templates/videos/public/manifest.json +0 -21
- package/corpus/templates/videos/public/placeholder.svg +0 -1
- package/corpus/templates/videos/public/robots.txt +0 -14
- package/corpus/templates/videos/react-router.config.ts +0 -7
- package/corpus/templates/videos/server/db/index.ts +0 -37
- package/corpus/templates/videos/server/db/schema.ts +0 -55
- package/corpus/templates/videos/server/middleware/auth.ts +0 -15
- package/corpus/templates/videos/server/plugins/agent-chat.ts +0 -29
- package/corpus/templates/videos/server/plugins/auth.ts +0 -14
- package/corpus/templates/videos/server/plugins/collab.ts +0 -11
- package/corpus/templates/videos/server/plugins/core-routes.ts +0 -22
- package/corpus/templates/videos/server/plugins/db.ts +0 -160
- package/corpus/templates/videos/server/routes/[...page].get.ts +0 -5
- package/corpus/templates/videos/server/routes/[...page].head.ts +0 -1
- package/corpus/templates/videos/server/routes/api/save-composition-defaults.post.ts +0 -274
- package/corpus/templates/videos/shared/api.ts +0 -75
- package/corpus/templates/videos/ssr-entry.ts +0 -15
- package/corpus/templates/videos/tsconfig.json +0 -21
- package/corpus/templates/videos/vite.config.ts +0 -19
- package/corpus/templates/videos/vitest.config.ts +0 -16
- package/docs/content/locales/ar-SA/template-videos.mdx +0 -336
- package/docs/content/locales/de-DE/template-videos.mdx +0 -336
- package/docs/content/locales/es-ES/template-videos.mdx +0 -336
- package/docs/content/locales/fr-FR/template-videos.mdx +0 -336
- package/docs/content/locales/hi-IN/template-videos.mdx +0 -336
- package/docs/content/locales/ja-JP/template-videos.mdx +0 -336
- package/docs/content/locales/ko-KR/template-videos.mdx +0 -336
- package/docs/content/locales/pt-BR/template-videos.mdx +0 -336
- package/docs/content/locales/zh-CN/template-videos.mdx +0 -334
- package/docs/content/locales/zh-TW/template-videos.mdx +0 -334
- package/docs/content/template-videos.mdx +0 -332
|
@@ -23,12 +23,17 @@ import {
|
|
|
23
23
|
usePresence,
|
|
24
24
|
useFollowUser,
|
|
25
25
|
LiveCursorOverlay,
|
|
26
|
+
RemoteSelectionRings,
|
|
27
|
+
RecentEditHighlights,
|
|
28
|
+
useRecentEdits,
|
|
26
29
|
useT,
|
|
27
30
|
useChangeVersion,
|
|
28
31
|
setAgentChatContextItem,
|
|
29
32
|
removeAgentChatContextItem,
|
|
30
33
|
useAvatarUrl,
|
|
31
34
|
type CollabUser,
|
|
35
|
+
type AttributedRecentEdit,
|
|
36
|
+
type OtherPresence,
|
|
32
37
|
type PromptComposerSubmitOptions,
|
|
33
38
|
} from "@agent-native/core/client";
|
|
34
39
|
import type { TweakDefinition } from "@shared/api";
|
|
@@ -57,6 +62,7 @@ import {
|
|
|
57
62
|
DESIGN_CAPABILITY_NAMES,
|
|
58
63
|
hasCapability,
|
|
59
64
|
} from "@shared/design-source-capabilities";
|
|
65
|
+
import { FULL_APP_BUILDING_ENABLED, readFusionApp } from "@shared/full-app";
|
|
60
66
|
import { shouldUseLiveFileContent } from "@shared/html-content";
|
|
61
67
|
import {
|
|
62
68
|
compile as compileMotionTimeline,
|
|
@@ -64,6 +70,16 @@ import {
|
|
|
64
70
|
} from "@shared/motion-compiler";
|
|
65
71
|
import type { MotionTrack } from "@shared/motion-timeline";
|
|
66
72
|
import { sortMotionKeyframes } from "@shared/motion-timeline";
|
|
73
|
+
import {
|
|
74
|
+
createCornerNode,
|
|
75
|
+
getPenPathGeometry,
|
|
76
|
+
parsePenNodes,
|
|
77
|
+
serializePenNodes,
|
|
78
|
+
serializePenPath,
|
|
79
|
+
type PenNode,
|
|
80
|
+
type PenPath,
|
|
81
|
+
type PenPoint,
|
|
82
|
+
} from "@shared/pen-path";
|
|
67
83
|
import {
|
|
68
84
|
resolveTweaksToCssVars,
|
|
69
85
|
type TweakSelections,
|
|
@@ -155,9 +171,11 @@ import {
|
|
|
155
171
|
CanvasContextMenu,
|
|
156
172
|
type CanvasContextMenuHandle,
|
|
157
173
|
} from "@/components/design/CanvasContextMenu";
|
|
158
|
-
import { type CodeWorkbenchActiveFile } from "@/components/design/
|
|
174
|
+
import { type CodeWorkbenchActiveFile } from "@/components/design/code-workbench/CodeWorkbench";
|
|
159
175
|
import {
|
|
160
176
|
DesignCanvas,
|
|
177
|
+
type CreatePrimitiveSpec,
|
|
178
|
+
type CreationTool,
|
|
161
179
|
type IframeContextMenuPayload,
|
|
162
180
|
type IframeHotkeyPayload,
|
|
163
181
|
type MotionTrackWire,
|
|
@@ -174,12 +192,15 @@ import {
|
|
|
174
192
|
type InspectCodeData,
|
|
175
193
|
type InspectorTab,
|
|
176
194
|
type ScreenGeometrySelection,
|
|
195
|
+
type StyleChangeMeta,
|
|
177
196
|
} from "@/components/design/EditPanel";
|
|
197
|
+
import { FusionAppBanner } from "@/components/design/FusionAppBanner";
|
|
178
198
|
import type { ExportSettingsValue } from "@/components/design/inspector";
|
|
179
199
|
import { InspectorAiActions } from "@/components/design/inspector/InspectorAiActions";
|
|
180
200
|
import {
|
|
181
201
|
LayersPanel,
|
|
182
202
|
type LayersPanelFile,
|
|
203
|
+
type LayersPanelHandle,
|
|
183
204
|
type LayersPanelMoveIntent,
|
|
184
205
|
type LayersPanelNode,
|
|
185
206
|
} from "@/components/design/LayersPanel";
|
|
@@ -199,6 +220,8 @@ import {
|
|
|
199
220
|
type CanvasPrimitiveInsert,
|
|
200
221
|
type FrameGeometry,
|
|
201
222
|
type MultiScreenCanvasTool,
|
|
223
|
+
type Point,
|
|
224
|
+
type VectorEditOverlayState,
|
|
202
225
|
} from "@/components/design/MultiScreenCanvas";
|
|
203
226
|
import { QuestionFlow } from "@/components/design/QuestionFlow";
|
|
204
227
|
import type { ReviewPanelProps } from "@/components/design/ReviewPanel";
|
|
@@ -504,6 +527,59 @@ export function getSelectedScreenGeometryForInspector(args: {
|
|
|
504
527
|
};
|
|
505
528
|
}
|
|
506
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Vector-edit foundations (P5 integration): resolves the canvas-space point
|
|
532
|
+
* where a screen's own screen-content-local (0,0) sits — i.e.
|
|
533
|
+
* `VectorEditOverlayState.originCanvas` — for a committed pen path owned by
|
|
534
|
+
* `screenId`.
|
|
535
|
+
*
|
|
536
|
+
* Mirrors the exact same fallback-merge (`getInitialFrameGeometry(index,
|
|
537
|
+
* ...)` overridden by any persisted `canvasFrameGeometryById` entry) that
|
|
538
|
+
* `getSelectedScreenGeometryForInspector` above and MultiScreenCanvas's own
|
|
539
|
+
* internal frame-geometry resolution both use, so the overlay lines up with
|
|
540
|
+
* whatever frame position is actually rendered on screen.
|
|
541
|
+
*
|
|
542
|
+
* The reserved board file (`__board__.html`, see shared/board-file.ts) is the
|
|
543
|
+
* one exception: `handleBoardDrawPrimitive`/`persistDraftPrimitive` commit
|
|
544
|
+
* board primitives with a 1:1 `{x:0,y:0,width:1,height:1}` frame (no offset
|
|
545
|
+
* subtraction — see MultiScreenCanvas's persistDraftPrimitive), so a board
|
|
546
|
+
* pen path's nodes are already in absolute canvas coordinates and
|
|
547
|
+
* `originCanvas` is `{0, 0}`.
|
|
548
|
+
*
|
|
549
|
+
* Returns `null` when the screen can't be resolved (not in `overviewScreens`
|
|
550
|
+
* and not the board file) — the caller should not enter vector-edit mode in
|
|
551
|
+
* that case.
|
|
552
|
+
*/
|
|
553
|
+
export function getScreenFrameOriginCanvas(args: {
|
|
554
|
+
screenId: string;
|
|
555
|
+
overviewScreens: Array<{
|
|
556
|
+
id: string;
|
|
557
|
+
width?: number;
|
|
558
|
+
height?: number;
|
|
559
|
+
}>;
|
|
560
|
+
canvasFrameGeometryById: CanvasFrameGeometryById;
|
|
561
|
+
boardFileId?: string | null;
|
|
562
|
+
}): Point | null {
|
|
563
|
+
if (args.boardFileId && args.screenId === args.boardFileId) {
|
|
564
|
+
return { x: 0, y: 0 };
|
|
565
|
+
}
|
|
566
|
+
const screenIndex = args.overviewScreens.findIndex(
|
|
567
|
+
(screen) => screen.id === args.screenId,
|
|
568
|
+
);
|
|
569
|
+
if (screenIndex < 0) return null;
|
|
570
|
+
const screen = args.overviewScreens[screenIndex];
|
|
571
|
+
if (!screen) return null;
|
|
572
|
+
const fallbackGeometry = getInitialFrameGeometry(screenIndex, {
|
|
573
|
+
width: screen.width ?? 1280,
|
|
574
|
+
height: screen.height ?? 2560,
|
|
575
|
+
});
|
|
576
|
+
const persistedGeometry = args.canvasFrameGeometryById[args.screenId] ?? {};
|
|
577
|
+
return {
|
|
578
|
+
x: persistedGeometry.x ?? fallbackGeometry.x,
|
|
579
|
+
y: persistedGeometry.y ?? fallbackGeometry.y,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
507
583
|
function fileIdFromLayerSelectionId(
|
|
508
584
|
layerId: string,
|
|
509
585
|
fileIds: Set<string>,
|
|
@@ -744,6 +820,28 @@ export function shouldReplacePreviewAfterVisualStyleCommit(args: {
|
|
|
744
820
|
return !args.runtimeApplied && !args.runtimeStyleApplied;
|
|
745
821
|
}
|
|
746
822
|
|
|
823
|
+
/**
|
|
824
|
+
* PF12: decide whether a style change from EditPanel (ScrubInput scrub tick /
|
|
825
|
+
* DesignColorPicker drag tick) should skip the expensive source commit
|
|
826
|
+
* (commitVisualStyles / commitStylesToSelectedLayers — projection parse, HTML
|
|
827
|
+
* patch, Yjs write, history entry) and instead only try the cheap live
|
|
828
|
+
* iframe preview.
|
|
829
|
+
*
|
|
830
|
+
* Only a genuine mid-gesture "preview" tick against a single selected element
|
|
831
|
+
* qualifies: `meta` is undefined for every existing call site that never
|
|
832
|
+
* passed gesture metadata (keyboard edits, agent edits, discrete commits),
|
|
833
|
+
* so this returns false for all of them — preserving prior full-commit
|
|
834
|
+
* behavior exactly. Multi-layer-selection edits (`selectedLayerCount > 1`)
|
|
835
|
+
* have no equivalent cheap multi-element preview channel, so those
|
|
836
|
+
* conservatively keep committing on every tick, same as before PF12.
|
|
837
|
+
*/
|
|
838
|
+
export function shouldSkipVisualStyleCommitForPreview(args: {
|
|
839
|
+
phase?: "preview" | "commit";
|
|
840
|
+
selectedLayerCount: number;
|
|
841
|
+
}): boolean {
|
|
842
|
+
return args.phase === "preview" && args.selectedLayerCount <= 1;
|
|
843
|
+
}
|
|
844
|
+
|
|
747
845
|
export function getLayerMoveIterationOrder<T>(
|
|
748
846
|
orderedIds: readonly T[],
|
|
749
847
|
placement: "before" | "after" | "inside",
|
|
@@ -1174,6 +1272,40 @@ function isSingleScreenAnnotationTool(tool: DesignTool): boolean {
|
|
|
1174
1272
|
return tool === "draw" || tool === "comment";
|
|
1175
1273
|
}
|
|
1176
1274
|
|
|
1275
|
+
/**
|
|
1276
|
+
* P4/single-screen placement: maps the editor's current tool + view mode to
|
|
1277
|
+
* the `CreationTool` DesignCanvas's single-screen click-to-place overlay
|
|
1278
|
+
* understands, or `null` when the overlay should not mount.
|
|
1279
|
+
*
|
|
1280
|
+
* Only mounts while actually focused on a single screen (`viewMode ===
|
|
1281
|
+
* "single"`) with an active file — overview mode keeps using
|
|
1282
|
+
* MultiScreenCanvas's own draft-primitive drawing, which this overlay does
|
|
1283
|
+
* not replace. `rect` (the DesignTool id) maps to CreationTool's
|
|
1284
|
+
* `"rectangle"`; `polygon`/`star`/`frame`/`draw`/`hand`/`comment`/`scale`/
|
|
1285
|
+
* `move` have no single-screen click-to-place equivalent yet and map to
|
|
1286
|
+
* `null` so the overlay never mounts for them (existing tool behavior for
|
|
1287
|
+
* those is unaffected).
|
|
1288
|
+
*/
|
|
1289
|
+
export function getSingleScreenCreationTool(args: {
|
|
1290
|
+
activeTool: DesignTool;
|
|
1291
|
+
viewMode: "single" | "overview";
|
|
1292
|
+
hasActiveFile: boolean;
|
|
1293
|
+
}): CreationTool | null {
|
|
1294
|
+
if (args.viewMode !== "single" || !args.hasActiveFile) return null;
|
|
1295
|
+
switch (args.activeTool) {
|
|
1296
|
+
case "rect":
|
|
1297
|
+
return "rectangle";
|
|
1298
|
+
case "ellipse":
|
|
1299
|
+
case "line":
|
|
1300
|
+
case "arrow":
|
|
1301
|
+
case "text":
|
|
1302
|
+
case "pen":
|
|
1303
|
+
return args.activeTool;
|
|
1304
|
+
default:
|
|
1305
|
+
return null;
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1177
1309
|
interface DesignFile {
|
|
1178
1310
|
id: string;
|
|
1179
1311
|
filename: string;
|
|
@@ -2433,6 +2565,208 @@ function reassignDuplicatedNodeIds(content: string): string {
|
|
|
2433
2565
|
);
|
|
2434
2566
|
}
|
|
2435
2567
|
|
|
2568
|
+
/** Mirrors DesignCanvas's own local CREATION_DEFAULT_LINE_LENGTH (single-
|
|
2569
|
+
* screen creation overlay) so a single-screen pen click gets a visible
|
|
2570
|
+
* minimal path of the same default length a line/arrow click would use,
|
|
2571
|
+
* rather than a zero-length point. */
|
|
2572
|
+
const SINGLE_SCREEN_PEN_CLICK_DEFAULT_LENGTH = 120;
|
|
2573
|
+
|
|
2574
|
+
/**
|
|
2575
|
+
* P4/single-screen placement: converts a `CreatePrimitiveSpec` emitted by
|
|
2576
|
+
* DesignCanvas's single-screen click-to-place overlay (coordinates already in
|
|
2577
|
+
* screen-content space, matching `appendCanvasPrimitiveToHtml`'s own
|
|
2578
|
+
* coordinate system) into the `CanvasPrimitiveInsert` shape the overview path
|
|
2579
|
+
* (`handleCreatePrimitive` / `appendCanvasPrimitiveToHtml`) already knows how
|
|
2580
|
+
* to persist. This deliberately reuses the exact same insert shape as
|
|
2581
|
+
* `draftPrimitiveToInsert` produces in MultiScreenCanvas.tsx so single-screen
|
|
2582
|
+
* and overview creation commit through one shared persistence path instead of
|
|
2583
|
+
* two.
|
|
2584
|
+
*
|
|
2585
|
+
* Pen is the one deferred case (see DesignCanvas's SingleScreenCreationOverlay
|
|
2586
|
+
* doc comment): a single-screen pen click only ever gets a single start
|
|
2587
|
+
* point, so — rather than force overview to get full multi-click pen
|
|
2588
|
+
* authoring — this commits a minimal open 2-node path (start point to a
|
|
2589
|
+
* fixed-length point to the right, matching the line/arrow click default
|
|
2590
|
+
* length) so the user gets a real, editable pen path immediately. True
|
|
2591
|
+
* single-screen multi-click pen authoring remains a follow-up.
|
|
2592
|
+
*/
|
|
2593
|
+
export function createPrimitiveInsertFromSpec(
|
|
2594
|
+
spec: CreatePrimitiveSpec,
|
|
2595
|
+
nodeId: string,
|
|
2596
|
+
): CanvasPrimitiveInsert | null {
|
|
2597
|
+
if (spec.tool === "line" || spec.tool === "arrow") {
|
|
2598
|
+
const points = spec.points;
|
|
2599
|
+
if (!points || points.length < 2) return null;
|
|
2600
|
+
const [start, end] = points;
|
|
2601
|
+
if (!start || !end) return null;
|
|
2602
|
+
const left = Math.min(start.x, end.x);
|
|
2603
|
+
const top = Math.min(start.y, end.y);
|
|
2604
|
+
const width = Math.max(1, Math.abs(end.x - start.x));
|
|
2605
|
+
const height = Math.max(1, Math.abs(end.y - start.y));
|
|
2606
|
+
return {
|
|
2607
|
+
kind: spec.tool,
|
|
2608
|
+
nodeId,
|
|
2609
|
+
geometry: { x: left, y: top, width, height },
|
|
2610
|
+
points,
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
if (spec.tool === "pen") {
|
|
2615
|
+
const start = spec.points?.[0];
|
|
2616
|
+
if (!start) return null;
|
|
2617
|
+
const end = {
|
|
2618
|
+
x: start.x + SINGLE_SCREEN_PEN_CLICK_DEFAULT_LENGTH,
|
|
2619
|
+
y: start.y,
|
|
2620
|
+
};
|
|
2621
|
+
const penPath: PenPath = {
|
|
2622
|
+
nodes: [createCornerNode(start), createCornerNode(end)],
|
|
2623
|
+
closed: false,
|
|
2624
|
+
};
|
|
2625
|
+
const geometry = getPenPathGeometry(penPath);
|
|
2626
|
+
return {
|
|
2627
|
+
kind: "path",
|
|
2628
|
+
nodeId,
|
|
2629
|
+
geometry,
|
|
2630
|
+
points: [start, end],
|
|
2631
|
+
pathData: serializePenPath(penPath),
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
const rect = spec.rect;
|
|
2636
|
+
if (!rect) return null;
|
|
2637
|
+
return {
|
|
2638
|
+
kind: spec.tool,
|
|
2639
|
+
nodeId,
|
|
2640
|
+
geometry: {
|
|
2641
|
+
x: rect.x,
|
|
2642
|
+
y: rect.y,
|
|
2643
|
+
width: Math.max(1, rect.width),
|
|
2644
|
+
height: Math.max(1, rect.height),
|
|
2645
|
+
},
|
|
2646
|
+
autoSize: spec.tool === "text" ? spec.fromClick : undefined,
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
/**
|
|
2651
|
+
* Reconstructs a structured `PenPath` (nodes + optional handles) from a `d`
|
|
2652
|
+
* string produced by `serializePenPath` (shared/pen-path.ts). This is the
|
|
2653
|
+
* deliberate INVERSE of that exact serializer — not a general SVG path
|
|
2654
|
+
* parser — so it only needs to understand the small grammar
|
|
2655
|
+
* `serializePenPath` actually emits: `M x y`, then per-segment `L x y` (both
|
|
2656
|
+
* handles coincide with their anchors) or `C c1x c1y c2x c2y x y`
|
|
2657
|
+
* (`c1`/`c2` are the FROM node's `handleOut` / TO node's `handleIn`,
|
|
2658
|
+
* respectively — see `serializeSegment`), optionally followed by a trailing
|
|
2659
|
+
* `Z` for a closed path (whose preceding segment is the wrap-around from the
|
|
2660
|
+
* last node back to the first, not a new node).
|
|
2661
|
+
*
|
|
2662
|
+
* This exists because `CanvasPrimitiveInsert` (MultiScreenCanvas.tsx) only
|
|
2663
|
+
* carries the already-flattened `pathData` string across the overview
|
|
2664
|
+
* commit boundary, not the richer `DraftPrimitive.penPath` MultiScreenCanvas
|
|
2665
|
+
* keeps internally — so committing a pen path drawn in OVERVIEW mode has no
|
|
2666
|
+
* other source for the structured node/handle data `data-an-pen-nodes`
|
|
2667
|
+
* needs. Single-screen pen placement (`createPrimitiveInsertFromSpec` above)
|
|
2668
|
+
* builds its `PenPath` directly and never needs this reverse parse.
|
|
2669
|
+
*
|
|
2670
|
+
* Returns `null` for anything that doesn't match the expected grammar
|
|
2671
|
+
* (empty/malformed `d`) rather than throwing, so a call site can always fall
|
|
2672
|
+
* back to skipping `data-an-pen-nodes` for that element.
|
|
2673
|
+
*/
|
|
2674
|
+
export function parsePenPathFromSerializedD(d: string): PenPath | null {
|
|
2675
|
+
const trimmed = d.trim();
|
|
2676
|
+
if (!trimmed) return null;
|
|
2677
|
+
const tokens = trimmed.match(/[MLCZ]|-?\d+(?:\.\d+)?/gi);
|
|
2678
|
+
if (!tokens || tokens.length === 0) return null;
|
|
2679
|
+
|
|
2680
|
+
const readNumbers = (count: number): PenPoint[] => {
|
|
2681
|
+
const points: PenPoint[] = [];
|
|
2682
|
+
for (let i = 0; i < count; i += 2) {
|
|
2683
|
+
const x = Number(tokens[cursor + i]);
|
|
2684
|
+
const y = Number(tokens[cursor + i + 1]);
|
|
2685
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
|
2686
|
+
throw new Error("invalid pen path number");
|
|
2687
|
+
}
|
|
2688
|
+
points.push({ x, y });
|
|
2689
|
+
}
|
|
2690
|
+
return points;
|
|
2691
|
+
};
|
|
2692
|
+
|
|
2693
|
+
let cursor = 0;
|
|
2694
|
+
const command = tokens[cursor];
|
|
2695
|
+
if (command?.toUpperCase() !== "M") return null;
|
|
2696
|
+
cursor += 1;
|
|
2697
|
+
|
|
2698
|
+
try {
|
|
2699
|
+
const [start] = readNumbers(2);
|
|
2700
|
+
if (!start) return null;
|
|
2701
|
+
cursor += 2;
|
|
2702
|
+
const nodes: PenNode[] = [{ point: start }];
|
|
2703
|
+
let closed = false;
|
|
2704
|
+
|
|
2705
|
+
while (cursor < tokens.length) {
|
|
2706
|
+
const token = tokens[cursor]?.toUpperCase();
|
|
2707
|
+
if (token === "Z") {
|
|
2708
|
+
cursor += 1;
|
|
2709
|
+
continue;
|
|
2710
|
+
}
|
|
2711
|
+
if (token === "L") {
|
|
2712
|
+
cursor += 1;
|
|
2713
|
+
const [to] = readNumbers(2);
|
|
2714
|
+
if (!to) return null;
|
|
2715
|
+
cursor += 2;
|
|
2716
|
+
// Same wrap-around case as the "C" branch below: a closed path's
|
|
2717
|
+
// final segment (immediately followed by Z) returns to the FIRST
|
|
2718
|
+
// node rather than describing a new one.
|
|
2719
|
+
const nextIsClose = tokens[cursor]?.toUpperCase() === "Z";
|
|
2720
|
+
if (nextIsClose) {
|
|
2721
|
+
closed = true;
|
|
2722
|
+
} else {
|
|
2723
|
+
nodes.push({ point: to });
|
|
2724
|
+
}
|
|
2725
|
+
continue;
|
|
2726
|
+
}
|
|
2727
|
+
if (token === "C") {
|
|
2728
|
+
cursor += 1;
|
|
2729
|
+
const coords = readNumbers(6);
|
|
2730
|
+
cursor += 6;
|
|
2731
|
+
const [c1, c2, to] = coords;
|
|
2732
|
+
if (!c1 || !c2 || !to) return null;
|
|
2733
|
+
const fromNode = nodes[nodes.length - 1];
|
|
2734
|
+
if (!fromNode) return null;
|
|
2735
|
+
if (!samePenPoint(c1, fromNode.point)) {
|
|
2736
|
+
fromNode.handleOut = c1;
|
|
2737
|
+
}
|
|
2738
|
+
// A closed path's final "C"/"L" segment (immediately followed by Z)
|
|
2739
|
+
// wraps from the last real node back to the FIRST node — it is not a
|
|
2740
|
+
// new node. Apply its handles to the existing first node instead of
|
|
2741
|
+
// pushing a duplicate.
|
|
2742
|
+
const nextIsClose = tokens[cursor]?.toUpperCase() === "Z";
|
|
2743
|
+
if (nextIsClose) {
|
|
2744
|
+
closed = true;
|
|
2745
|
+
const firstNode = nodes[0];
|
|
2746
|
+
if (firstNode && !samePenPoint(c2, firstNode.point)) {
|
|
2747
|
+
firstNode.handleIn = c2;
|
|
2748
|
+
}
|
|
2749
|
+
} else {
|
|
2750
|
+
const node: PenNode = { point: to };
|
|
2751
|
+
if (!samePenPoint(c2, to)) node.handleIn = c2;
|
|
2752
|
+
nodes.push(node);
|
|
2753
|
+
}
|
|
2754
|
+
continue;
|
|
2755
|
+
}
|
|
2756
|
+
// Unknown token — grammar mismatch, bail rather than guess.
|
|
2757
|
+
return null;
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
return { nodes, closed };
|
|
2761
|
+
} catch {
|
|
2762
|
+
return null;
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
function samePenPoint(a: PenPoint, b: PenPoint): boolean {
|
|
2767
|
+
return Math.abs(a.x - b.x) < 0.05 && Math.abs(a.y - b.y) < 0.05;
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2436
2770
|
function primitiveLayerName(primitive: CanvasPrimitiveInsert): string {
|
|
2437
2771
|
switch (primitive.kind) {
|
|
2438
2772
|
case "frame":
|
|
@@ -2715,6 +3049,108 @@ function appendCanvasPrimitiveToHtml(
|
|
|
2715
3049
|
}
|
|
2716
3050
|
}
|
|
2717
3051
|
|
|
3052
|
+
/**
|
|
3053
|
+
* Vector-edit foundations: stamps `data-an-pen-nodes` (the compact
|
|
3054
|
+
* serializePenNodes encoding — see shared/pen-path.ts) onto the committed
|
|
3055
|
+
* pen-path SVG element identified by `data-agent-native-node-id === nodeId`,
|
|
3056
|
+
* so the structured node/handle data survives independently of the
|
|
3057
|
+
* flattened `d` attribute and can later be re-hydrated into vector edit
|
|
3058
|
+
* mode. Returns `content` unchanged (never null) if the node can't be found
|
|
3059
|
+
* or `content` fails to parse — this is a best-effort enrichment step, never
|
|
3060
|
+
* a hard requirement for the primitive to commit successfully.
|
|
3061
|
+
*/
|
|
3062
|
+
function setPenNodesAttributeOnElement(
|
|
3063
|
+
content: string,
|
|
3064
|
+
nodeId: string,
|
|
3065
|
+
penPath: PenPath,
|
|
3066
|
+
): string {
|
|
3067
|
+
if (typeof window === "undefined") return content;
|
|
3068
|
+
try {
|
|
3069
|
+
const doc = new DOMParser().parseFromString(content, "text/html");
|
|
3070
|
+
// nodeId always comes from uniqueLayerId(...) (alphanumeric/hyphen/UUID
|
|
3071
|
+
// chars only, never quotes), but escape defensively anyway since this
|
|
3072
|
+
// value is interpolated into a CSS attribute-selector string.
|
|
3073
|
+
const safeNodeId = nodeId.replace(/["\\]/g, "\\$&");
|
|
3074
|
+
const element = doc.querySelector(
|
|
3075
|
+
`[data-agent-native-node-id="${safeNodeId}"]`,
|
|
3076
|
+
);
|
|
3077
|
+
if (!element) return content;
|
|
3078
|
+
element.setAttribute("data-an-pen-nodes", serializePenNodes(penPath));
|
|
3079
|
+
return `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
|
|
3080
|
+
} catch {
|
|
3081
|
+
return content;
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
/**
|
|
3086
|
+
* Vector-edit foundations: writes an edited PenPath back onto its committed
|
|
3087
|
+
* SVG element — the `<path>` child's `d` (serializePenPath, matching what
|
|
3088
|
+
* appendCanvasPrimitiveToHtml's explicitPathData branch produces) AND the
|
|
3089
|
+
* `<svg>` root's `data-an-pen-nodes` (serializePenNodes, the structured
|
|
3090
|
+
* round-trip source), plus the root's `viewBox`/left/top/width/height so the
|
|
3091
|
+
* element's bounding box stays correct after anchors/handles moved it.
|
|
3092
|
+
* `nodeId` is the element's `data-agent-native-node-id` value (screen-content
|
|
3093
|
+
* space — same coordinate system the path's own nodes are already in, see
|
|
3094
|
+
* parsePenPathFromSerializedD's doc comment for that finding).
|
|
3095
|
+
*
|
|
3096
|
+
* Returns `content` unchanged (never null/throws) if the element can't be
|
|
3097
|
+
* found, isn't an SVG pen-path root, or has no `<path>` child — the caller
|
|
3098
|
+
* treats that as a no-op commit rather than losing the user's edit.
|
|
3099
|
+
*/
|
|
3100
|
+
function writeBackVectorEditedPenPath(
|
|
3101
|
+
content: string,
|
|
3102
|
+
nodeId: string,
|
|
3103
|
+
penPath: PenPath,
|
|
3104
|
+
): string {
|
|
3105
|
+
if (typeof window === "undefined") return content;
|
|
3106
|
+
try {
|
|
3107
|
+
const doc = new DOMParser().parseFromString(content, "text/html");
|
|
3108
|
+
const safeNodeId = nodeId.replace(/["\\]/g, "\\$&");
|
|
3109
|
+
const svg = doc.querySelector(
|
|
3110
|
+
`[data-agent-native-node-id="${safeNodeId}"]`,
|
|
3111
|
+
);
|
|
3112
|
+
const path = svg?.querySelector("path");
|
|
3113
|
+
if (!svg || !path) return content;
|
|
3114
|
+
|
|
3115
|
+
const d = serializePenPath(penPath);
|
|
3116
|
+
const geometry = getPenPathGeometry(penPath);
|
|
3117
|
+
const isClosed = Boolean(penPath.closed && penPath.nodes.length > 1);
|
|
3118
|
+
|
|
3119
|
+
path.setAttribute("d", d);
|
|
3120
|
+
if (isClosed && path.getAttribute("fill") === "none") {
|
|
3121
|
+
path.setAttribute("fill", "#D9D9D9");
|
|
3122
|
+
} else if (!isClosed) {
|
|
3123
|
+
path.setAttribute("fill", "none");
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
svg.setAttribute("data-an-pen-nodes", serializePenNodes(penPath));
|
|
3127
|
+
svg.setAttribute(
|
|
3128
|
+
"viewBox",
|
|
3129
|
+
`${geometry.x} ${geometry.y} ${geometry.width} ${geometry.height}`,
|
|
3130
|
+
);
|
|
3131
|
+
const existingStyle = svg.getAttribute("style") ?? "";
|
|
3132
|
+
const rotationMatch = existingStyle.match(/transform:[^;]+/);
|
|
3133
|
+
svg.setAttribute(
|
|
3134
|
+
"style",
|
|
3135
|
+
[
|
|
3136
|
+
"position:absolute",
|
|
3137
|
+
`left:${Math.round(geometry.x)}px`,
|
|
3138
|
+
`top:${Math.round(geometry.y)}px`,
|
|
3139
|
+
`width:${Math.max(1, Math.round(geometry.width))}px`,
|
|
3140
|
+
`height:${Math.max(1, Math.round(geometry.height))}px`,
|
|
3141
|
+
"overflow:visible",
|
|
3142
|
+
rotationMatch?.[0] ?? "",
|
|
3143
|
+
]
|
|
3144
|
+
.filter(Boolean)
|
|
3145
|
+
.join(";"),
|
|
3146
|
+
);
|
|
3147
|
+
|
|
3148
|
+
return `<!DOCTYPE html>\n${doc.documentElement.outerHTML}`;
|
|
3149
|
+
} catch {
|
|
3150
|
+
return content;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
|
|
2718
3154
|
function polygonPointsForHtmlShape(
|
|
2719
3155
|
kind: "polygon" | "star",
|
|
2720
3156
|
width: number,
|
|
@@ -4237,11 +4673,11 @@ type DesignLeftPanel =
|
|
|
4237
4673
|
| "import"
|
|
4238
4674
|
| "code";
|
|
4239
4675
|
|
|
4240
|
-
const SHOW_DESIGN_CODE_LEFT_PANEL =
|
|
4676
|
+
const SHOW_DESIGN_CODE_LEFT_PANEL = true;
|
|
4241
4677
|
|
|
4242
|
-
const
|
|
4243
|
-
import("@/components/design/
|
|
4244
|
-
default: module.
|
|
4678
|
+
const CodeWorkbench = lazy(() =>
|
|
4679
|
+
import("@/components/design/code-workbench/CodeWorkbench").then((module) => ({
|
|
4680
|
+
default: module.CodeWorkbench,
|
|
4245
4681
|
})),
|
|
4246
4682
|
);
|
|
4247
4683
|
|
|
@@ -5196,6 +5632,48 @@ function cloneCanvasFrameGeometry(
|
|
|
5196
5632
|
);
|
|
5197
5633
|
}
|
|
5198
5634
|
|
|
5635
|
+
/**
|
|
5636
|
+
* Freshness guard for geometry undo/redo. Returns the ids of frames that a
|
|
5637
|
+
* geometry history entry touched whose LIVE geometry no longer matches what the
|
|
5638
|
+
* entry expects (`expected` = the state this entry previously wrote). A
|
|
5639
|
+
* non-empty result means a concurrent peer/agent moved those frames since the
|
|
5640
|
+
* snapshot was captured, so replaying the entry's stored "before"/"after" would
|
|
5641
|
+
* clobber their change. Frames absent from live geometry (deleted) are treated
|
|
5642
|
+
* as changed. Only compares the frames the entry itself changed, so unrelated
|
|
5643
|
+
* concurrent edits to OTHER frames don't block this undo.
|
|
5644
|
+
*/
|
|
5645
|
+
function frameGeometryEquals(
|
|
5646
|
+
a: CanvasFrameGeometry | undefined,
|
|
5647
|
+
b: CanvasFrameGeometry | undefined,
|
|
5648
|
+
): boolean {
|
|
5649
|
+
return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
|
|
5650
|
+
}
|
|
5651
|
+
|
|
5652
|
+
function staleGeometryFrameIds(
|
|
5653
|
+
entry: GeometryHistoryEntry,
|
|
5654
|
+
live: CanvasFrameGeometryById,
|
|
5655
|
+
expected: CanvasFrameGeometryById,
|
|
5656
|
+
): string[] {
|
|
5657
|
+
// A frame is "touched" when ANY geometry field differs between before and
|
|
5658
|
+
// after — moves (x/y), rotation, and z-order count, not just viewport size.
|
|
5659
|
+
const touched = new Set<string>(
|
|
5660
|
+
[...Object.keys(entry.before), ...Object.keys(entry.after)].filter(
|
|
5661
|
+
(frameId) =>
|
|
5662
|
+
!frameGeometryEquals(entry.before[frameId], entry.after[frameId]),
|
|
5663
|
+
),
|
|
5664
|
+
);
|
|
5665
|
+
const stale: string[] = [];
|
|
5666
|
+
for (const frameId of touched) {
|
|
5667
|
+
const expectedGeo = expected[frameId];
|
|
5668
|
+
const liveGeo = live[frameId];
|
|
5669
|
+
if (!expectedGeo) continue; // entry didn't establish this frame's geometry
|
|
5670
|
+
if (!liveGeo || JSON.stringify(liveGeo) !== JSON.stringify(expectedGeo)) {
|
|
5671
|
+
stale.push(frameId);
|
|
5672
|
+
}
|
|
5673
|
+
}
|
|
5674
|
+
return stale;
|
|
5675
|
+
}
|
|
5676
|
+
|
|
5199
5677
|
function viewportSizeFromFrameGeometry(
|
|
5200
5678
|
geometry: CanvasFrameGeometry | undefined,
|
|
5201
5679
|
) {
|
|
@@ -5363,6 +5841,18 @@ export default function DesignEditor() {
|
|
|
5363
5841
|
const [selectedElement, setSelectedElement] = useState<ElementInfo | null>(
|
|
5364
5842
|
null,
|
|
5365
5843
|
);
|
|
5844
|
+
// Vector-edit mode (P5 integration): active while the user is editing a
|
|
5845
|
+
// committed pen path's anchors/handles on the overview canvas. `path` is
|
|
5846
|
+
// the LIVE working copy (path-local coordinates, matching pen-path.ts);
|
|
5847
|
+
// `originCanvas` is recomputed from the owning screen's current frame
|
|
5848
|
+
// geometry on every render (see vectorEditOverlayState below) rather than
|
|
5849
|
+
// stored here, so dragging/resizing the screen frame while editing doesn't
|
|
5850
|
+
// leave the overlay pinned to a stale position. null when not editing.
|
|
5851
|
+
const [vectorEditingState, setVectorEditingState] = useState<{
|
|
5852
|
+
screenId: string;
|
|
5853
|
+
nodeId: string;
|
|
5854
|
+
path: PenPath;
|
|
5855
|
+
} | null>(null);
|
|
5366
5856
|
const [pendingVisualStyleEdits, setPendingVisualStyleEdits] = useState<
|
|
5367
5857
|
PendingVisualStyleEdit[]
|
|
5368
5858
|
>([]);
|
|
@@ -5806,7 +6296,7 @@ export default function DesignEditor() {
|
|
|
5806
6296
|
const setWidth =
|
|
5807
6297
|
side === "left" ? setLeftSidebarWidth : setRightSidebarWidth;
|
|
5808
6298
|
const minWidth = side === "left" ? (codePanelOpen ? 520 : 220) : 240;
|
|
5809
|
-
const maxWidth = side === "left" ? (codePanelOpen ?
|
|
6299
|
+
const maxWidth = side === "left" ? (codePanelOpen ? 1100 : 420) : 390;
|
|
5810
6300
|
const target =
|
|
5811
6301
|
side === "left"
|
|
5812
6302
|
? leftSidebarContentRef.current
|
|
@@ -6309,7 +6799,10 @@ export default function DesignEditor() {
|
|
|
6309
6799
|
pendingQuestionsVisible,
|
|
6310
6800
|
]);
|
|
6311
6801
|
|
|
6312
|
-
// Current user info for collaborative presence
|
|
6802
|
+
// Current user info for collaborative presence. The avatar (if the user has
|
|
6803
|
+
// uploaded one) is plumbed through so peers see the user's face on cursors,
|
|
6804
|
+
// selection rings, edit highlights, and the presence bar.
|
|
6805
|
+
const currentUserAvatarUrl = useAvatarUrl(session?.email);
|
|
6313
6806
|
const currentUser: CollabUser | undefined = useMemo(
|
|
6314
6807
|
() =>
|
|
6315
6808
|
session?.email
|
|
@@ -6317,9 +6810,12 @@ export default function DesignEditor() {
|
|
|
6317
6810
|
name: session.name?.trim() || emailToName(session.email),
|
|
6318
6811
|
email: session.email,
|
|
6319
6812
|
color: emailToColor(session.email),
|
|
6813
|
+
...(currentUserAvatarUrl
|
|
6814
|
+
? { avatarUrl: currentUserAvatarUrl }
|
|
6815
|
+
: {}),
|
|
6320
6816
|
}
|
|
6321
6817
|
: undefined,
|
|
6322
|
-
[session?.email, session?.name],
|
|
6818
|
+
[session?.email, session?.name, currentUserAvatarUrl],
|
|
6323
6819
|
);
|
|
6324
6820
|
const signInToSaveHref = buildSignInHrefForDesignIntent("save");
|
|
6325
6821
|
const signInToShareHref = buildSignInHrefForDesignIntent("share");
|
|
@@ -6997,6 +7493,14 @@ export default function DesignEditor() {
|
|
|
6997
7493
|
() => getCanvasFrameGeometry(designDataJson),
|
|
6998
7494
|
[designDataJson],
|
|
6999
7495
|
);
|
|
7496
|
+
// Freshest live geometry for the geometry-undo freshness guard. Read from a
|
|
7497
|
+
// ref (not a render-time closure) so undo/redo compare against the geometry a
|
|
7498
|
+
// concurrent peer/agent may have just written, not the value captured when
|
|
7499
|
+
// handleUndo's callback was memoized.
|
|
7500
|
+
const liveFrameGeometryRef = useRef(canvasFrameGeometryById);
|
|
7501
|
+
useEffect(() => {
|
|
7502
|
+
liveFrameGeometryRef.current = canvasFrameGeometryById;
|
|
7503
|
+
}, [canvasFrameGeometryById]);
|
|
7000
7504
|
|
|
7001
7505
|
// ── Board file ─────────────────────────────────────────────────────────────
|
|
7002
7506
|
// The board is a reserved design_file (filename "__board__.html") whose id is
|
|
@@ -8049,6 +8553,37 @@ export default function DesignEditor() {
|
|
|
8049
8553
|
user: currentUser,
|
|
8050
8554
|
});
|
|
8051
8555
|
|
|
8556
|
+
// ── Overview presence (Task 1) ──────────────────────────────────────────────
|
|
8557
|
+
//
|
|
8558
|
+
// In single-screen mode the collab doc above already drives selection rings +
|
|
8559
|
+
// recent-edit highlights over the one active iframe. Overview (MultiScreenCanvas)
|
|
8560
|
+
// has no single active collab doc, so we open a SEPARATE, presence-only
|
|
8561
|
+
// subscription here. Design decisions:
|
|
8562
|
+
//
|
|
8563
|
+
// • Subscription model: a SINGLE subscription for the most-relevant file —
|
|
8564
|
+
// `activeFileId` (which in overview defaults to the selected/first screen
|
|
8565
|
+
// and is advanced to the worked file by the agent's view-screen / navigate
|
|
8566
|
+
// actions). We deliberately do NOT open one subscription per visible frame:
|
|
8567
|
+
// an overview board can hold many screens, and N polling docs would be
|
|
8568
|
+
// wasteful. Actions publish agent presence to the FILE id (see
|
|
8569
|
+
// edit-design.ts / apply-visual-edit.ts → agentEnterDocument(file.id)), so
|
|
8570
|
+
// this one doc surfaces the agent's `selection` + `recentEdits` for the file
|
|
8571
|
+
// it is editing.
|
|
8572
|
+
// • Only mounts in overview so we never run two live docs for the same file.
|
|
8573
|
+
const overviewPresenceFileId =
|
|
8574
|
+
viewMode === "overview"
|
|
8575
|
+
? (activeFileId ?? overviewScreens[0]?.id ?? null)
|
|
8576
|
+
: null;
|
|
8577
|
+
const { awareness: overviewAwareness, ydoc: overviewYdoc } =
|
|
8578
|
+
useCollaborativeDoc({
|
|
8579
|
+
docId:
|
|
8580
|
+
isSignedIn && canEditDesign && overviewPresenceFileId
|
|
8581
|
+
? overviewPresenceFileId
|
|
8582
|
+
: null,
|
|
8583
|
+
requestSource: TAB_ID,
|
|
8584
|
+
user: currentUser,
|
|
8585
|
+
});
|
|
8586
|
+
|
|
8052
8587
|
// Track collab-sourced content for the active file.
|
|
8053
8588
|
// When Y.Doc is synced and has content, use it as the source of truth
|
|
8054
8589
|
// instead of the DB-fetched content so live remote edits appear instantly.
|
|
@@ -8508,6 +9043,10 @@ export default function DesignEditor() {
|
|
|
8508
9043
|
|
|
8509
9044
|
// Canvas container ref for cursor overlay coordinate mapping.
|
|
8510
9045
|
const canvasContextMenuRef = useRef<CanvasContextMenuHandle | null>(null);
|
|
9046
|
+
// L12: imperative handle so Cmd+R and the canvas context-menu Rename item
|
|
9047
|
+
// can start the layers panel's real inline rename editor on the selected
|
|
9048
|
+
// layer (see beginRename in LayersPanel.tsx).
|
|
9049
|
+
const layersPanelRef = useRef<LayersPanelHandle | null>(null);
|
|
8511
9050
|
const canvasContainerRef = useRef<HTMLDivElement>(null);
|
|
8512
9051
|
const activeEditorDragRef = useRef(false);
|
|
8513
9052
|
|
|
@@ -8698,6 +9237,315 @@ export default function DesignEditor() {
|
|
|
8698
9237
|
};
|
|
8699
9238
|
}, [activeFileId, zoom, setPresence]);
|
|
8700
9239
|
|
|
9240
|
+
// ── Remote selection rings + lingering edit highlights on the canvas ────────
|
|
9241
|
+
//
|
|
9242
|
+
// The agent (and human peers) publish selection descriptors + recentEdits into
|
|
9243
|
+
// awareness. Both are resolved by locating the target element INSIDE the active
|
|
9244
|
+
// screen's iframe, then transforming the iframe-local rect into parent-viewport
|
|
9245
|
+
// coordinates. The iframe sits inside a `transform: scale(zoom)` wrapper, so its
|
|
9246
|
+
// on-screen size differs from its internal layout size; we derive the scale from
|
|
9247
|
+
// `boundingRect.width / iframe.clientWidth` (robust to any wrapper transform)
|
|
9248
|
+
// instead of reading the zoom value directly. Cross-origin/unmounted iframes
|
|
9249
|
+
// return null (the ring/highlight is silently skipped).
|
|
9250
|
+
|
|
9251
|
+
// Map an iframe-local DOMRect (element or Range) to the parent viewport.
|
|
9252
|
+
const mapIframeRectToViewport = useCallback(
|
|
9253
|
+
(iframe: HTMLIFrameElement, rect: DOMRect): DOMRect | null => {
|
|
9254
|
+
const frameRect = iframe.getBoundingClientRect();
|
|
9255
|
+
if (frameRect.width === 0 || frameRect.height === 0) return null;
|
|
9256
|
+
// Effective on-screen scale of the iframe's internal coordinate system.
|
|
9257
|
+
const layoutWidth = iframe.clientWidth || frameRect.width;
|
|
9258
|
+
const layoutHeight = iframe.clientHeight || frameRect.height;
|
|
9259
|
+
const scaleX = layoutWidth ? frameRect.width / layoutWidth : 1;
|
|
9260
|
+
const scaleY = layoutHeight ? frameRect.height / layoutHeight : 1;
|
|
9261
|
+
return new DOMRect(
|
|
9262
|
+
frameRect.left + rect.left * scaleX,
|
|
9263
|
+
frameRect.top + rect.top * scaleY,
|
|
9264
|
+
rect.width * scaleX,
|
|
9265
|
+
rect.height * scaleY,
|
|
9266
|
+
);
|
|
9267
|
+
},
|
|
9268
|
+
[],
|
|
9269
|
+
);
|
|
9270
|
+
|
|
9271
|
+
// Resolve a CSS selector to a viewport rect inside a specific iframe. The
|
|
9272
|
+
// iframe is the coordinate anchor, so the same logic serves both the
|
|
9273
|
+
// single-screen preview iframe and each overview frame iframe (see the
|
|
9274
|
+
// overview resolvers below, which pass the frame the agent is editing).
|
|
9275
|
+
const resolveSelectorRectInIframe = useCallback(
|
|
9276
|
+
(iframe: HTMLIFrameElement | null, selector: string): DOMRect | null => {
|
|
9277
|
+
if (!iframe) return null;
|
|
9278
|
+
let doc: Document | null = null;
|
|
9279
|
+
try {
|
|
9280
|
+
doc = iframe.contentDocument;
|
|
9281
|
+
} catch {
|
|
9282
|
+
return null; // cross-origin — cannot inspect
|
|
9283
|
+
}
|
|
9284
|
+
if (!doc) return null;
|
|
9285
|
+
let el: Element | null = null;
|
|
9286
|
+
try {
|
|
9287
|
+
el = doc.querySelector(selector);
|
|
9288
|
+
} catch {
|
|
9289
|
+
return null; // invalid selector
|
|
9290
|
+
}
|
|
9291
|
+
if (!el) return null;
|
|
9292
|
+
return mapIframeRectToViewport(iframe, el.getBoundingClientRect());
|
|
9293
|
+
},
|
|
9294
|
+
[mapIframeRectToViewport],
|
|
9295
|
+
);
|
|
9296
|
+
|
|
9297
|
+
// Resolve a text quote to a viewport rect by walking a specific iframe body.
|
|
9298
|
+
const resolveTextQuoteRectInIframe = useCallback(
|
|
9299
|
+
(iframe: HTMLIFrameElement | null, quote: string): DOMRect | null => {
|
|
9300
|
+
const needle = quote.trim();
|
|
9301
|
+
if (!needle) return null;
|
|
9302
|
+
if (!iframe) return null;
|
|
9303
|
+
let doc: Document | null = null;
|
|
9304
|
+
try {
|
|
9305
|
+
doc = iframe.contentDocument;
|
|
9306
|
+
} catch {
|
|
9307
|
+
return null;
|
|
9308
|
+
}
|
|
9309
|
+
if (!doc?.body) return null;
|
|
9310
|
+
const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT);
|
|
9311
|
+
let node: Node | null = walker.nextNode();
|
|
9312
|
+
while (node) {
|
|
9313
|
+
const text = node.nodeValue ?? "";
|
|
9314
|
+
const idx = text.indexOf(needle);
|
|
9315
|
+
if (idx !== -1 && node.parentElement) {
|
|
9316
|
+
try {
|
|
9317
|
+
const range = doc.createRange();
|
|
9318
|
+
range.setStart(node, idx);
|
|
9319
|
+
range.setEnd(node, idx + needle.length);
|
|
9320
|
+
const rect = range.getBoundingClientRect();
|
|
9321
|
+
if (rect.width > 0 || rect.height > 0) {
|
|
9322
|
+
return mapIframeRectToViewport(iframe, rect);
|
|
9323
|
+
}
|
|
9324
|
+
} catch {
|
|
9325
|
+
// fall back to the parent element rect below
|
|
9326
|
+
}
|
|
9327
|
+
return mapIframeRectToViewport(
|
|
9328
|
+
iframe,
|
|
9329
|
+
node.parentElement.getBoundingClientRect(),
|
|
9330
|
+
);
|
|
9331
|
+
}
|
|
9332
|
+
node = walker.nextNode();
|
|
9333
|
+
}
|
|
9334
|
+
return null;
|
|
9335
|
+
},
|
|
9336
|
+
[mapIframeRectToViewport],
|
|
9337
|
+
);
|
|
9338
|
+
|
|
9339
|
+
// Resolve a CSS selector to a viewport rect inside the active screen iframe.
|
|
9340
|
+
const resolveSelectorRect = useCallback(
|
|
9341
|
+
(selector: string): DOMRect | null =>
|
|
9342
|
+
resolveSelectorRectInIframe(canvasIframeRef.current, selector),
|
|
9343
|
+
[canvasIframeRef, resolveSelectorRectInIframe],
|
|
9344
|
+
);
|
|
9345
|
+
|
|
9346
|
+
// Resolve a text quote to a viewport rect by walking the iframe body's text.
|
|
9347
|
+
const resolveTextQuoteRect = useCallback(
|
|
9348
|
+
(quote: string): DOMRect | null =>
|
|
9349
|
+
resolveTextQuoteRectInIframe(canvasIframeRef.current, quote),
|
|
9350
|
+
[canvasIframeRef, resolveTextQuoteRectInIframe],
|
|
9351
|
+
);
|
|
9352
|
+
|
|
9353
|
+
// resolveRect for RemoteSelectionRings — descriptor is a CSS selector string.
|
|
9354
|
+
const resolveSelectionRect = useCallback(
|
|
9355
|
+
(descriptor: string): DOMRect | null => resolveSelectorRect(descriptor),
|
|
9356
|
+
[resolveSelectorRect],
|
|
9357
|
+
);
|
|
9358
|
+
|
|
9359
|
+
// resolveRect for RecentEditHighlights — dispatch on descriptor kind.
|
|
9360
|
+
const resolveRecentEditRect = useCallback(
|
|
9361
|
+
(edit: AttributedRecentEdit): DOMRect | null => {
|
|
9362
|
+
const d = edit.descriptor;
|
|
9363
|
+
if (d.kind === "selector" && typeof d.selector === "string") {
|
|
9364
|
+
return resolveSelectorRect(d.selector);
|
|
9365
|
+
}
|
|
9366
|
+
if (d.kind === "text" && typeof d.quote === "string") {
|
|
9367
|
+
return resolveTextQuoteRect(d.quote);
|
|
9368
|
+
}
|
|
9369
|
+
// "paths" and whole-"doc" descriptors have no canvas region here.
|
|
9370
|
+
return null;
|
|
9371
|
+
},
|
|
9372
|
+
[resolveSelectorRect, resolveTextQuoteRect],
|
|
9373
|
+
);
|
|
9374
|
+
|
|
9375
|
+
const recentEdits = useRecentEdits(others);
|
|
9376
|
+
|
|
9377
|
+
// Re-key the presence array on zoom so the overlays recompute their rects when
|
|
9378
|
+
// the canvas zooms (the container itself doesn't resize, so the overlays'
|
|
9379
|
+
// ResizeObserver wouldn't otherwise fire). Cheap: a new array reference only.
|
|
9380
|
+
const othersForOverlays = useMemo<OtherPresence[]>(
|
|
9381
|
+
() => others.slice(),
|
|
9382
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9383
|
+
[others, zoom],
|
|
9384
|
+
);
|
|
9385
|
+
const recentEditsForOverlays = useMemo<AttributedRecentEdit[]>(
|
|
9386
|
+
() => recentEdits.slice(),
|
|
9387
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9388
|
+
[recentEdits, zoom],
|
|
9389
|
+
);
|
|
9390
|
+
|
|
9391
|
+
// ── Synthesized AI cursor (Task 3) ──────────────────────────────────────────
|
|
9392
|
+
//
|
|
9393
|
+
// The agent publishes a selection/recentEdit but no cursor. Derive a moving
|
|
9394
|
+
// cursor for it from whatever region currently resolves, normalized against
|
|
9395
|
+
// the canvas container the same way human cursors are (see
|
|
9396
|
+
// handleCanvasPointerMove) so LiveCursorOverlay places it correctly. The 120ms
|
|
9397
|
+
// CSS transition in the overlay animates it smoothly between edit targets.
|
|
9398
|
+
const othersWithAgentCursor = useMemo<OtherPresence[]>(() => {
|
|
9399
|
+
const container = canvasContainerRef.current;
|
|
9400
|
+
if (!container) return othersForOverlays;
|
|
9401
|
+
const containerRect = container.getBoundingClientRect();
|
|
9402
|
+
if (containerRect.width === 0 || containerRect.height === 0) {
|
|
9403
|
+
return othersForOverlays;
|
|
9404
|
+
}
|
|
9405
|
+
return othersForOverlays.map((other) => {
|
|
9406
|
+
// Only synthesize when the agent has no real cursor of its own.
|
|
9407
|
+
if (!other.isAgent || other.presence.cursor) return other;
|
|
9408
|
+
// Prefer the agent's active selection; fall back to its latest recentEdit.
|
|
9409
|
+
let rect: DOMRect | null = null;
|
|
9410
|
+
const selection = other.presence.selection as
|
|
9411
|
+
| string
|
|
9412
|
+
| { selector?: string }
|
|
9413
|
+
| null
|
|
9414
|
+
| undefined;
|
|
9415
|
+
const selector =
|
|
9416
|
+
typeof selection === "string" ? selection : selection?.selector;
|
|
9417
|
+
if (selector) rect = resolveSelectorRect(selector);
|
|
9418
|
+
if (!rect) {
|
|
9419
|
+
const ring = other.presence.recentEdits;
|
|
9420
|
+
if (Array.isArray(ring)) {
|
|
9421
|
+
for (let i = ring.length - 1; i >= 0 && !rect; i--) {
|
|
9422
|
+
const entry = ring[i] as AttributedRecentEdit;
|
|
9423
|
+
if (entry?.descriptor) {
|
|
9424
|
+
rect = resolveRecentEditRect({
|
|
9425
|
+
...entry,
|
|
9426
|
+
clientId: other.clientId,
|
|
9427
|
+
user: other.user,
|
|
9428
|
+
isAgent: true,
|
|
9429
|
+
});
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
9432
|
+
}
|
|
9433
|
+
}
|
|
9434
|
+
if (!rect) return other;
|
|
9435
|
+
// Normalize the rect's center to container fractions (matches human cursors).
|
|
9436
|
+
const cx = rect.left + rect.width / 2 - containerRect.left;
|
|
9437
|
+
const cy = rect.top + rect.height / 2 - containerRect.top;
|
|
9438
|
+
return {
|
|
9439
|
+
...other,
|
|
9440
|
+
presence: {
|
|
9441
|
+
...other.presence,
|
|
9442
|
+
cursor: {
|
|
9443
|
+
x: Math.max(0, Math.min(1, cx / containerRect.width)),
|
|
9444
|
+
y: Math.max(0, Math.min(1, cy / containerRect.height)),
|
|
9445
|
+
},
|
|
9446
|
+
},
|
|
9447
|
+
};
|
|
9448
|
+
});
|
|
9449
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9450
|
+
}, [
|
|
9451
|
+
othersForOverlays,
|
|
9452
|
+
resolveSelectorRect,
|
|
9453
|
+
resolveRecentEditRect,
|
|
9454
|
+
canvasContainerRef,
|
|
9455
|
+
zoom,
|
|
9456
|
+
]);
|
|
9457
|
+
|
|
9458
|
+
// ── Overview presence overlays (Task 1) ─────────────────────────────────────
|
|
9459
|
+
//
|
|
9460
|
+
// Element-level rings + recent-edit highlights over the overview board. The
|
|
9461
|
+
// agent's descriptors are published on the overview presence doc above and
|
|
9462
|
+
// resolved INSIDE the frame the agent is editing. Because we resolve against
|
|
9463
|
+
// the frame iframe's `getBoundingClientRect()` (via mapIframeRectToViewport),
|
|
9464
|
+
// the board pan/zoom transform is already baked into the returned viewport
|
|
9465
|
+
// coords — RemoteSelectionRings / RecentEditHighlights convert those to
|
|
9466
|
+
// container-relative against the same `canvasContainerRef`, so we do NOT
|
|
9467
|
+
// duplicate MultiScreenCanvas's transform math.
|
|
9468
|
+
const { others: overviewOthers } = usePresence(
|
|
9469
|
+
overviewAwareness,
|
|
9470
|
+
overviewYdoc?.clientID ?? null,
|
|
9471
|
+
);
|
|
9472
|
+
|
|
9473
|
+
// Find the overview frame iframe for a given file id. Frames tag their iframe
|
|
9474
|
+
// with `data-screen-iframe-id` = screen id (or `<id>::bp-<width>` for an
|
|
9475
|
+
// active breakpoint), so match the exact id or the breakpoint-prefixed form.
|
|
9476
|
+
const getOverviewFrameIframe = useCallback(
|
|
9477
|
+
(fileId: string | null): HTMLIFrameElement | null => {
|
|
9478
|
+
if (!fileId) return null;
|
|
9479
|
+
const container = canvasContainerRef.current;
|
|
9480
|
+
if (!container) return null;
|
|
9481
|
+
const escaped =
|
|
9482
|
+
typeof CSS !== "undefined" && CSS.escape ? CSS.escape(fileId) : fileId;
|
|
9483
|
+
return (
|
|
9484
|
+
container.querySelector<HTMLIFrameElement>(
|
|
9485
|
+
`iframe[data-screen-iframe-id="${escaped}"]`,
|
|
9486
|
+
) ??
|
|
9487
|
+
container.querySelector<HTMLIFrameElement>(
|
|
9488
|
+
`iframe[data-screen-iframe-id^="${escaped}::bp-"]`,
|
|
9489
|
+
) ??
|
|
9490
|
+
null
|
|
9491
|
+
);
|
|
9492
|
+
},
|
|
9493
|
+
[canvasContainerRef],
|
|
9494
|
+
);
|
|
9495
|
+
|
|
9496
|
+
// Overview resolvers: the agent's presence rides on `overviewPresenceFileId`,
|
|
9497
|
+
// so resolve every descriptor inside that file's frame iframe.
|
|
9498
|
+
const resolveOverviewSelectionRect = useCallback(
|
|
9499
|
+
(descriptor: string): DOMRect | null =>
|
|
9500
|
+
resolveSelectorRectInIframe(
|
|
9501
|
+
getOverviewFrameIframe(overviewPresenceFileId),
|
|
9502
|
+
descriptor,
|
|
9503
|
+
),
|
|
9504
|
+
[
|
|
9505
|
+
getOverviewFrameIframe,
|
|
9506
|
+
overviewPresenceFileId,
|
|
9507
|
+
resolveSelectorRectInIframe,
|
|
9508
|
+
],
|
|
9509
|
+
);
|
|
9510
|
+
const resolveOverviewRecentEditRect = useCallback(
|
|
9511
|
+
(edit: AttributedRecentEdit): DOMRect | null => {
|
|
9512
|
+
const iframe = getOverviewFrameIframe(overviewPresenceFileId);
|
|
9513
|
+
if (!iframe) return null;
|
|
9514
|
+
const d = edit.descriptor;
|
|
9515
|
+
if (d.kind === "selector" && typeof d.selector === "string") {
|
|
9516
|
+
return resolveSelectorRectInIframe(iframe, d.selector);
|
|
9517
|
+
}
|
|
9518
|
+
if (d.kind === "text" && typeof d.quote === "string") {
|
|
9519
|
+
return resolveTextQuoteRectInIframe(iframe, d.quote);
|
|
9520
|
+
}
|
|
9521
|
+
// "paths" and whole-"doc" descriptors have no single canvas region.
|
|
9522
|
+
return null;
|
|
9523
|
+
},
|
|
9524
|
+
[
|
|
9525
|
+
getOverviewFrameIframe,
|
|
9526
|
+
overviewPresenceFileId,
|
|
9527
|
+
resolveSelectorRectInIframe,
|
|
9528
|
+
resolveTextQuoteRectInIframe,
|
|
9529
|
+
],
|
|
9530
|
+
);
|
|
9531
|
+
|
|
9532
|
+
// Show only the agent's presence in overview (human peers edit inside their
|
|
9533
|
+
// own focused screen; the board treatment is about surfacing agent work). Re-
|
|
9534
|
+
// key on the overview zoom so rings recompute when the board zooms — the
|
|
9535
|
+
// container itself doesn't resize, so the overlays' ResizeObserver wouldn't
|
|
9536
|
+
// otherwise fire (same trick as the single-screen `othersForOverlays`).
|
|
9537
|
+
const overviewAgentOthers = useMemo<OtherPresence[]>(
|
|
9538
|
+
() => overviewOthers.filter((o) => o.isAgent),
|
|
9539
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9540
|
+
[overviewOthers, overviewCanvasZoom],
|
|
9541
|
+
);
|
|
9542
|
+
const overviewRecentEdits = useRecentEdits(overviewAgentOthers);
|
|
9543
|
+
const overviewRecentEditsForOverlays = useMemo<AttributedRecentEdit[]>(
|
|
9544
|
+
() => overviewRecentEdits.slice(),
|
|
9545
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
9546
|
+
[overviewRecentEdits, overviewCanvasZoom],
|
|
9547
|
+
);
|
|
9548
|
+
|
|
8701
9549
|
// Follow mode — clicking an avatar in the toolbar follows that participant.
|
|
8702
9550
|
const [followingEmail, setFollowingEmail] = useState<string | null>(null);
|
|
8703
9551
|
const followingId = useMemo(() => {
|
|
@@ -9121,19 +9969,42 @@ export default function DesignEditor() {
|
|
|
9121
9969
|
const activeCanvasSourceType =
|
|
9122
9970
|
normalizeDesignSourceType(activeOverviewScreen?.sourceType) ??
|
|
9123
9971
|
designSourceType;
|
|
9972
|
+
// P4: arms DesignCanvas's single-screen click-to-place overlay only while
|
|
9973
|
+
// focused on a single screen with an active creation tool selected —
|
|
9974
|
+
// `null` in every other case leaves the overlay unmounted (see
|
|
9975
|
+
// getSingleScreenCreationTool's doc comment for the full tool mapping).
|
|
9976
|
+
const activeSingleScreenCreationTool = getSingleScreenCreationTool({
|
|
9977
|
+
activeTool,
|
|
9978
|
+
viewMode,
|
|
9979
|
+
hasActiveFile: Boolean(activeFile),
|
|
9980
|
+
});
|
|
9124
9981
|
const sourceCapabilities = useMemo(() => {
|
|
9125
9982
|
const caps = resolveSourceCapabilities(designSourceType);
|
|
9126
9983
|
return DESIGN_CAPABILITY_NAMES.filter((name) => hasCapability(caps, name));
|
|
9127
9984
|
}, [designSourceType]);
|
|
9128
9985
|
|
|
9129
|
-
//
|
|
9130
|
-
//
|
|
9131
|
-
//
|
|
9132
|
-
// the
|
|
9986
|
+
// Full-app-building linkage (see shared/full-app.ts). Non-null only for
|
|
9987
|
+
// designs created via the "Full app" creation mode — drives FusionAppBanner
|
|
9988
|
+
// and the fusion preview URL fallback below. Flag-independent on read: once
|
|
9989
|
+
// a design has this data (created while the flag was on), the banner and
|
|
9990
|
+
// canvas wiring keep working even if the flag is later flipped off, so
|
|
9991
|
+
// existing full-app designs never regress.
|
|
9992
|
+
const fusionApp = useMemo(
|
|
9993
|
+
() => readFusionApp(designDataJson),
|
|
9994
|
+
[designDataJson],
|
|
9995
|
+
);
|
|
9996
|
+
|
|
9997
|
+
// Builder-hosted preview URL for fusion-source designs. Prefers the flat
|
|
9998
|
+
// `fusionUrl` written by the "Make it real" migration; falls back to the
|
|
9999
|
+
// full-app-building `fusionApp.previewUrl` linkage so container dev-server
|
|
10000
|
+
// screens render the same way once the container reports a preview URL.
|
|
10001
|
+
// Threaded into DesignCanvas so the fusion preview renders (and so the
|
|
10002
|
+
// bridge trust check can validate the frame's origin against it).
|
|
9133
10003
|
const designFusionUrl = useMemo(() => {
|
|
9134
10004
|
const raw = (designDataJson as { fusionUrl?: unknown }).fusionUrl;
|
|
9135
|
-
|
|
9136
|
-
|
|
10005
|
+
if (typeof raw === "string" && raw) return raw;
|
|
10006
|
+
return fusionApp?.previewUrl;
|
|
10007
|
+
}, [designDataJson, fusionApp]);
|
|
9137
10008
|
|
|
9138
10009
|
// §6.1 — open a component instance's source. open-component-source selects the
|
|
9139
10010
|
// component root in the editor and emits a navigate app-state; for real-app
|
|
@@ -10137,13 +11008,38 @@ export default function DesignEditor() {
|
|
|
10137
11008
|
: storedContent;
|
|
10138
11009
|
})()
|
|
10139
11010
|
: storedContent);
|
|
10140
|
-
const
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
11011
|
+
const insertedContent = appendCanvasPrimitiveToHtml(
|
|
11012
|
+
baseContent,
|
|
11013
|
+
primitive,
|
|
11014
|
+
{ preserveNegativePosition: targetFile.id === boardFileId },
|
|
11015
|
+
);
|
|
11016
|
+
if (!insertedContent) {
|
|
10144
11017
|
toast.error(t("designEditor.toasts.primitiveInsertFailed"));
|
|
10145
11018
|
return false;
|
|
10146
11019
|
}
|
|
11020
|
+
// Vector-edit foundations: stash the structured pen path (nodes +
|
|
11021
|
+
// handles) alongside the flattened `d` so a later double-click/Enter
|
|
11022
|
+
// can re-hydrate it into an editable path instead of only having the
|
|
11023
|
+
// already-flattened curve. `primitive.pathData` is the only carrier of
|
|
11024
|
+
// pen geometry that crosses the MultiScreenCanvas -> DesignEditor
|
|
11025
|
+
// boundary for an OVERVIEW-drawn pen path (see
|
|
11026
|
+
// parsePenPathFromSerializedD's doc comment for why this reconstructs
|
|
11027
|
+
// rather than receives the structured path directly).
|
|
11028
|
+
const nextContent =
|
|
11029
|
+
primitive.kind === "path" && primitive.pathData && primitive.nodeId
|
|
11030
|
+
? (() => {
|
|
11031
|
+
const reconstructed = parsePenPathFromSerializedD(
|
|
11032
|
+
primitive.pathData!,
|
|
11033
|
+
);
|
|
11034
|
+
return reconstructed
|
|
11035
|
+
? setPenNodesAttributeOnElement(
|
|
11036
|
+
insertedContent,
|
|
11037
|
+
primitive.nodeId!,
|
|
11038
|
+
reconstructed,
|
|
11039
|
+
)
|
|
11040
|
+
: insertedContent;
|
|
11041
|
+
})()
|
|
11042
|
+
: insertedContent;
|
|
10147
11043
|
const projectedNodeId = primitive.nodeId
|
|
10148
11044
|
? buildCodeLayerProjection(nextContent).nodes.find(
|
|
10149
11045
|
(node) =>
|
|
@@ -10353,6 +11249,109 @@ export default function DesignEditor() {
|
|
|
10353
11249
|
[boardFileId, canEditDesign, handleCreatePrimitive, handlePrimitiveCreated],
|
|
10354
11250
|
);
|
|
10355
11251
|
|
|
11252
|
+
/**
|
|
11253
|
+
* P4: DesignCanvas's single-screen click-to-place creation overlay
|
|
11254
|
+
* (`activeCreationTool`/`onCreatePrimitive` below) commits through this
|
|
11255
|
+
* same `handleCreatePrimitive`/`handlePrimitiveCreated` pair overview
|
|
11256
|
+
* drawing already uses — `createPrimitiveInsertFromSpec` just translates
|
|
11257
|
+
* the overlay's screen-content-space spec into the shared
|
|
11258
|
+
* `CanvasPrimitiveInsert` shape first. Always returns the active tool to
|
|
11259
|
+
* "move" afterward (Figma/overview parity) since single-screen pen never
|
|
11260
|
+
* has a multi-click "mid-path" state to preserve (see
|
|
11261
|
+
* createPrimitiveInsertFromSpec's pen doc comment).
|
|
11262
|
+
*/
|
|
11263
|
+
const handleSingleScreenCreatePrimitive = useCallback(
|
|
11264
|
+
(spec: CreatePrimitiveSpec) => {
|
|
11265
|
+
if (!activeFile || !canEditDesign) return;
|
|
11266
|
+
const nodeId = uniqueLayerId(spec.tool === "pen" ? "path" : spec.tool);
|
|
11267
|
+
const primitive = createPrimitiveInsertFromSpec(spec, nodeId);
|
|
11268
|
+
if (!primitive) return;
|
|
11269
|
+
const result = handleCreatePrimitive(activeFile.id, primitive);
|
|
11270
|
+
if (!result) return;
|
|
11271
|
+
const resultNodeId = typeof result === "string" ? result : nodeId;
|
|
11272
|
+
handlePrimitiveCreated(activeFile.id, resultNodeId, {
|
|
11273
|
+
selectFrame: false,
|
|
11274
|
+
});
|
|
11275
|
+
},
|
|
11276
|
+
[activeFile, canEditDesign, handleCreatePrimitive, handlePrimitiveCreated],
|
|
11277
|
+
);
|
|
11278
|
+
|
|
11279
|
+
/**
|
|
11280
|
+
* P5/vector-edit: commits the working PenPath from MultiScreenCanvas's
|
|
11281
|
+
* `vectorEdit` overlay back onto its owning screen. `"preview"` phases
|
|
11282
|
+
* (live anchor/handle drag) only update `vectorEditingState.path` — the
|
|
11283
|
+
* overlay renders straight off that in-memory path, so nothing needs to
|
|
11284
|
+
* touch the iframe/content until the gesture actually settles. `"commit"`
|
|
11285
|
+
* phases (mirroring the same preview/commit gesture-signal convention as
|
|
11286
|
+
* ScrubInput/DesignColorPicker's onChangeComplete) additionally write the
|
|
11287
|
+
* new `d` + data-an-pen-nodes back into the owning screen's HTML through
|
|
11288
|
+
* the existing applyFileContentUpdate persistence path, so a single mouse
|
|
11289
|
+
* drag becomes exactly one saved edit (and one undo/history entry) rather
|
|
11290
|
+
* than one per intermediate frame.
|
|
11291
|
+
*/
|
|
11292
|
+
const handleVectorEditChange = useCallback(
|
|
11293
|
+
(nextPath: PenPath, phase: "preview" | "commit") => {
|
|
11294
|
+
setVectorEditingState((current) => {
|
|
11295
|
+
if (!current) return current;
|
|
11296
|
+
if (phase === "commit") {
|
|
11297
|
+
const baseContent = getScreenContent(current.screenId);
|
|
11298
|
+
if (baseContent) {
|
|
11299
|
+
const nextContent = writeBackVectorEditedPenPath(
|
|
11300
|
+
baseContent,
|
|
11301
|
+
current.nodeId,
|
|
11302
|
+
nextPath,
|
|
11303
|
+
);
|
|
11304
|
+
if (nextContent !== baseContent) {
|
|
11305
|
+
applyFileContentUpdate(current.screenId, nextContent, {
|
|
11306
|
+
skipPreview: current.screenId !== activeFile?.id,
|
|
11307
|
+
});
|
|
11308
|
+
}
|
|
11309
|
+
}
|
|
11310
|
+
}
|
|
11311
|
+
return { ...current, path: nextPath };
|
|
11312
|
+
});
|
|
11313
|
+
},
|
|
11314
|
+
[activeFile?.id, applyFileContentUpdate, getScreenContent],
|
|
11315
|
+
);
|
|
11316
|
+
|
|
11317
|
+
const handleVectorEditExit = useCallback(() => {
|
|
11318
|
+
setVectorEditingState(null);
|
|
11319
|
+
}, []);
|
|
11320
|
+
|
|
11321
|
+
/**
|
|
11322
|
+
* P5/vector-edit: the VectorEditOverlayState prop threaded to
|
|
11323
|
+
* MultiScreenCanvas. `originCanvas` is recomputed on every render from the
|
|
11324
|
+
* owning screen's CURRENT frame geometry (see getScreenFrameOriginCanvas)
|
|
11325
|
+
* rather than cached in vectorEditingState, so dragging/resizing the
|
|
11326
|
+
* screen frame mid-edit can't leave the overlay pinned to a stale
|
|
11327
|
+
* position. `null` whenever not editing or the origin can't be resolved
|
|
11328
|
+
* (e.g. the owning screen was deleted mid-edit) — MultiScreenCanvas
|
|
11329
|
+
* doesn't render the overlay in that case.
|
|
11330
|
+
*/
|
|
11331
|
+
const vectorEditOverlayState = useMemo<VectorEditOverlayState | null>(() => {
|
|
11332
|
+
if (!vectorEditingState) return null;
|
|
11333
|
+
const originCanvas = getScreenFrameOriginCanvas({
|
|
11334
|
+
screenId: vectorEditingState.screenId,
|
|
11335
|
+
overviewScreens,
|
|
11336
|
+
canvasFrameGeometryById,
|
|
11337
|
+
boardFileId,
|
|
11338
|
+
});
|
|
11339
|
+
if (!originCanvas) return null;
|
|
11340
|
+
return {
|
|
11341
|
+
path: vectorEditingState.path,
|
|
11342
|
+
originCanvas,
|
|
11343
|
+
onChange: handleVectorEditChange,
|
|
11344
|
+
onExit: handleVectorEditExit,
|
|
11345
|
+
};
|
|
11346
|
+
}, [
|
|
11347
|
+
boardFileId,
|
|
11348
|
+
canvasFrameGeometryById,
|
|
11349
|
+
handleVectorEditChange,
|
|
11350
|
+
handleVectorEditExit,
|
|
11351
|
+
overviewScreens,
|
|
11352
|
+
vectorEditingState,
|
|
11353
|
+
]);
|
|
11354
|
+
|
|
10356
11355
|
const handleOverviewScreenSelectionChange = useCallback(
|
|
10357
11356
|
(ids: string[]) => {
|
|
10358
11357
|
const pendingId = pendingOverviewScreenSelectionRef.current;
|
|
@@ -10410,11 +11409,24 @@ export default function DesignEditor() {
|
|
|
10410
11409
|
});
|
|
10411
11410
|
}, [canEditDesign]);
|
|
10412
11411
|
|
|
11412
|
+
// T14/P4: text/shape/pen tools used to always force a jump to overview —
|
|
11413
|
+
// that's still correct when the user is already IN overview (or has no
|
|
11414
|
+
// screen focused yet), but when a single screen is already focused these
|
|
11415
|
+
// tools should arm single-screen click-to-place instead of yanking the
|
|
11416
|
+
// user out to overview to draw. Overview mode itself is left completely
|
|
11417
|
+
// alone below (same flushSync/setViewMode("overview") as before).
|
|
10413
11418
|
const handleTextTool = useCallback(() => {
|
|
10414
11419
|
if (!canEditDesign) return;
|
|
10415
11420
|
blurActiveDesignEditableTarget();
|
|
10416
11421
|
flushSync(() => {
|
|
10417
11422
|
setActiveTool("text");
|
|
11423
|
+
if (viewModeRef.current === "single" && activeFile) {
|
|
11424
|
+
setMode("edit");
|
|
11425
|
+
setDrawMode(false);
|
|
11426
|
+
setPinMode(false);
|
|
11427
|
+
setSelectedElement(null);
|
|
11428
|
+
return;
|
|
11429
|
+
}
|
|
10418
11430
|
viewModeRef.current = "overview";
|
|
10419
11431
|
setViewMode("overview");
|
|
10420
11432
|
setMode("edit");
|
|
@@ -10422,7 +11434,7 @@ export default function DesignEditor() {
|
|
|
10422
11434
|
setPinMode(false);
|
|
10423
11435
|
setSelectedElement(null);
|
|
10424
11436
|
});
|
|
10425
|
-
}, [canEditDesign]);
|
|
11437
|
+
}, [activeFile, canEditDesign]);
|
|
10426
11438
|
|
|
10427
11439
|
const handleShapeTool = useCallback(
|
|
10428
11440
|
(tool: ShapeTool) => {
|
|
@@ -10430,6 +11442,13 @@ export default function DesignEditor() {
|
|
|
10430
11442
|
blurActiveDesignEditableTarget();
|
|
10431
11443
|
flushSync(() => {
|
|
10432
11444
|
setActiveTool(tool);
|
|
11445
|
+
if (viewModeRef.current === "single" && activeFile) {
|
|
11446
|
+
setMode("edit");
|
|
11447
|
+
setDrawMode(false);
|
|
11448
|
+
setPinMode(false);
|
|
11449
|
+
setSelectedElement(null);
|
|
11450
|
+
return;
|
|
11451
|
+
}
|
|
10433
11452
|
viewModeRef.current = "overview";
|
|
10434
11453
|
setViewMode("overview");
|
|
10435
11454
|
setMode("edit");
|
|
@@ -10438,7 +11457,7 @@ export default function DesignEditor() {
|
|
|
10438
11457
|
setSelectedElement(null);
|
|
10439
11458
|
});
|
|
10440
11459
|
},
|
|
10441
|
-
[canEditDesign],
|
|
11460
|
+
[activeFile, canEditDesign],
|
|
10442
11461
|
);
|
|
10443
11462
|
|
|
10444
11463
|
const handleRectTool = useCallback(() => {
|
|
@@ -10474,6 +11493,13 @@ export default function DesignEditor() {
|
|
|
10474
11493
|
blurActiveDesignEditableTarget();
|
|
10475
11494
|
flushSync(() => {
|
|
10476
11495
|
setActiveTool("pen");
|
|
11496
|
+
if (viewModeRef.current === "single" && activeFile) {
|
|
11497
|
+
setMode("edit");
|
|
11498
|
+
setDrawMode(false);
|
|
11499
|
+
setPinMode(false);
|
|
11500
|
+
setSelectedElement(null);
|
|
11501
|
+
return;
|
|
11502
|
+
}
|
|
10477
11503
|
viewModeRef.current = "overview";
|
|
10478
11504
|
setViewMode("overview");
|
|
10479
11505
|
setMode("edit");
|
|
@@ -10481,7 +11507,7 @@ export default function DesignEditor() {
|
|
|
10481
11507
|
setPinMode(false);
|
|
10482
11508
|
setSelectedElement(null);
|
|
10483
11509
|
});
|
|
10484
|
-
}, [canEditDesign]);
|
|
11510
|
+
}, [activeFile, canEditDesign]);
|
|
10485
11511
|
|
|
10486
11512
|
const handleHandTool = useCallback(() => {
|
|
10487
11513
|
if (!canEditDesign) return;
|
|
@@ -11721,7 +12747,7 @@ export default function DesignEditor() {
|
|
|
11721
12747
|
);
|
|
11722
12748
|
|
|
11723
12749
|
const handleStyleChange = useCallback(
|
|
11724
|
-
(property: string, value: string) => {
|
|
12750
|
+
(property: string, value: string, meta?: StyleChangeMeta) => {
|
|
11725
12751
|
const selector = selectedElement?.selector ?? "body";
|
|
11726
12752
|
if (
|
|
11727
12753
|
textEditingState.active &&
|
|
@@ -11737,6 +12763,36 @@ export default function DesignEditor() {
|
|
|
11737
12763
|
return;
|
|
11738
12764
|
}
|
|
11739
12765
|
}
|
|
12766
|
+
// PF12: a mid-gesture scrub/color-drag preview tick (ScrubInput's
|
|
12767
|
+
// `phase: "preview"`, DesignColorPicker's per-tick `onChange`) is cheap
|
|
12768
|
+
// to show live but must NOT run the expensive source commit
|
|
12769
|
+
// (projection parse + HTML patch + history entry) on every tick — only
|
|
12770
|
+
// the gesture's final "commit" (or a caller that never passes meta at
|
|
12771
|
+
// all, e.g. keyboard/agent edits) does that. Route preview ticks
|
|
12772
|
+
// through the same cheap iframe postMessage path the text-range case
|
|
12773
|
+
// above already uses, and skip commitVisualStyles entirely so there is
|
|
12774
|
+
// no source write — and therefore no history entry — for any preview
|
|
12775
|
+
// tick. Multi-layer-selection commits (commitStylesToSelectedLayers)
|
|
12776
|
+
// have no equivalent cheap multi-element preview channel, so previews
|
|
12777
|
+
// for that case conservatively fall through to the existing full-commit
|
|
12778
|
+
// behavior below (unchanged from before PF12).
|
|
12779
|
+
if (
|
|
12780
|
+
shouldSkipVisualStyleCommitForPreview({
|
|
12781
|
+
phase: meta?.phase,
|
|
12782
|
+
selectedLayerCount: selectedLayerTargetsRef.current.length,
|
|
12783
|
+
})
|
|
12784
|
+
) {
|
|
12785
|
+
const sendStyleChange = (window as any).__designCanvasSendStyle;
|
|
12786
|
+
if (typeof sendStyleChange === "function") {
|
|
12787
|
+
sendStyleChange(selector, property, value, {
|
|
12788
|
+
selectorCandidates: selectedCanvasSelectorCandidates,
|
|
12789
|
+
nodeId: selectedElement?.sourceId,
|
|
12790
|
+
});
|
|
12791
|
+
}
|
|
12792
|
+
// No live bridge available for this preview tick (e.g. inactive
|
|
12793
|
+
// screen) — nothing cheap to do; wait for the gesture's "commit".
|
|
12794
|
+
return;
|
|
12795
|
+
}
|
|
11740
12796
|
if (commitStylesToSelectedLayers({ [property]: value })) return;
|
|
11741
12797
|
commitVisualStyles(selector, { [property]: value });
|
|
11742
12798
|
},
|
|
@@ -11768,7 +12824,7 @@ export default function DesignEditor() {
|
|
|
11768
12824
|
);
|
|
11769
12825
|
|
|
11770
12826
|
const handleStylesChange = useCallback(
|
|
11771
|
-
(styles: Record<string, string
|
|
12827
|
+
(styles: Record<string, string>, meta?: StyleChangeMeta) => {
|
|
11772
12828
|
const selector = selectedElement?.selector ?? "body";
|
|
11773
12829
|
const entries = Object.entries(styles).filter(([, value]) =>
|
|
11774
12830
|
Boolean(value),
|
|
@@ -11797,6 +12853,28 @@ export default function DesignEditor() {
|
|
|
11797
12853
|
return;
|
|
11798
12854
|
}
|
|
11799
12855
|
}
|
|
12856
|
+
// PF12: same preview/commit split as handleStyleChange — see its
|
|
12857
|
+
// comment for the full undo-safety rationale. A batched multi-property
|
|
12858
|
+
// preview tick (e.g. EditPanel's shadow X/Y/blur/spread popover) is
|
|
12859
|
+
// still just a live preview: send every property to the cheap iframe
|
|
12860
|
+
// bridge and skip the expensive multi-property commitVisualStyles call.
|
|
12861
|
+
if (
|
|
12862
|
+
shouldSkipVisualStyleCommitForPreview({
|
|
12863
|
+
phase: meta?.phase,
|
|
12864
|
+
selectedLayerCount: selectedLayerTargetsRef.current.length,
|
|
12865
|
+
})
|
|
12866
|
+
) {
|
|
12867
|
+
const sendStyleChange = (window as any).__designCanvasSendStyle;
|
|
12868
|
+
if (typeof sendStyleChange === "function") {
|
|
12869
|
+
entries.forEach(([property, value]) => {
|
|
12870
|
+
sendStyleChange(selector, property, value, {
|
|
12871
|
+
selectorCandidates: selectedCanvasSelectorCandidates,
|
|
12872
|
+
nodeId: selectedElement?.sourceId,
|
|
12873
|
+
});
|
|
12874
|
+
});
|
|
12875
|
+
}
|
|
12876
|
+
return;
|
|
12877
|
+
}
|
|
11800
12878
|
if (commitStylesToSelectedLayers(Object.fromEntries(entries))) return;
|
|
11801
12879
|
commitVisualStyles(selector, Object.fromEntries(entries));
|
|
11802
12880
|
},
|
|
@@ -14823,6 +15901,24 @@ export default function DesignEditor() {
|
|
|
14823
15901
|
if (!canUseOverviewHistory) return false;
|
|
14824
15902
|
const entry = geometryUndoStackRef.current.pop();
|
|
14825
15903
|
if (!entry) return false;
|
|
15904
|
+
// Freshness guard: this entry last wrote `entry.after`. If a peer/agent
|
|
15905
|
+
// has since moved any of the frames it touched, replaying `entry.before`
|
|
15906
|
+
// would silently clobber their change — drop this entry instead. The pop
|
|
15907
|
+
// above already removed it, so undo skips forward to the next entry.
|
|
15908
|
+
const stale = staleGeometryFrameIds(
|
|
15909
|
+
entry,
|
|
15910
|
+
liveFrameGeometryRef.current,
|
|
15911
|
+
entry.after,
|
|
15912
|
+
);
|
|
15913
|
+
if (stale.length > 0) {
|
|
15914
|
+
console.debug(
|
|
15915
|
+
"[design] skipping stale geometry undo; frames changed since capture:",
|
|
15916
|
+
stale,
|
|
15917
|
+
);
|
|
15918
|
+
toast.info(t("designEditor.toasts.undoSkippedConcurrentEdit"));
|
|
15919
|
+
// Try the next undo entry rather than swallowing the whole gesture.
|
|
15920
|
+
return undoGeometry();
|
|
15921
|
+
}
|
|
14826
15922
|
geometryRedoStackRef.current = [
|
|
14827
15923
|
...geometryRedoStackRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
|
|
14828
15924
|
entry,
|
|
@@ -14929,6 +16025,7 @@ export default function DesignEditor() {
|
|
|
14929
16025
|
syncUndoRedoState,
|
|
14930
16026
|
updateLiveScreenSnapshotContent,
|
|
14931
16027
|
writeFrameGeometrySnapshot,
|
|
16028
|
+
t,
|
|
14932
16029
|
]);
|
|
14933
16030
|
|
|
14934
16031
|
const handleRedo = useCallback(() => {
|
|
@@ -15111,6 +16208,22 @@ export default function DesignEditor() {
|
|
|
15111
16208
|
if (!canUseOverviewHistory) return false;
|
|
15112
16209
|
const entry = geometryRedoStackRef.current.pop();
|
|
15113
16210
|
if (!entry) return false;
|
|
16211
|
+
// Freshness guard: when this entry was undone it wrote `entry.before`. If
|
|
16212
|
+
// a peer/agent has since moved any touched frame, replaying `entry.after`
|
|
16213
|
+
// would clobber that change — drop this entry and try the next redo.
|
|
16214
|
+
const stale = staleGeometryFrameIds(
|
|
16215
|
+
entry,
|
|
16216
|
+
liveFrameGeometryRef.current,
|
|
16217
|
+
entry.before,
|
|
16218
|
+
);
|
|
16219
|
+
if (stale.length > 0) {
|
|
16220
|
+
console.debug(
|
|
16221
|
+
"[design] skipping stale geometry redo; frames changed since capture:",
|
|
16222
|
+
stale,
|
|
16223
|
+
);
|
|
16224
|
+
toast.info(t("designEditor.toasts.redoSkippedConcurrentEdit"));
|
|
16225
|
+
return redoGeometry();
|
|
16226
|
+
}
|
|
15114
16227
|
geometryUndoStackRef.current = [
|
|
15115
16228
|
...geometryUndoStackRef.current.slice(-(MAX_DESIGN_UNDO_STACK - 1)),
|
|
15116
16229
|
entry,
|
|
@@ -15240,6 +16353,7 @@ export default function DesignEditor() {
|
|
|
15240
16353
|
t,
|
|
15241
16354
|
updateLiveScreenSnapshotContent,
|
|
15242
16355
|
writeFrameGeometrySnapshot,
|
|
16356
|
+
t,
|
|
15243
16357
|
]);
|
|
15244
16358
|
|
|
15245
16359
|
const handleZoomIn = useCallback(() => {
|
|
@@ -15355,6 +16469,12 @@ export default function DesignEditor() {
|
|
|
15355
16469
|
pendingOverviewLayerSelectionRef.current = null;
|
|
15356
16470
|
clearPendingOverviewLayerSelectionTimer();
|
|
15357
16471
|
setCreatedOverviewLayerSelection(null);
|
|
16472
|
+
// P5/vector-edit: MultiScreenCanvas (the only place the vectorEdit
|
|
16473
|
+
// overlay renders) unmounts on leaving overview, so an active
|
|
16474
|
+
// vector-edit session has nothing left to render into — clear it
|
|
16475
|
+
// rather than leaving a stale/orphaned session in memory that would
|
|
16476
|
+
// resurface if the user returns to overview later.
|
|
16477
|
+
setVectorEditingState(null);
|
|
15358
16478
|
runEditorViewTransition(() => {
|
|
15359
16479
|
if (fileId) setActiveFileId(fileId);
|
|
15360
16480
|
setDrawMode(false);
|
|
@@ -15597,6 +16717,36 @@ export default function DesignEditor() {
|
|
|
15597
16717
|
]),
|
|
15598
16718
|
[],
|
|
15599
16719
|
);
|
|
16720
|
+
/**
|
|
16721
|
+
* P5/vector-edit entry point. Only reachable while `viewMode === "overview"`
|
|
16722
|
+
* — MultiScreenCanvas's `vectorEdit` overlay (the only place this state is
|
|
16723
|
+
* rendered) doesn't exist in single-screen mode, so entering vector edit
|
|
16724
|
+
* from a single-screen selection is deliberately deferred rather than
|
|
16725
|
+
* silently doing something broken; see FINAL REPORT for the exact
|
|
16726
|
+
* deferral. Returns true when vector edit mode was entered (caller should
|
|
16727
|
+
* not also drill into the screen), false otherwise (not a pen path, or its
|
|
16728
|
+
* origin couldn't be resolved).
|
|
16729
|
+
*/
|
|
16730
|
+
const enterVectorEditForSelection = useCallback(
|
|
16731
|
+
(owner: { fileId: string; node: CodeLayerNode }): boolean => {
|
|
16732
|
+
const penNodesAttr = owner.node.dataAttributes["data-an-pen-nodes"];
|
|
16733
|
+
if (!penNodesAttr) return false;
|
|
16734
|
+
const path = parsePenNodes(penNodesAttr);
|
|
16735
|
+
if (!path) return false;
|
|
16736
|
+
const originCanvas = getScreenFrameOriginCanvas({
|
|
16737
|
+
screenId: owner.fileId,
|
|
16738
|
+
overviewScreens,
|
|
16739
|
+
canvasFrameGeometryById,
|
|
16740
|
+
boardFileId,
|
|
16741
|
+
});
|
|
16742
|
+
if (!originCanvas) return false;
|
|
16743
|
+
const nodeId =
|
|
16744
|
+
owner.node.dataAttributes["data-agent-native-node-id"] ?? owner.node.id;
|
|
16745
|
+
setVectorEditingState({ screenId: owner.fileId, nodeId, path });
|
|
16746
|
+
return true;
|
|
16747
|
+
},
|
|
16748
|
+
[boardFileId, canvasFrameGeometryById, overviewScreens],
|
|
16749
|
+
);
|
|
15600
16750
|
const handleEnterHotkey = useCallback(() => {
|
|
15601
16751
|
if (viewMode !== "overview") {
|
|
15602
16752
|
if (selectedLayerIdsState.length === 1) {
|
|
@@ -15616,6 +16766,20 @@ export default function DesignEditor() {
|
|
|
15616
16766
|
}
|
|
15617
16767
|
return;
|
|
15618
16768
|
}
|
|
16769
|
+
// P5/vector-edit: Enter on a single selected committed pen path (has
|
|
16770
|
+
// data-an-pen-nodes — see setPenNodesAttributeOnElement) enters vector
|
|
16771
|
+
// edit mode instead of drilling into the owning screen. Vector edit's
|
|
16772
|
+
// overlay only exists on MultiScreenCanvas (overview), so this
|
|
16773
|
+
// deliberately only fires here — see enterVectorEditForSelection's doc
|
|
16774
|
+
// comment for the single-screen deferral.
|
|
16775
|
+
if (selectedLayerIdsState.length === 1) {
|
|
16776
|
+
const layerId = selectedLayerIdsState[0]!;
|
|
16777
|
+
const owner = codeLayerOwnerByNodeIdRef.current.get(layerId);
|
|
16778
|
+
const penNodesAttr = owner?.node.dataAttributes["data-an-pen-nodes"];
|
|
16779
|
+
if (owner && penNodesAttr && enterVectorEditForSelection(owner)) {
|
|
16780
|
+
return;
|
|
16781
|
+
}
|
|
16782
|
+
}
|
|
15619
16783
|
const target = getOverviewEnterTarget({
|
|
15620
16784
|
activeFileId: activeFile?.id ?? activeFileId,
|
|
15621
16785
|
overviewSelectedScreenIds,
|
|
@@ -15627,6 +16791,7 @@ export default function DesignEditor() {
|
|
|
15627
16791
|
activeFile?.id,
|
|
15628
16792
|
activeFileId,
|
|
15629
16793
|
enterSingleScreen,
|
|
16794
|
+
enterVectorEditForSelection,
|
|
15630
16795
|
overviewSelectedScreenIds,
|
|
15631
16796
|
selectedLayerIdsState,
|
|
15632
16797
|
viewMode,
|
|
@@ -15772,6 +16937,22 @@ export default function DesignEditor() {
|
|
|
15772
16937
|
setOverviewSelectAllRequest((request) => request + 1);
|
|
15773
16938
|
}, [activeFile, files, getFreshActiveContent]);
|
|
15774
16939
|
|
|
16940
|
+
// L12: shared by Cmd+R and the canvas context-menu Rename item — the single
|
|
16941
|
+
// currently-selected layer id eligible for the layers-panel inline rename,
|
|
16942
|
+
// or null when zero or more-than-one layers are selected (screen/file rows
|
|
16943
|
+
// are excluded; renaming those is a separate flow). `__`-prefixed and file
|
|
16944
|
+
// ids are filtered the same way handleSelectAllFrames/other selection
|
|
16945
|
+
// helpers already do.
|
|
16946
|
+
const getSingleSelectedRenamableLayerId = useCallback((): string | null => {
|
|
16947
|
+
const fileIds = new Set(files.map((file) => file.id));
|
|
16948
|
+
const selectedRenamableLayerIds = selectedLayerIdsState.filter(
|
|
16949
|
+
(layerId) => !layerId.startsWith("__") && !fileIds.has(layerId),
|
|
16950
|
+
);
|
|
16951
|
+
return selectedRenamableLayerIds.length === 1
|
|
16952
|
+
? selectedRenamableLayerIds[0]!
|
|
16953
|
+
: null;
|
|
16954
|
+
}, [files, selectedLayerIdsState]);
|
|
16955
|
+
|
|
15775
16956
|
const shouldHandleEditorHotkey = useCallback((event: KeyboardEvent) => {
|
|
15776
16957
|
const key = event.key.length === 1 ? event.key.toLowerCase() : event.key;
|
|
15777
16958
|
const primary = event.metaKey || event.ctrlKey;
|
|
@@ -15815,21 +16996,16 @@ export default function DesignEditor() {
|
|
|
15815
16996
|
// L12: Cmd+R (and the context-menu Rename item, both routed through
|
|
15816
16997
|
// useDesignHotkeys' onRename) previously always renamed the DESIGN
|
|
15817
16998
|
// TITLE, even while a layer was selected — surprising when the user's
|
|
15818
|
-
// focus is clearly on a specific layer. Route to the layer
|
|
15819
|
-
//
|
|
15820
|
-
//
|
|
15821
|
-
//
|
|
15822
|
-
//
|
|
15823
|
-
// selects the layer and nudges the user to double-click it in the panel
|
|
15824
|
-
// rather than silently doing nothing or renaming the wrong thing.
|
|
16999
|
+
// focus is clearly on a specific layer. Route to the layer's real inline
|
|
17000
|
+
// rename editor (LayersPanel ref's beginRename) when exactly one
|
|
17001
|
+
// selectable (non-file-row) layer is selected; only fall back to
|
|
17002
|
+
// renaming the design title when nothing (or more than one layer) is
|
|
17003
|
+
// selected.
|
|
15825
17004
|
onRename: () => {
|
|
15826
17005
|
if (!canEditDesign) return;
|
|
15827
|
-
const
|
|
15828
|
-
|
|
15829
|
-
|
|
15830
|
-
);
|
|
15831
|
-
if (selectedRenamableLayerIds.length === 1) {
|
|
15832
|
-
toast.info(t("designEditor.toasts.layerRenameSelectLayer"));
|
|
17006
|
+
const layerId = getSingleSelectedRenamableLayerId();
|
|
17007
|
+
if (layerId) {
|
|
17008
|
+
layersPanelRef.current?.beginRename(layerId);
|
|
15833
17009
|
return;
|
|
15834
17010
|
}
|
|
15835
17011
|
setTitleDraft(design?.title ?? "");
|
|
@@ -17722,6 +18898,50 @@ ${serializedHtml}
|
|
|
17722
18898
|
// connected inspector components; not all render paths call it directly.
|
|
17723
18899
|
void requestLocalhostWrite;
|
|
17724
18900
|
|
|
18901
|
+
// Localhost workspace roots for the code workbench: one per distinct
|
|
18902
|
+
// connection referenced by this design's localhost-backed screens.
|
|
18903
|
+
const workbenchLocalhostConnections = useMemo(() => {
|
|
18904
|
+
const seen = new Map<string, { connectionId: string; label: string }>();
|
|
18905
|
+
for (const screen of overviewScreens) {
|
|
18906
|
+
if (screen.sourceType !== "localhost" || !screen.connectionId) continue;
|
|
18907
|
+
if (seen.has(screen.connectionId)) continue;
|
|
18908
|
+
let label = "Local app" /* i18n-ignore */;
|
|
18909
|
+
const screenUrl = screen.url ?? screen.previewUrl;
|
|
18910
|
+
if (screenUrl) {
|
|
18911
|
+
try {
|
|
18912
|
+
label = new URL(screenUrl).host || label;
|
|
18913
|
+
} catch {
|
|
18914
|
+
// Keep the fallback label for malformed screen URLs.
|
|
18915
|
+
}
|
|
18916
|
+
}
|
|
18917
|
+
seen.set(screen.connectionId, {
|
|
18918
|
+
connectionId: screen.connectionId,
|
|
18919
|
+
label,
|
|
18920
|
+
});
|
|
18921
|
+
}
|
|
18922
|
+
return [...seen.values()];
|
|
18923
|
+
}, [overviewScreens]);
|
|
18924
|
+
|
|
18925
|
+
// Consent round trip for code-workbench saves to local files: opens the
|
|
18926
|
+
// shared write-consent dialog and retries the save once granted.
|
|
18927
|
+
const handleWorkbenchLocalWriteConsent = useCallback(
|
|
18928
|
+
(connectionId: string, retry: () => void, filePath?: string) => {
|
|
18929
|
+
if (!id || !canEditDesign) return;
|
|
18930
|
+
setLocalhostConsentConnectionId(connectionId);
|
|
18931
|
+
setLocalhostWriteConsentPayload({
|
|
18932
|
+
rootPath:
|
|
18933
|
+
workbenchLocalhostConnections.find(
|
|
18934
|
+
(connection) => connection.connectionId === connectionId,
|
|
18935
|
+
)?.label ?? connectionId,
|
|
18936
|
+
files: filePath ? [filePath] : [],
|
|
18937
|
+
onGranted: () => retry(),
|
|
18938
|
+
onCancel: () => {},
|
|
18939
|
+
});
|
|
18940
|
+
setLocalhostWriteConsentOpen(true);
|
|
18941
|
+
},
|
|
18942
|
+
[canEditDesign, id, workbenchLocalhostConnections],
|
|
18943
|
+
);
|
|
18944
|
+
|
|
17725
18945
|
/**
|
|
17726
18946
|
* Derive a relative file path from the active localhost screen.
|
|
17727
18947
|
* Prefers `sourceFile` (the build-output relative path recorded at connect
|
|
@@ -20437,6 +21657,7 @@ ${serializedHtml}
|
|
|
20437
21657
|
</div>
|
|
20438
21658
|
<div className="min-h-0 flex-1">
|
|
20439
21659
|
<LayersPanel
|
|
21660
|
+
ref={layersPanelRef}
|
|
20440
21661
|
screens={layerPanelFiles}
|
|
20441
21662
|
activeScreenId={activeFileId ?? undefined}
|
|
20442
21663
|
screenOverviewActive={viewMode === "overview"}
|
|
@@ -20601,7 +21822,7 @@ ${serializedHtml}
|
|
|
20601
21822
|
</div>
|
|
20602
21823
|
}
|
|
20603
21824
|
>
|
|
20604
|
-
<
|
|
21825
|
+
<CodeWorkbench
|
|
20605
21826
|
designId={id}
|
|
20606
21827
|
activeFileId={routeCodeFileId}
|
|
20607
21828
|
activeFilename={routeCodeFilename}
|
|
@@ -20609,6 +21830,10 @@ ${serializedHtml}
|
|
|
20609
21830
|
selectedSelector={selectedCanvasSelector}
|
|
20610
21831
|
canEdit={canEditDesign}
|
|
20611
21832
|
onActiveFileChange={setActiveCodeFile}
|
|
21833
|
+
localhostConnections={workbenchLocalhostConnections}
|
|
21834
|
+
onRequestLocalWriteConsent={
|
|
21835
|
+
handleWorkbenchLocalWriteConsent
|
|
21836
|
+
}
|
|
20612
21837
|
/>
|
|
20613
21838
|
</Suspense>
|
|
20614
21839
|
) : null}
|
|
@@ -20688,7 +21913,12 @@ ${serializedHtml}
|
|
|
20688
21913
|
(selectedScreenIds.length > 0 && files.length > 1)),
|
|
20689
21914
|
)}
|
|
20690
21915
|
canReorder={canEditDesign && Boolean(selectedElement)}
|
|
20691
|
-
|
|
21916
|
+
// L12: rename is only offered for a single selectable layer
|
|
21917
|
+
// target (matching Cmd+R's routing) — the design-title rename
|
|
21918
|
+
// flow lives elsewhere (the title control), not this menu.
|
|
21919
|
+
canRename={
|
|
21920
|
+
canEditDesign && Boolean(getSingleSelectedRenamableLayerId())
|
|
21921
|
+
}
|
|
20692
21922
|
canToggleLocked={canEditDesign && Boolean(activeLayerId)}
|
|
20693
21923
|
canToggleHidden={canEditDesign && Boolean(activeLayerId)}
|
|
20694
21924
|
canCopyProps={Boolean(selectedElement)}
|
|
@@ -20698,7 +21928,6 @@ ${serializedHtml}
|
|
|
20698
21928
|
canCopyAsCode={Boolean(selectedElement?.selector)}
|
|
20699
21929
|
canGroup={canGroup}
|
|
20700
21930
|
canUngroup={canUngroup}
|
|
20701
|
-
hiddenActions={["rename"]}
|
|
20702
21931
|
getCanvasPoint={getContextCanvasPoint}
|
|
20703
21932
|
onPasteHere={(details) =>
|
|
20704
21933
|
void handlePasteSelection(
|
|
@@ -20720,6 +21949,14 @@ ${serializedHtml}
|
|
|
20720
21949
|
onBringToFront={() => changeSelectedZIndex("front")}
|
|
20721
21950
|
onSendBackward={() => changeSelectedZIndex("backward")}
|
|
20722
21951
|
onSendToBack={() => changeSelectedZIndex("back")}
|
|
21952
|
+
// L12: start the layers panel's real inline rename editor on the
|
|
21953
|
+
// single selected layer — canRename above already gates this item
|
|
21954
|
+
// to exactly that case, so getSingleSelectedRenamableLayerId()
|
|
21955
|
+
// should always resolve here.
|
|
21956
|
+
onRename={() => {
|
|
21957
|
+
const layerId = getSingleSelectedRenamableLayerId();
|
|
21958
|
+
if (layerId) layersPanelRef.current?.beginRename(layerId);
|
|
21959
|
+
}}
|
|
20723
21960
|
onToggleLocked={() => {
|
|
20724
21961
|
if (activeLayerId) {
|
|
20725
21962
|
handleToggleLayerLocked(activeLayerId, !activeLayerLocked);
|
|
@@ -20783,6 +22020,21 @@ ${serializedHtml}
|
|
|
20783
22020
|
}}
|
|
20784
22021
|
/>
|
|
20785
22022
|
)}
|
|
22023
|
+
{/* Full-app building status/controls. Renders only for
|
|
22024
|
+
designs backed by a fusion app (see readFusionApp) and
|
|
22025
|
+
only while the flag is on — the fusion actions the
|
|
22026
|
+
banner calls are gated on the same flag, so rendering it
|
|
22027
|
+
with the flag off would show controls that all error. */}
|
|
22028
|
+
{FULL_APP_BUILDING_ENABLED && id && fusionApp && (
|
|
22029
|
+
<FusionAppBanner
|
|
22030
|
+
designId={id}
|
|
22031
|
+
status={fusionApp.status}
|
|
22032
|
+
statusMessage={fusionApp.statusMessage}
|
|
22033
|
+
previewUrl={fusionApp.previewUrl}
|
|
22034
|
+
editorUrl={fusionApp.editorUrl}
|
|
22035
|
+
deployedUrl={fusionApp.deployedUrl}
|
|
22036
|
+
/>
|
|
22037
|
+
)}
|
|
20786
22038
|
{showPendingVisualStyleApply ? (
|
|
20787
22039
|
<div className="pointer-events-none absolute bottom-5 right-5 z-[70] flex items-end">
|
|
20788
22040
|
<DropdownMenu>
|
|
@@ -20827,139 +22079,164 @@ ${serializedHtml}
|
|
|
20827
22079
|
</div>
|
|
20828
22080
|
) : null}
|
|
20829
22081
|
{viewMode === "overview" ? (
|
|
20830
|
-
|
|
20831
|
-
|
|
20832
|
-
|
|
20833
|
-
|
|
20834
|
-
|
|
20835
|
-
|
|
20836
|
-
|
|
20837
|
-
|
|
20838
|
-
|
|
20839
|
-
|
|
20840
|
-
|
|
20841
|
-
|
|
20842
|
-
|
|
20843
|
-
|
|
20844
|
-
|
|
20845
|
-
|
|
20846
|
-
|
|
20847
|
-
|
|
20848
|
-
|
|
20849
|
-
|
|
20850
|
-
|
|
20851
|
-
|
|
20852
|
-
|
|
20853
|
-
|
|
20854
|
-
|
|
20855
|
-
|
|
20856
|
-
|
|
20857
|
-
|
|
20858
|
-
|
|
20859
|
-
|
|
20860
|
-
|
|
20861
|
-
|
|
20862
|
-
|
|
20863
|
-
|
|
20864
|
-
|
|
20865
|
-
|
|
20866
|
-
|
|
20867
|
-
|
|
20868
|
-
|
|
20869
|
-
|
|
20870
|
-
|
|
20871
|
-
|
|
20872
|
-
|
|
20873
|
-
|
|
20874
|
-
|
|
20875
|
-
|
|
20876
|
-
|
|
20877
|
-
|
|
20878
|
-
|
|
20879
|
-
|
|
20880
|
-
|
|
20881
|
-
|
|
20882
|
-
|
|
20883
|
-
|
|
20884
|
-
|
|
20885
|
-
|
|
20886
|
-
|
|
20887
|
-
|
|
20888
|
-
|
|
20889
|
-
|
|
20890
|
-
|
|
20891
|
-
|
|
20892
|
-
|
|
20893
|
-
|
|
20894
|
-
|
|
20895
|
-
|
|
20896
|
-
|
|
20897
|
-
|
|
20898
|
-
|
|
20899
|
-
|
|
20900
|
-
|
|
20901
|
-
|
|
20902
|
-
|
|
20903
|
-
|
|
20904
|
-
|
|
20905
|
-
|
|
20906
|
-
|
|
20907
|
-
|
|
20908
|
-
|
|
20909
|
-
?
|
|
20910
|
-
|
|
20911
|
-
|
|
20912
|
-
|
|
20913
|
-
|
|
20914
|
-
|
|
20915
|
-
|
|
20916
|
-
|
|
20917
|
-
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
20921
|
-
|
|
20922
|
-
|
|
20923
|
-
|
|
20924
|
-
|
|
20925
|
-
|
|
20926
|
-
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
|
|
20930
|
-
|
|
20931
|
-
|
|
20932
|
-
|
|
20933
|
-
|
|
20934
|
-
|
|
20935
|
-
|
|
20936
|
-
|
|
20937
|
-
|
|
20938
|
-
|
|
20939
|
-
|
|
20940
|
-
|
|
20941
|
-
|
|
20942
|
-
|
|
20943
|
-
|
|
20944
|
-
|
|
20945
|
-
|
|
20946
|
-
|
|
20947
|
-
|
|
20948
|
-
|
|
20949
|
-
|
|
20950
|
-
|
|
20951
|
-
|
|
20952
|
-
|
|
20953
|
-
|
|
20954
|
-
|
|
20955
|
-
|
|
20956
|
-
|
|
20957
|
-
|
|
20958
|
-
|
|
20959
|
-
|
|
20960
|
-
|
|
20961
|
-
|
|
20962
|
-
|
|
22082
|
+
<>
|
|
22083
|
+
<MultiScreenCanvas
|
|
22084
|
+
screens={overviewScreens}
|
|
22085
|
+
zoom={overviewCanvasZoom}
|
|
22086
|
+
onZoomChange={setExplicitOverviewCanvasZoom}
|
|
22087
|
+
activeId={activeFileId}
|
|
22088
|
+
selectedScreenIds={overviewSelectedScreenIds}
|
|
22089
|
+
fullViewScreenIds={fullViewScreenIds}
|
|
22090
|
+
activeScreenHasHoveredChild={
|
|
22091
|
+
Boolean(hoveredElement) &&
|
|
22092
|
+
!hoveredElementIsScreenRoot &&
|
|
22093
|
+
hoveredElementScreenId === activeFileId
|
|
22094
|
+
}
|
|
22095
|
+
hoveredChildScreenId={hoveredChildScreenId}
|
|
22096
|
+
directlyHoveredScreenId={hoveredScreenRootId}
|
|
22097
|
+
previewDeviceFrame={deviceFrame}
|
|
22098
|
+
activeTool={activeTool}
|
|
22099
|
+
onActiveToolChange={handleOverviewActiveToolChange}
|
|
22100
|
+
selectAllRequest={overviewSelectAllRequest}
|
|
22101
|
+
clearSelectionRequest={overviewClearSelectionRequest}
|
|
22102
|
+
onScreenSelectionChange={
|
|
22103
|
+
handleOverviewScreenSelectionChange
|
|
22104
|
+
}
|
|
22105
|
+
geometryById={canvasFrameGeometryById}
|
|
22106
|
+
onGeometryChange={queueFrameGeometrySave}
|
|
22107
|
+
onGeometryCommit={handleGeometryCommit}
|
|
22108
|
+
vectorEdit={vectorEditOverlayState}
|
|
22109
|
+
onCreatePrimitive={handleCreatePrimitive}
|
|
22110
|
+
onPrimitiveCreated={handlePrimitiveCreated}
|
|
22111
|
+
onPrimitiveReparent={handleOverviewPrimitiveReparent}
|
|
22112
|
+
onCrossScreenElementDrop={handleCrossScreenElementDrop}
|
|
22113
|
+
boardFileId={boardFileId}
|
|
22114
|
+
boardIsActive={activeFileId === boardFileId}
|
|
22115
|
+
boardFileContent={boardFileContent}
|
|
22116
|
+
boardFrameGeometry={boardFrameGeometry}
|
|
22117
|
+
boardClearSelectionRequest={
|
|
22118
|
+
overviewClearSelectionRequest
|
|
22119
|
+
}
|
|
22120
|
+
boardSelectedSelector={
|
|
22121
|
+
activeFileId === boardFileId
|
|
22122
|
+
? selectedCanvasSelector
|
|
22123
|
+
: null
|
|
22124
|
+
}
|
|
22125
|
+
boardSelectedSelectorCandidates={
|
|
22126
|
+
activeFileId === boardFileId
|
|
22127
|
+
? selectedCanvasSelectorCandidates
|
|
22128
|
+
: undefined
|
|
22129
|
+
}
|
|
22130
|
+
boardHoveredSelector={
|
|
22131
|
+
hoveredElementScreenId === boardFileId
|
|
22132
|
+
? hoveredCanvasSelector
|
|
22133
|
+
: null
|
|
22134
|
+
}
|
|
22135
|
+
boardHoveredSelectorCandidates={
|
|
22136
|
+
hoveredElementScreenId === boardFileId
|
|
22137
|
+
? hoveredCanvasSelectorCandidates
|
|
22138
|
+
: undefined
|
|
22139
|
+
}
|
|
22140
|
+
boardLockedSelectors={
|
|
22141
|
+
boardFileId
|
|
22142
|
+
? getLayerSelectorsForFile(
|
|
22143
|
+
boardFileId,
|
|
22144
|
+
lockedLayerIds,
|
|
22145
|
+
)
|
|
22146
|
+
: undefined
|
|
22147
|
+
}
|
|
22148
|
+
boardHiddenSelectors={
|
|
22149
|
+
boardFileId
|
|
22150
|
+
? getLayerSelectorsForFile(
|
|
22151
|
+
boardFileId,
|
|
22152
|
+
hiddenLayerIds,
|
|
22153
|
+
)
|
|
22154
|
+
: undefined
|
|
22155
|
+
}
|
|
22156
|
+
onBoardDrawPrimitive={
|
|
22157
|
+
canEditDesign ? handleBoardDrawPrimitive : undefined
|
|
22158
|
+
}
|
|
22159
|
+
boardEditMode={canEditDesign}
|
|
22160
|
+
onBoardElementSelect={
|
|
22161
|
+
boardFileId ? handleBoardElementSelect : undefined
|
|
22162
|
+
}
|
|
22163
|
+
onBoardElementMarqueeSelect={
|
|
22164
|
+
boardFileId
|
|
22165
|
+
? handleBoardElementMarqueeSelect
|
|
22166
|
+
: undefined
|
|
22167
|
+
}
|
|
22168
|
+
onBoardElementHover={
|
|
22169
|
+
boardFileId ? handleBoardElementHover : undefined
|
|
22170
|
+
}
|
|
22171
|
+
onBoardElementClear={
|
|
22172
|
+
boardFileId ? handleBoardElementClear : undefined
|
|
22173
|
+
}
|
|
22174
|
+
onBoardIframeHotkey={handleIframeHotkey}
|
|
22175
|
+
onBoardFigmaClipboardPaste={
|
|
22176
|
+
handleCanvasFigmaClipboardPaste
|
|
22177
|
+
}
|
|
22178
|
+
onBoardIframeContextMenu={handleIframeContextMenu}
|
|
22179
|
+
onBoardTextEditingStateChange={
|
|
22180
|
+
handleBoardTextEditingStateChange
|
|
22181
|
+
}
|
|
22182
|
+
onBoardElementDblClickText={
|
|
22183
|
+
boardFileId
|
|
22184
|
+
? handleBoardElementDblClickText
|
|
22185
|
+
: undefined
|
|
22186
|
+
}
|
|
22187
|
+
onBoardVisualStyleChange={
|
|
22188
|
+
boardFileId ? handleBoardVisualStyleChange : undefined
|
|
22189
|
+
}
|
|
22190
|
+
onBoardVisualStructureChange={
|
|
22191
|
+
boardFileId
|
|
22192
|
+
? handleBoardVisualStructureChange
|
|
22193
|
+
: undefined
|
|
22194
|
+
}
|
|
22195
|
+
onBoardVisualDuplicateChange={
|
|
22196
|
+
boardFileId
|
|
22197
|
+
? handleBoardVisualDuplicateChange
|
|
22198
|
+
: undefined
|
|
22199
|
+
}
|
|
22200
|
+
onBoardTextContentChange={
|
|
22201
|
+
boardFileId ? handleBoardTextContentChange : undefined
|
|
22202
|
+
}
|
|
22203
|
+
onCreateScreenFrame={handleCreateScreenFrame}
|
|
22204
|
+
onDeleteSelection={handleDeleteOverviewSelection}
|
|
22205
|
+
onSelectionChange={handleOverviewScreenSelectionChange}
|
|
22206
|
+
onLayerMarqueeSelectionChange={
|
|
22207
|
+
handleLayerMarqueeSelectionChange
|
|
22208
|
+
}
|
|
22209
|
+
selectedLayerSelectorGroupsByScreen={
|
|
22210
|
+
selectedLayerSelectorGroupsByScreen
|
|
22211
|
+
}
|
|
22212
|
+
onPick={handleOverviewScreenPick}
|
|
22213
|
+
onEdit={enterSingleScreen}
|
|
22214
|
+
onDuplicate={handleDuplicateScreen}
|
|
22215
|
+
onAddBreakpoint={handleOverviewAddBreakpoint}
|
|
22216
|
+
onActiveBreakpointChange={
|
|
22217
|
+
handleOverviewActiveBreakpointChange
|
|
22218
|
+
}
|
|
22219
|
+
renderScreenContent={renderScreenContent}
|
|
22220
|
+
/>
|
|
22221
|
+
{/* Presence (overview): the agent's selection ring +
|
|
22222
|
+
fading recent-edit highlights, resolved element-level
|
|
22223
|
+
inside the frame it is editing and positioned over the
|
|
22224
|
+
board. See overview presence pipeline above. */}
|
|
22225
|
+
{overviewAgentOthers.length > 0 && (
|
|
22226
|
+
<RemoteSelectionRings
|
|
22227
|
+
others={overviewAgentOthers}
|
|
22228
|
+
resolveRect={resolveOverviewSelectionRect}
|
|
22229
|
+
containerRef={canvasContainerRef}
|
|
22230
|
+
/>
|
|
22231
|
+
)}
|
|
22232
|
+
{overviewRecentEditsForOverlays.length > 0 && (
|
|
22233
|
+
<RecentEditHighlights
|
|
22234
|
+
edits={overviewRecentEditsForOverlays}
|
|
22235
|
+
resolveRect={resolveOverviewRecentEditRect}
|
|
22236
|
+
containerRef={canvasContainerRef}
|
|
22237
|
+
/>
|
|
22238
|
+
)}
|
|
22239
|
+
</>
|
|
20963
22240
|
) : (
|
|
20964
22241
|
<>
|
|
20965
22242
|
<DesignCanvas
|
|
@@ -20988,6 +22265,8 @@ ${serializedHtml}
|
|
|
20988
22265
|
interactMode={mode === "interact"}
|
|
20989
22266
|
readOnly={!canEditDesign}
|
|
20990
22267
|
scaleMode={activeTool === "scale"}
|
|
22268
|
+
activeCreationTool={activeSingleScreenCreationTool}
|
|
22269
|
+
onCreatePrimitive={handleSingleScreenCreatePrimitive}
|
|
20991
22270
|
clearSelectionRequest={overviewClearSelectionRequest}
|
|
20992
22271
|
selectedSelector={selectedCanvasSelector}
|
|
20993
22272
|
selectedSelectorCandidates={
|
|
@@ -21071,10 +22350,30 @@ ${serializedHtml}
|
|
|
21071
22350
|
}
|
|
21072
22351
|
}}
|
|
21073
22352
|
/>
|
|
21074
|
-
{/* Presence:
|
|
22353
|
+
{/* Presence: remote selection rings (human peers + AI),
|
|
22354
|
+
resolved into the active screen's iframe. */}
|
|
21075
22355
|
{others.length > 0 && (
|
|
22356
|
+
<RemoteSelectionRings
|
|
22357
|
+
others={othersForOverlays}
|
|
22358
|
+
resolveRect={resolveSelectionRect}
|
|
22359
|
+
containerRef={canvasContainerRef}
|
|
22360
|
+
/>
|
|
22361
|
+
)}
|
|
22362
|
+
{/* Presence: lingering fading highlights over regions a
|
|
22363
|
+
peer or the AI just edited. */}
|
|
22364
|
+
{recentEditsForOverlays.length > 0 && (
|
|
22365
|
+
<RecentEditHighlights
|
|
22366
|
+
edits={recentEditsForOverlays}
|
|
22367
|
+
resolveRect={resolveRecentEditRect}
|
|
22368
|
+
containerRef={canvasContainerRef}
|
|
22369
|
+
/>
|
|
22370
|
+
)}
|
|
22371
|
+
{/* Presence: live cursor overlay for remote participants.
|
|
22372
|
+
The AI gets a synthesized cursor derived from its
|
|
22373
|
+
current edit target (see othersWithAgentCursor). */}
|
|
22374
|
+
{othersWithAgentCursor.length > 0 && (
|
|
21076
22375
|
<LiveCursorOverlay
|
|
21077
|
-
others={
|
|
22376
|
+
others={othersWithAgentCursor}
|
|
21078
22377
|
containerRef={canvasContainerRef}
|
|
21079
22378
|
/>
|
|
21080
22379
|
)}
|
|
@@ -21605,7 +22904,7 @@ ${serializedHtml}
|
|
|
21605
22904
|
{/* Localhost write-consent dialog: shown when the agent or editor wants to
|
|
21606
22905
|
persist an edit to a local HTML/CSS source file and no valid grant
|
|
21607
22906
|
exists for the active connection yet. */}
|
|
21608
|
-
{id && activeLocalhostConnectionId && (
|
|
22907
|
+
{id && (activeLocalhostConnectionId || localhostConsentConnectionId) && (
|
|
21609
22908
|
<LocalhostWriteConsentDialog
|
|
21610
22909
|
open={localhostWriteConsentOpen}
|
|
21611
22910
|
onOpenChange={(next) => {
|