@agent-native/core 0.85.6 → 0.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +130 -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 +6 -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/AssistantChat.tsx +126 -17
- 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/index.ts +48 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +0 -4
- 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/use-pinch-zoom.ts +76 -11
- package/corpus/core/src/client/visual-style-controls.tsx +475 -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/presence.ts +63 -3
- 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/jobs/scheduler.ts +17 -14
- 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/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 +13 -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/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 +33 -6
- package/corpus/templates/analytics/actions/navigate.ts +14 -4
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +1 -1
- package/corpus/templates/analytics/actions/view-screen.ts +57 -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 +7 -0
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +15 -1
- package/corpus/templates/analytics/app/i18n-data.ts +151 -10
- package/corpus/templates/analytics/app/pages/Agents.tsx +118 -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-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/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/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 +93 -2
- 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/desktop/src/lib/recorder.ts +245 -77
- 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/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/package.json +3 -3
- package/corpus/templates/content/server/plugins/db.ts +57 -1
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +190 -13
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +16 -4
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +46 -3
- 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 +83 -13
- 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 +24 -69
- package/corpus/templates/design/actions/apply-fusion-edits.ts +161 -0
- package/corpus/templates/design/actions/apply-motion-edit.ts +29 -2
- package/corpus/templates/design/actions/apply-visual-edit.ts +38 -3
- package/corpus/templates/design/actions/connect-localhost.ts +25 -18
- 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/export-pdf.ts +8 -1
- package/corpus/templates/design/actions/export-zip.ts +10 -1
- package/corpus/templates/design/actions/get-fusion-deploy-status.ts +86 -0
- package/corpus/templates/design/actions/get-motion-timeline.ts +26 -20
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +10 -0
- package/corpus/templates/design/actions/insert-asset.ts +190 -7
- 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/open-visual-edit.ts +27 -5
- package/corpus/templates/design/actions/present-design-variants.ts +2 -2
- 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/revoke-localhost-write-consent.ts +18 -14
- package/corpus/templates/design/actions/run-design-audit.ts +7 -4
- 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 +162 -36
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +12 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +600 -66
- package/corpus/templates/design/app/components/design/EditPanel.tsx +793 -304
- package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +328 -0
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +671 -154
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +1 -1
- package/corpus/templates/design/app/components/design/MotionDock.tsx +234 -46
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2070 -420
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +25 -2
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +7 -98
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +640 -72
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +605 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +50 -26
- 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 +53 -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 +363 -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 +155 -0
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +551 -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 +734 -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/AutoLayoutMatrix.tsx +16 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +217 -113
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +111 -5
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +79 -16
- package/corpus/templates/design/app/components/design/inspector/index.ts +9 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +67 -2
- package/corpus/templates/design/app/components/design/types.ts +16 -0
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +80 -1
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +24 -6
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +15 -1
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +6 -16
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +77 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/design/app/i18n-data.ts +31 -0
- package/corpus/templates/design/app/lib/design-import.ts +95 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +5586 -1112
- package/corpus/templates/design/app/pages/Index.tsx +90 -17
- package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-inspector-fixes-mixed-selections-show-mixed-instead-of-wrong.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-keyframe-timeline-works-reliably-drag-keyframes-smoothly-pic.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-much-faster-editor-smooth-dragging-zooming-and-panel-updates.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-pen-tool-refinements-click-the-first-point-to-close-with-a-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-text-editing-enter-adds-a-line-break-international-ime-typin.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-panel-is-now-a-full-vs-code-style-editor-with-a-fil.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-undo-and-redo-are-dependable-across-agent-edits-screen-switc.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/design-export.ts +43 -1
- 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/board-file.ts +25 -5
- package/corpus/templates/design/shared/canvas-math.ts +754 -9
- package/corpus/templates/design/shared/code-layer.ts +304 -26
- package/corpus/templates/design/shared/collab-selection.ts +52 -0
- package/corpus/templates/design/shared/color-utils.ts +84 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +16 -7
- package/corpus/templates/design/shared/full-app.ts +126 -0
- package/corpus/templates/design/shared/motion-compiler.ts +75 -31
- package/corpus/templates/design/shared/motion-timeline.ts +335 -0
- package/corpus/templates/design/shared/pen-path.ts +496 -23
- 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/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/pages/PlansPage.tsx +215 -63
- 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 +4 -0
- 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/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/editor/EditorSidebar.tsx +47 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +32 -35
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +625 -199
- package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +342 -0
- package/corpus/templates/slides/app/components/editor/TweaksPanel.tsx +46 -96
- 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 +46 -0
- package/corpus/templates/slides/app/i18n/de-DE.ts +46 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +46 -0
- package/corpus/templates/slides/app/i18n/es-ES.ts +46 -0
- package/corpus/templates/slides/app/i18n/fr-FR.ts +46 -0
- package/corpus/templates/slides/app/i18n/hi-IN.ts +46 -0
- package/corpus/templates/slides/app/i18n/ja-JP.ts +46 -0
- package/corpus/templates/slides/app/i18n/ko-KR.ts +46 -0
- package/corpus/templates/slides/app/i18n/pt-BR.ts +46 -0
- package/corpus/templates/slides/app/i18n/zh-CN.ts +46 -0
- package/corpus/templates/slides/app/i18n/zh-TW.ts +46 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +25 -13
- 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-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/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/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +120 -15
- package/dist/client/AssistantChat.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/index.d.ts +6 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +7 -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/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/use-pinch-zoom.d.ts.map +1 -1
- package/dist/client/use-pinch-zoom.js +76 -11
- package/dist/client/use-pinch-zoom.js.map +1 -1
- package/dist/client/visual-style-controls.d.ts +79 -0
- package/dist/client/visual-style-controls.d.ts.map +1 -0
- package/dist/client/visual-style-controls.js +150 -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 +14 -3
- 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/presence.d.ts.map +1 -1
- package/dist/collab/presence.js +54 -3
- package/dist/collab/presence.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/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/jobs/scheduler.js +17 -10
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- 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/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/routes.d.ts +6 -6
- 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/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 +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/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/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 +6 -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/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
|
@@ -12,8 +12,11 @@ import {
|
|
|
12
12
|
IconSearch,
|
|
13
13
|
} from "@tabler/icons-react";
|
|
14
14
|
import {
|
|
15
|
+
forwardRef,
|
|
16
|
+
memo,
|
|
15
17
|
useCallback,
|
|
16
18
|
useEffect,
|
|
19
|
+
useImperativeHandle,
|
|
17
20
|
useLayoutEffect,
|
|
18
21
|
useMemo,
|
|
19
22
|
useRef,
|
|
@@ -22,6 +25,8 @@ import {
|
|
|
22
25
|
type DragEvent,
|
|
23
26
|
type MouseEvent,
|
|
24
27
|
type ReactNode,
|
|
28
|
+
type Ref,
|
|
29
|
+
type RefObject,
|
|
25
30
|
} from "react";
|
|
26
31
|
|
|
27
32
|
import { Button } from "@/components/ui/button";
|
|
@@ -40,9 +45,6 @@ import {
|
|
|
40
45
|
} from "@/components/ui/tooltip";
|
|
41
46
|
import { cn } from "@/lib/utils";
|
|
42
47
|
|
|
43
|
-
const LAYER_ROW_BASE_INDENT = 4;
|
|
44
|
-
const LAYER_ROW_DEPTH_INDENT = 18;
|
|
45
|
-
|
|
46
48
|
export type LayersPanelNodeType =
|
|
47
49
|
| "file"
|
|
48
50
|
| "screen"
|
|
@@ -181,6 +183,20 @@ export interface LayersPanelProps {
|
|
|
181
183
|
boardElements?: LayersPanelNode[];
|
|
182
184
|
}
|
|
183
185
|
|
|
186
|
+
// L12: imperative handle so an external trigger (Cmd+R hotkey, canvas
|
|
187
|
+
// context-menu Rename item) can start the panel's inline rename editor on a
|
|
188
|
+
// specific layer, matching Figma. See beginRename below for what it does.
|
|
189
|
+
export interface LayersPanelHandle {
|
|
190
|
+
/**
|
|
191
|
+
* Starts inline rename for the given layer id. Returns false (and does
|
|
192
|
+
* nothing) when the id doesn't resolve to a renamable row — i.e. it isn't
|
|
193
|
+
* in the current tree, or the node has `renamable === false`. On success,
|
|
194
|
+
* expands the layer's collapsed ancestors so the row is visible, scrolls
|
|
195
|
+
* it into view, and focuses+selects the rename input once it mounts.
|
|
196
|
+
*/
|
|
197
|
+
beginRename: (layerId: string) => boolean;
|
|
198
|
+
}
|
|
199
|
+
|
|
184
200
|
export interface FlatLayerRow {
|
|
185
201
|
node: LayersPanelNode;
|
|
186
202
|
rowKey: string;
|
|
@@ -215,10 +231,13 @@ let activeDropIntent: LayersPanelMoveIntent | null = null;
|
|
|
215
231
|
|
|
216
232
|
const ROW_BASE_INDENT = 4;
|
|
217
233
|
const ROW_INDENT_STEP = 28;
|
|
218
|
-
const ROW_MAX_INDENT = 96;
|
|
219
234
|
|
|
235
|
+
// No indent cap: deeply nested trees (Figma-style component instances easily
|
|
236
|
+
// exceed 4 levels) must stay visually distinguishable by depth. A previous
|
|
237
|
+
// 96px cap made every row at depth >= 4 render at the same indent, making
|
|
238
|
+
// nested structure ambiguous in the panel.
|
|
220
239
|
function rowIndent(depth: number): number {
|
|
221
|
-
return
|
|
240
|
+
return ROW_BASE_INDENT + depth * ROW_INDENT_STEP;
|
|
222
241
|
}
|
|
223
242
|
|
|
224
243
|
function defaultLabels(t: ReturnType<typeof useT>): LayersPanelLabels {
|
|
@@ -348,7 +367,24 @@ function filterNode(
|
|
|
348
367
|
return null;
|
|
349
368
|
}
|
|
350
369
|
|
|
351
|
-
|
|
370
|
+
// ORDER CONVENTION (L5): the panel's top row within a sibling group is the
|
|
371
|
+
// topmost-RENDERED layer, matching Figma. LayersPanelNode.children arrives in
|
|
372
|
+
// DOM order (first array element = first DOM child = bottom of the paint
|
|
373
|
+
// stack for overlapping siblings; last DOM child = topmost paint). So the
|
|
374
|
+
// panel must display each sibling group in REVERSE DOM order. This is the
|
|
375
|
+
// single place that convention is applied — everything else (drop-placement
|
|
376
|
+
// mapping in dropPlacementForEvent/handleDrop callers, and the CL:2769
|
|
377
|
+
// moveNode "inside" insertion point) is written to agree with it:
|
|
378
|
+
// - drop "above row X" (before X in the reversed panel list) => DOM order
|
|
379
|
+
// "after" X (closer to the paint-top), i.e. inserted after X in the DOM.
|
|
380
|
+
// - drop "below row X" (after X in the reversed panel list) => DOM order
|
|
381
|
+
// "before" X, i.e. inserted before X in the DOM.
|
|
382
|
+
// - "inside" a container drops at the END of the panel's child list, i.e.
|
|
383
|
+
// the FIRST DOM child position (contentStart), so the dropped node
|
|
384
|
+
// becomes the bottom-most-painted / top-of-panel-list child. See
|
|
385
|
+
// mapPanelPlacementToDomPlacement below and its use at the LP/DE drop
|
|
386
|
+
// boundary.
|
|
387
|
+
export function flattenRows(
|
|
352
388
|
nodes: LayersPanelNode[],
|
|
353
389
|
expandedIds: ReadonlySet<string>,
|
|
354
390
|
forceExpanded: boolean,
|
|
@@ -357,7 +393,8 @@ function flattenRows(
|
|
|
357
393
|
ancestorIds: string[] = [],
|
|
358
394
|
rows: FlatLayerRow[] = [],
|
|
359
395
|
) {
|
|
360
|
-
nodes.
|
|
396
|
+
const displayOrder = [...nodes].reverse();
|
|
397
|
+
displayOrder.forEach((node, index) => {
|
|
361
398
|
const children = node.children ?? [];
|
|
362
399
|
const hasChildren = children.length > 0;
|
|
363
400
|
const canAcceptChildren = CONTAINER_TYPES.has(node.type);
|
|
@@ -385,6 +422,33 @@ function flattenRows(
|
|
|
385
422
|
return rows;
|
|
386
423
|
}
|
|
387
424
|
|
|
425
|
+
/**
|
|
426
|
+
* Maps a panel-order drop placement (computed from where the user dropped
|
|
427
|
+
* relative to a row's position in the reversed, top-row-is-topmost panel
|
|
428
|
+
* list) to the DOM-order placement the underlying moveNode/applyMoveNodeEdit
|
|
429
|
+
* primitive expects (see CL applyMoveNodeEdit: "before" = anchor.start,
|
|
430
|
+
* "after" = anchor.end, "inside" = anchor.contentEnd i.e. last DOM child).
|
|
431
|
+
*
|
|
432
|
+
* Because the panel displays each sibling group in reverse DOM order:
|
|
433
|
+
* - "before" in the panel (drop above row X, i.e. towards the top/topmost)
|
|
434
|
+
* means the moved node should render ABOVE X, i.e. paint AFTER X in the
|
|
435
|
+
* DOM => DOM placement "after".
|
|
436
|
+
* - "after" in the panel (drop below row X, towards the bottom/backmost)
|
|
437
|
+
* means the moved node should render BELOW X, i.e. paint BEFORE X in the
|
|
438
|
+
* DOM => DOM placement "before".
|
|
439
|
+
* - "inside" is unchanged in kind, but the DOM primitive already inserts at
|
|
440
|
+
* contentEnd (last DOM child), which is exactly the panel's "top of this
|
|
441
|
+
* group's list" — i.e. inside-drops naturally land at the top of the
|
|
442
|
+
* panel's child list with no further mapping needed.
|
|
443
|
+
*/
|
|
444
|
+
export function mapPanelPlacementToDomPlacement(
|
|
445
|
+
placement: LayersPanelMoveIntent["placement"],
|
|
446
|
+
): LayersPanelMoveIntent["placement"] {
|
|
447
|
+
if (placement === "before") return "after";
|
|
448
|
+
if (placement === "after") return "before";
|
|
449
|
+
return "inside";
|
|
450
|
+
}
|
|
451
|
+
|
|
388
452
|
function nextExpandedIds(
|
|
389
453
|
ids: readonly string[],
|
|
390
454
|
nodeId: string,
|
|
@@ -399,6 +463,34 @@ function nextExpandedIds(
|
|
|
399
463
|
return Array.from(next);
|
|
400
464
|
}
|
|
401
465
|
|
|
466
|
+
/**
|
|
467
|
+
* L1: pure computation for the auto-expand-ancestors-of-selection effect.
|
|
468
|
+
* Given the current selection's ancestor ids and the CURRENT expanded set,
|
|
469
|
+
* returns the next expanded id list with any missing ancestors added, or
|
|
470
|
+
* null if nothing needs to change. Extracted as a pure function (mirroring
|
|
471
|
+
* shouldResyncLayerSelectionAnchor) so the auto-expand decision is testable
|
|
472
|
+
* without mounting the component. The caller is responsible for only
|
|
473
|
+
* invoking this once per NEW selection signature — see the
|
|
474
|
+
* lastAutoExpandedSelectionRef gate in the effect below, which is what
|
|
475
|
+
* actually fixes the collapse-bounces-back-instantly bug (this function
|
|
476
|
+
* itself is a straightforward set-union and isn't where that bug lived).
|
|
477
|
+
*/
|
|
478
|
+
export function nextAutoExpandedIds(args: {
|
|
479
|
+
selectedAncestorIds: readonly string[];
|
|
480
|
+
expandedIds: readonly string[];
|
|
481
|
+
}): string[] | null {
|
|
482
|
+
if (args.selectedAncestorIds.length === 0) return null;
|
|
483
|
+
const next = new Set(args.expandedIds);
|
|
484
|
+
let changed = false;
|
|
485
|
+
args.selectedAncestorIds.forEach((id) => {
|
|
486
|
+
if (!next.has(id)) {
|
|
487
|
+
next.add(id);
|
|
488
|
+
changed = true;
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
return changed ? Array.from(next) : null;
|
|
492
|
+
}
|
|
493
|
+
|
|
402
494
|
function collectAncestorIds(
|
|
403
495
|
nodes: LayersPanelNode[],
|
|
404
496
|
targetIds: ReadonlySet<string>,
|
|
@@ -424,6 +516,54 @@ function collectAncestorIds(
|
|
|
424
516
|
return Array.from(ancestors);
|
|
425
517
|
}
|
|
426
518
|
|
|
519
|
+
// Full-tree ancestor map (id -> ancestor id chain from root), independent of
|
|
520
|
+
// expand/collapse or search-filter state. Used at drag start to correctly
|
|
521
|
+
// identify selected descendants even when their row is currently not
|
|
522
|
+
// rendered in visibleRows (e.g. inside a collapsed ancestor) — see
|
|
523
|
+
// getDraggedLayerIdsForRows below.
|
|
524
|
+
export function buildAncestorIdMap(
|
|
525
|
+
nodes: LayersPanelNode[],
|
|
526
|
+
): Map<string, string[]> {
|
|
527
|
+
const map = new Map<string, string[]>();
|
|
528
|
+
|
|
529
|
+
function visit(node: LayersPanelNode, ancestorIds: string[]) {
|
|
530
|
+
map.set(node.id, ancestorIds);
|
|
531
|
+
const children = node.children ?? [];
|
|
532
|
+
children.forEach((child) => visit(child, [...ancestorIds, node.id]));
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
nodes.forEach((node) => visit(node, []));
|
|
536
|
+
return map;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// L12: find a node anywhere in the full (unfiltered) tree by id, alongside
|
|
540
|
+
// its ancestor id chain. Used by beginRename to validate the target and to
|
|
541
|
+
// know which ancestors must be expanded for the row to become visible,
|
|
542
|
+
// independent of the current search/expand state.
|
|
543
|
+
export function findNodeWithAncestors(
|
|
544
|
+
nodes: LayersPanelNode[],
|
|
545
|
+
targetId: string,
|
|
546
|
+
): { node: LayersPanelNode; ancestorIds: string[] } | null {
|
|
547
|
+
function visit(
|
|
548
|
+
node: LayersPanelNode,
|
|
549
|
+
ancestorIds: string[],
|
|
550
|
+
): { node: LayersPanelNode; ancestorIds: string[] } | null {
|
|
551
|
+
if (node.id === targetId) return { node, ancestorIds };
|
|
552
|
+
const children = node.children ?? [];
|
|
553
|
+
for (const child of children) {
|
|
554
|
+
const found = visit(child, [...ancestorIds, node.id]);
|
|
555
|
+
if (found) return found;
|
|
556
|
+
}
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
for (const node of nodes) {
|
|
561
|
+
const found = visit(node, []);
|
|
562
|
+
if (found) return found;
|
|
563
|
+
}
|
|
564
|
+
return null;
|
|
565
|
+
}
|
|
566
|
+
|
|
427
567
|
export function getLayerSelectionAnchorFromExternalSelection(args: {
|
|
428
568
|
selectedIds: readonly string[];
|
|
429
569
|
selectableVisibleIds: readonly string[];
|
|
@@ -451,20 +591,48 @@ export function getTreeOrderedLayerIds(
|
|
|
451
591
|
];
|
|
452
592
|
}
|
|
453
593
|
|
|
594
|
+
// Shift-range selection is a straight slice through the flattened visible
|
|
595
|
+
// rows, so when the range spans an expanded parent AND some of its children,
|
|
596
|
+
// both end up selected. Figma normalizes this away: selecting an ancestor
|
|
597
|
+
// already implies its descendants for move/visual purposes, so a descendant
|
|
598
|
+
// whose ancestor is also in the resulting set should be dropped from the
|
|
599
|
+
// selection (the ancestor "wins"). Order-preserving.
|
|
600
|
+
export function dropDescendantsOfSelectedAncestors(
|
|
601
|
+
ids: readonly string[],
|
|
602
|
+
visibleRows: readonly FlatLayerRow[],
|
|
603
|
+
): string[] {
|
|
604
|
+
const idSet = new Set(ids);
|
|
605
|
+
const ancestorIdsById = new Map(
|
|
606
|
+
visibleRows.map((row) => [row.node.id, row.ancestorIds] as const),
|
|
607
|
+
);
|
|
608
|
+
return ids.filter((id) => {
|
|
609
|
+
const ancestorIds = ancestorIdsById.get(id);
|
|
610
|
+
return !ancestorIds?.some((ancestorId) => idSet.has(ancestorId));
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
454
614
|
export function getDraggedLayerIdsForRows(args: {
|
|
455
615
|
selectedIds: readonly string[];
|
|
456
616
|
nodeId: string;
|
|
457
617
|
visibleRows: readonly FlatLayerRow[];
|
|
618
|
+
// Full-tree ancestor map (see buildAncestorIdMap), independent of
|
|
619
|
+
// expand/collapse or search-filter state. Required to correctly drop
|
|
620
|
+
// selected descendants whose row is currently not in visibleRows (e.g.
|
|
621
|
+
// inside a collapsed dragged parent) — falling back to visibleRows-only
|
|
622
|
+
// ancestor lookup would treat those as separate top-level drags and
|
|
623
|
+
// extract them from the parent being dragged. Optional only for
|
|
624
|
+
// call-site/back-compat convenience; always pass it in the real panel.
|
|
625
|
+
ancestorIdMap?: ReadonlyMap<string, string[]>;
|
|
458
626
|
}): string[] {
|
|
459
627
|
const rawDraggedIds = args.selectedIds.includes(args.nodeId)
|
|
460
628
|
? getTreeOrderedLayerIds(args.selectedIds, args.visibleRows)
|
|
461
629
|
: [args.nodeId];
|
|
462
630
|
const draggedIdSet = new Set(rawDraggedIds);
|
|
463
631
|
return rawDraggedIds.filter((id) => {
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
);
|
|
632
|
+
const ancestorIds =
|
|
633
|
+
args.ancestorIdMap?.get(id) ??
|
|
634
|
+
args.visibleRows.find((row) => row.node.id === id)?.ancestorIds;
|
|
635
|
+
return !ancestorIds?.some((ancestorId) => draggedIdSet.has(ancestorId));
|
|
468
636
|
});
|
|
469
637
|
}
|
|
470
638
|
|
|
@@ -491,44 +659,83 @@ function layerCanShowBadge(node: LayersPanelNode) {
|
|
|
491
659
|
);
|
|
492
660
|
}
|
|
493
661
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
662
|
+
// PF8: DesignEditor re-renders on many state changes unrelated to the layers
|
|
663
|
+
// tree (drag gestures, zoom, canvas hover, etc). All of LayersPanel's call-site
|
|
664
|
+
// props are already stabilized (useMemo/useCallback/plain state — see
|
|
665
|
+
// DesignEditor.tsx's layerPanelFiles/overviewLayerPanelFiles/
|
|
666
|
+
// activeLayerPanelNodes/boardElements and the onXxx handlers passed to
|
|
667
|
+
// <LayersPanel>), so a default shallow-prop comparator is sufficient here;
|
|
668
|
+
// no custom comparator is needed or wanted since it would risk silently
|
|
669
|
+
// ignoring a genuinely-changed prop.
|
|
670
|
+
// L12: forwardRef is composed OUTSIDE memo (memo(forwardRef(...))) — this is
|
|
671
|
+
// the standard ordering and keeps the default shallow-prop memo comparator
|
|
672
|
+
// applying to the same props as before; the ref itself is never part of that
|
|
673
|
+
// comparison (React handles ref identity separately from memo's prop diff).
|
|
674
|
+
function LayersPanelImpl(
|
|
675
|
+
{
|
|
676
|
+
screens,
|
|
677
|
+
activeScreenId,
|
|
678
|
+
screenOverviewActive = false,
|
|
679
|
+
files,
|
|
680
|
+
layers,
|
|
681
|
+
codeLayers,
|
|
682
|
+
elementLayers,
|
|
683
|
+
selectedIds,
|
|
684
|
+
expandedIds,
|
|
685
|
+
searchQuery,
|
|
686
|
+
className,
|
|
687
|
+
footer,
|
|
688
|
+
labels: labelsProp,
|
|
689
|
+
onSearchQueryChange,
|
|
690
|
+
onScreenSelect,
|
|
691
|
+
onScreenOverview,
|
|
692
|
+
onAddScreen,
|
|
693
|
+
onExpandedIdsChange,
|
|
694
|
+
onSelectionChange,
|
|
695
|
+
onRename,
|
|
696
|
+
onToggleLocked,
|
|
697
|
+
onToggleHidden,
|
|
698
|
+
onHoverLayer,
|
|
699
|
+
onLeaveLayer,
|
|
700
|
+
onMoveLayer,
|
|
701
|
+
canMoveLayer,
|
|
702
|
+
boardElements,
|
|
703
|
+
}: LayersPanelProps,
|
|
704
|
+
ref: Ref<LayersPanelHandle>,
|
|
705
|
+
) {
|
|
523
706
|
const t = useT();
|
|
524
707
|
const labels = useMemo(() => mergeLabels(labelsProp, t), [labelsProp, t]);
|
|
525
708
|
const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]);
|
|
526
709
|
const selectedIdsRef = useRef<readonly string[]>(selectedIds);
|
|
710
|
+
// Rows need the full visible-row order for keyboard navigation and
|
|
711
|
+
// multi-drag payload ordering, but that's whole-tree state, not a per-row
|
|
712
|
+
// primitive. Route it through a stable ref instead of a prop so passing it
|
|
713
|
+
// to LayerRow doesn't defeat React.memo (the ref object identity never
|
|
714
|
+
// changes; only .current does).
|
|
715
|
+
const visibleRowsRef = useRef<FlatLayerRow[]>([]);
|
|
716
|
+
// Full (unfiltered-by-expand/collapse) root nodes, threaded the same way so
|
|
717
|
+
// drag start can build a full-tree ancestor map (see buildAncestorIdMap)
|
|
718
|
+
// without adding a per-row array prop that would defeat React.memo.
|
|
719
|
+
const rootsRef = useRef<LayersPanelNode[]>([]);
|
|
720
|
+
// Same idea for expandedIds: onToggleExpanded needs the current expanded
|
|
721
|
+
// set to compute the next one, but reading it from a ref lets the
|
|
722
|
+
// per-row callback stay referentially stable across renders.
|
|
723
|
+
const expandedIdsRef = useRef<readonly string[]>(expandedIds);
|
|
724
|
+
expandedIdsRef.current = expandedIds;
|
|
527
725
|
const lastPanelSelectionSignatureRef = useRef(selectedIds.join("\0"));
|
|
528
726
|
const expandedIdSet = useMemo(() => new Set(expandedIds), [expandedIds]);
|
|
529
727
|
const lastSelectionAnchorRef = useRef<string | null>(selectedIds[0] ?? null);
|
|
530
728
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
531
729
|
const rowElementRefs = useRef(new Map<string, HTMLDivElement>());
|
|
730
|
+
// L20: edge auto-scroll during a row drag. scrollContainerRef is the
|
|
731
|
+
// scrollable rows list; autoScrollFrameRef holds the active rAF handle (or
|
|
732
|
+
// null when idle); autoScrollDirectionRef holds the current scroll
|
|
733
|
+
// direction/speed so the rAF loop keeps scrolling smoothly across frames
|
|
734
|
+
// without needing dragover to fire every frame (dragover cadence is
|
|
735
|
+
// browser-throttled and not reliable enough on its own for smooth scroll).
|
|
736
|
+
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
737
|
+
const autoScrollFrameRef = useRef<number | null>(null);
|
|
738
|
+
const autoScrollSpeedRef = useRef(0);
|
|
532
739
|
const [renamingId, setRenamingId] = useState<string | null>(null);
|
|
533
740
|
const [renameDraft, setRenameDraft] = useState("");
|
|
534
741
|
const renameOriginalNameRef = useRef<string>("");
|
|
@@ -570,6 +777,13 @@ export function LayersPanel({
|
|
|
570
777
|
[visibleRows],
|
|
571
778
|
);
|
|
572
779
|
|
|
780
|
+
// Keep the row-facing refs current every render. This runs during render
|
|
781
|
+
// (not an effect) so event handlers created during this same commit already
|
|
782
|
+
// see the latest arrays; it never triggers a re-render itself since only
|
|
783
|
+
// `.current` is written.
|
|
784
|
+
visibleRowsRef.current = visibleRows;
|
|
785
|
+
rootsRef.current = roots;
|
|
786
|
+
|
|
573
787
|
useLayoutEffect(() => {
|
|
574
788
|
selectedIdsRef.current = selectedIds;
|
|
575
789
|
const signature = selectedIds.join("\0");
|
|
@@ -591,18 +805,28 @@ export function LayersPanel({
|
|
|
591
805
|
});
|
|
592
806
|
}, [selectableVisibleIds, selectedIds]);
|
|
593
807
|
|
|
808
|
+
// Auto-expand ancestors of the current selection. This must run only when
|
|
809
|
+
// the SELECTION changes (a new selection signature), not whenever
|
|
810
|
+
// expandedIds changes — otherwise collapsing an ancestor of the selected
|
|
811
|
+
// layer (which changes expandedIds but not the selection) would
|
|
812
|
+
// immediately re-expand it, since selectedAncestorIds still contains it.
|
|
813
|
+
// Track the selection signature we last auto-expanded for in a ref so the
|
|
814
|
+
// effect can bail out on every render triggered purely by a collapse.
|
|
815
|
+
const lastAutoExpandedSelectionRef = useRef<string | null>(null);
|
|
594
816
|
useEffect(() => {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
changed = true;
|
|
602
|
-
}
|
|
817
|
+
const signature = selectedIds.join("\0");
|
|
818
|
+
if (lastAutoExpandedSelectionRef.current === signature) return;
|
|
819
|
+
lastAutoExpandedSelectionRef.current = signature;
|
|
820
|
+
const nextExpanded = nextAutoExpandedIds({
|
|
821
|
+
selectedAncestorIds,
|
|
822
|
+
expandedIds: expandedIdsRef.current,
|
|
603
823
|
});
|
|
604
|
-
if (
|
|
605
|
-
|
|
824
|
+
if (nextExpanded) onExpandedIdsChange(nextExpanded);
|
|
825
|
+
// Intentionally NOT depending on expandedIds: this effect must only react
|
|
826
|
+
// to selection changes (selectedIds / selectedAncestorIds), and reads the
|
|
827
|
+
// current expanded set from expandedIdsRef so a collapse doesn't retrigger it.
|
|
828
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
829
|
+
}, [onExpandedIdsChange, selectedAncestorIds, selectedIds]);
|
|
606
830
|
|
|
607
831
|
const selectedScrollId = selectedIds[selectedIds.length - 1] ?? null;
|
|
608
832
|
const selectedScrollRowKey = useMemo(() => {
|
|
@@ -656,9 +880,17 @@ export function LayersPanel({
|
|
|
656
880
|
if (from >= 0 && to >= 0) {
|
|
657
881
|
const [start, end] = from < to ? [from, to] : [to, from];
|
|
658
882
|
const rangeIds = selectableVisibleIds.slice(start, end + 1);
|
|
659
|
-
|
|
883
|
+
const merged = options.additive
|
|
660
884
|
? Array.from(new Set([...currentSelectedIds, ...rangeIds]))
|
|
661
885
|
: rangeIds;
|
|
886
|
+
// A range that spans an expanded parent and some of its children
|
|
887
|
+
// would otherwise co-select both. Normalize so a selected
|
|
888
|
+
// descendant whose ancestor is also selected gets dropped — the
|
|
889
|
+
// ancestor selection already implies it.
|
|
890
|
+
nextIds = dropDescendantsOfSelectedAncestors(
|
|
891
|
+
merged,
|
|
892
|
+
visibleRowsRef.current,
|
|
893
|
+
);
|
|
662
894
|
} else {
|
|
663
895
|
nextIds = [id];
|
|
664
896
|
}
|
|
@@ -718,6 +950,81 @@ export function LayersPanel({
|
|
|
718
950
|
[],
|
|
719
951
|
);
|
|
720
952
|
|
|
953
|
+
// L12: id of a layer whose rename was started externally (beginRename) and
|
|
954
|
+
// is waiting for its row to become visible/mounted so the input can be
|
|
955
|
+
// focused. Ancestor expansion is asynchronous (it flows out through
|
|
956
|
+
// onExpandedIdsChange and back in via the expandedIds prop), so we can't
|
|
957
|
+
// synchronously focus the input the same tick beginRename runs — the row
|
|
958
|
+
// may not exist in the DOM yet. The effect below watches for the row to
|
|
959
|
+
// appear in rowElementRefs and finishes the job once it does.
|
|
960
|
+
const pendingRenameFocusIdRef = useRef<string | null>(null);
|
|
961
|
+
|
|
962
|
+
const beginRename = useCallback(
|
|
963
|
+
(layerId: string): boolean => {
|
|
964
|
+
if (!onRename) return false;
|
|
965
|
+
const found = findNodeWithAncestors(rootsRef.current, layerId);
|
|
966
|
+
if (!found || found.node.renamable === false) return false;
|
|
967
|
+
const { node, ancestorIds } = found;
|
|
968
|
+
|
|
969
|
+
renameOriginalNameRef.current = node.name;
|
|
970
|
+
setRenamingId(node.id);
|
|
971
|
+
setRenameDraft(node.name);
|
|
972
|
+
|
|
973
|
+
const nextExpanded = nextAutoExpandedIds({
|
|
974
|
+
selectedAncestorIds: ancestorIds,
|
|
975
|
+
expandedIds: expandedIdsRef.current,
|
|
976
|
+
});
|
|
977
|
+
if (nextExpanded) onExpandedIdsChange(nextExpanded);
|
|
978
|
+
|
|
979
|
+
pendingRenameFocusIdRef.current = node.id;
|
|
980
|
+
return true;
|
|
981
|
+
},
|
|
982
|
+
[onExpandedIdsChange, onRename],
|
|
983
|
+
);
|
|
984
|
+
|
|
985
|
+
useImperativeHandle(ref, () => ({ beginRename }), [beginRename]);
|
|
986
|
+
|
|
987
|
+
// Finishes an in-flight beginRename once its row is mounted: scrolls it
|
|
988
|
+
// into view and focuses+selects the rename input (the input already
|
|
989
|
+
// select-on-focuses via its own onFocus handler below). Depends on
|
|
990
|
+
// renamingId and visibleRows so it re-checks whenever either the rename
|
|
991
|
+
// target or ancestor-expansion state changes — the row can become visible
|
|
992
|
+
// either on this same render (already expanded) or a later one (ancestors
|
|
993
|
+
// needed expanding first, which round-trips through onExpandedIdsChange).
|
|
994
|
+
useEffect(() => {
|
|
995
|
+
const pendingId = pendingRenameFocusIdRef.current;
|
|
996
|
+
if (!pendingId || renamingId !== pendingId) return;
|
|
997
|
+
const rowKey = visibleRows.find((row) => row.node.id === pendingId)?.rowKey;
|
|
998
|
+
if (!rowKey) return;
|
|
999
|
+
const frame = window.requestAnimationFrame(() => {
|
|
1000
|
+
const rowElement = rowElementRefs.current.get(rowKey);
|
|
1001
|
+
rowElement?.scrollIntoView({ block: "nearest" });
|
|
1002
|
+
rowElement
|
|
1003
|
+
?.querySelector<HTMLInputElement>("input")
|
|
1004
|
+
?.focus({ preventScroll: true });
|
|
1005
|
+
});
|
|
1006
|
+
pendingRenameFocusIdRef.current = null;
|
|
1007
|
+
return () => window.cancelAnimationFrame(frame);
|
|
1008
|
+
}, [renamingId, visibleRows]);
|
|
1009
|
+
|
|
1010
|
+
// Id-first, stable callbacks for LayerRow. Each reads current
|
|
1011
|
+
// expandedIds/onExpandedIdsChange/onRename from refs/closure-captured
|
|
1012
|
+
// props at call time rather than recreating a fresh per-row closure every
|
|
1013
|
+
// render — this keeps LayerRow's props referentially stable so
|
|
1014
|
+
// React.memo(LayerRow) actually skips re-renders.
|
|
1015
|
+
const handleToggleExpanded = useCallback(
|
|
1016
|
+
(id: string, expanded: boolean) => {
|
|
1017
|
+
onExpandedIdsChange(
|
|
1018
|
+
nextExpandedIds(expandedIdsRef.current, id, expanded),
|
|
1019
|
+
);
|
|
1020
|
+
},
|
|
1021
|
+
[onExpandedIdsChange],
|
|
1022
|
+
);
|
|
1023
|
+
|
|
1024
|
+
const handleCancelRename = useCallback(() => {
|
|
1025
|
+
setRenamingId(null);
|
|
1026
|
+
}, []);
|
|
1027
|
+
|
|
721
1028
|
const hasAnyRows = roots.length > 0;
|
|
722
1029
|
const screenRows = screens ?? files ?? [];
|
|
723
1030
|
const openSearch = useCallback(() => {
|
|
@@ -745,6 +1052,65 @@ export function LayersPanel({
|
|
|
745
1052
|
);
|
|
746
1053
|
}, [collapseTargetId, expandedIds, onExpandedIdsChange]);
|
|
747
1054
|
|
|
1055
|
+
// L20: auto-scroll the rows list while dragging near the top/bottom edge.
|
|
1056
|
+
// Runs a rAF loop so the scroll speed stays smooth and independent of the
|
|
1057
|
+
// browser's dragover event cadence.
|
|
1058
|
+
const AUTO_SCROLL_EDGE_PX = 40;
|
|
1059
|
+
const AUTO_SCROLL_MAX_SPEED_PX = 14;
|
|
1060
|
+
|
|
1061
|
+
const stopAutoScroll = useCallback(() => {
|
|
1062
|
+
if (autoScrollFrameRef.current !== null) {
|
|
1063
|
+
window.cancelAnimationFrame(autoScrollFrameRef.current);
|
|
1064
|
+
autoScrollFrameRef.current = null;
|
|
1065
|
+
}
|
|
1066
|
+
autoScrollSpeedRef.current = 0;
|
|
1067
|
+
}, []);
|
|
1068
|
+
|
|
1069
|
+
const runAutoScrollFrame = useCallback(() => {
|
|
1070
|
+
const container = scrollContainerRef.current;
|
|
1071
|
+
const speed = autoScrollSpeedRef.current;
|
|
1072
|
+
if (!container || speed === 0) {
|
|
1073
|
+
autoScrollFrameRef.current = null;
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
container.scrollTop += speed;
|
|
1077
|
+
autoScrollFrameRef.current =
|
|
1078
|
+
window.requestAnimationFrame(runAutoScrollFrame);
|
|
1079
|
+
}, []);
|
|
1080
|
+
|
|
1081
|
+
const handleRowsDragOver = useCallback(
|
|
1082
|
+
(event: DragEvent<HTMLDivElement>) => {
|
|
1083
|
+
if (!activeDragState) {
|
|
1084
|
+
stopAutoScroll();
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const container = scrollContainerRef.current;
|
|
1088
|
+
if (!container) return;
|
|
1089
|
+
const rect = container.getBoundingClientRect();
|
|
1090
|
+
const offsetFromTop = event.clientY - rect.top;
|
|
1091
|
+
const offsetFromBottom = rect.bottom - event.clientY;
|
|
1092
|
+
let speed = 0;
|
|
1093
|
+
if (offsetFromTop < AUTO_SCROLL_EDGE_PX) {
|
|
1094
|
+
const intensity = 1 - Math.max(0, offsetFromTop) / AUTO_SCROLL_EDGE_PX;
|
|
1095
|
+
speed = -Math.ceil(intensity * AUTO_SCROLL_MAX_SPEED_PX);
|
|
1096
|
+
} else if (offsetFromBottom < AUTO_SCROLL_EDGE_PX) {
|
|
1097
|
+
const intensity =
|
|
1098
|
+
1 - Math.max(0, offsetFromBottom) / AUTO_SCROLL_EDGE_PX;
|
|
1099
|
+
speed = Math.ceil(intensity * AUTO_SCROLL_MAX_SPEED_PX);
|
|
1100
|
+
}
|
|
1101
|
+
autoScrollSpeedRef.current = speed;
|
|
1102
|
+
if (speed !== 0 && autoScrollFrameRef.current === null) {
|
|
1103
|
+
autoScrollFrameRef.current =
|
|
1104
|
+
window.requestAnimationFrame(runAutoScrollFrame);
|
|
1105
|
+
} else if (speed === 0) {
|
|
1106
|
+
stopAutoScroll();
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
[runAutoScrollFrame, stopAutoScroll],
|
|
1110
|
+
);
|
|
1111
|
+
|
|
1112
|
+
useEffect(() => stopAutoScroll, [stopAutoScroll]);
|
|
1113
|
+
|
|
748
1114
|
return (
|
|
749
1115
|
<aside
|
|
750
1116
|
className={cn(
|
|
@@ -866,55 +1232,71 @@ export function LayersPanel({
|
|
|
866
1232
|
</div>
|
|
867
1233
|
) : null}
|
|
868
1234
|
|
|
869
|
-
<div
|
|
1235
|
+
<div
|
|
1236
|
+
ref={scrollContainerRef}
|
|
1237
|
+
className="min-h-0 flex-1 overflow-auto overscroll-contain py-2"
|
|
1238
|
+
onDragOver={handleRowsDragOver}
|
|
1239
|
+
onDrop={stopAutoScroll}
|
|
1240
|
+
onDragEnd={stopAutoScroll}
|
|
1241
|
+
>
|
|
870
1242
|
{visibleRows.length ? (
|
|
871
1243
|
<div
|
|
872
1244
|
className="w-max min-w-full px-2"
|
|
873
1245
|
role="tree"
|
|
874
1246
|
aria-label={labels.title}
|
|
875
1247
|
>
|
|
876
|
-
{visibleRows.map((row) =>
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1248
|
+
{visibleRows.map((row) => {
|
|
1249
|
+
// Per-row primitives computed here (not inside LayerRow) so the
|
|
1250
|
+
// row only receives booleans/strings it needs — no whole-tree
|
|
1251
|
+
// arrays that would force a re-render every time any other
|
|
1252
|
+
// row's selection state changes.
|
|
1253
|
+
const isSelected = selectedIdSet.has(row.node.id);
|
|
1254
|
+
const isInSelectedSubtree = row.ancestorIds.some((id) =>
|
|
1255
|
+
selectedIdSet.has(id),
|
|
1256
|
+
);
|
|
1257
|
+
const isActiveScreen =
|
|
1258
|
+
row.node.id === activeScreenId &&
|
|
1259
|
+
(row.node.type === "file" ||
|
|
1260
|
+
row.node.type === "screen" ||
|
|
1261
|
+
row.node.type === "frame");
|
|
1262
|
+
const isRenaming = renamingId === row.node.id;
|
|
1263
|
+
const activeDropPlacement =
|
|
1264
|
+
dropIndicator?.targetId === row.node.id
|
|
1265
|
+
? dropIndicator.placement
|
|
1266
|
+
: null;
|
|
1267
|
+
return (
|
|
1268
|
+
<LayerRow
|
|
1269
|
+
key={row.rowKey}
|
|
1270
|
+
row={row}
|
|
1271
|
+
labels={labels}
|
|
1272
|
+
isExpanded={expandedIdSet.has(row.node.id)}
|
|
1273
|
+
isSelected={isSelected}
|
|
1274
|
+
isInSelectedSubtree={isInSelectedSubtree}
|
|
1275
|
+
isActiveScreen={isActiveScreen}
|
|
1276
|
+
isRenaming={isRenaming}
|
|
1277
|
+
renameDraft={isRenaming ? renameDraft : ""}
|
|
1278
|
+
registerRowElement={registerRowElement}
|
|
1279
|
+
onRenameDraftChange={setRenameDraft}
|
|
1280
|
+
onCommitRename={commitRename}
|
|
1281
|
+
onCancelRename={handleCancelRename}
|
|
1282
|
+
onStartRename={startRename}
|
|
1283
|
+
onRename={onRename}
|
|
1284
|
+
onSelect={selectNode}
|
|
1285
|
+
onToggleExpanded={handleToggleExpanded}
|
|
1286
|
+
onToggleLocked={onToggleLocked}
|
|
1287
|
+
onToggleHidden={onToggleHidden}
|
|
1288
|
+
onHoverLayer={onHoverLayer}
|
|
1289
|
+
onLeaveLayer={onLeaveLayer}
|
|
1290
|
+
onMoveLayer={onMoveLayer}
|
|
1291
|
+
canMoveLayer={canMoveLayer}
|
|
1292
|
+
activeDropPlacement={activeDropPlacement}
|
|
1293
|
+
onDropIndicatorChange={setDropIndicator}
|
|
1294
|
+
selectedIdsRef={selectedIdsRef}
|
|
1295
|
+
visibleRowsRef={visibleRowsRef}
|
|
1296
|
+
rootsRef={rootsRef}
|
|
1297
|
+
/>
|
|
1298
|
+
);
|
|
1299
|
+
})}
|
|
918
1300
|
</div>
|
|
919
1301
|
) : (
|
|
920
1302
|
<div className="px-3 py-8 text-center !text-[11px] text-muted-foreground">
|
|
@@ -927,34 +1309,16 @@ export function LayersPanel({
|
|
|
927
1309
|
);
|
|
928
1310
|
}
|
|
929
1311
|
|
|
930
|
-
function
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
onRenameDraftChange,
|
|
941
|
-
onCommitRename,
|
|
942
|
-
onCancelRename,
|
|
943
|
-
onStartRename,
|
|
944
|
-
onRename,
|
|
945
|
-
onSelect,
|
|
946
|
-
onToggleExpanded,
|
|
947
|
-
onToggleLocked,
|
|
948
|
-
onToggleHidden,
|
|
949
|
-
onHoverLayer,
|
|
950
|
-
onLeaveLayer,
|
|
951
|
-
onMoveLayer,
|
|
952
|
-
canMoveLayer,
|
|
953
|
-
dropIndicator,
|
|
954
|
-
onDropIndicatorChange,
|
|
955
|
-
selectedIds,
|
|
956
|
-
visibleRows,
|
|
957
|
-
}: {
|
|
1312
|
+
// L12: forwardRef wraps the implementation function, and memo wraps the
|
|
1313
|
+
// forwardRef result — memo(forwardRef(Impl)), the standard composition order.
|
|
1314
|
+
// displayName is set explicitly because forwardRef's returned object doesn't
|
|
1315
|
+
// inherit the inner function's name the way a plain function component would
|
|
1316
|
+
// (React devtools/debugging would otherwise show "ForwardRef").
|
|
1317
|
+
const LayersPanelWithRef = forwardRef(LayersPanelImpl);
|
|
1318
|
+
LayersPanelWithRef.displayName = "LayersPanel";
|
|
1319
|
+
export const LayersPanel = memo(LayersPanelWithRef);
|
|
1320
|
+
|
|
1321
|
+
interface LayerRowProps {
|
|
958
1322
|
row: FlatLayerRow;
|
|
959
1323
|
labels: LayersPanelLabels;
|
|
960
1324
|
isExpanded: boolean;
|
|
@@ -962,11 +1326,11 @@ function LayerRow({
|
|
|
962
1326
|
isInSelectedSubtree: boolean;
|
|
963
1327
|
isActiveScreen: boolean;
|
|
964
1328
|
isRenaming: boolean;
|
|
965
|
-
|
|
1329
|
+
registerRowElement: (rowKey: string, element: HTMLDivElement | null) => void;
|
|
966
1330
|
renameDraft: string;
|
|
967
1331
|
onRenameDraftChange: (value: string) => void;
|
|
968
1332
|
onCommitRename: (id: string) => void;
|
|
969
|
-
onCancelRename: () => void;
|
|
1333
|
+
onCancelRename: (id: string) => void;
|
|
970
1334
|
onStartRename: (node: LayersPanelNode) => void;
|
|
971
1335
|
onRename?: (id: string, name: string) => void;
|
|
972
1336
|
onSelect: (
|
|
@@ -978,39 +1342,108 @@ function LayerRow({
|
|
|
978
1342
|
source: "keyboard" | "pointer";
|
|
979
1343
|
},
|
|
980
1344
|
) => void;
|
|
981
|
-
onToggleExpanded: (expanded: boolean) => void;
|
|
1345
|
+
onToggleExpanded: (id: string, expanded: boolean) => void;
|
|
982
1346
|
onToggleLocked?: (id: string, locked: boolean) => void;
|
|
983
1347
|
onToggleHidden?: (id: string, hidden: boolean) => void;
|
|
984
1348
|
onHoverLayer?: (id: string) => void;
|
|
985
1349
|
onLeaveLayer?: (id: string) => void;
|
|
986
1350
|
onMoveLayer?: (intent: LayersPanelMoveIntent) => void;
|
|
987
1351
|
canMoveLayer?: (intent: LayersPanelMoveIntent) => boolean;
|
|
988
|
-
|
|
1352
|
+
// Only this row's own drop-indicator placement ("before" | "after" |
|
|
1353
|
+
// "inside" | null) — not the whole dropIndicator object, so a dragover on
|
|
1354
|
+
// one row doesn't force every other row to re-render.
|
|
1355
|
+
activeDropPlacement: LayersPanelMoveIntent["placement"] | null;
|
|
989
1356
|
onDropIndicatorChange: (intent: LayersPanelMoveIntent | null) => void;
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
1357
|
+
// Whole-tree state needed for keyboard nav / multi-drag ordering, threaded
|
|
1358
|
+
// through stable refs instead of arrays so it never defeats memo — see the
|
|
1359
|
+
// comment where these refs are created in LayersPanel.
|
|
1360
|
+
selectedIdsRef: RefObject<readonly string[]>;
|
|
1361
|
+
visibleRowsRef: RefObject<FlatLayerRow[]>;
|
|
1362
|
+
// Full (unfiltered) root nodes, used only at drag start to build a
|
|
1363
|
+
// full-tree ancestor map — see buildAncestorIdMap and L13 in the drag-start
|
|
1364
|
+
// handler below.
|
|
1365
|
+
rootsRef: RefObject<LayersPanelNode[]>;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
const LayerRow = memo(function LayerRow({
|
|
1369
|
+
row,
|
|
1370
|
+
labels,
|
|
1371
|
+
isExpanded,
|
|
1372
|
+
isSelected,
|
|
1373
|
+
isInSelectedSubtree,
|
|
1374
|
+
isActiveScreen,
|
|
1375
|
+
isRenaming,
|
|
1376
|
+
registerRowElement,
|
|
1377
|
+
renameDraft,
|
|
1378
|
+
onRenameDraftChange,
|
|
1379
|
+
onCommitRename,
|
|
1380
|
+
onCancelRename,
|
|
1381
|
+
onStartRename,
|
|
1382
|
+
onRename,
|
|
1383
|
+
onSelect,
|
|
1384
|
+
onToggleExpanded,
|
|
1385
|
+
onToggleLocked,
|
|
1386
|
+
onToggleHidden,
|
|
1387
|
+
onHoverLayer,
|
|
1388
|
+
onLeaveLayer,
|
|
1389
|
+
onMoveLayer,
|
|
1390
|
+
canMoveLayer,
|
|
1391
|
+
activeDropPlacement,
|
|
1392
|
+
onDropIndicatorChange,
|
|
1393
|
+
selectedIdsRef,
|
|
1394
|
+
visibleRowsRef,
|
|
1395
|
+
rootsRef,
|
|
1396
|
+
}: LayerRowProps) {
|
|
993
1397
|
const { node, depth, hasChildren, canAcceptChildren } = row;
|
|
994
1398
|
const isComponentLayer = layerNodeIsComponent(node);
|
|
995
1399
|
const selectable = node.selectable !== false;
|
|
996
1400
|
const lockable = node.lockable !== false && Boolean(onToggleLocked);
|
|
997
1401
|
const hideable = node.hideable !== false && Boolean(onToggleHidden);
|
|
998
|
-
|
|
999
|
-
|
|
1402
|
+
// L8: being a valid DRAG SOURCE and being a valid DROP ANCHOR (before/
|
|
1403
|
+
// after/inside target) are different concerns and must not share one gate.
|
|
1404
|
+
// - dragSourceEligible: only "locked" should block picking this row up to
|
|
1405
|
+
// drag it — locked means "don't let me move", not "don't let me be
|
|
1406
|
+
// referenced". Hidden no longer blocks dragging a row: visibility is
|
|
1407
|
+
// orthogonal to whether the layer can be reordered.
|
|
1408
|
+
// - anchorEligible: locked/hidden rows must still be usable as before/
|
|
1409
|
+
// after/inside drop targets so the user can position new layers next to
|
|
1410
|
+
// or inside a locked/hidden one without having to unlock/show it first.
|
|
1411
|
+
// (The corresponding DE:15181 canMoveLayer gate is fixed separately by
|
|
1412
|
+
// the DesignEditor owner; this only removes LP's OWN early-return that
|
|
1413
|
+
// would otherwise block reaching canMoveLayer at all.)
|
|
1414
|
+
const dragSourceEligible = selectable && !node.locked;
|
|
1415
|
+
const anchorEligible = selectable;
|
|
1416
|
+
const draggable = dragSourceEligible && Boolean(onMoveLayer);
|
|
1000
1417
|
const canDropInside = layerCanDropInside(
|
|
1001
1418
|
node,
|
|
1002
1419
|
hasChildren,
|
|
1003
1420
|
canAcceptChildren,
|
|
1004
1421
|
);
|
|
1005
|
-
|
|
1006
|
-
|
|
1422
|
+
// L10: whether this row's bottom hover zone should resolve to "inside"
|
|
1423
|
+
// instead of "after" — see dropPlacementForEvent.
|
|
1424
|
+
const isExpandedWithChildren = hasChildren && isExpanded;
|
|
1425
|
+
const activeDrop = activeDropPlacement;
|
|
1007
1426
|
// Tracks whether the user pressed Escape to cancel rename so that the
|
|
1008
1427
|
// subsequent blur event does not commit the edit.
|
|
1009
1428
|
const renameCancelledRef = useRef(false);
|
|
1429
|
+
// L20: spring-loaded expand. Tracks the pending timer id for "hovering
|
|
1430
|
+
// this collapsed container during a drag" so a sustained hover expands it
|
|
1431
|
+
// (Figma-style) without requiring the user to drop and re-drag. Cleared on
|
|
1432
|
+
// drag-leave/drop/dragend and whenever the hover target/placement changes.
|
|
1433
|
+
const springLoadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
1434
|
+
const SPRING_LOAD_DELAY_MS = 600;
|
|
1435
|
+
const clearSpringLoadTimer = () => {
|
|
1436
|
+
if (springLoadTimerRef.current !== null) {
|
|
1437
|
+
clearTimeout(springLoadTimerRef.current);
|
|
1438
|
+
springLoadTimerRef.current = null;
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
useEffect(() => clearSpringLoadTimer, []);
|
|
1010
1442
|
|
|
1011
1443
|
const readSelectedIdsFromTree = (target: HTMLElement): string[] => {
|
|
1012
1444
|
const tree = target.closest('[role="tree"]');
|
|
1013
|
-
if (!tree)
|
|
1445
|
+
if (!tree)
|
|
1446
|
+
return selectedIdsRef.current.filter((id) => !id.startsWith("__"));
|
|
1014
1447
|
return Array.from(
|
|
1015
1448
|
tree.querySelectorAll<HTMLElement>(
|
|
1016
1449
|
'[role="treeitem"][aria-selected="true"] [data-layer-row-button][data-layer-node-id]',
|
|
@@ -1061,7 +1494,7 @@ function LayerRow({
|
|
|
1061
1494
|
target.focus();
|
|
1062
1495
|
return true;
|
|
1063
1496
|
};
|
|
1064
|
-
const currentIndex =
|
|
1497
|
+
const currentIndex = visibleRowsRef.current.findIndex(
|
|
1065
1498
|
(visibleRow) => visibleRow.rowKey === row.rowKey,
|
|
1066
1499
|
);
|
|
1067
1500
|
const focusableButtons = getFocusableButtons();
|
|
@@ -1105,7 +1538,7 @@ function LayerRow({
|
|
|
1105
1538
|
}
|
|
1106
1539
|
if (event.key === "ArrowRight" && hasChildren && !isExpanded) {
|
|
1107
1540
|
event.preventDefault();
|
|
1108
|
-
onToggleExpanded(true);
|
|
1541
|
+
onToggleExpanded(node.id, true);
|
|
1109
1542
|
return;
|
|
1110
1543
|
}
|
|
1111
1544
|
if (event.key === "ArrowRight" && hasChildren && isExpanded) {
|
|
@@ -1113,7 +1546,9 @@ function LayerRow({
|
|
|
1113
1546
|
const nextButton = focusableButtons[currentFocusableIndex + 1];
|
|
1114
1547
|
const childId = nextButton?.dataset.layerNodeId;
|
|
1115
1548
|
const child = childId
|
|
1116
|
-
?
|
|
1549
|
+
? visibleRowsRef.current.find(
|
|
1550
|
+
(visibleRow) => visibleRow.node.id === childId,
|
|
1551
|
+
)
|
|
1117
1552
|
: null;
|
|
1118
1553
|
if (child?.ancestorIds.includes(node.id)) {
|
|
1119
1554
|
focusVisibleButton(currentFocusableIndex + 1);
|
|
@@ -1122,7 +1557,7 @@ function LayerRow({
|
|
|
1122
1557
|
}
|
|
1123
1558
|
if (event.key === "ArrowLeft" && hasChildren && isExpanded) {
|
|
1124
1559
|
event.preventDefault();
|
|
1125
|
-
onToggleExpanded(false);
|
|
1560
|
+
onToggleExpanded(node.id, false);
|
|
1126
1561
|
return;
|
|
1127
1562
|
}
|
|
1128
1563
|
if (event.key === "ArrowLeft" && row.ancestorIds.length > 0) {
|
|
@@ -1137,10 +1572,18 @@ function LayerRow({
|
|
|
1137
1572
|
event.preventDefault();
|
|
1138
1573
|
return;
|
|
1139
1574
|
}
|
|
1575
|
+
// Build the full-tree ancestor map once, here at drag start, rather than
|
|
1576
|
+
// on every render — this is O(tree size) so it must not run per-row per
|
|
1577
|
+
// render. It uses the FULL root tree (not visibleRows) so a selected
|
|
1578
|
+
// descendant nested inside a currently-collapsed dragged ancestor is
|
|
1579
|
+
// still correctly recognized as a descendant and excluded from the drag
|
|
1580
|
+
// payload (see L13 / buildAncestorIdMap).
|
|
1581
|
+
const ancestorIdMap = buildAncestorIdMap(rootsRef.current);
|
|
1140
1582
|
const draggedIds = getDraggedLayerIdsForRows({
|
|
1141
|
-
selectedIds,
|
|
1583
|
+
selectedIds: selectedIdsRef.current,
|
|
1142
1584
|
nodeId: node.id,
|
|
1143
|
-
visibleRows,
|
|
1585
|
+
visibleRows: visibleRowsRef.current,
|
|
1586
|
+
ancestorIdMap,
|
|
1144
1587
|
});
|
|
1145
1588
|
event.dataTransfer.effectAllowed = "move";
|
|
1146
1589
|
event.dataTransfer.setData("application/x-design-layer-id", node.id);
|
|
@@ -1153,31 +1596,81 @@ function LayerRow({
|
|
|
1153
1596
|
activeDragState = { sourceId: node.id, draggedIds };
|
|
1154
1597
|
};
|
|
1155
1598
|
|
|
1599
|
+
// A dragover that is rejected (any early-return path below) must clear a
|
|
1600
|
+
// stale indicator that a PRIOR dragover on this same row left behind — e.g.
|
|
1601
|
+
// hovering near the row edge changes the placement from "inside" to
|
|
1602
|
+
// "before", which canMoveLayer may now reject even though the previous
|
|
1603
|
+
// placement was accepted. Without this, the indicator line/ring lingers on
|
|
1604
|
+
// a row that no longer has a valid drop target.
|
|
1605
|
+
const clearStaleIndicatorForThisRow = () => {
|
|
1606
|
+
if (activeDropIntent?.targetId === node.id) activeDropIntent = null;
|
|
1607
|
+
if (activeDropPlacement !== null) onDropIndicatorChange(null);
|
|
1608
|
+
clearSpringLoadTimer();
|
|
1609
|
+
};
|
|
1610
|
+
|
|
1156
1611
|
const handleDragOver = (event: DragEvent<HTMLDivElement>) => {
|
|
1157
|
-
if (!onMoveLayer || !
|
|
1612
|
+
if (!onMoveLayer || !anchorEligible) {
|
|
1613
|
+
clearStaleIndicatorForThisRow();
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1158
1616
|
// dataTransfer.getData() always returns "" during dragover per spec.
|
|
1159
1617
|
// Read from the module-level activeDragState set in handleDragStart instead.
|
|
1160
|
-
if (!activeDragState)
|
|
1618
|
+
if (!activeDragState) {
|
|
1619
|
+
clearStaleIndicatorForThisRow();
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1161
1622
|
const { sourceId, draggedIds } = activeDragState;
|
|
1162
|
-
if (sourceId === node.id)
|
|
1623
|
+
if (sourceId === node.id) {
|
|
1624
|
+
clearStaleIndicatorForThisRow();
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1163
1627
|
const cleanedIds = draggedIds.filter(
|
|
1164
1628
|
(id) => id && id !== node.id && !id.startsWith("__"),
|
|
1165
1629
|
);
|
|
1166
|
-
if (cleanedIds.length === 0)
|
|
1630
|
+
if (cleanedIds.length === 0) {
|
|
1631
|
+
clearStaleIndicatorForThisRow();
|
|
1632
|
+
return;
|
|
1633
|
+
}
|
|
1167
1634
|
const intent = {
|
|
1168
1635
|
draggedIds: cleanedIds,
|
|
1169
1636
|
targetId: node.id,
|
|
1170
|
-
placement: dropPlacementForEvent(
|
|
1637
|
+
placement: dropPlacementForEvent(
|
|
1638
|
+
event,
|
|
1639
|
+
canDropInside,
|
|
1640
|
+
isExpandedWithChildren,
|
|
1641
|
+
),
|
|
1171
1642
|
} satisfies LayersPanelMoveIntent;
|
|
1172
|
-
if (canMoveLayer && !canMoveLayer(intent))
|
|
1643
|
+
if (canMoveLayer && !canMoveLayer(intent)) {
|
|
1644
|
+
clearStaleIndicatorForThisRow();
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1173
1647
|
event.preventDefault();
|
|
1174
1648
|
event.dataTransfer.dropEffect = "move";
|
|
1175
1649
|
activeDropIntent = intent;
|
|
1176
1650
|
onDropIndicatorChange(intent);
|
|
1651
|
+
|
|
1652
|
+
// L20 spring-loaded expand: sustained "inside" hover over a collapsed
|
|
1653
|
+
// container expands it after a delay so the user can drop into nested
|
|
1654
|
+
// children without a separate drop-then-redrag step. Only arm the timer
|
|
1655
|
+
// once per qualifying hover — if a timer is already pending for this row
|
|
1656
|
+
// we leave it running rather than resetting it on every dragover tick.
|
|
1657
|
+
if (
|
|
1658
|
+
intent.placement === "inside" &&
|
|
1659
|
+
hasChildren &&
|
|
1660
|
+
!isExpanded &&
|
|
1661
|
+
springLoadTimerRef.current === null
|
|
1662
|
+
) {
|
|
1663
|
+
springLoadTimerRef.current = setTimeout(() => {
|
|
1664
|
+
springLoadTimerRef.current = null;
|
|
1665
|
+
onToggleExpanded(node.id, true);
|
|
1666
|
+
}, SPRING_LOAD_DELAY_MS);
|
|
1667
|
+
} else if (intent.placement !== "inside" || !hasChildren || isExpanded) {
|
|
1668
|
+
clearSpringLoadTimer();
|
|
1669
|
+
}
|
|
1177
1670
|
};
|
|
1178
1671
|
|
|
1179
1672
|
const handleDrop = (event: DragEvent<HTMLDivElement>) => {
|
|
1180
|
-
if (!onMoveLayer || !
|
|
1673
|
+
if (!onMoveLayer || !anchorEligible) {
|
|
1181
1674
|
onDropIndicatorChange(null);
|
|
1182
1675
|
return;
|
|
1183
1676
|
}
|
|
@@ -1216,7 +1709,11 @@ function LayerRow({
|
|
|
1216
1709
|
: ({
|
|
1217
1710
|
draggedIds: cleanedIds,
|
|
1218
1711
|
targetId: node.id,
|
|
1219
|
-
placement: dropPlacementForEvent(
|
|
1712
|
+
placement: dropPlacementForEvent(
|
|
1713
|
+
event,
|
|
1714
|
+
canDropInside,
|
|
1715
|
+
isExpandedWithChildren,
|
|
1716
|
+
),
|
|
1220
1717
|
} satisfies LayersPanelMoveIntent);
|
|
1221
1718
|
if (!canMoveLayer || canMoveLayer(intent)) {
|
|
1222
1719
|
onMoveLayer(intent);
|
|
@@ -1224,6 +1721,7 @@ function LayerRow({
|
|
|
1224
1721
|
}
|
|
1225
1722
|
activeDropIntent = null;
|
|
1226
1723
|
onDropIndicatorChange(null);
|
|
1724
|
+
clearSpringLoadTimer();
|
|
1227
1725
|
};
|
|
1228
1726
|
|
|
1229
1727
|
const showContextMenu =
|
|
@@ -1234,7 +1732,7 @@ function LayerRow({
|
|
|
1234
1732
|
<ContextMenu>
|
|
1235
1733
|
<ContextMenuTrigger asChild disabled={!showContextMenu}>
|
|
1236
1734
|
<div
|
|
1237
|
-
ref={
|
|
1735
|
+
ref={(element) => registerRowElement(row.rowKey, element)}
|
|
1238
1736
|
role="treeitem"
|
|
1239
1737
|
aria-expanded={hasChildren ? isExpanded : undefined}
|
|
1240
1738
|
aria-level={depth + 1}
|
|
@@ -1253,12 +1751,14 @@ function LayerRow({
|
|
|
1253
1751
|
return;
|
|
1254
1752
|
if (activeDropIntent?.targetId === node.id) activeDropIntent = null;
|
|
1255
1753
|
onDropIndicatorChange(null);
|
|
1754
|
+
clearSpringLoadTimer();
|
|
1256
1755
|
}}
|
|
1257
1756
|
onDrop={handleDrop}
|
|
1258
1757
|
onDragEnd={() => {
|
|
1259
1758
|
activeDragState = null;
|
|
1260
1759
|
activeDropIntent = null;
|
|
1261
1760
|
onDropIndicatorChange(null);
|
|
1761
|
+
clearSpringLoadTimer();
|
|
1262
1762
|
}}
|
|
1263
1763
|
onMouseEnter={() => onHoverLayer?.(node.id)}
|
|
1264
1764
|
onMouseLeave={() => onLeaveLayer?.(node.id)}
|
|
@@ -1303,7 +1803,7 @@ function LayerRow({
|
|
|
1303
1803
|
aria-label={isExpanded ? labels.collapse : labels.expand}
|
|
1304
1804
|
onClick={(event) => {
|
|
1305
1805
|
event.stopPropagation();
|
|
1306
|
-
onToggleExpanded(!isExpanded);
|
|
1806
|
+
onToggleExpanded(node.id, !isExpanded);
|
|
1307
1807
|
}}
|
|
1308
1808
|
>
|
|
1309
1809
|
{isExpanded ? (
|
|
@@ -1345,6 +1845,7 @@ function LayerRow({
|
|
|
1345
1845
|
value={renameDraft}
|
|
1346
1846
|
onClick={(event) => event.stopPropagation()}
|
|
1347
1847
|
onChange={(event) => onRenameDraftChange(event.target.value)}
|
|
1848
|
+
onFocus={(event) => event.currentTarget.select()}
|
|
1348
1849
|
onBlur={() => {
|
|
1349
1850
|
// Escape sets renameCancelledRef before blur fires; skip commit.
|
|
1350
1851
|
if (renameCancelledRef.current) {
|
|
@@ -1373,7 +1874,7 @@ function LayerRow({
|
|
|
1373
1874
|
event.preventDefault();
|
|
1374
1875
|
event.stopPropagation();
|
|
1375
1876
|
renameCancelledRef.current = true;
|
|
1376
|
-
onCancelRename();
|
|
1877
|
+
onCancelRename(node.id);
|
|
1377
1878
|
}
|
|
1378
1879
|
}}
|
|
1379
1880
|
className="h-6 min-w-0 flex-1 rounded-[4px] border border-[var(--design-editor-accent-color)] bg-[var(--design-editor-panel-bg)] px-1.5 text-[12px] text-foreground outline-none"
|
|
@@ -1510,7 +2011,7 @@ function LayerRow({
|
|
|
1510
2011
|
) : null}
|
|
1511
2012
|
</ContextMenu>
|
|
1512
2013
|
);
|
|
1513
|
-
}
|
|
2014
|
+
});
|
|
1514
2015
|
|
|
1515
2016
|
function IconTooltipButton({
|
|
1516
2017
|
label,
|
|
@@ -2019,13 +2520,29 @@ function layerCanDropInside(
|
|
|
2019
2520
|
);
|
|
2020
2521
|
}
|
|
2021
2522
|
|
|
2022
|
-
|
|
2523
|
+
/**
|
|
2524
|
+
* L10: for an EXPANDED container that already has children, the bottom hover
|
|
2525
|
+
* zone sits (visually, in the panel) directly above that container's
|
|
2526
|
+
* first-listed child row — not above its next sibling. A plain "after"
|
|
2527
|
+
* placement there would target the position following the container's
|
|
2528
|
+
* ENTIRE subtree (anchor.end in applyMoveNodeEdit), which visually
|
|
2529
|
+
* contradicts the indicator's position between the container and its first
|
|
2530
|
+
* child. Resolve that zone to "inside" instead (still targeting the
|
|
2531
|
+
* container), which inserts at contentEnd — under the L5 reversed-order
|
|
2532
|
+
* convention that is exactly the container's first-panel-row / topmost-paint
|
|
2533
|
+
* child slot, matching what the indicator visually promises.
|
|
2534
|
+
*/
|
|
2535
|
+
export function dropPlacementForEvent(
|
|
2023
2536
|
event: DragEvent<HTMLDivElement>,
|
|
2024
2537
|
canDropInside: boolean,
|
|
2538
|
+
isExpandedWithChildren = false,
|
|
2025
2539
|
): LayersPanelMoveIntent["placement"] {
|
|
2026
2540
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
2027
2541
|
const offset = event.clientY - rect.top;
|
|
2028
2542
|
if (offset < rect.height * 0.3) return "before";
|
|
2029
|
-
if (offset > rect.height * 0.7)
|
|
2543
|
+
if (offset > rect.height * 0.7) {
|
|
2544
|
+
if (canDropInside && isExpandedWithChildren) return "inside";
|
|
2545
|
+
return "after";
|
|
2546
|
+
}
|
|
2030
2547
|
return canDropInside ? "inside" : "after";
|
|
2031
2548
|
}
|