@agent-native/core 0.88.0 → 0.90.2
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 +96 -2
- package/corpus/core/docs/content/agent-native-toolkit.mdx +87 -146
- package/corpus/core/docs/content/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/components.mdx +16 -4
- package/corpus/core/docs/content/data-programs.mdx +329 -0
- package/corpus/core/docs/content/deployment.mdx +9 -9
- package/corpus/core/docs/content/extensions.mdx +8 -7
- package/corpus/core/docs/content/generative-ui.mdx +5 -3
- package/corpus/core/docs/content/key-concepts.mdx +6 -9
- package/corpus/core/docs/content/locales/ar-SA/agent-native-toolkit.mdx +100 -70
- package/corpus/core/docs/content/locales/ar-SA/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/ar-SA/components.mdx +12 -2
- package/corpus/core/docs/content/locales/ar-SA/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/ar-SA/deployment.mdx +8 -9
- package/corpus/core/docs/content/locales/ar-SA/extensions.mdx +6 -4
- package/corpus/core/docs/content/locales/ar-SA/key-concepts.mdx +7 -9
- package/corpus/core/docs/content/locales/ar-SA/real-time-collaboration.mdx +9 -10
- package/corpus/core/docs/content/locales/de-DE/agent-native-toolkit.mdx +100 -74
- package/corpus/core/docs/content/locales/de-DE/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/de-DE/components.mdx +12 -2
- package/corpus/core/docs/content/locales/de-DE/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/de-DE/deployment.mdx +8 -9
- package/corpus/core/docs/content/locales/de-DE/extensions.mdx +6 -4
- package/corpus/core/docs/content/locales/de-DE/key-concepts.mdx +8 -10
- package/corpus/core/docs/content/locales/de-DE/real-time-collaboration.mdx +9 -10
- package/corpus/core/docs/content/locales/es-ES/agent-native-toolkit.mdx +100 -74
- package/corpus/core/docs/content/locales/es-ES/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/es-ES/components.mdx +12 -2
- package/corpus/core/docs/content/locales/es-ES/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/es-ES/deployment.mdx +8 -9
- package/corpus/core/docs/content/locales/es-ES/extensions.mdx +6 -4
- package/corpus/core/docs/content/locales/es-ES/key-concepts.mdx +8 -10
- package/corpus/core/docs/content/locales/es-ES/real-time-collaboration.mdx +9 -10
- package/corpus/core/docs/content/locales/fr-FR/agent-native-toolkit.mdx +100 -76
- package/corpus/core/docs/content/locales/fr-FR/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/fr-FR/components.mdx +12 -2
- package/corpus/core/docs/content/locales/fr-FR/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/fr-FR/deployment.mdx +8 -9
- package/corpus/core/docs/content/locales/fr-FR/extensions.mdx +6 -4
- package/corpus/core/docs/content/locales/fr-FR/key-concepts.mdx +8 -10
- package/corpus/core/docs/content/locales/fr-FR/real-time-collaboration.mdx +9 -10
- package/corpus/core/docs/content/locales/hi-IN/agent-native-toolkit.mdx +100 -71
- package/corpus/core/docs/content/locales/hi-IN/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/hi-IN/components.mdx +20 -10
- package/corpus/core/docs/content/locales/hi-IN/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/hi-IN/deployment.mdx +8 -9
- package/corpus/core/docs/content/locales/hi-IN/extensions.mdx +6 -4
- package/corpus/core/docs/content/locales/hi-IN/key-concepts.mdx +8 -10
- package/corpus/core/docs/content/locales/hi-IN/real-time-collaboration.mdx +9 -10
- package/corpus/core/docs/content/locales/ja-JP/agent-native-toolkit.mdx +100 -64
- package/corpus/core/docs/content/locales/ja-JP/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/ja-JP/components.mdx +12 -2
- package/corpus/core/docs/content/locales/ja-JP/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/ja-JP/deployment.mdx +8 -9
- package/corpus/core/docs/content/locales/ja-JP/extensions.mdx +6 -4
- package/corpus/core/docs/content/locales/ja-JP/key-concepts.mdx +9 -11
- package/corpus/core/docs/content/locales/ja-JP/real-time-collaboration.mdx +9 -10
- package/corpus/core/docs/content/locales/ko-KR/agent-native-toolkit.mdx +100 -60
- package/corpus/core/docs/content/locales/ko-KR/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/ko-KR/components.mdx +20 -10
- package/corpus/core/docs/content/locales/ko-KR/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/ko-KR/deployment.mdx +8 -9
- package/corpus/core/docs/content/locales/ko-KR/extensions.mdx +16 -14
- package/corpus/core/docs/content/locales/ko-KR/key-concepts.mdx +9 -11
- package/corpus/core/docs/content/locales/ko-KR/real-time-collaboration.mdx +9 -10
- package/corpus/core/docs/content/locales/pt-BR/agent-native-toolkit.mdx +100 -74
- package/corpus/core/docs/content/locales/pt-BR/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/pt-BR/components.mdx +12 -2
- package/corpus/core/docs/content/locales/pt-BR/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/pt-BR/deployment.mdx +8 -9
- package/corpus/core/docs/content/locales/pt-BR/extensions.mdx +6 -4
- package/corpus/core/docs/content/locales/pt-BR/key-concepts.mdx +8 -11
- package/corpus/core/docs/content/locales/pt-BR/real-time-collaboration.mdx +9 -10
- package/corpus/core/docs/content/locales/zh-CN/agent-native-toolkit.mdx +100 -58
- package/corpus/core/docs/content/locales/zh-CN/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/zh-CN/components.mdx +20 -10
- package/corpus/core/docs/content/locales/zh-CN/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/zh-CN/deployment.mdx +7 -9
- package/corpus/core/docs/content/locales/zh-CN/extensions.mdx +16 -14
- package/corpus/core/docs/content/locales/zh-CN/key-concepts.mdx +7 -11
- package/corpus/core/docs/content/locales/zh-CN/real-time-collaboration.mdx +10 -11
- package/corpus/core/docs/content/locales/zh-TW/agent-native-toolkit.mdx +100 -58
- package/corpus/core/docs/content/locales/zh-TW/cloneable-saas.mdx +19 -19
- package/corpus/core/docs/content/locales/zh-TW/components.mdx +20 -10
- package/corpus/core/docs/content/locales/zh-TW/data-programs.mdx +329 -0
- package/corpus/core/docs/content/locales/zh-TW/deployment.mdx +7 -9
- package/corpus/core/docs/content/locales/zh-TW/extensions.mdx +16 -14
- package/corpus/core/docs/content/locales/zh-TW/key-concepts.mdx +7 -11
- package/corpus/core/docs/content/locales/zh-TW/real-time-collaboration.mdx +10 -11
- package/corpus/core/docs/content/real-time-collaboration.mdx +10 -11
- package/corpus/core/docs/content/toolkit-app-adapters.mdx +46 -0
- package/corpus/core/docs/content/toolkit-collaboration-ui.mdx +48 -0
- package/corpus/core/docs/content/toolkit-sharing-ui.mdx +33 -0
- package/corpus/core/docs/content/toolkit-shell-hooks.mdx +27 -0
- package/corpus/core/docs/content/toolkit-ui.mdx +69 -0
- package/corpus/core/package.json +5 -1
- package/corpus/core/src/agent/durable-background.ts +53 -0
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +7 -1
- package/corpus/core/src/agent/engine/anthropic-engine.ts +29 -1
- package/corpus/core/src/agent/engine/builder-engine.ts +1 -0
- package/corpus/core/src/agent/engine/output-tokens.ts +26 -13
- package/corpus/core/src/agent/engine/registry.ts +45 -26
- package/corpus/core/src/agent/engine/translate-ai-sdk.ts +56 -7
- package/corpus/core/src/agent/engine/translate-anthropic.ts +43 -1
- package/corpus/core/src/agent/engine/types.ts +16 -0
- package/corpus/core/src/agent/model-config.ts +100 -0
- package/corpus/core/src/agent/production-agent.ts +492 -241
- package/corpus/core/src/agent/runtime-context.ts +50 -12
- package/corpus/core/src/agent/tool-result-images.ts +178 -0
- package/corpus/core/src/brand-kit/fig/index.ts +8 -4
- package/corpus/core/src/client/AgentPanel.tsx +89 -32
- package/corpus/core/src/client/AssistantChat.tsx +95 -46
- package/corpus/core/src/client/KeepTabOpenNotice.tsx +176 -0
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +7 -0
- package/corpus/core/src/client/RunStuckBanner.tsx +13 -1
- package/corpus/core/src/client/chat/run-recovery.tsx +7 -1
- package/corpus/core/src/client/clipboard.ts +17 -13
- package/corpus/core/src/client/components/AgentPresenceChip.tsx +4 -69
- package/corpus/core/src/client/components/LiveCursorOverlay.tsx +5 -240
- package/corpus/core/src/client/components/PresenceBar.tsx +4 -346
- package/corpus/core/src/client/components/RecentEditHighlights.tsx +4 -219
- package/corpus/core/src/client/components/RemoteSelectionRings.tsx +5 -220
- package/corpus/core/src/client/composer/TiptapComposer.tsx +94 -38
- package/corpus/core/src/client/db-admin/SqlEditor.tsx +13 -3
- package/corpus/core/src/client/dynamic-suggestions.ts +6 -1
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +0 -14
- package/corpus/core/src/client/extensions/ExtensionsListPage.tsx +29 -30
- package/corpus/core/src/client/extensions/ExtensionsSidebarSection.tsx +2 -1
- package/corpus/core/src/client/host-bridge.ts +29 -8
- package/corpus/core/src/client/i18n.tsx +7 -1
- package/corpus/core/src/client/index.ts +5 -0
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +34 -69
- package/corpus/core/src/client/rich-markdown-editor/SharedRichEditor.tsx +4 -0
- package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
- package/corpus/core/src/client/settings/SettingsPanel.tsx +409 -230
- package/corpus/core/src/client/settings/SettingsTabsPage.tsx +5 -7
- package/corpus/core/src/client/settings/index.ts +5 -1
- package/corpus/core/src/client/sharing/VisibilityBadge.tsx +4 -37
- package/corpus/core/src/client/use-action.ts +39 -2
- package/corpus/core/src/client/use-db-sync.ts +44 -18
- package/corpus/core/src/client/use-run-stuck-detection.ts +10 -1
- package/corpus/core/src/coding-tools/run-code.ts +473 -74
- package/corpus/core/src/coding-tools/sandbox/background.ts +406 -0
- package/corpus/core/src/coding-tools/sandbox/executions-store.ts +517 -0
- package/corpus/core/src/coding-tools/sandbox/index.ts +53 -13
- package/corpus/core/src/coding-tools/sandbox/local-child-process-adapter.ts +61 -4
- package/corpus/core/src/collab/client.ts +870 -562
- package/corpus/core/src/collab/presence.ts +12 -23
- package/corpus/core/src/collab/recent-edits.ts +13 -32
- package/corpus/core/src/data-programs/actions.ts +335 -0
- package/corpus/core/src/data-programs/contract.ts +284 -0
- package/corpus/core/src/data-programs/execute.ts +693 -0
- package/corpus/core/src/data-programs/index.ts +72 -0
- package/corpus/core/src/data-programs/schema.ts +148 -0
- package/corpus/core/src/data-programs/store.ts +755 -0
- package/corpus/core/src/db/create-get-db.ts +32 -2
- package/corpus/core/src/deploy/build.ts +34 -0
- package/corpus/core/src/extensions/actions.ts +2 -2
- package/corpus/core/src/file-upload/builder.ts +11 -0
- package/corpus/core/src/integrations/webhook-handler.ts +27 -8
- package/corpus/core/src/local-artifacts/index.ts +1 -1
- package/corpus/core/src/localization/default-messages.ts +1 -2
- package/corpus/core/src/mcp-client/index.ts +29 -0
- package/corpus/core/src/onboarding/default-steps.ts +5 -2
- package/corpus/core/src/resources/store.ts +34 -3
- package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +4 -8
- package/corpus/core/src/scripts/agent-engines/test-agent-engine.ts +9 -2
- package/corpus/core/src/scripts/db/tool-mode.ts +2 -2
- package/corpus/core/src/scripts/resources/migrate-learnings.ts +9 -13
- package/corpus/core/src/server/agent-chat-plugin.ts +154 -62
- package/corpus/core/src/server/agent-discovery.ts +67 -7
- package/corpus/core/src/server/auth.ts +90 -3
- package/corpus/core/src/server/builder-design-systems.ts +2 -2
- package/corpus/core/src/server/core-routes-plugin.ts +119 -22
- package/corpus/core/src/server/credential-provider.ts +28 -8
- package/corpus/core/src/server/csrf.ts +3 -0
- package/corpus/core/src/server/framework-request-handler.ts +52 -4
- package/corpus/core/src/server/oauth-helpers.ts +11 -1
- package/corpus/core/src/server/onboarding-html.ts +80 -48
- package/corpus/core/src/server/prompts/shared-rules.ts +2 -2
- package/corpus/core/src/server/transcribe-voice.ts +93 -9
- package/corpus/core/src/styles/agent-native.css +57 -45
- package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/core/src/templates/default/AGENTS.md +11 -14
- package/corpus/core/src/templates/default/actions/navigate.ts +1 -1
- package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/de-DE.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/en-US.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/es-ES.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +0 -1
- package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +0 -1
- package/corpus/core/src/templates/default/app/routes/settings.tsx +22 -33
- package/corpus/core/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +20 -7
- package/corpus/core/src/templates/headless/AGENTS.md +9 -6
- package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/data-programs/SKILL.md +128 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +10 -4
- package/corpus/core/src/vite/agents-bundle-plugin.ts +26 -1
- package/corpus/core/src/vite/client.ts +12 -0
- package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +8 -0
- package/corpus/templates/analytics/.agents/skills/data-programs/SKILL.md +348 -0
- package/corpus/templates/analytics/.agents/skills/provider-api/SKILL.md +10 -0
- package/corpus/templates/analytics/AGENTS.md +31 -32
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
- package/corpus/templates/analytics/actions/delete-data-source-credentials.ts +32 -0
- package/corpus/templates/analytics/actions/ensure-risk-meeting-dashboard.ts +247 -0
- package/corpus/templates/analytics/actions/update-data-source-credentials.ts +123 -0
- package/corpus/templates/analytics/app/components/InlineMetric.tsx +2 -2
- package/corpus/templates/analytics/app/components/dashboard/CumulativeNetChart.tsx +2 -7
- package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +11 -16
- package/corpus/templates/analytics/app/components/dashboard/DateRangePicker.tsx +1 -2
- package/corpus/templates/analytics/app/components/dashboard/MetricCard.tsx +2 -7
- package/corpus/templates/analytics/app/components/dashboard/RecentActivity.tsx +3 -6
- package/corpus/templates/analytics/app/components/dashboard/RevenueChart.tsx +1 -6
- package/corpus/templates/analytics/app/components/dashboard/RevenueComparisonChart.tsx +2 -7
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +11 -15
- package/corpus/templates/analytics/app/components/dashboard/StatsCard.tsx +1 -7
- package/corpus/templates/analytics/app/components/dashboard/TimeSeriesChart.tsx +2 -7
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +16 -16
- package/corpus/templates/analytics/app/components/layout/Header.tsx +2 -2
- package/corpus/templates/analytics/app/components/layout/HeaderActions.tsx +2 -1
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -2
- package/corpus/templates/analytics/app/components/layout/MobileNav.tsx +33 -17
- package/corpus/templates/analytics/app/components/layout/NewAnalysisDialog.tsx +6 -6
- package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +4 -3
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +119 -68
- package/corpus/templates/analytics/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/alert.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/avatar.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/button.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/calendar.tsx +1 -1
- package/corpus/templates/analytics/app/components/ui/card.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/collapsible.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/command.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/date-picker.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/input.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/label.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/progress.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/select.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/sonner.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/table.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/tabs.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/toaster.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/toggle-group.tsx +1 -0
- package/corpus/templates/analytics/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/analytics/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/analytics/app/global.css +26 -3
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +27 -4
- package/corpus/templates/analytics/app/i18n-data.ts +277 -42
- package/corpus/templates/analytics/app/pages/Agents.tsx +19 -19
- package/corpus/templates/analytics/app/pages/DataDictionary.tsx +21 -21
- package/corpus/templates/analytics/app/pages/DataSources.tsx +31 -54
- package/corpus/templates/analytics/app/pages/NotFound.tsx +2 -1
- package/corpus/templates/analytics/app/pages/Placeholder.tsx +2 -1
- package/corpus/templates/analytics/app/pages/Settings.tsx +19 -37
- package/corpus/templates/analytics/app/pages/adhoc/DashboardSkeleton.tsx +3 -2
- package/corpus/templates/analytics/app/pages/adhoc/_shared/KpiChart.tsx +8 -13
- package/corpus/templates/analytics/app/pages/adhoc/_shared/components/DateRangeInput.tsx +1 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/ChartTypePicker.tsx +2 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/DateRangePicker.tsx +1 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventCombobox.tsx +7 -7
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventPanel.tsx +2 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/EventRow.tsx +2 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/ExplorerChart.tsx +3 -8
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/FilterBuilder.tsx +5 -4
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/GroupByPicker.tsx +2 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +7 -7
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +7 -7
- package/corpus/templates/analytics/app/pages/adhoc/explorer/components/SqlPreview.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/explorer/index.tsx +16 -16
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/ChartCard.tsx +16 -21
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +32 -32
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +14 -14
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/EmailReportDialog.tsx +14 -13
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +200 -52
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +23 -28
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewSqlPopover.tsx +13 -12
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +15 -15
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +32 -36
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -1
- package/corpus/templates/analytics/app/pages/analyses/AnalysesList.tsx +8 -7
- package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +19 -19
- package/corpus/templates/analytics/app/pages/analyses/LegacyFusionAnalysis.tsx +11 -15
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +14 -14
- package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +7 -12
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +12 -12
- package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +25 -25
- package/corpus/templates/analytics/app/root.tsx +22 -19
- package/corpus/templates/analytics/app/routes/catalog.tsx +8 -8
- package/corpus/templates/analytics/changelog/2026-07-06-dashboard-lists-and-headers-stay-compact.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-06-dashboards-can-now-chart-data-programs-saved-server-side-scr.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-06-extension-pages-and-mobile-headers-now-share-one-background-shade.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +5 -0
- package/corpus/templates/analytics/learnings.defaults.md +36 -0
- package/corpus/templates/analytics/seeds/data-programs/risk-meeting-cohort.js +254 -0
- package/corpus/templates/analytics/seeds/data-programs/risk-meeting-pylon-early-warning.js +184 -0
- package/corpus/templates/analytics/server/handlers/sql-query.ts +1 -1
- package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +109 -1
- package/corpus/templates/assets/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +5 -0
- package/corpus/templates/assets/.agents/skills/logo-composite/SKILL.md +34 -6
- package/corpus/templates/assets/AGENTS.md +23 -4
- package/corpus/templates/assets/actions/_generation-preset-settings.ts +48 -0
- package/corpus/templates/assets/actions/_helpers.ts +8 -1
- package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +64 -0
- package/corpus/templates/assets/actions/create-generation-preset.ts +11 -3
- package/corpus/templates/assets/actions/generate-image-batch.ts +7 -2
- package/corpus/templates/assets/actions/generate-image.ts +342 -25
- package/corpus/templates/assets/actions/get-library.ts +4 -3
- package/corpus/templates/assets/actions/navigate.ts +2 -1
- package/corpus/templates/assets/actions/refresh-generation-run.ts +5 -1
- package/corpus/templates/assets/actions/update-asset.ts +1 -0
- package/corpus/templates/assets/actions/update-generation-preset.ts +8 -1
- package/corpus/templates/assets/actions/view-screen.ts +8 -1
- package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +3 -2
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +25 -11
- package/corpus/templates/assets/app/components/layout/Header.tsx +1 -1
- package/corpus/templates/assets/app/components/layout/PageShell.tsx +4 -4
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +12 -12
- package/corpus/templates/assets/app/components/library/CreateLibraryDialog.tsx +8 -8
- package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +7 -7
- package/corpus/templates/assets/app/components/library/LibraryCard.tsx +8 -8
- package/corpus/templates/assets/app/components/library/LibraryPresetGrid.tsx +3 -2
- package/corpus/templates/assets/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/button.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/card.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/input.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/label.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/select.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/sonner.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/tabs.tsx +1 -0
- package/corpus/templates/assets/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/assets/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/assets/app/global.css +1 -0
- package/corpus/templates/assets/app/hooks/use-image-model-menu.ts +1 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +11 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +64 -0
- package/corpus/templates/assets/app/i18n-data.ts +650 -0
- package/corpus/templates/assets/app/root.tsx +12 -9
- package/corpus/templates/assets/app/routes/asset.$id.tsx +21 -20
- package/corpus/templates/assets/app/routes/audit.tsx +15 -14
- package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +93 -87
- package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1474 -0
- package/corpus/templates/assets/app/routes/library.tsx +74 -32
- package/corpus/templates/assets/app/routes/settings.tsx +21 -35
- package/corpus/templates/assets/changelog/2026-07-06-mobile-layouts-no-longer-stack-two-top-headers.md +5 -0
- package/corpus/templates/assets/server/lib/generation.ts +366 -44
- package/corpus/templates/assets/server/lib/image-processing.ts +252 -0
- package/corpus/templates/assets/server/lib/preset-skeleton.ts +137 -0
- package/corpus/templates/assets/shared/api.ts +47 -0
- package/corpus/templates/brain/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/brain/.agents/skills/ask-across-everything/SKILL.md +6 -1
- package/corpus/templates/brain/.agents/skills/brain/SKILL.md +63 -5
- package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +112 -0
- package/corpus/templates/brain/AGENTS.md +93 -8
- package/corpus/templates/brain/app/components/brain/CanonicalPreviewSheet.tsx +6 -5
- package/corpus/templates/brain/app/components/brain/Surface.tsx +3 -8
- package/corpus/templates/brain/app/components/layout/Layout.tsx +7 -7
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +6 -6
- package/corpus/templates/brain/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/button.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/card.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/input.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/label.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/progress.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/select.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/table.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/brain/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/brain/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/brain/app/global.css +1 -0
- package/corpus/templates/brain/app/hooks/use-distillation-bridge.ts +9 -4
- package/corpus/templates/brain/app/root.tsx +11 -8
- package/corpus/templates/brain/app/routes/knowledge.tsx +19 -19
- package/corpus/templates/brain/app/routes/ops.tsx +18 -18
- package/corpus/templates/brain/app/routes/review.tsx +22 -27
- package/corpus/templates/brain/app/routes/search.tsx +20 -20
- package/corpus/templates/brain/app/routes/settings.tsx +25 -40
- package/corpus/templates/brain/app/routes/sources.tsx +23 -23
- package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/calendar/actions/find-a-time.ts +29 -16
- package/corpus/templates/calendar/actions/list-bookings.ts +33 -2
- package/corpus/templates/calendar/actions/list-events.ts +34 -8
- package/corpus/templates/calendar/actions/update-calendar-visual-preferences.ts +105 -34
- package/corpus/templates/calendar/app/components/ThemeToggle.tsx +5 -5
- package/corpus/templates/calendar/app/components/TimezoneCombobox.tsx +6 -6
- package/corpus/templates/calendar/app/components/booking/BookingConfirmation.tsx +2 -1
- package/corpus/templates/calendar/app/components/booking/BookingForm.tsx +9 -8
- package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +2 -2
- package/corpus/templates/calendar/app/components/booking/TimeSlotPicker.tsx +3 -2
- package/corpus/templates/calendar/app/components/calendar/AddCalendarDialog.tsx +10 -15
- package/corpus/templates/calendar/app/components/calendar/AttendeeAutocomplete.tsx +6 -6
- package/corpus/templates/calendar/app/components/calendar/CommandPalette.tsx +73 -3
- package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +27 -27
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +553 -267
- package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +9 -12
- package/corpus/templates/calendar/app/components/calendar/EventAttendeesSection.tsx +10 -13
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +7 -7
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +21 -21
- package/corpus/templates/calendar/app/components/calendar/EventDialog.tsx +11 -11
- package/corpus/templates/calendar/app/components/calendar/EventOptionControls.tsx +9 -9
- package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +9 -9
- package/corpus/templates/calendar/app/components/calendar/GoogleConnectBanner.tsx +6 -6
- package/corpus/templates/calendar/app/components/calendar/GoogleSetupWizard.tsx +3 -3
- package/corpus/templates/calendar/app/components/calendar/GoogleSyncButton.tsx +1 -1
- package/corpus/templates/calendar/app/components/calendar/GuestNotificationDialog.tsx +8 -10
- package/corpus/templates/calendar/app/components/calendar/IntegrationsSidebar.tsx +12 -12
- package/corpus/templates/calendar/app/components/calendar/KeyboardShortcutsHelp.tsx +2 -2
- package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +93 -41
- package/corpus/templates/calendar/app/components/calendar/PeopleSearchDialog.tsx +6 -6
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +530 -299
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +229 -134
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +135 -108
- package/corpus/templates/calendar/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/button.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/card.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/collapsible.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/command.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/input.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/label.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/radio-group.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/select.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/table.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/tabs.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/calendar/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/calendar/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/calendar/app/global.css +1 -0
- package/corpus/templates/calendar/app/hooks/use-event-drag.ts +63 -9
- package/corpus/templates/calendar/app/hooks/use-google-auth.ts +15 -27
- package/corpus/templates/calendar/app/hooks/use-grid-create-drag.ts +254 -0
- package/corpus/templates/calendar/app/hooks/use-mobile.ts +1 -0
- package/corpus/templates/calendar/app/hooks/use-view-preferences.ts +287 -21
- package/corpus/templates/calendar/app/hooks/use-zoom-auth.ts +56 -19
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/calendar/app/i18n-data.ts +31 -0
- package/corpus/templates/calendar/app/lib/event-colors.ts +29 -8
- package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +11 -11
- package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +45 -50
- package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -2
- package/corpus/templates/calendar/app/pages/BookingsList.tsx +12 -12
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +438 -271
- package/corpus/templates/calendar/app/pages/ManageBookingPage.tsx +13 -13
- package/corpus/templates/calendar/app/pages/Settings.tsx +15 -29
- package/corpus/templates/calendar/app/root.tsx +19 -15
- package/corpus/templates/calendar/app/routes/event.tsx +2 -2
- package/corpus/templates/calendar/changelog/2026-07-06-calendar-now-loads-view-preferences-much-more-efficiently.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-connected-calendars-keep-their-own-color-when-you-customize-.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-create-events-from-the-command-menu-with-natural-phrases-lik.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-drag-across-empty-week-or-day-slots-to-create-an-event-with-.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-dragging-and-resizing-events-is-much-smoother-especially-on-.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-each-connected-google-account-can-now-have-its-own-calendar-.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-events-spanning-multiple-days-now-show-on-every-day-they-cov.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-google-calendar-now-finishes-connecting-correctly-after-sign.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-public-booking-pages-respond-faster.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-06-the-app-header-no-longer-shows-the-global-notifications-bell.md +6 -0
- package/corpus/templates/calendar/package.json +1 -0
- package/corpus/templates/calendar/server/handlers/bookings.ts +46 -52
- package/corpus/templates/calendar/server/handlers/google-auth.ts +21 -5
- package/corpus/templates/calendar/server/lib/google-calendar.ts +32 -3
- package/corpus/templates/calendar/shared/calendar-view-preferences.ts +66 -2
- package/corpus/templates/chat/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/chat/app/components/layout/Layout.tsx +6 -6
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +12 -12
- package/corpus/templates/chat/app/components/ui/button.tsx +1 -0
- package/corpus/templates/chat/app/components/ui/card.tsx +1 -0
- package/corpus/templates/chat/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/chat/app/components/ui/input.tsx +1 -0
- package/corpus/templates/chat/app/components/ui/label.tsx +1 -0
- package/corpus/templates/chat/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/chat/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/chat/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/chat/app/global.css +1 -0
- package/corpus/templates/chat/app/root.tsx +7 -4
- package/corpus/templates/chat/app/routes/settings.tsx +6 -21
- package/corpus/templates/clips/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +18 -4
- package/corpus/templates/clips/.env.example +8 -0
- package/corpus/templates/clips/actions/accept-invite.ts +3 -0
- package/corpus/templates/clips/actions/add-recording-to-space.ts +55 -27
- package/corpus/templates/clips/actions/add-vocabulary-term.ts +3 -1
- package/corpus/templates/clips/actions/archive-recording.ts +5 -12
- package/corpus/templates/clips/actions/cleanup-transcript.ts +5 -4
- package/corpus/templates/clips/actions/clear-edits.ts +42 -29
- package/corpus/templates/clips/actions/create-meeting.ts +87 -40
- package/corpus/templates/clips/actions/create-recording.ts +6 -3
- package/corpus/templates/clips/actions/delete-meeting.ts +25 -0
- package/corpus/templates/clips/actions/delete-recording-permanent.ts +3 -0
- package/corpus/templates/clips/actions/disconnect-calendar.ts +10 -4
- package/corpus/templates/clips/actions/export-insights-csv.ts +28 -8
- package/corpus/templates/clips/actions/finalize-meeting.ts +106 -33
- package/corpus/templates/clips/actions/finalize-recording.ts +201 -3
- package/corpus/templates/clips/actions/get-organization-insights.ts +51 -7
- package/corpus/templates/clips/actions/import-loom-recording.ts +17 -0
- package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +18 -1
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +7 -1
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +16 -0
- package/corpus/templates/clips/actions/list-clip-views.ts +60 -0
- package/corpus/templates/clips/actions/regenerate-summary.ts +10 -0
- package/corpus/templates/clips/actions/remove-vocabulary-term.ts +59 -0
- package/corpus/templates/clips/actions/request-transcript.ts +170 -25
- package/corpus/templates/clips/actions/restore-recording.ts +5 -12
- package/corpus/templates/clips/actions/save-browser-transcript.ts +13 -2
- package/corpus/templates/clips/actions/set-chapters.ts +6 -15
- package/corpus/templates/clips/actions/set-thumbnail.ts +77 -36
- package/corpus/templates/clips/actions/split-recording.ts +49 -35
- package/corpus/templates/clips/actions/stitch-recordings.ts +7 -4
- package/corpus/templates/clips/actions/stop-meeting-recording.ts +15 -1
- package/corpus/templates/clips/actions/sync-calendars.ts +10 -108
- package/corpus/templates/clips/actions/trash-recording.ts +43 -14
- package/corpus/templates/clips/actions/trim-recording.ts +48 -27
- package/corpus/templates/clips/actions/undo-edit.ts +49 -37
- package/corpus/templates/clips/app/components/capture-install-options.tsx +6 -6
- package/corpus/templates/clips/app/components/dictate/vocabulary-section.tsx +208 -0
- package/corpus/templates/clips/app/components/editable-recording-title.tsx +2 -2
- package/corpus/templates/clips/app/components/editor/chapters-editor.tsx +7 -7
- package/corpus/templates/clips/app/components/editor/editor-toolbar.tsx +26 -26
- package/corpus/templates/clips/app/components/editor/split-button.tsx +5 -4
- package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +9 -9
- package/corpus/templates/clips/app/components/editor/thumbnail-picker.tsx +11 -16
- package/corpus/templates/clips/app/components/editor/timeline.tsx +3 -3
- package/corpus/templates/clips/app/components/editor/transcript-editor.tsx +5 -5
- package/corpus/templates/clips/app/components/library/bulk-action-toolbar.tsx +4 -4
- package/corpus/templates/clips/app/components/library/create-space-dialog.tsx +8 -8
- package/corpus/templates/clips/app/components/library/empty-state.tsx +2 -1
- package/corpus/templates/clips/app/components/library/folder-tree.tsx +14 -14
- package/corpus/templates/clips/app/components/library/library-grid.tsx +24 -98
- package/corpus/templates/clips/app/components/library/library-layout.tsx +25 -21
- package/corpus/templates/clips/app/components/library/organization-switcher.tsx +11 -11
- package/corpus/templates/clips/app/components/library/recording-card.tsx +44 -50
- package/corpus/templates/clips/app/components/library/search-bar.tsx +5 -5
- package/corpus/templates/clips/app/components/library/sort-menu.tsx +5 -4
- package/corpus/templates/clips/app/components/library/tag-input.tsx +4 -4
- package/corpus/templates/clips/app/components/meetings/attendee-stack.tsx +2 -6
- package/corpus/templates/clips/app/components/meetings/auto-record-prompt.tsx +1 -1
- package/corpus/templates/clips/app/components/meetings/bullet-link.tsx +4 -4
- package/corpus/templates/clips/app/components/meetings/canvas-editor.tsx +5 -5
- package/corpus/templates/clips/app/components/meetings/meeting-card.tsx +3 -3
- package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +17 -9
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +6 -11
- package/corpus/templates/clips/app/components/meetings/transcript-bubbles.tsx +215 -21
- package/corpus/templates/clips/app/components/player/access-password-prompt.tsx +3 -2
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +14 -14
- package/corpus/templates/clips/app/components/player/delete-recording-menu.tsx +7 -6
- package/corpus/templates/clips/app/components/player/insights-panel.tsx +18 -6
- package/corpus/templates/clips/app/components/player/player-controls.tsx +42 -36
- package/corpus/templates/clips/app/components/player/reactions-tray.tsx +4 -4
- package/corpus/templates/clips/app/components/player/settings-panel.tsx +13 -12
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +80 -49
- package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +3 -2
- package/corpus/templates/clips/app/components/player/timestamped-comment-button.tsx +2 -2
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +7 -7
- package/corpus/templates/clips/app/components/player/video-player.tsx +182 -15
- package/corpus/templates/clips/app/components/recorder/camera-visualizer.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +49 -36
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +399 -57
- package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +6 -5
- package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +7 -6
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +10 -10
- package/corpus/templates/clips/app/components/sharing/viewed-by-popover.tsx +132 -0
- package/corpus/templates/clips/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/avatar.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/button.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/card.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/collapsible.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/context-menu.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/input.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/label.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/scroll-area.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/select.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/slider.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/sonner.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/table.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/tabs.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/clips/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/clips/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/clips/app/components/workspace/branding-editor.tsx +5 -9
- package/corpus/templates/clips/app/components/workspace/insights-hub.tsx +9 -19
- package/corpus/templates/clips/app/components/workspace/invite-dialog.tsx +10 -9
- package/corpus/templates/clips/app/components/workspace/members-list.tsx +11 -10
- package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +2 -1
- package/corpus/templates/clips/app/components/workspace/top-creators-table.tsx +3 -2
- package/corpus/templates/clips/app/components/workspace/top-videos-table.tsx +3 -2
- package/corpus/templates/clips/app/global.css +1 -0
- package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +2 -1
- package/corpus/templates/clips/app/hooks/use-mobile.ts +1 -0
- package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +14 -10
- package/corpus/templates/clips/app/hooks/use-view-tracking.ts +14 -0
- package/corpus/templates/clips/app/i18n/ar-SA.ts +56 -0
- package/corpus/templates/clips/app/i18n/de-DE.ts +53 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +52 -0
- package/corpus/templates/clips/app/i18n/es-ES.ts +53 -0
- package/corpus/templates/clips/app/i18n/fr-FR.ts +53 -0
- package/corpus/templates/clips/app/i18n/hi-IN.ts +52 -0
- package/corpus/templates/clips/app/i18n/ja-JP.ts +53 -0
- package/corpus/templates/clips/app/i18n/ko-KR.ts +52 -0
- package/corpus/templates/clips/app/i18n/pt-BR.ts +53 -0
- package/corpus/templates/clips/app/i18n/zh-CN.ts +51 -0
- package/corpus/templates/clips/app/i18n/zh-TW.ts +51 -0
- package/corpus/templates/clips/app/lib/recorder-preferences.ts +4 -0
- package/corpus/templates/clips/app/lib/timestamp-mapping.ts +5 -0
- package/corpus/templates/clips/app/root.tsx +26 -23
- package/corpus/templates/clips/app/routes/_app.dictate.tsx +39 -23
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +27 -1
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +134 -39
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +20 -20
- package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -3
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +92 -60
- package/corpus/templates/clips/app/routes/_app.settings.organization.tsx +8 -13
- package/corpus/templates/clips/app/routes/_app.spaces._index.tsx +1 -1
- package/corpus/templates/clips/app/routes/_app.trash.tsx +96 -31
- package/corpus/templates/clips/app/routes/bug-report.done.tsx +1 -1
- package/corpus/templates/clips/app/routes/bug-report.tsx +11 -11
- package/corpus/templates/clips/app/routes/download.tsx +2 -2
- package/corpus/templates/clips/app/routes/embed.$shareId.tsx +2 -1
- package/corpus/templates/clips/app/routes/invite.$token.tsx +8 -13
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +263 -251
- package/corpus/templates/clips/app/routes/record.tsx +185 -78
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +51 -29
- package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +71 -16
- package/corpus/templates/clips/changelog/2026-07-05-mobile-clip-pages-use-a-tighter-viewer-layout-with-inline-ac.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-ai-setup-now-switches-to-your-saved-provider-key-and-uses-fa.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-browsers-that-can-t-play-a-clip-s-video-format-now-see-a-cle.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-calendar-connections-no-longer-ask-you-to-reconnect-after-te.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-chrome-extension-recordings-keep-your-selected-microphone.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-clip-recordings-now-use-streaming-uploads-by-default.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-clip-titles-in-the-library-grid-now-open-the-clip-instead-of.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-desktop-countdown-now-dims-the-screen-before-recording.md +5 -0
- package/corpus/templates/clips/changelog/2026-07-06-desktop-popover-no-longer-drops-bluetooth-headphones-into-call-quality-mode.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-desktop-recorder-now-refreshes-cameras-and-mics-when-the-pop.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-desktop-recordings-keep-microphone-volume-clear-while-recor.md +5 -0
- package/corpus/templates/clips/changelog/2026-07-06-desktop-recordings-no-longer-get-stuck-optimizing-when-scree.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-dictation-history-from-the-desktop-app-now-appears-in-the-di.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-dictation-no-longer-overwrites-your-clipboard-and-messaging-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-double-tap-your-dictation-key-for-hands-free-dictation-tap-a.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-fixed-desktop-recordings-sometimes-uploading-without-audio-a.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-fixed-desktop-recordings-that-could-get-stuck-uploading-and-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-fixed-recordings-sometimes-capturing-from-the-wrong-micropho.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-hands-free-dictation-and-meeting-cleanup-recover-more-reliab.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-live-meetings-show-time-remaining-and-you-can-end-a-meeting-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-manage-your-personal-dictation-dictionary-from-the-dictate-t.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-meetings-interrupted-by-quitting-or-crashing-now-close-out-c.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-mic-only-desktop-recordings-now-use-the-selected-microphone-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-paste-your-last-dictation-anytime-with-cmd-ctrl-v-or-from-th.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-press-esc-to-cancel-a-dictation-in-progress.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-recordings-whose-video-file-contains-no-audio-track-no-longe.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-search-inside-meeting-transcripts.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-see-who-viewed-your-shared-clips-and-when-click-the-view-cou.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-share-links-for-agents-now-appear-as-soon-as-the-share-menu-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-share-popovers-now-stay-above-video-playback-controls.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-shared-clip-pages-now-recover-automatically-from-transient-v.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-the-desktop-app-now-walks-you-through-permissions-on-first-l.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-transcript-cleanup-preferences-now-save-per-user.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-transcripts-now-retry-automatically-after-transient-failures.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-06-video-compression-now-runs-in-the-background-without-blocking.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-native-microphone-recordings-now-reduce-steady-background-noise.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-07-recording-transcripts-now-line-up-with-the-video-start-afte.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/background.ts +141 -48
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +383 -59
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +51 -10
- package/corpus/templates/clips/chrome-extension/src/popup.ts +57 -6
- package/corpus/templates/clips/desktop/package.json +1 -1
- package/corpus/templates/clips/desktop/scripts/sign-macos-local.ts +67 -0
- package/corpus/templates/clips/desktop/src/app.tsx +38 -13
- package/corpus/templates/clips/desktop/src/components/SourceRow.tsx +1 -0
- package/corpus/templates/clips/desktop/src/hooks/useMediaDevices.ts +88 -28
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +165 -55
- package/corpus/templates/clips/desktop/src/hooks/useMicMeter.ts +35 -10
- package/corpus/templates/clips/desktop/src/lib/backtrack.ts +12 -0
- package/corpus/templates/clips/desktop/src/lib/media-capture-constraints.ts +129 -34
- package/corpus/templates/clips/desktop/src/lib/media-device-selection.ts +157 -0
- package/corpus/templates/clips/desktop/src/lib/permissions.ts +2 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +109 -47
- package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +22 -2
- package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +29 -1
- package/corpus/templates/clips/desktop/src/lib/updater.ts +6 -0
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +372 -31
- package/corpus/templates/clips/desktop/src/overlays/finalizing.tsx +90 -0
- package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +4 -6
- package/corpus/templates/clips/desktop/src/overlays/onboarding.tsx +214 -9
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +37 -52
- package/corpus/templates/clips/desktop/src/styles.css +1 -1
- package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +7 -0
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +371 -43
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +79 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1418 -260
- package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +188 -44
- package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +197 -8
- package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +64 -8
- package/corpus/templates/clips/desktop/src-tauri/src/state.rs +3 -2
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +10 -0
- package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +29 -8
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +136 -13
- package/corpus/templates/clips/desktop/vite.config.ts +3 -0
- package/corpus/templates/clips/package.json +1 -1
- package/corpus/templates/clips/server/db/schema.ts +26 -0
- package/corpus/templates/clips/server/jobs/builder-media-compression.ts +41 -0
- package/corpus/templates/clips/server/jobs/stale-meeting-sweeper.ts +294 -0
- package/corpus/templates/clips/server/lib/builder-media-compression.ts +976 -0
- package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +107 -56
- package/corpus/templates/clips/server/lib/env-flags.ts +4 -0
- package/corpus/templates/clips/server/lib/google-calendar-client.ts +8 -1
- package/corpus/templates/clips/server/lib/media-worker.ts +153 -0
- package/corpus/templates/clips/server/lib/s3-upload-provider.ts +58 -17
- package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +17 -5
- package/corpus/templates/clips/server/lib/video-remux.ts +90 -0
- package/corpus/templates/clips/server/plugins/calendar-jobs.ts +4 -0
- package/corpus/templates/clips/server/plugins/db.ts +196 -0
- package/corpus/templates/clips/server/register-secrets.ts +78 -0
- package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.get.ts +15 -0
- package/corpus/templates/clips/server/routes/_agent-native/clips/user-prefs.put.ts +27 -0
- package/corpus/templates/clips/server/routes/api/media-worker/callback.post.ts +88 -0
- package/corpus/templates/clips/server/routes/api/public-recording.get.ts +87 -7
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +11 -4
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +55 -13
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +24 -42
- package/corpus/templates/clips/server/routes/api/view-event.post.ts +29 -1
- package/corpus/templates/clips/shared/finalize-recovery.ts +23 -3
- package/corpus/templates/clips/shared/media-device-selection.ts +157 -0
- package/corpus/templates/clips/shared/media-worker-contract.ts +146 -0
- package/corpus/templates/content/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +67 -2
- package/corpus/templates/content/AGENTS.md +73 -65
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +188 -8
- package/corpus/templates/content/actions/_builder-docs-client.ts +41 -3
- package/corpus/templates/content/actions/_database-source-utils.ts +998 -160
- package/corpus/templates/content/actions/_database-utils.ts +44 -4
- package/corpus/templates/content/actions/_local-file-documents.ts +90 -1
- package/corpus/templates/content/actions/_notion-action-utils.ts +12 -4
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -1
- package/corpus/templates/content/actions/attach-content-database-source.ts +85 -12
- package/corpus/templates/content/actions/change-content-database-source-role.ts +9 -1
- package/corpus/templates/content/actions/delete-content-database.ts +1 -0
- package/corpus/templates/content/actions/delete-document.ts +8 -0
- package/corpus/templates/content/actions/get-document.ts +11 -2
- package/corpus/templates/content/actions/list-content-databases.ts +57 -9
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +23 -6
- package/corpus/templates/content/actions/refresh-content-database-source.ts +26 -3
- package/corpus/templates/content/actions/remove-local-file-source.ts +100 -0
- package/corpus/templates/content/actions/restore-content-database.ts +1 -0
- package/corpus/templates/content/actions/sync-notion-comments.ts +174 -20
- package/corpus/templates/content/actions/update-document.ts +134 -5
- package/corpus/templates/content/app/blocks/SourceComponentBlock.tsx +31 -3
- package/corpus/templates/content/app/blocks/contentBlockRegistry.tsx +4 -4
- package/corpus/templates/content/app/components/EmptyState.tsx +1 -1
- package/corpus/templates/content/app/components/ThemeToggle.tsx +6 -6
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +5 -5
- package/corpus/templates/content/app/components/editor/BuilderBodySyncingNotice.tsx +21 -0
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +5 -5
- package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +1 -1
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +9 -14630
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +142 -79
- package/corpus/templates/content/app/components/editor/DocumentEditorSkeleton.tsx +36 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +98 -46
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +29 -44
- package/corpus/templates/content/app/components/editor/EmojiPicker.tsx +5 -4
- package/corpus/templates/content/app/components/editor/LinkHoverPreview.tsx +5 -4
- package/corpus/templates/content/app/components/editor/NotionConflictBanner.tsx +1 -1
- package/corpus/templates/content/app/components/editor/NotionSyncBar.tsx +2 -2
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +5 -5
- package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +13 -13
- package/corpus/templates/content/app/components/editor/VersionHistoryPanel.tsx +10 -10
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +28 -11
- package/corpus/templates/content/app/components/editor/body-hydration.ts +112 -0
- package/corpus/templates/content/app/components/editor/builder-body-hydration-pump.ts +52 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +736 -69
- package/corpus/templates/content/app/components/editor/database/navigation-state.ts +36 -1
- package/corpus/templates/content/app/components/editor/database/settings.tsx +3224 -0
- package/corpus/templates/content/app/components/editor/database/shared.tsx +19 -19
- package/corpus/templates/content/app/components/editor/database/text.ts +23 -0
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +50 -9
- package/corpus/templates/content/app/components/editor/extensions/AudioBlock.tsx +19 -18
- package/corpus/templates/content/app/components/editor/extensions/ImageBlock.tsx +57 -31
- package/corpus/templates/content/app/components/editor/extensions/LocalMdxComponentNode.tsx +8 -8
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +9 -0
- package/corpus/templates/content/app/components/editor/extensions/VideoBlock.tsx +19 -18
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +21 -1
- package/corpus/templates/content/app/components/layout/Header.tsx +1 -5
- package/corpus/templates/content/app/components/layout/Layout.tsx +25 -8
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +214 -56
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +33 -30
- package/corpus/templates/content/app/components/sidebar/NotionButton.tsx +11 -11
- package/corpus/templates/content/app/components/sidebar/document-sidebar-sections.ts +109 -1
- package/corpus/templates/content/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/content/app/components/ui/button.tsx +1 -0
- package/corpus/templates/content/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/content/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/content/app/components/ui/input.tsx +1 -0
- package/corpus/templates/content/app/components/ui/label.tsx +1 -0
- package/corpus/templates/content/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/content/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/content/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/content/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/content/app/components/ui/sonner.tsx +1 -0
- package/corpus/templates/content/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/content/app/components/ui/table.tsx +1 -0
- package/corpus/templates/content/app/components/ui/toaster.tsx +1 -0
- package/corpus/templates/content/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/content/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/content/app/global.css +42 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +346 -42
- package/corpus/templates/content/app/hooks/use-db-sync.ts +9 -0
- package/corpus/templates/content/app/hooks/use-documents.ts +136 -37
- package/corpus/templates/content/app/hooks/use-mobile.ts +1 -0
- package/corpus/templates/content/app/hooks/use-notion.ts +85 -15
- package/corpus/templates/content/app/i18n/zh-TW.ts +49 -0
- package/corpus/templates/content/app/i18n-data.ts +508 -0
- package/corpus/templates/content/app/root.tsx +57 -29
- package/corpus/templates/content/app/routes/_app._index.tsx +1 -1
- package/corpus/templates/content/app/routes/_app.local-files.tsx +268 -81
- package/corpus/templates/content/app/routes/_app.settings.tsx +4 -21
- package/corpus/templates/content/changelog/2026-07-01-builder-database-sources-keep-loading-rows-in-the-background.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attach-starts-with-usable-rows.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-attachment-now-stays-connected-after-large.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-components-show-preservation-and-review-states.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-builder-source-refreshes-now-show-progress-for-large-docs-blog.md +6 -0
- package/corpus/templates/content/changelog/2026-07-01-database-pages-now-delete-to-trash-without-route-errors.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-source-refreshes-recover-from-stalled-background-row.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-builder-sourced-rows-open-with-sync-protection.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-database-rows-and-pages-open-faster-from-recently-loaded-dat.md +6 -0
- package/corpus/templates/content/changelog/2026-07-02-preview-panels-close-reliably-narrow-sidebar-row-actions-sta.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-builder-sync-progress-no-longer-shows-continue.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-document-headers-and-page-bodies-now-share-one-background-shade.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-export-submenus-stay-visible-outside-the-page-actions-menu.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-fixed-collaborative-documents-reapplying-content-before-save.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-local-files-now-remember-sidebar-collapse-state-and-include-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-notion-comment-sync-now-preserves-threads-replies-stay-attac.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-notion-sync-is-far-more-reliable-auto-sync-no-longer-overwri.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-opening-pages-now-highlights-the-sidebar-item-and-shows-the-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-presence-markers-in-the-editor-are-now-compact-instead-of-co.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-sidebar-collapse-now-keeps-the-same-background-shade-as-the-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-06-the-content-header-no-longer-shows-the-global-notifications-.md +6 -0
- package/corpus/templates/content/parity/matrix.md +24 -24
- package/corpus/templates/content/parity/matrix.ts +7 -3
- package/corpus/templates/content/server/db/schema.ts +15 -0
- package/corpus/templates/content/server/lib/notion-sync.ts +481 -39
- package/corpus/templates/content/server/lib/notion.ts +212 -48
- package/corpus/templates/content/server/plugins/db.ts +20 -0
- package/corpus/templates/content/server/routes/api/documents/[id]/notion/resolve.post.ts +14 -2
- package/corpus/templates/content/server/routes/api/notion/callback.get.ts +54 -6
- package/corpus/templates/content/shared/api.ts +13 -0
- package/corpus/templates/content/shared/builder-mdx.ts +84 -15
- package/corpus/templates/content/shared/builder-source-component-registry.ts +177 -0
- package/corpus/templates/content/shared/nfm.ts +459 -66
- package/corpus/templates/content/shared/source-component-block.ts +72 -6
- package/corpus/templates/content/vite.config.ts +17 -1
- package/corpus/templates/design/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +44 -25
- package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +98 -0
- package/corpus/templates/design/.agents/skills/shader-fills/SKILL.md +162 -0
- package/corpus/templates/design/AGENTS.md +12 -4
- package/corpus/templates/design/actions/add-breakpoint.ts +6 -3
- package/corpus/templates/design/actions/apply-a11y-fix.ts +57 -37
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +41 -20
- package/corpus/templates/design/actions/apply-motion-edit.ts +251 -38
- package/corpus/templates/design/actions/apply-shader-fill.ts +90 -66
- package/corpus/templates/design/actions/apply-visual-edit.ts +282 -55
- package/corpus/templates/design/actions/create-component.ts +29 -60
- package/corpus/templates/design/actions/create-file.ts +9 -2
- package/corpus/templates/design/actions/duplicate-design.ts +31 -2
- package/corpus/templates/design/actions/edit-design.ts +36 -38
- package/corpus/templates/design/actions/generate-design-title.ts +87 -0
- package/corpus/templates/design/actions/generate-design.ts +53 -18
- package/corpus/templates/design/actions/generate-screens.ts +108 -4
- package/corpus/templates/design/actions/get-motion-timeline.ts +35 -7
- package/corpus/templates/design/actions/import-design-source.ts +8 -58
- package/corpus/templates/design/actions/insert-asset.ts +34 -29
- package/corpus/templates/design/actions/insert-design-native-asset.ts +129 -32
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +35 -30
- package/corpus/templates/design/actions/present-design-variants.ts +7 -2
- package/corpus/templates/design/actions/remove-motion-timeline.ts +31 -36
- package/corpus/templates/design/actions/run-design-audit.ts +127 -1
- package/corpus/templates/design/actions/take-design-screenshot.ts +673 -0
- package/corpus/templates/design/actions/update-file.ts +190 -75
- package/corpus/templates/design/app/components/ThemeToggle.tsx +6 -6
- package/corpus/templates/design/app/components/design/BreakpointBar.tsx +403 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +480 -233
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +708 -53
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +2 -2
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +99 -24
- package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +6 -97
- package/corpus/templates/design/app/components/design/DrawOverlay.tsx +7 -7
- package/corpus/templates/design/app/components/design/EditPanel.tsx +1120 -6977
- package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +9 -8
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +433 -99
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +6 -5
- package/corpus/templates/design/app/components/design/MotionDock.tsx +1138 -117
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +2610 -1398
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +3 -3
- package/corpus/templates/design/app/components/design/ReadOnlyDesignBanner.tsx +22 -0
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +3 -3
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +13 -13
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +14 -14
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +6 -6
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +2221 -244
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +187 -11
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +376 -1
- package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +746 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +17 -0
- package/corpus/templates/design/app/components/design/code-workbench/ActivityBar.tsx +3 -3
- package/corpus/templates/design/app/components/design/code-workbench/SideBar.tsx +5 -8
- package/corpus/templates/design/app/components/design/code-workbench/editor/EditorTabs.tsx +6 -6
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +2 -1
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +18 -20
- package/corpus/templates/design/app/components/design/code-workbench/quickinput/QuickInput.tsx +1 -1
- package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +6 -6
- package/corpus/templates/design/app/components/design/edit-panel/appearance-properties.tsx +518 -0
- package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +562 -0
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +763 -0
- package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +94 -0
- package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +780 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +446 -0
- package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +79 -0
- package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +315 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +344 -0
- package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +594 -0
- package/corpus/templates/design/app/components/design/edit-panel/frame-presets-panel.tsx +119 -0
- package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +248 -0
- package/corpus/templates/design/app/components/design/edit-panel/interaction-state-helpers.ts +45 -0
- package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +693 -0
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +724 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +207 -0
- package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +628 -0
- package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +91 -0
- package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +649 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-change-types.ts +118 -0
- package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +88 -0
- package/corpus/templates/design/app/components/design/edit-panel/transform-helpers.ts +100 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +117 -0
- package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +501 -0
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +3 -3
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +98 -47
- package/corpus/templates/design/app/components/design/inspector/BreakpointOverrideIndicator.tsx +82 -0
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +3 -3
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +207 -105
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +6 -6
- package/corpus/templates/design/app/components/design/inspector/GlslShaderPanel.tsx +1097 -0
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +44 -0
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +6 -6
- package/corpus/templates/design/app/components/design/inspector/InteractionStatePanel.tsx +258 -0
- package/corpus/templates/design/app/components/design/inspector/MotionKeyframeDiamond.tsx +144 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +106 -16
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +14 -14
- package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +9 -9
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +6 -6
- package/corpus/templates/design/app/components/design/inspector/frame-size-presets.ts +116 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +26 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +29 -0
- package/corpus/templates/design/app/components/design/multi-screen/alt-hover-measurement.ts +171 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +220 -0
- package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +35 -0
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +134 -0
- package/corpus/templates/design/app/components/design/multi-screen/culling.ts +185 -0
- package/corpus/templates/design/app/components/design/multi-screen/gradient-overlay-geometry.ts +120 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +892 -0
- package/corpus/templates/design/app/components/design/multi-screen/vector-edit-geometry.ts +21 -0
- package/corpus/templates/design/app/components/design/types.ts +15 -0
- package/corpus/templates/design/app/components/design-system/DesignSystemCard.tsx +3 -2
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +124 -23
- package/corpus/templates/design/app/components/layout/Header.tsx +2 -2
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +5 -5
- package/corpus/templates/design/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/design/app/components/ui/avatar.tsx +1 -0
- package/corpus/templates/design/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/design/app/components/ui/button.tsx +1 -0
- package/corpus/templates/design/app/components/ui/card.tsx +1 -0
- package/corpus/templates/design/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/design/app/components/ui/collapsible.tsx +1 -0
- package/corpus/templates/design/app/components/ui/context-menu.tsx +1 -0
- package/corpus/templates/design/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/design/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/design/app/components/ui/input.tsx +1 -0
- package/corpus/templates/design/app/components/ui/label.tsx +1 -0
- package/corpus/templates/design/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/design/app/components/ui/select.tsx +1 -0
- package/corpus/templates/design/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/design/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/design/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/design/app/components/ui/slider.tsx +1 -0
- package/corpus/templates/design/app/components/ui/sonner.tsx +1 -0
- package/corpus/templates/design/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/design/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/design/app/components/ui/tabs.tsx +1 -0
- package/corpus/templates/design/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/design/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/design/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +7 -7
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +7 -7
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +3 -3
- package/corpus/templates/design/app/global.css +9 -8
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +259 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +166 -13
- package/corpus/templates/design/app/i18n-breakpoints.ts +205 -0
- package/corpus/templates/design/app/i18n-data.ts +1702 -187
- package/corpus/templates/design/app/i18n-motion.ts +563 -0
- package/corpus/templates/design/app/lib/design-import.ts +73 -0
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +6 -6
- package/corpus/templates/design/app/pages/DesignSystems.tsx +31 -31
- package/corpus/templates/design/app/pages/Index.tsx +66 -51
- package/corpus/templates/design/app/pages/NotFound.tsx +2 -1
- package/corpus/templates/design/app/pages/Present.tsx +2 -1
- package/corpus/templates/design/app/pages/VisualEdit.tsx +2 -1
- package/corpus/templates/design/app/root.tsx +7 -4
- package/corpus/templates/design/app/routes/settings.tsx +22 -49
- package/corpus/templates/design/changelog/2026-06-29-design-inspector-controls-now-match-figmas-softer-dark-chrome.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +1 -1
- package/corpus/templates/design/changelog/2026-06-30-multi-select-outlines-now-stay-consistent-across-all-screens.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +1 -1
- package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +1 -1
- package/corpus/templates/design/changelog/2026-07-05-code-sidebar-tabs-now-use-a-quieter-active-highlight-that-ma.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-designs-get-ai-generated-names-from-your-prompt.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-local-code-screens-keep-their-connection-when-duplicated-and.md +6 -0
- package/corpus/templates/design/changelog/2026-07-05-press-a-to-draw-screens-on-the-canvas-inside-a-screen-it-dr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-3d-rotation-and-perspective-controls-on-every-element.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-ai-generated-and-duplicated-screens-now-get-their-editor-sel.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-applying-shaders-and-fills-in-quick-succession-can-no-longer.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-frames-in-the-overview-now-support-click-to-selec.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-segments-in-the-inspector-now-show-a-device-icon-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-breakpoint-targeting-now-lives-in-one-compact-control-in-the.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-can-no-longer-be-overwritten-by-stale-collabora.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-canvas-edits-inside-screens-save-reliably-again.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-cleaner-inspector-removed-the-ask-ai-box-from-the-design-tab.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-clicking-empty-canvas-space-now-deselects-an-element-inside-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-an-element-onto-a-rectangle-now-nests-it-inside-wit.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dragging-screens-and-objects-on-the-canvas-is-now-much-smoot.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-drawing-a-shape-or-text-on-the-canvas-no-longer-flashes-away.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-dropping-an-element-between-two-others-inside-a-screen-now-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-duplicating-a-screen-now-places-the-copy-exactly-where-you-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edit-gradients-directly-on-elements-inside-screens-scale-str.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-edits-from-collaborators-and-the-agent-now-update-the-canvas.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fig-files-now-stay-on-the-builder-indexing-path-through-desi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-bug-where-most-style-position-and-text-edits-briefly.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-confusing-raw-error-message-when-a-file-upload-fails.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-a-rare-case-where-an-edit-could-be-silently-overwritte.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inserted-assets-sometimes-disappearing-reappearing-or-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-inspector-value-scrubbing-producing-erratic-numbers-an.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-padding-gap-and-min-max-size-scrubbing-not-updating-th.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-text-stroke-color-turning-black-and-glyphs-disappearin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-collapsed-effects-and-stroke-inspector-sections-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-new-design-dialog-closing-unexpectedly-when-using-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-fixed-the-typography-section-missing-when-selecting-text-nes.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-full-view-no-longer-flashes-back-to-the-overview-on-entry-no.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-keyframe-animation-timeline-per-property-keyframes-easing-cu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-mobile-layouts-no-longer-stack-two-top-headers.md +5 -0
- package/corpus/templates/design/changelog/2026-07-06-moving-restyling-and-scrubbing-values-on-elements-in-ai-gene.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-and-corner-radius-value-scrubbing-now-snaps-to-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-padding-handles-now-only-resize-on-the-handle-line-itself-sh.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-preview-and-edit-hover-focus-and-pressed-states-with-real-cs.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-promoting-an-element-to-a-component-now-works-correctly-for-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-reduced-url-history-churn-and-selection-overlay-flicker-whil.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-removed-the-decorative-dot-before-screen-names-in-the-overvi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-responsive-breakpoints-edit-at-any-width-with-cascading-over.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-screen-labels-no-longer-turn-blue-when-you-hover-inside-the-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-scrolling-a-screen-with-an-element-selected-stays-smooth-on-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selecting-multiple-elements-and-dragging-now-moves-the-whole.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-borders-handles-and-value-readouts-now-stay-the-sa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-selection-handles-and-editing-chrome-now-stay-a-constant-siz.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-shader-fills-and-effects-ai-authored-or-preset-glsl-shaders-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-drawn-on-the-canvas-now-defaults-to-white-and-inter-in-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-text-dropped-into-a-shape-now-stays-readable-instead-of-disa.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-add-tweak-controls-prompt-no-longer-jumps-to-the-corner-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-design-agent-now-visually-reviews-rendered-screenshots-o.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-padding-value-readout-now-stays-visible-while-you-hover-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-selected-element-attachment-now-clears-from-the-chat-com.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-the-zoom-menu-is-more-compact-with-tight-rows-and-right-alig.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-undo-and-redo-no-longer-flash-the-screen-changes-now-apply-i.md +6 -0
- package/corpus/templates/design/changelog/2026-07-06-zoom-presets-like-zoom-to-50-now-stay-on-the-focused-screen-.md +6 -0
- package/corpus/templates/design/server/handlers/import-design-file.ts +4 -43
- package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +2 -2
- package/corpus/templates/design/server/lib/import-design-files.ts +11 -2
- package/corpus/templates/design/server/lib/visible-clipboard-html.ts +32 -0
- package/corpus/templates/design/server/source-workspace.ts +139 -59
- package/corpus/templates/design/shared/board-file.ts +11 -2
- package/corpus/templates/design/shared/breakpoint-media.ts +543 -0
- package/corpus/templates/design/shared/canvas-math.ts +354 -35
- package/corpus/templates/design/shared/code-layer.ts +277 -8
- package/corpus/templates/design/shared/design-review.ts +1 -0
- package/corpus/templates/design/shared/design-state.ts +11 -4
- package/corpus/templates/design/shared/interaction-states.ts +548 -0
- package/corpus/templates/design/shared/motion-compiler.ts +186 -23
- package/corpus/templates/design/shared/motion-easing.ts +294 -0
- package/corpus/templates/design/shared/motion-timeline.ts +400 -34
- package/corpus/templates/design/shared/prompt-title.ts +95 -0
- package/corpus/templates/design/shared/responsive-classes.ts +448 -10
- package/corpus/templates/design/shared/screen-annotation.ts +46 -0
- package/corpus/templates/design/shared/shader-fills.ts +1009 -0
- package/corpus/templates/design/shared/shader-presets.ts +852 -4
- package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/dispatch/actions/list-staged-datasets.ts +3 -0
- package/corpus/templates/dispatch/app/routes/settings.tsx +3 -17
- package/corpus/templates/forms/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/forms/app/components/CloudUpgrade.tsx +10 -10
- package/corpus/templates/forms/app/components/ThemeToggle.tsx +5 -5
- package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +11 -10
- package/corpus/templates/forms/app/components/builder/FieldRenderer.tsx +10 -13
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +17 -17
- package/corpus/templates/forms/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/button.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/calendar.tsx +1 -1
- package/corpus/templates/forms/app/components/ui/card.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/command.tsx +1 -1
- package/corpus/templates/forms/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/input.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/label.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/radio-group.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/scroll-area.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/select.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/slider.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/table.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/tabs.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/forms/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/forms/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/forms/app/global.css +1 -0
- package/corpus/templates/forms/app/hooks/use-mobile.ts +1 -0
- package/corpus/templates/forms/app/hooks/use-navigation-state.ts +0 -1
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +24 -26
- package/corpus/templates/forms/app/pages/FormFillPage.tsx +2 -2
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +21 -21
- package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +11 -11
- package/corpus/templates/forms/app/pages/ResponsesPage.tsx +4 -4
- package/corpus/templates/forms/app/root.tsx +11 -8
- package/corpus/templates/forms/app/routes/_app.settings.tsx +6 -21
- package/corpus/templates/forms/app/routes/form-preview.tsx +3 -3
- package/corpus/templates/forms/changelog/2026-07-06-the-form-builder-header-no-longer-shows-the-global-notificat.md +6 -0
- package/corpus/templates/macros/app/components/AddExerciseDialog.tsx +1 -1
- package/corpus/templates/macros/app/components/AddMealDialog.tsx +1 -1
- package/corpus/templates/macros/app/components/AddWeightDialog.tsx +1 -1
- package/corpus/templates/macros/app/components/DailyProgress.tsx +7 -7
- package/corpus/templates/macros/app/components/WeeklyCaloriesChart.tsx +2 -1
- package/corpus/templates/macros/app/components/WeightCard.tsx +10 -10
- package/corpus/templates/macros/app/components/WeightTracker.tsx +1 -1
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +6 -10
- package/corpus/templates/macros/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/macros/app/components/ui/label.tsx +1 -0
- package/corpus/templates/macros/app/components/ui/select.tsx +1 -0
- package/corpus/templates/macros/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/macros/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/macros/app/components/ui/sonner.tsx +1 -0
- package/corpus/templates/macros/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/macros/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/macros/app/global.css +1 -0
- package/corpus/templates/macros/app/root.tsx +17 -14
- package/corpus/templates/macros/app/routes/_index.tsx +1 -1
- package/corpus/templates/macros/app/routes/analytics.tsx +8 -8
- package/corpus/templates/macros/app/routes/settings.tsx +4 -19
- package/corpus/templates/mail/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +67 -0
- package/corpus/templates/mail/.agents/skills/draft-queue/SKILL.md +35 -1
- package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +74 -33
- package/corpus/templates/mail/AGENTS.md +102 -16
- package/corpus/templates/mail/actions/archive-email.ts +67 -11
- package/corpus/templates/mail/actions/manage-snippets.ts +106 -0
- package/corpus/templates/mail/actions/mark-read.ts +45 -11
- package/corpus/templates/mail/actions/send-queued-drafts.ts +77 -42
- package/corpus/templates/mail/actions/star-email.ts +55 -10
- package/corpus/templates/mail/actions/trash-email.ts +48 -9
- package/corpus/templates/mail/app/components/GoogleConnectBanner.tsx +1 -1
- package/corpus/templates/mail/app/components/ThemeToggle.tsx +6 -6
- package/corpus/templates/mail/app/components/agent-engine-picker.tsx +10 -10
- package/corpus/templates/mail/app/components/email/CodeBlockLangPicker.tsx +4 -3
- package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +5 -5
- package/corpus/templates/mail/app/components/email/ComposeEditor.tsx +109 -10
- package/corpus/templates/mail/app/components/email/ComposeModal.tsx +44 -19
- package/corpus/templates/mail/app/components/email/ComposeSlashMenu.tsx +139 -17
- package/corpus/templates/mail/app/components/email/EmailList.tsx +522 -314
- package/corpus/templates/mail/app/components/email/EmailListItem.tsx +130 -45
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +9 -9
- package/corpus/templates/mail/app/components/email/InlineReplyComposer.tsx +35 -13
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +13 -13
- package/corpus/templates/mail/app/components/email/MobileActionBar.tsx +13 -13
- package/corpus/templates/mail/app/components/email/RecipientInput.tsx +53 -42
- package/corpus/templates/mail/app/components/email/SendLaterButton.tsx +5 -4
- package/corpus/templates/mail/app/components/email/SnoozeModal.tsx +1 -1
- package/corpus/templates/mail/app/components/email/SnoozePopover.tsx +6 -6
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +19 -23
- package/corpus/templates/mail/app/components/layout/HeaderActions.tsx +12 -4
- package/corpus/templates/mail/app/components/layout/SearchBar.tsx +138 -18
- package/corpus/templates/mail/app/components/settings/GmailFiltersSection.tsx +23 -23
- package/corpus/templates/mail/app/components/settings/SnippetsSection.tsx +303 -0
- package/corpus/templates/mail/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/button.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/calendar.tsx +1 -1
- package/corpus/templates/mail/app/components/ui/command.tsx +1 -1
- package/corpus/templates/mail/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/drawer.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/select.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/mail/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/mail/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/mail/app/global.css +20 -3
- package/corpus/templates/mail/app/hooks/use-emails.ts +187 -0
- package/corpus/templates/mail/app/hooks/use-snippets.ts +103 -0
- package/corpus/templates/mail/app/i18n/ar-SA.ts +17 -0
- package/corpus/templates/mail/app/i18n/de-DE.ts +18 -0
- package/corpus/templates/mail/app/i18n/en-US.ts +17 -0
- package/corpus/templates/mail/app/i18n/es-ES.ts +17 -0
- package/corpus/templates/mail/app/i18n/fr-FR.ts +18 -0
- package/corpus/templates/mail/app/i18n/hi-IN.ts +17 -0
- package/corpus/templates/mail/app/i18n/ja-JP.ts +18 -0
- package/corpus/templates/mail/app/i18n/ko-KR.ts +17 -0
- package/corpus/templates/mail/app/i18n/pt-BR.ts +17 -0
- package/corpus/templates/mail/app/i18n/zh-CN.ts +16 -0
- package/corpus/templates/mail/app/i18n/zh-TW.ts +16 -0
- package/corpus/templates/mail/app/pages/DraftQueuePage.tsx +14 -14
- package/corpus/templates/mail/app/pages/InboxPage.tsx +3 -3
- package/corpus/templates/mail/app/pages/NotFound.tsx +2 -1
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +64 -66
- package/corpus/templates/mail/app/root.tsx +21 -18
- package/corpus/templates/mail/app/routes/_index.tsx +2 -1
- package/corpus/templates/mail/app/routes/email.tsx +2 -2
- package/corpus/templates/mail/changelog/2026-07-06-a-queued-draft-can-no-longer-be-sent-twice-by-simultaneous-s.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-bulk-archive-trash-star-and-mark-read-now-complete-in-one-fa.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-dark-mode-sidebars-and-notifications-now-use-the-softer-gray.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-inbox-automations-pick-up-new-mail-faster.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-inbox-lists-stay-fast-as-scheduled-and-snoozed-mail-accumula.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-long-inboxes-scroll-smoothly-and-stay-fast-as-more-mail-load.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-paste-or-drop-images-straight-into-the-composer-to-send-them.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-save-reusable-snippets-and-insert-them-from-the-compose-slas.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-scrolling-older-mail-keeps-loading-correctly-when-no-gmail-a.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-search-shows-instant-matches-from-already-loaded-mail-while-.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-sends-now-fail-with-a-clear-error-instead-of-quietly-missing.md +6 -0
- package/corpus/templates/mail/changelog/2026-07-06-the-mail-header-no-longer-shows-the-global-notifications-bel.md +6 -0
- package/corpus/templates/mail/package.json +1 -0
- package/corpus/templates/mail/server/db/schema.ts +11 -0
- package/corpus/templates/mail/server/handlers/emails.ts +20 -1
- package/corpus/templates/mail/server/lib/automation-engine.ts +53 -25
- package/corpus/templates/mail/server/lib/google-auth.ts +140 -9
- package/corpus/templates/mail/server/lib/jobs.ts +22 -6
- package/corpus/templates/mail/server/lib/outgoing-email.ts +11 -2
- package/corpus/templates/mail/server/lib/queued-drafts.ts +145 -4
- package/corpus/templates/mail/server/plugins/db.ts +29 -0
- package/corpus/templates/plan/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/plan/app/components/editor/PlanDocumentEditor.tsx +42 -3
- package/corpus/templates/plan/app/components/layout/Layout.tsx +5 -4
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +20 -20
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +3 -2
- package/corpus/templates/plan/app/components/plan/DocumentArea.tsx +2 -2
- package/corpus/templates/plan/app/components/plan/GuestModeBanner.tsx +2 -1
- package/corpus/templates/plan/app/components/plan/LocalCodebasePicker.tsx +8 -8
- package/corpus/templates/plan/app/components/plan/PlanContentRenderer.tsx +6 -1
- package/corpus/templates/plan/app/components/plan/PlanImageViewer.tsx +8 -12
- package/corpus/templates/plan/app/components/plan/PlanMarkdownEditor.tsx +6 -1
- package/corpus/templates/plan/app/components/plan/PlanVisualSurface.tsx +3 -3
- package/corpus/templates/plan/app/components/plan/planBlocks.tsx +4 -4
- package/corpus/templates/plan/app/components/plan/wireframe/kit/rough.tsx +61 -27
- package/corpus/templates/plan/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/avatar.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/badge.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/button.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/card.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/collapsible.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/input.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/label.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/scroll-area.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/select.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/sonner.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/toggle-group.tsx +1 -0
- package/corpus/templates/plan/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/plan/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/plan/app/global.css +13 -0
- package/corpus/templates/plan/app/hooks/use-plan-presence.ts +11 -1
- package/corpus/templates/plan/app/lib/plan-access-prompt.ts +74 -0
- package/corpus/templates/plan/app/lib/plan-annotation-runtime.ts +1435 -0
- package/corpus/templates/plan/app/lib/plan-comment-editor-helpers.ts +142 -0
- package/corpus/templates/plan/app/lib/plan-local-bridge.ts +376 -0
- package/corpus/templates/plan/app/lib/plan-native-anchors.ts +682 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +216 -2747
- package/corpus/templates/plan/app/root.tsx +13 -10
- package/corpus/templates/plan/app/routes/_index.tsx +1 -2
- package/corpus/templates/plan/app/routes/local-plans.$slug.tsx +1 -1
- package/corpus/templates/plan/app/routes/plans.$id.tsx +1 -2
- package/corpus/templates/plan/app/routes/plans.tsx +1 -2
- package/corpus/templates/plan/app/routes/recaps.$id.tsx +1 -2
- package/corpus/templates/plan/app/routes/recaps.tsx +1 -2
- package/corpus/templates/plan/app/routes/settings.tsx +7 -21
- package/corpus/templates/plan/changelog/2026-07-05-plan-pages-open-at-the-top-unless-a-shared-link-targets-a-sp.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-06-fixed-collaborative-plan-editing-so-content-no-longer-duplic.md +6 -0
- package/corpus/templates/plan/changelog/2026-07-06-fixed-visual-reports-using-excessive-cpu-while-rendering-ske.md +6 -0
- package/corpus/templates/slides/.agents/skills/actions/SKILL.md +55 -0
- package/corpus/templates/slides/app/components/ThemeToggle.tsx +6 -6
- package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +5 -5
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +7 -7
- package/corpus/templates/slides/app/components/deck/ExcalidrawSlide.tsx +2 -1
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +1 -1
- package/corpus/templates/slides/app/components/design-system/DesignSystemCard.tsx +3 -2
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +15 -14
- package/corpus/templates/slides/app/components/editor/AnimationsPanel.tsx +7 -7
- package/corpus/templates/slides/app/components/editor/BlockBubbleMenu.tsx +5 -5
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +6 -6
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +29 -16
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +10 -9
- package/corpus/templates/slides/app/components/editor/GenerateSlidesDialog.tsx +6 -5
- package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +12 -12
- package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +4 -3
- package/corpus/templates/slides/app/components/editor/ImageSearchPanel.tsx +5 -4
- package/corpus/templates/slides/app/components/editor/ImportButton.tsx +14 -4
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -1
- package/corpus/templates/slides/app/components/editor/ShareDialog.tsx +10 -10
- package/corpus/templates/slides/app/components/editor/SlideBubbleMenu.tsx +10 -10
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +6 -6
- package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +2 -1
- package/corpus/templates/slides/app/components/editor/TweaksPanel.tsx +1 -1
- package/corpus/templates/slides/app/components/layout/Header.tsx +2 -2
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +5 -5
- package/corpus/templates/slides/app/components/ui/alert-dialog.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/button.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/card.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/dialog.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/dropdown-menu.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/input.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/label.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/popover.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/scroll-area.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/select.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/separator.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/sheet.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/skeleton.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/spinner.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/textarea.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/toast.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/toaster.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/toggle-group.tsx +1 -0
- package/corpus/templates/slides/app/components/ui/toolkit-provider.tsx +12 -0
- package/corpus/templates/slides/app/components/ui/tooltip.tsx +1 -0
- package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +6 -6
- package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +7 -7
- package/corpus/templates/slides/app/global.css +1 -0
- package/corpus/templates/slides/app/hooks/use-toast.ts +1 -0
- package/corpus/templates/slides/app/lib/upload-response.ts +71 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +3 -3
- package/corpus/templates/slides/app/pages/DesignSystems.tsx +1 -1
- package/corpus/templates/slides/app/pages/Index.tsx +14 -17
- package/corpus/templates/slides/app/root.tsx +13 -10
- package/corpus/templates/slides/app/routes/settings.tsx +6 -21
- package/corpus/templates/slides/changelog/2026-07-06-mobile-layouts-no-longer-stack-two-top-headers.md +5 -0
- package/corpus/templates/slides/server/handlers/uploads.ts +7 -2
- package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
- package/dist/agent/durable-background.d.ts +29 -0
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +46 -0
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +7 -2
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +28 -2
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +1 -1
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/output-tokens.d.ts +3 -3
- package/dist/agent/engine/output-tokens.d.ts.map +1 -1
- package/dist/agent/engine/output-tokens.js +16 -14
- package/dist/agent/engine/output-tokens.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +3 -3
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +28 -25
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/engine/translate-ai-sdk.d.ts +14 -2
- package/dist/agent/engine/translate-ai-sdk.d.ts.map +1 -1
- package/dist/agent/engine/translate-ai-sdk.js +33 -18
- package/dist/agent/engine/translate-ai-sdk.js.map +1 -1
- package/dist/agent/engine/translate-anthropic.d.ts.map +1 -1
- package/dist/agent/engine/translate-anthropic.js +42 -1
- package/dist/agent/engine/translate-anthropic.js.map +1 -1
- package/dist/agent/engine/types.d.ts +15 -0
- package/dist/agent/engine/types.d.ts.map +1 -1
- package/dist/agent/engine/types.js.map +1 -1
- package/dist/agent/model-config.d.ts +8 -0
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +86 -0
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts +91 -5
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +389 -180
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/runtime-context.d.ts +29 -0
- package/dist/agent/runtime-context.d.ts.map +1 -1
- package/dist/agent/runtime-context.js +40 -11
- package/dist/agent/runtime-context.js.map +1 -1
- package/dist/agent/tool-result-images.d.ts +49 -0
- package/dist/agent/tool-result-images.d.ts.map +1 -0
- package/dist/agent/tool-result-images.js +141 -0
- package/dist/agent/tool-result-images.js.map +1 -0
- package/dist/brand-kit/fig/index.d.ts.map +1 -1
- package/dist/brand-kit/fig/index.js +6 -3
- package/dist/brand-kit/fig/index.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +67 -18
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +72 -40
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/KeepTabOpenNotice.d.ts +55 -0
- package/dist/client/KeepTabOpenNotice.d.ts.map +1 -0
- package/dist/client/KeepTabOpenNotice.js +85 -0
- package/dist/client/KeepTabOpenNotice.js.map +1 -0
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +2 -1
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts +7 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +7 -2
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +7 -1
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/clipboard.d.ts.map +1 -1
- package/dist/client/clipboard.js +5 -6
- package/dist/client/clipboard.js.map +1 -1
- package/dist/client/components/AgentPresenceChip.d.ts +1 -11
- package/dist/client/components/AgentPresenceChip.d.ts.map +1 -1
- package/dist/client/components/AgentPresenceChip.js +1 -41
- package/dist/client/components/AgentPresenceChip.js.map +1 -1
- package/dist/client/components/LiveCursorOverlay.d.ts +1 -44
- package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
- package/dist/client/components/LiveCursorOverlay.js +1 -123
- package/dist/client/components/LiveCursorOverlay.js.map +1 -1
- package/dist/client/components/PresenceBar.d.ts +1 -26
- package/dist/client/components/PresenceBar.d.ts.map +1 -1
- package/dist/client/components/PresenceBar.js +1 -164
- package/dist/client/components/PresenceBar.js.map +1 -1
- package/dist/client/components/RecentEditHighlights.d.ts +1 -42
- package/dist/client/components/RecentEditHighlights.d.ts.map +1 -1
- package/dist/client/components/RecentEditHighlights.js +1 -145
- package/dist/client/components/RecentEditHighlights.js.map +1 -1
- package/dist/client/components/RemoteSelectionRings.d.ts +1 -51
- package/dist/client/components/RemoteSelectionRings.d.ts.map +1 -1
- package/dist/client/components/RemoteSelectionRings.js +1 -129
- package/dist/client/components/RemoteSelectionRings.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +4 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +74 -26
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/db-admin/SqlEditor.d.ts.map +1 -1
- package/dist/client/db-admin/SqlEditor.js +13 -3
- package/dist/client/db-admin/SqlEditor.js.map +1 -1
- package/dist/client/dynamic-suggestions.d.ts.map +1 -1
- package/dist/client/dynamic-suggestions.js +7 -1
- package/dist/client/dynamic-suggestions.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +7 -7
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsListPage.js +5 -7
- package/dist/client/extensions/ExtensionsListPage.js.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js +1 -1
- package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -1
- package/dist/client/host-bridge.d.ts +2 -0
- package/dist/client/host-bridge.d.ts.map +1 -1
- package/dist/client/host-bridge.js +26 -10
- package/dist/client/host-bridge.js.map +1 -1
- package/dist/client/i18n.d.ts +1 -0
- package/dist/client/i18n.d.ts.map +1 -1
- package/dist/client/i18n.js +3 -1
- package/dist/client/i18n.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +12 -31
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +3 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +2 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts +3 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +17 -8
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts +3 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +103 -9
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
- package/dist/client/settings/SettingsTabsPage.js +5 -5
- package/dist/client/settings/SettingsTabsPage.js.map +1 -1
- package/dist/client/settings/index.d.ts +1 -1
- package/dist/client/settings/index.d.ts.map +1 -1
- package/dist/client/settings/index.js +1 -1
- package/dist/client/settings/index.js.map +1 -1
- package/dist/client/sharing/VisibilityBadge.d.ts +1 -10
- package/dist/client/sharing/VisibilityBadge.d.ts.map +1 -1
- package/dist/client/sharing/VisibilityBadge.js +1 -18
- package/dist/client/sharing/VisibilityBadge.js.map +1 -1
- package/dist/client/use-action.d.ts +3 -0
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +34 -3
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/use-db-sync.d.ts +4 -0
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +37 -18
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-run-stuck-detection.d.ts +8 -1
- package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
- package/dist/client/use-run-stuck-detection.js +3 -2
- package/dist/client/use-run-stuck-detection.js.map +1 -1
- package/dist/coding-tools/run-code.d.ts +36 -0
- package/dist/coding-tools/run-code.d.ts.map +1 -1
- package/dist/coding-tools/run-code.js +333 -62
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/coding-tools/sandbox/background.d.ts +141 -0
- package/dist/coding-tools/sandbox/background.d.ts.map +1 -0
- package/dist/coding-tools/sandbox/background.js +279 -0
- package/dist/coding-tools/sandbox/background.js.map +1 -0
- package/dist/coding-tools/sandbox/executions-store.d.ts +138 -0
- package/dist/coding-tools/sandbox/executions-store.d.ts.map +1 -0
- package/dist/coding-tools/sandbox/executions-store.js +380 -0
- package/dist/coding-tools/sandbox/executions-store.js.map +1 -0
- package/dist/coding-tools/sandbox/index.d.ts +22 -13
- package/dist/coding-tools/sandbox/index.d.ts.map +1 -1
- package/dist/coding-tools/sandbox/index.js +31 -13
- package/dist/coding-tools/sandbox/index.js.map +1 -1
- package/dist/coding-tools/sandbox/local-child-process-adapter.d.ts.map +1 -1
- package/dist/coding-tools/sandbox/local-child-process-adapter.js +61 -4
- package/dist/coding-tools/sandbox/local-child-process-adapter.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/client.d.ts +16 -12
- package/dist/collab/client.d.ts.map +1 -1
- package/dist/collab/client.js +744 -496
- package/dist/collab/client.js.map +1 -1
- package/dist/collab/presence.d.ts +2 -20
- package/dist/collab/presence.d.ts.map +1 -1
- package/dist/collab/presence.js +3 -0
- package/dist/collab/presence.js.map +1 -1
- package/dist/collab/recent-edits.d.ts +2 -37
- package/dist/collab/recent-edits.d.ts.map +1 -1
- package/dist/collab/recent-edits.js +2 -9
- package/dist/collab/recent-edits.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/data-programs/actions.d.ts +19 -0
- package/dist/data-programs/actions.d.ts.map +1 -0
- package/dist/data-programs/actions.js +279 -0
- package/dist/data-programs/actions.js.map +1 -0
- package/dist/data-programs/contract.d.ts +70 -0
- package/dist/data-programs/contract.d.ts.map +1 -0
- package/dist/data-programs/contract.js +225 -0
- package/dist/data-programs/contract.js.map +1 -0
- package/dist/data-programs/execute.d.ts +96 -0
- package/dist/data-programs/execute.d.ts.map +1 -0
- package/dist/data-programs/execute.js +454 -0
- package/dist/data-programs/execute.js.map +1 -0
- package/dist/data-programs/index.d.ts +11 -0
- package/dist/data-programs/index.d.ts.map +1 -0
- package/dist/data-programs/index.js +11 -0
- package/dist/data-programs/index.js.map +1 -0
- package/dist/data-programs/schema.d.ts +517 -0
- package/dist/data-programs/schema.d.ts.map +1 -0
- package/dist/data-programs/schema.js +138 -0
- package/dist/data-programs/schema.js.map +1 -0
- package/dist/data-programs/store.d.ts +158 -0
- package/dist/data-programs/store.d.ts.map +1 -0
- package/dist/data-programs/store.js +493 -0
- package/dist/data-programs/store.js.map +1 -0
- package/dist/db/create-get-db.d.ts +25 -0
- package/dist/db/create-get-db.d.ts.map +1 -1
- package/dist/db/create-get-db.js +25 -2
- package/dist/db/create-get-db.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +34 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/extensions/actions.js +2 -2
- package/dist/extensions/actions.js.map +1 -1
- package/dist/file-upload/builder.d.ts.map +1 -1
- package/dist/file-upload/builder.js +9 -0
- package/dist/file-upload/builder.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +21 -10
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/local-artifacts/index.js +1 -1
- package/dist/local-artifacts/index.js.map +1 -1
- package/dist/localization/default-messages.d.ts +0 -1
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +1 -2
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp-client/index.d.ts +9 -0
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +23 -0
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/default-steps.d.ts.map +1 -1
- package/dist/onboarding/default-steps.js +2 -2
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +35 -3
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +4 -8
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/agent-engines/test-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/test-agent-engine.js +7 -2
- package/dist/scripts/agent-engines/test-agent-engine.js.map +1 -1
- package/dist/scripts/db/tool-mode.js +3 -3
- package/dist/scripts/db/tool-mode.js.map +1 -1
- package/dist/scripts/resources/migrate-learnings.d.ts +5 -1
- package/dist/scripts/resources/migrate-learnings.d.ts.map +1 -1
- package/dist/scripts/resources/migrate-learnings.js +8 -10
- package/dist/scripts/resources/migrate-learnings.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +20 -6
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +138 -59
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +62 -8
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +75 -3
- package/dist/server/auth.js.map +1 -1
- package/dist/server/builder-design-systems.js +2 -2
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +100 -20
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +6 -3
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +25 -8
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +3 -0
- package/dist/server/csrf.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +37 -1
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/oauth-helpers.d.ts.map +1 -1
- package/dist/server/oauth-helpers.js +9 -1
- package/dist/server/oauth-helpers.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +80 -48
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/prompts/shared-rules.js +2 -2
- package/dist/server/prompts/shared-rules.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +77 -15
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/styles/agent-native.css +57 -45
- package/dist/templates/default/.agents/skills/actions/SKILL.md +55 -0
- package/dist/templates/default/AGENTS.md +11 -14
- package/dist/templates/default/actions/navigate.ts +1 -1
- package/dist/templates/default/app/i18n/ar-SA.ts +0 -1
- package/dist/templates/default/app/i18n/de-DE.ts +0 -1
- package/dist/templates/default/app/i18n/en-US.ts +0 -1
- package/dist/templates/default/app/i18n/es-ES.ts +0 -1
- package/dist/templates/default/app/i18n/fr-FR.ts +0 -1
- package/dist/templates/default/app/i18n/hi-IN.ts +0 -1
- package/dist/templates/default/app/i18n/ja-JP.ts +0 -1
- package/dist/templates/default/app/i18n/ko-KR.ts +0 -1
- package/dist/templates/default/app/i18n/pt-BR.ts +0 -1
- package/dist/templates/default/app/i18n/zh-CN.ts +0 -1
- package/dist/templates/default/app/i18n/zh-TW.ts +0 -1
- package/dist/templates/default/app/routes/settings.tsx +22 -33
- package/dist/templates/headless/.agents/skills/agent-native-docs/SKILL.md +20 -7
- package/dist/templates/headless/AGENTS.md +9 -6
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +55 -0
- package/dist/templates/workspace-core/.agents/skills/data-programs/SKILL.md +128 -0
- package/dist/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +10 -4
- package/dist/vite/agents-bundle-plugin.d.ts.map +1 -1
- package/dist/vite/agents-bundle-plugin.js +26 -1
- package/dist/vite/agents-bundle-plugin.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +11 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/agent-native-toolkit.mdx +87 -146
- package/docs/content/cloneable-saas.mdx +19 -19
- package/docs/content/components.mdx +16 -4
- package/docs/content/data-programs.mdx +329 -0
- package/docs/content/deployment.mdx +9 -9
- package/docs/content/extensions.mdx +8 -7
- package/docs/content/generative-ui.mdx +5 -3
- package/docs/content/key-concepts.mdx +6 -9
- package/docs/content/locales/ar-SA/agent-native-toolkit.mdx +100 -70
- package/docs/content/locales/ar-SA/cloneable-saas.mdx +19 -19
- package/docs/content/locales/ar-SA/components.mdx +12 -2
- package/docs/content/locales/ar-SA/data-programs.mdx +329 -0
- package/docs/content/locales/ar-SA/deployment.mdx +8 -9
- package/docs/content/locales/ar-SA/extensions.mdx +6 -4
- package/docs/content/locales/ar-SA/key-concepts.mdx +7 -9
- package/docs/content/locales/ar-SA/real-time-collaboration.mdx +9 -10
- package/docs/content/locales/de-DE/agent-native-toolkit.mdx +100 -74
- package/docs/content/locales/de-DE/cloneable-saas.mdx +19 -19
- package/docs/content/locales/de-DE/components.mdx +12 -2
- package/docs/content/locales/de-DE/data-programs.mdx +329 -0
- package/docs/content/locales/de-DE/deployment.mdx +8 -9
- package/docs/content/locales/de-DE/extensions.mdx +6 -4
- package/docs/content/locales/de-DE/key-concepts.mdx +8 -10
- package/docs/content/locales/de-DE/real-time-collaboration.mdx +9 -10
- package/docs/content/locales/es-ES/agent-native-toolkit.mdx +100 -74
- package/docs/content/locales/es-ES/cloneable-saas.mdx +19 -19
- package/docs/content/locales/es-ES/components.mdx +12 -2
- package/docs/content/locales/es-ES/data-programs.mdx +329 -0
- package/docs/content/locales/es-ES/deployment.mdx +8 -9
- package/docs/content/locales/es-ES/extensions.mdx +6 -4
- package/docs/content/locales/es-ES/key-concepts.mdx +8 -10
- package/docs/content/locales/es-ES/real-time-collaboration.mdx +9 -10
- package/docs/content/locales/fr-FR/agent-native-toolkit.mdx +100 -76
- package/docs/content/locales/fr-FR/cloneable-saas.mdx +19 -19
- package/docs/content/locales/fr-FR/components.mdx +12 -2
- package/docs/content/locales/fr-FR/data-programs.mdx +329 -0
- package/docs/content/locales/fr-FR/deployment.mdx +8 -9
- package/docs/content/locales/fr-FR/extensions.mdx +6 -4
- package/docs/content/locales/fr-FR/key-concepts.mdx +8 -10
- package/docs/content/locales/fr-FR/real-time-collaboration.mdx +9 -10
- package/docs/content/locales/hi-IN/agent-native-toolkit.mdx +100 -71
- package/docs/content/locales/hi-IN/cloneable-saas.mdx +19 -19
- package/docs/content/locales/hi-IN/components.mdx +20 -10
- package/docs/content/locales/hi-IN/data-programs.mdx +329 -0
- package/docs/content/locales/hi-IN/deployment.mdx +8 -9
- package/docs/content/locales/hi-IN/extensions.mdx +6 -4
- package/docs/content/locales/hi-IN/key-concepts.mdx +8 -10
- package/docs/content/locales/hi-IN/real-time-collaboration.mdx +9 -10
- package/docs/content/locales/ja-JP/agent-native-toolkit.mdx +100 -64
- package/docs/content/locales/ja-JP/cloneable-saas.mdx +19 -19
- package/docs/content/locales/ja-JP/components.mdx +12 -2
- package/docs/content/locales/ja-JP/data-programs.mdx +329 -0
- package/docs/content/locales/ja-JP/deployment.mdx +8 -9
- package/docs/content/locales/ja-JP/extensions.mdx +6 -4
- package/docs/content/locales/ja-JP/key-concepts.mdx +9 -11
- package/docs/content/locales/ja-JP/real-time-collaboration.mdx +9 -10
- package/docs/content/locales/ko-KR/agent-native-toolkit.mdx +100 -60
- package/docs/content/locales/ko-KR/cloneable-saas.mdx +19 -19
- package/docs/content/locales/ko-KR/components.mdx +20 -10
- package/docs/content/locales/ko-KR/data-programs.mdx +329 -0
- package/docs/content/locales/ko-KR/deployment.mdx +8 -9
- package/docs/content/locales/ko-KR/extensions.mdx +16 -14
- package/docs/content/locales/ko-KR/key-concepts.mdx +9 -11
- package/docs/content/locales/ko-KR/real-time-collaboration.mdx +9 -10
- package/docs/content/locales/pt-BR/agent-native-toolkit.mdx +100 -74
- package/docs/content/locales/pt-BR/cloneable-saas.mdx +19 -19
- package/docs/content/locales/pt-BR/components.mdx +12 -2
- package/docs/content/locales/pt-BR/data-programs.mdx +329 -0
- package/docs/content/locales/pt-BR/deployment.mdx +8 -9
- package/docs/content/locales/pt-BR/extensions.mdx +6 -4
- package/docs/content/locales/pt-BR/key-concepts.mdx +8 -11
- package/docs/content/locales/pt-BR/real-time-collaboration.mdx +9 -10
- package/docs/content/locales/zh-CN/agent-native-toolkit.mdx +100 -58
- package/docs/content/locales/zh-CN/cloneable-saas.mdx +19 -19
- package/docs/content/locales/zh-CN/components.mdx +20 -10
- package/docs/content/locales/zh-CN/data-programs.mdx +329 -0
- package/docs/content/locales/zh-CN/deployment.mdx +7 -9
- package/docs/content/locales/zh-CN/extensions.mdx +16 -14
- package/docs/content/locales/zh-CN/key-concepts.mdx +7 -11
- package/docs/content/locales/zh-CN/real-time-collaboration.mdx +10 -11
- package/docs/content/locales/zh-TW/agent-native-toolkit.mdx +100 -58
- package/docs/content/locales/zh-TW/cloneable-saas.mdx +19 -19
- package/docs/content/locales/zh-TW/components.mdx +20 -10
- package/docs/content/locales/zh-TW/data-programs.mdx +329 -0
- package/docs/content/locales/zh-TW/deployment.mdx +7 -9
- package/docs/content/locales/zh-TW/extensions.mdx +16 -14
- package/docs/content/locales/zh-TW/key-concepts.mdx +7 -11
- package/docs/content/locales/zh-TW/real-time-collaboration.mdx +10 -11
- package/docs/content/real-time-collaboration.mdx +10 -11
- package/docs/content/toolkit-app-adapters.mdx +46 -0
- package/docs/content/toolkit-collaboration-ui.mdx +48 -0
- package/docs/content/toolkit-sharing-ui.mdx +33 -0
- package/docs/content/toolkit-shell-hooks.mdx +27 -0
- package/docs/content/toolkit-ui.mdx +69 -0
- package/package.json +6 -2
- package/src/templates/default/.agents/skills/actions/SKILL.md +55 -0
- package/src/templates/default/AGENTS.md +11 -14
- package/src/templates/default/actions/navigate.spec.ts +53 -0
- package/src/templates/default/actions/navigate.ts +1 -1
- package/src/templates/default/app/i18n/ar-SA.ts +0 -1
- package/src/templates/default/app/i18n/de-DE.ts +0 -1
- package/src/templates/default/app/i18n/en-US.ts +0 -1
- package/src/templates/default/app/i18n/es-ES.ts +0 -1
- package/src/templates/default/app/i18n/fr-FR.ts +0 -1
- package/src/templates/default/app/i18n/hi-IN.ts +0 -1
- package/src/templates/default/app/i18n/ja-JP.ts +0 -1
- package/src/templates/default/app/i18n/ko-KR.ts +0 -1
- package/src/templates/default/app/i18n/pt-BR.ts +0 -1
- package/src/templates/default/app/i18n/zh-CN.ts +0 -1
- package/src/templates/default/app/i18n/zh-TW.ts +0 -1
- package/src/templates/default/app/routes/settings.tsx +22 -33
- package/src/templates/headless/.agents/skills/agent-native-docs/SKILL.md +20 -7
- package/src/templates/headless/AGENTS.md +9 -6
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +55 -0
- package/src/templates/workspace-core/.agents/skills/data-programs/SKILL.md +128 -0
- package/src/templates/workspace-core/.agents/skills/harness-agents/SKILL.md +10 -4
- package/corpus/templates/design/app/components/design/inspector/InspectorAiActions.tsx +0 -153
- package/corpus/templates/design/app/hooks/useAgentEditRequest.ts +0 -133
- package/corpus/templates/design/app/pages/DesignEditor.tsx +0 -22928
- package/corpus/templates/design/server/lib/figma-import/clipboard.ts +0 -122
- package/corpus/templates/design/server/lib/figma-import/decode.ts +0 -455
- package/corpus/templates/design/server/lib/figma-import/processor.ts +0 -116
- package/corpus/templates/design/server/lib/figma-import/render-html.ts +0 -397
- package/corpus/templates/design/server/lib/figma-import/types.ts +0 -60
|
@@ -52,6 +52,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
52
52
|
var designCanvasScreenId = __DESIGN_CANVAS_SCREEN_ID__ || "";
|
|
53
53
|
var designCanvasBoardSurface = !!__DESIGN_CANVAS_BOARD_SURFACE__;
|
|
54
54
|
var scaleToolEnabled = false;
|
|
55
|
+
// Interaction-state forced preview (phase 2 — see shared/interaction-states.ts's
|
|
56
|
+
// "Forced-preview mechanism" doc comment). Tracks which single node id
|
|
57
|
+
// currently carries the `data-an-state-preview` attribute so a later
|
|
58
|
+
// `state-preview` message for a DIFFERENT node clears the previous one
|
|
59
|
+
// first — only one element can be force-previewing a state at a time,
|
|
60
|
+
// matching the inspector's single-selection InteractionStatePanel.
|
|
61
|
+
var statePreviewNodeId: string | null = null;
|
|
55
62
|
var editorChromeScaleX = Math.max(
|
|
56
63
|
0.05,
|
|
57
64
|
Number(__EDITOR_CHROME_SCALE_X__) || 1,
|
|
@@ -627,6 +634,33 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
627
634
|
var sourceBacked =
|
|
628
635
|
hasStableOwnSource(el) || !!closestStableSourceElement(el);
|
|
629
636
|
var sourceId = sourceBacked ? getSourceId(el) || getSelector(el) : "";
|
|
637
|
+
// Id-on-demand (empty-node-id fix, bridge side): AI-generated screens
|
|
638
|
+
// frequently ship with NO data-agent-native-node-id anywhere, which
|
|
639
|
+
// breaks every id-keyed operation host-side ("Could not move that
|
|
640
|
+
// layer", `Node with data-agent-native-node-id="" not found`). When the
|
|
641
|
+
// element has no stable own id, mint a durable candidate once and expose
|
|
642
|
+
// it as `pendingNodeId` in the payload so the HOST can persist it into
|
|
643
|
+
// the source as the element's real data-agent-native-node-id. The mint
|
|
644
|
+
// is stored under data-an-pending-node-id — an attribute that
|
|
645
|
+
// getSourceId/getSelector/closestStableSourceElement deliberately do NOT
|
|
646
|
+
// read — so until the host persists it, resolution still flows through
|
|
647
|
+
// the existing structural-selector fallback unchanged.
|
|
648
|
+
var pendingNodeId = "";
|
|
649
|
+
if (
|
|
650
|
+
!getSourceId(el) &&
|
|
651
|
+
el !== document.body &&
|
|
652
|
+
el !== document.documentElement &&
|
|
653
|
+
el.getAttribute &&
|
|
654
|
+
el.setAttribute
|
|
655
|
+
) {
|
|
656
|
+
pendingNodeId = el.getAttribute("data-an-pending-node-id") || "";
|
|
657
|
+
if (!pendingNodeId) {
|
|
658
|
+
pendingNodeId = freshRuntimeNodeId("pending");
|
|
659
|
+
try {
|
|
660
|
+
el.setAttribute("data-an-pending-node-id", pendingNodeId);
|
|
661
|
+
} catch (_err) {}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
630
664
|
var parentLayout = parentStyles
|
|
631
665
|
? {
|
|
632
666
|
display: parentStyles.display,
|
|
@@ -735,6 +769,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
735
769
|
componentName: componentName || undefined,
|
|
736
770
|
id: el.id || undefined,
|
|
737
771
|
sourceId: sourceId,
|
|
772
|
+
pendingNodeId: pendingNodeId || undefined,
|
|
738
773
|
selector: getSelector(el),
|
|
739
774
|
classes: Array.from(el.classList),
|
|
740
775
|
computedStyles: {
|
|
@@ -792,6 +827,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
792
827
|
outlineStyle: cs.outlineStyle,
|
|
793
828
|
outlineColor: cs.outlineColor,
|
|
794
829
|
outlineOffset: cs.outlineOffset,
|
|
830
|
+
// Text glyph outline (Figma-parity text "Stroke") — CSS has no
|
|
831
|
+
// unprefixed alias, so this is read via the vendor-prefixed
|
|
832
|
+
// longhands directly. See applyStyleEdit/normalizeStyleProperty in
|
|
833
|
+
// shared/code-layer.ts for the matching write-side allow-list entry.
|
|
834
|
+
webkitTextStrokeWidth: (
|
|
835
|
+
cs as unknown as { webkitTextStrokeWidth?: string }
|
|
836
|
+
).webkitTextStrokeWidth,
|
|
837
|
+
webkitTextStrokeColor: (
|
|
838
|
+
cs as unknown as { webkitTextStrokeColor?: string }
|
|
839
|
+
).webkitTextStrokeColor,
|
|
795
840
|
boxShadow: cs.boxShadow,
|
|
796
841
|
textShadow: cs.textShadow,
|
|
797
842
|
filter: cs.filter,
|
|
@@ -1062,6 +1107,72 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1062
1107
|
selectionOverlay.appendChild(spacingOverlay);
|
|
1063
1108
|
document.body.appendChild(selectionOverlay);
|
|
1064
1109
|
|
|
1110
|
+
// ── Gradient edit overlay (in-iframe parity for MultiScreenCanvas's
|
|
1111
|
+
// GradientEditOverlay) ──────────────────────────────────────────────────
|
|
1112
|
+
// Renders the same gradient line + endpoint squares + round stop markers
|
|
1113
|
+
// over an element *inside* this screen's iframe content, driven entirely
|
|
1114
|
+
// by `gradient-edit-target` / `gradient-edit-clear` postMessages from the
|
|
1115
|
+
// parent (DesignEditor forwards its existing `gradientEditTarget` state
|
|
1116
|
+
// for the active screen — see the doc comment on
|
|
1117
|
+
// `gradientEditOverlayTarget` below for the exact wiring contract). Linear
|
|
1118
|
+
// gradients only, matching MultiScreenCanvas's overlay scope: an
|
|
1119
|
+
// unparseable or non-linear `cssValue` renders nothing.
|
|
1120
|
+
//
|
|
1121
|
+
// The math below (gradientLineEndpoints/gradientStopPoints/
|
|
1122
|
+
// angleFromDraggedEndpoint/stopPercentFromDraggedPoint) is a direct port
|
|
1123
|
+
// of the same-named pure functions exported from MultiScreenCanvas.tsx —
|
|
1124
|
+
// this file cannot import them (bridge sources may not import/require
|
|
1125
|
+
// anything, see bridge.guard.spec.ts), so the formulas are duplicated
|
|
1126
|
+
// here verbatim. Keep both copies in sync if the math ever changes.
|
|
1127
|
+
var gradientOverlay = document.createElement("div");
|
|
1128
|
+
gradientOverlay.setAttribute("data-agent-native-edit-overlay", "gradient");
|
|
1129
|
+
gradientOverlay.style.cssText =
|
|
1130
|
+
"position:fixed;z-index:99998;pointer-events:none;display:none;box-sizing:border-box;";
|
|
1131
|
+
var gradientOverlaySvg = document.createElementNS(
|
|
1132
|
+
"http://www.w3.org/2000/svg",
|
|
1133
|
+
"svg",
|
|
1134
|
+
);
|
|
1135
|
+
gradientOverlaySvg.setAttribute("data-gradient-edit-line", "");
|
|
1136
|
+
gradientOverlaySvg.style.cssText =
|
|
1137
|
+
"position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none;";
|
|
1138
|
+
var gradientOverlayLineOutline = document.createElementNS(
|
|
1139
|
+
"http://www.w3.org/2000/svg",
|
|
1140
|
+
"line",
|
|
1141
|
+
);
|
|
1142
|
+
gradientOverlayLineOutline.setAttribute("stroke", "rgba(255,255,255,0.95)");
|
|
1143
|
+
var gradientOverlayLine = document.createElementNS(
|
|
1144
|
+
"http://www.w3.org/2000/svg",
|
|
1145
|
+
"line",
|
|
1146
|
+
);
|
|
1147
|
+
gradientOverlayLine.setAttribute(
|
|
1148
|
+
"stroke",
|
|
1149
|
+
"var(--design-editor-accent-color)",
|
|
1150
|
+
);
|
|
1151
|
+
gradientOverlaySvg.appendChild(gradientOverlayLineOutline);
|
|
1152
|
+
gradientOverlaySvg.appendChild(gradientOverlayLine);
|
|
1153
|
+
gradientOverlay.appendChild(gradientOverlaySvg);
|
|
1154
|
+
var gradientOverlayStartHandle = document.createElement("span");
|
|
1155
|
+
gradientOverlayStartHandle.setAttribute("data-gradient-endpoint", "start");
|
|
1156
|
+
gradientOverlayStartHandle.setAttribute("role", "slider");
|
|
1157
|
+
gradientOverlayStartHandle.setAttribute(
|
|
1158
|
+
"aria-label",
|
|
1159
|
+
"Gradient start" /* i18n-ignore */,
|
|
1160
|
+
);
|
|
1161
|
+
gradientOverlayStartHandle.style.cssText =
|
|
1162
|
+
"position:absolute;pointer-events:auto;cursor:move;border-radius:2px;box-sizing:border-box;background:var(--design-editor-accent-contrast-color);border:1px solid var(--design-editor-accent-color);box-shadow:0 1px 2px rgba(0,0,0,0.3);";
|
|
1163
|
+
var gradientOverlayEndHandle = document.createElement("span");
|
|
1164
|
+
gradientOverlayEndHandle.setAttribute("data-gradient-endpoint", "end");
|
|
1165
|
+
gradientOverlayEndHandle.setAttribute("role", "slider");
|
|
1166
|
+
gradientOverlayEndHandle.setAttribute(
|
|
1167
|
+
"aria-label",
|
|
1168
|
+
"Gradient end" /* i18n-ignore */,
|
|
1169
|
+
);
|
|
1170
|
+
gradientOverlayEndHandle.style.cssText =
|
|
1171
|
+
gradientOverlayStartHandle.style.cssText;
|
|
1172
|
+
gradientOverlay.appendChild(gradientOverlayStartHandle);
|
|
1173
|
+
gradientOverlay.appendChild(gradientOverlayEndHandle);
|
|
1174
|
+
document.body.appendChild(gradientOverlay);
|
|
1175
|
+
|
|
1065
1176
|
var transformBadge = document.createElement("div");
|
|
1066
1177
|
transformBadge.setAttribute("data-agent-native-transform-badge", "");
|
|
1067
1178
|
transformBadge.style.cssText =
|
|
@@ -1080,6 +1191,25 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1080
1191
|
"position:fixed;z-index:100000;display:none;pointer-events:none;background:var(--design-editor-accent-color);border-radius:999px;box-shadow:0 0 0 1px var(--design-editor-accent-color);";
|
|
1081
1192
|
document.body.appendChild(insertionGuide);
|
|
1082
1193
|
|
|
1194
|
+
// Alignment/smart-guide lines shown while dragging (and resizing) an
|
|
1195
|
+
// element inside the iframe — Figma-style snap-to-sibling guides. Two
|
|
1196
|
+
// shared singleton divs (one per axis) are repositioned per-frame rather
|
|
1197
|
+
// than pooled, matching the insertionGuide convention above. Tagged as an
|
|
1198
|
+
// edit-overlay so elementFromEditorPoint/isOverlayElement never treat a
|
|
1199
|
+
// guide line as a hit-test or drop target. Color matches the overview
|
|
1200
|
+
// canvas's alignment guides (bg-destructive/90) translated to raw CSS.
|
|
1201
|
+
var snapGuideV = document.createElement("div");
|
|
1202
|
+
snapGuideV.setAttribute("data-agent-native-edit-overlay", "snap-guide");
|
|
1203
|
+
snapGuideV.style.cssText =
|
|
1204
|
+
"position:fixed;z-index:100000;display:none;pointer-events:none;width:1px;background:hsl(var(--destructive) / 0.9);";
|
|
1205
|
+
document.body.appendChild(snapGuideV);
|
|
1206
|
+
|
|
1207
|
+
var snapGuideH = document.createElement("div");
|
|
1208
|
+
snapGuideH.setAttribute("data-agent-native-edit-overlay", "snap-guide");
|
|
1209
|
+
snapGuideH.style.cssText =
|
|
1210
|
+
"position:fixed;z-index:100000;display:none;pointer-events:none;height:1px;background:hsl(var(--destructive) / 0.9);";
|
|
1211
|
+
document.body.appendChild(snapGuideH);
|
|
1212
|
+
|
|
1083
1213
|
var measurementOverlay = document.createElement("div");
|
|
1084
1214
|
measurementOverlay.setAttribute("data-agent-native-measurement-overlay", "");
|
|
1085
1215
|
measurementOverlay.style.cssText =
|
|
@@ -1137,7 +1267,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1137
1267
|
} catch (_err) {}
|
|
1138
1268
|
});
|
|
1139
1269
|
|
|
1140
|
-
function updateComponentTag(el: Element | null): void {
|
|
1270
|
+
function updateComponentTag(el: Element | null, knownRect?: DOMRect): void {
|
|
1141
1271
|
if (!el) {
|
|
1142
1272
|
clearComponentTag();
|
|
1143
1273
|
return;
|
|
@@ -1157,17 +1287,27 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1157
1287
|
componentTagOverlay.setAttribute("data-component-node-id", nodeId);
|
|
1158
1288
|
componentTagOverlay.setAttribute("data-component-name", compName);
|
|
1159
1289
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1290
|
+
// Reuse the caller's fresh rect when available: positionOverlay() already
|
|
1291
|
+
// read this element's rect this frame, and an extra getBoundingClientRect
|
|
1292
|
+
// here is a second forced layout when overlay styles were just written.
|
|
1293
|
+
var rect = knownRect || el.getBoundingClientRect();
|
|
1294
|
+
// Constant-screen-size chrome: pill font/padding/offsets and the
|
|
1295
|
+
// component-root outline compensate for the host's iframe scale.
|
|
1296
|
+
var line = chromeLineScale();
|
|
1297
|
+
var tagHeight = 22 * line;
|
|
1298
|
+
var tagTop = rect.top - tagHeight - 4 * line;
|
|
1299
|
+
if (tagTop < 4 * line) tagTop = rect.top + 4 * line;
|
|
1164
1300
|
componentTagOverlay.style.display = "block";
|
|
1301
|
+
componentTagOverlay.style.fontSize = 11 * line + "px";
|
|
1302
|
+
componentTagOverlay.style.padding = 2 * line + "px " + 6 * line + "px";
|
|
1303
|
+
componentTagOverlay.style.borderRadius = 4 * line + "px";
|
|
1304
|
+
componentTagOverlay.style.borderWidth = 1 * line + "px";
|
|
1165
1305
|
componentTagOverlay.style.left = rect.left + "px";
|
|
1166
1306
|
componentTagOverlay.style.top = tagTop + "px";
|
|
1167
1307
|
// Purple outline on the selection overlay distinguishes component roots.
|
|
1168
1308
|
selectionOverlay.style.outline =
|
|
1169
|
-
"
|
|
1170
|
-
selectionOverlay.style.outlineOffset = "
|
|
1309
|
+
2 * line + "px solid " + chromeStrongColorForElement(el);
|
|
1310
|
+
selectionOverlay.style.outlineOffset = 2 * line + "px";
|
|
1171
1311
|
}
|
|
1172
1312
|
|
|
1173
1313
|
function clearComponentTag(): void {
|
|
@@ -1311,6 +1451,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1311
1451
|
var lastSpacingPointerPoint: { x: number; y: number } | null = null;
|
|
1312
1452
|
var spacingHandleStateByKey: Record<string, { value: number }> = {};
|
|
1313
1453
|
var spacingHandleNodesByKey: Record<string, Element> = {};
|
|
1454
|
+
var spacingHatchNodesByKey: Record<string, Element> = {};
|
|
1314
1455
|
var spacingOverlayRenderKey = "";
|
|
1315
1456
|
var activeDragCancel: (() => boolean) | null = null;
|
|
1316
1457
|
var activeCrossScreenStyleSnapshot: unknown | undefined = undefined;
|
|
@@ -1424,7 +1565,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1424
1565
|
var pos = handle.getAttribute("data-corner") || "";
|
|
1425
1566
|
handle.style.width = 7 * sx + "px";
|
|
1426
1567
|
handle.style.height = 7 * sy + "px";
|
|
1427
|
-
handle.style.borderWidth =
|
|
1568
|
+
handle.style.borderWidth = 1 * line + "px";
|
|
1428
1569
|
if (pos.indexOf("n") !== -1) handle.style.top = -4 * sy + "px";
|
|
1429
1570
|
if (pos.indexOf("s") !== -1) handle.style.bottom = -4 * sy + "px";
|
|
1430
1571
|
if (pos.indexOf("w") !== -1) handle.style.left = -4 * sx + "px";
|
|
@@ -1734,6 +1875,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1734
1875
|
spacingOverlay.innerHTML = "";
|
|
1735
1876
|
spacingHandleStateByKey = {};
|
|
1736
1877
|
spacingHandleNodesByKey = {};
|
|
1878
|
+
spacingHatchNodesByKey = {};
|
|
1737
1879
|
spacingOverlayRenderKey = "";
|
|
1738
1880
|
if (!spacingDrag) spacingBadge.style.display = "none";
|
|
1739
1881
|
}
|
|
@@ -1777,14 +1919,28 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1777
1919
|
var stripe =
|
|
1778
1920
|
kind === "gap" ? "rgba(255, 79, 216, 0.58)" : "rgba(46, 168, 255, 0.52)";
|
|
1779
1921
|
var angle = orientation === "vertical" ? "135deg" : "45deg";
|
|
1922
|
+
// Constant-screen-size chrome: stripe density compensates for the host's
|
|
1923
|
+
// iframe scale so the hatch pattern reads identically at any canvas zoom
|
|
1924
|
+
// instead of blurring together at low zoom.
|
|
1925
|
+
var scale = chromeLineScale();
|
|
1780
1926
|
return (
|
|
1781
1927
|
"repeating-linear-gradient(" +
|
|
1782
1928
|
angle +
|
|
1783
1929
|
", " +
|
|
1784
1930
|
stripe +
|
|
1785
|
-
" 0
|
|
1931
|
+
" 0 " +
|
|
1932
|
+
1 * scale +
|
|
1933
|
+
"px, " +
|
|
1786
1934
|
tint +
|
|
1787
|
-
"
|
|
1935
|
+
" " +
|
|
1936
|
+
1 * scale +
|
|
1937
|
+
"px " +
|
|
1938
|
+
4 * scale +
|
|
1939
|
+
"px, transparent " +
|
|
1940
|
+
4 * scale +
|
|
1941
|
+
"px " +
|
|
1942
|
+
7 * scale +
|
|
1943
|
+
"px)"
|
|
1788
1944
|
);
|
|
1789
1945
|
}
|
|
1790
1946
|
|
|
@@ -1794,6 +1950,43 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1794
1950
|
return Math.max(0, Math.min(999, rounded));
|
|
1795
1951
|
}
|
|
1796
1952
|
|
|
1953
|
+
// Figma-style handle hit area: only the small handle *line* itself (plus a
|
|
1954
|
+
// few px of pointer tolerance) should start a padding drag. The rest of the
|
|
1955
|
+
// padding band must fall through to normal element move/select — dragging
|
|
1956
|
+
// anywhere else inside the element (even inside the padding region) moves
|
|
1957
|
+
// the element, it does not resize padding. Gap handles keep the previous
|
|
1958
|
+
// full-region hit area (out of scope for this fix; not covered by the
|
|
1959
|
+
// reported UX regression). Base tolerance is in editor-chrome (unscaled)
|
|
1960
|
+
// pixels; callers multiply by chromeLineScale() so the hit area keeps a
|
|
1961
|
+
// constant on-screen size regardless of canvas zoom, matching how the
|
|
1962
|
+
// handle line's own thickness (chromeLineScale()) is derived.
|
|
1963
|
+
var PADDING_HANDLE_HIT_TOLERANCE_BASE = 4;
|
|
1964
|
+
|
|
1965
|
+
function hitRectForPaddingHandle(
|
|
1966
|
+
line: { x: number; y: number; width: number; height: number } | undefined,
|
|
1967
|
+
region: { x: number; y: number; width: number; height: number },
|
|
1968
|
+
tolerance: number,
|
|
1969
|
+
): { x: number; y: number; width: number; height: number } {
|
|
1970
|
+
if (!line) return region;
|
|
1971
|
+
var minX = Math.min(line.x, region.x);
|
|
1972
|
+
var minY = Math.min(line.y, region.y);
|
|
1973
|
+
var maxX = Math.max(line.x + line.width, region.x + region.width);
|
|
1974
|
+
var maxY = Math.max(line.y + line.height, region.y + region.height);
|
|
1975
|
+
// Only the line itself matters for hit-testing; expand just the line's own
|
|
1976
|
+
// rect by the tolerance, then clamp to the padding region bounds so the
|
|
1977
|
+
// hit area never spills outside the visual padding band.
|
|
1978
|
+
var hitX = Math.max(minX, line.x - tolerance);
|
|
1979
|
+
var hitY = Math.max(minY, line.y - tolerance);
|
|
1980
|
+
var hitRight = Math.min(maxX, line.x + line.width + tolerance);
|
|
1981
|
+
var hitBottom = Math.min(maxY, line.y + line.height + tolerance);
|
|
1982
|
+
return {
|
|
1983
|
+
x: hitX,
|
|
1984
|
+
y: hitY,
|
|
1985
|
+
width: Math.max(1, hitRight - hitX),
|
|
1986
|
+
height: Math.max(1, hitBottom - hitY),
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1797
1990
|
function makeSpacingHandle(config: {
|
|
1798
1991
|
key: string;
|
|
1799
1992
|
groupKey?: string;
|
|
@@ -1804,10 +1997,24 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1804
1997
|
orientation: string;
|
|
1805
1998
|
value: number;
|
|
1806
1999
|
region: { x: number; y: number; width: number; height: number };
|
|
1807
|
-
line?:
|
|
2000
|
+
line?: { x: number; y: number; width: number; height: number };
|
|
1808
2001
|
}): unknown {
|
|
1809
2002
|
var region = config.region;
|
|
1810
2003
|
if (!region || region.width <= 0 || region.height <= 0) return null;
|
|
2004
|
+
var roundedRegion = {
|
|
2005
|
+
x: Math.round(region.x),
|
|
2006
|
+
y: Math.round(region.y),
|
|
2007
|
+
width: Math.max(1, Math.round(region.width)),
|
|
2008
|
+
height: Math.max(1, Math.round(region.height)),
|
|
2009
|
+
};
|
|
2010
|
+
var hit =
|
|
2011
|
+
config.kind === "padding"
|
|
2012
|
+
? hitRectForPaddingHandle(
|
|
2013
|
+
config.line,
|
|
2014
|
+
roundedRegion,
|
|
2015
|
+
PADDING_HANDLE_HIT_TOLERANCE_BASE * chromeLineScale(),
|
|
2016
|
+
)
|
|
2017
|
+
: roundedRegion;
|
|
1811
2018
|
return {
|
|
1812
2019
|
key: config.key,
|
|
1813
2020
|
groupKey: config.groupKey || config.key,
|
|
@@ -1817,12 +2024,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1817
2024
|
side: config.side || "",
|
|
1818
2025
|
orientation: config.orientation,
|
|
1819
2026
|
value: clampSpacingValue(config.value),
|
|
1820
|
-
region:
|
|
1821
|
-
|
|
1822
|
-
y: Math.round(region.y),
|
|
1823
|
-
width: Math.max(1, Math.round(region.width)),
|
|
1824
|
-
height: Math.max(1, Math.round(region.height)),
|
|
1825
|
-
},
|
|
2027
|
+
region: roundedRegion,
|
|
2028
|
+
hit: hit,
|
|
1826
2029
|
line: config.line,
|
|
1827
2030
|
};
|
|
1828
2031
|
}
|
|
@@ -1876,7 +2079,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1876
2079
|
var paddingLeft = readPx(cs.paddingLeft);
|
|
1877
2080
|
var sx = chromeScaleX();
|
|
1878
2081
|
var sy = chromeScaleY();
|
|
1879
|
-
var line =
|
|
2082
|
+
var line = chromeLineScale();
|
|
1880
2083
|
var hLineWidth = Math.max(6, Math.min(18, rect.width * 0.12)) * sx;
|
|
1881
2084
|
var vLineHeight = Math.max(6, Math.min(18, rect.height * 0.12)) * sy;
|
|
1882
2085
|
var innerLeft = borderLeft;
|
|
@@ -1996,7 +2199,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
1996
2199
|
var handles = [];
|
|
1997
2200
|
var sx = chromeScaleX();
|
|
1998
2201
|
var sy = chromeScaleY();
|
|
1999
|
-
var line =
|
|
2202
|
+
var line = chromeLineScale();
|
|
2000
2203
|
var hLineWidth = 8 * sx;
|
|
2001
2204
|
var vLineHeight = 8 * sy;
|
|
2002
2205
|
var isFlex = cs.display === "flex" || cs.display === "inline-flex";
|
|
@@ -2104,6 +2307,14 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2104
2307
|
);
|
|
2105
2308
|
}
|
|
2106
2309
|
|
|
2310
|
+
// Figma-style live value readout for the padding handle: shown while
|
|
2311
|
+
// hovering OR dragging the handle line, positioned ~12px above and to the
|
|
2312
|
+
// right of the pointer (matching showTransformBadge's cursor-relative
|
|
2313
|
+
// offset idiom, but anchored to the badge's bottom-left corner via
|
|
2314
|
+
// translateY(-100%) so the box actually sits above the cursor instead of
|
|
2315
|
+
// growing downward through it) and live-updating as the value changes.
|
|
2316
|
+
// Falls back to the handle-region center when no cursor point is known yet
|
|
2317
|
+
// (e.g. a hover activated programmatically rather than by a pointer move).
|
|
2107
2318
|
function showSpacingBadgeForHandle(
|
|
2108
2319
|
handle: {
|
|
2109
2320
|
key: string;
|
|
@@ -2118,20 +2329,41 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2118
2329
|
line: { x: number; y: number; width: number; height: number } | undefined;
|
|
2119
2330
|
} | null,
|
|
2120
2331
|
value: number,
|
|
2332
|
+
cursorPoint?: { x: number; y: number } | null,
|
|
2121
2333
|
): void {
|
|
2122
2334
|
if (!selectedEl || !handle) {
|
|
2123
2335
|
spacingBadge.style.display = "none";
|
|
2124
2336
|
return;
|
|
2125
2337
|
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2338
|
+
// Constant-screen-size chrome: the host CSS-scales this iframe by the
|
|
2339
|
+
// canvas zoom, so every intrinsic size here (font, padding, radius,
|
|
2340
|
+
// cursor offset) multiplies by chromeLineScale() to render at the same
|
|
2341
|
+
// apparent size at any zoom — the badge was previously unscaled, which
|
|
2342
|
+
// made it microscopic at low overview zooms (the "value box never
|
|
2343
|
+
// appears" report) and oversized when zoomed in.
|
|
2344
|
+
var line = chromeLineScale();
|
|
2345
|
+
var point = cursorPoint || lastSpacingPointerPoint;
|
|
2346
|
+
var x: number;
|
|
2347
|
+
var y: number;
|
|
2348
|
+
if (point) {
|
|
2349
|
+
x = point.x + 12 * line;
|
|
2350
|
+
y = point.y - 12 * line;
|
|
2351
|
+
} else {
|
|
2352
|
+
var rect = selectedEl.getBoundingClientRect();
|
|
2353
|
+
x = rect.left + handle.region.x + handle.region.width / 2;
|
|
2354
|
+
y = rect.top + handle.region.y + handle.region.height / 2;
|
|
2355
|
+
}
|
|
2356
|
+
spacingBadge.textContent = String(clampSpacingValue(value)) + "px";
|
|
2130
2357
|
spacingBadge.style.display = "block";
|
|
2131
2358
|
spacingBadge.style.background = spacingColor(handle.kind);
|
|
2359
|
+
spacingBadge.style.fontSize = 10 * line + "px";
|
|
2360
|
+
spacingBadge.style.padding = 2 * line + "px " + 4 * line + "px";
|
|
2361
|
+
spacingBadge.style.borderRadius = 3 * line + "px";
|
|
2132
2362
|
spacingBadge.style.left = x + "px";
|
|
2133
2363
|
spacingBadge.style.top = y + "px";
|
|
2134
|
-
spacingBadge.style.transform =
|
|
2364
|
+
spacingBadge.style.transform = point
|
|
2365
|
+
? "translateY(-100%)"
|
|
2366
|
+
: "translate(-50%, -50%)";
|
|
2135
2367
|
}
|
|
2136
2368
|
|
|
2137
2369
|
function renderSpacingHandle(
|
|
@@ -2145,13 +2377,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2145
2377
|
orientation: string;
|
|
2146
2378
|
value: number;
|
|
2147
2379
|
region: { x: number; y: number; width: number; height: number };
|
|
2380
|
+
hit: { x: number; y: number; width: number; height: number };
|
|
2148
2381
|
line: { x: number; y: number; width: number; height: number } | undefined;
|
|
2149
2382
|
} | null,
|
|
2150
2383
|
activeGroupKeys: Record<string, boolean>,
|
|
2384
|
+
hoverGroupKeys: Record<string, boolean>,
|
|
2151
2385
|
): void {
|
|
2152
2386
|
if (!handle) return;
|
|
2153
2387
|
spacingHandleStateByKey[handle.key] = handle;
|
|
2154
|
-
var
|
|
2388
|
+
var active = Boolean(activeGroupKeys[handle.groupKey]);
|
|
2389
|
+
var hovered = Boolean(hoverGroupKeys[handle.groupKey]);
|
|
2155
2390
|
var lineNode = document.createElement("span");
|
|
2156
2391
|
lineNode.setAttribute("data-agent-native-spacing-line", handle.kind);
|
|
2157
2392
|
lineNode.style.position = "absolute";
|
|
@@ -2160,11 +2395,42 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2160
2395
|
lineNode.style.borderRadius = "999px";
|
|
2161
2396
|
lineNode.style.left = handle.line.x + "px";
|
|
2162
2397
|
lineNode.style.top = handle.line.y + "px";
|
|
2163
|
-
|
|
2164
|
-
|
|
2398
|
+
// No 1px floor: at zoom > 100% the pill's thickness is intentionally
|
|
2399
|
+
// sub-1px in iframe space (thickness * host scale = constant screen px).
|
|
2400
|
+
lineNode.style.width = handle.line.width + "px";
|
|
2401
|
+
lineNode.style.height = handle.line.height + "px";
|
|
2165
2402
|
lineNode.style.background = spacingColor(handle.kind);
|
|
2166
2403
|
spacingOverlay.appendChild(lineNode);
|
|
2167
2404
|
|
|
2405
|
+
// Visual-only hatch band over the full padding region. Purely decorative
|
|
2406
|
+
// (pointer-events: none) — it must never intercept clicks, since only the
|
|
2407
|
+
// small hit node below is allowed to start a padding drag. Hatch is a
|
|
2408
|
+
// hover affordance only: it shows the band the user is about to resize,
|
|
2409
|
+
// and disappears the instant a drag starts (kind === "padding" only, per
|
|
2410
|
+
// the reported regression; gap handles are out of scope for this fix).
|
|
2411
|
+
// Constant-screen-size chrome: tile the hatch pattern at a size that
|
|
2412
|
+
// compensates for the host's iframe scale (matches spacingFill's scaled
|
|
2413
|
+
// stripe stops — a fixed 6px tile would clip the scaled pattern).
|
|
2414
|
+
var hatchTile = 6 * chromeLineScale() + "px";
|
|
2415
|
+
if (handle.kind === "padding") {
|
|
2416
|
+
var hatchNode = document.createElement("span");
|
|
2417
|
+
hatchNode.setAttribute("data-agent-native-spacing-hatch", handle.kind);
|
|
2418
|
+
hatchNode.style.position = "absolute";
|
|
2419
|
+
hatchNode.style.display = "block";
|
|
2420
|
+
hatchNode.style.boxSizing = "border-box";
|
|
2421
|
+
hatchNode.style.pointerEvents = "none";
|
|
2422
|
+
hatchNode.style.backgroundSize = hatchTile + " " + hatchTile;
|
|
2423
|
+
hatchNode.style.left = handle.region.x + "px";
|
|
2424
|
+
hatchNode.style.top = handle.region.y + "px";
|
|
2425
|
+
hatchNode.style.width = handle.region.width + "px";
|
|
2426
|
+
hatchNode.style.height = handle.region.height + "px";
|
|
2427
|
+
hatchNode.style.background = hovered
|
|
2428
|
+
? spacingFill(handle.kind, handle.orientation)
|
|
2429
|
+
: "transparent";
|
|
2430
|
+
spacingHatchNodesByKey[handle.key] = hatchNode;
|
|
2431
|
+
spacingOverlay.appendChild(hatchNode);
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2168
2434
|
var regionNode = document.createElement("span");
|
|
2169
2435
|
regionNode.setAttribute("data-agent-native-spacing-region", handle.kind);
|
|
2170
2436
|
regionNode.setAttribute("data-orientation", handle.orientation);
|
|
@@ -2173,19 +2439,26 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2173
2439
|
regionNode.style.display = "block";
|
|
2174
2440
|
regionNode.style.boxSizing = "border-box";
|
|
2175
2441
|
regionNode.style.pointerEvents = "auto";
|
|
2176
|
-
regionNode.style.backgroundSize = "
|
|
2442
|
+
regionNode.style.backgroundSize = hatchTile + " " + hatchTile;
|
|
2177
2443
|
regionNode.style.cursor =
|
|
2178
2444
|
handle.orientation === "vertical" ? "ew-resize" : "ns-resize";
|
|
2179
|
-
|
|
2180
|
-
regionNode.style.
|
|
2181
|
-
regionNode.style.
|
|
2182
|
-
regionNode.style.
|
|
2183
|
-
regionNode.style.
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2445
|
+
var hitRect = handle.kind === "padding" ? handle.hit : handle.region;
|
|
2446
|
+
regionNode.style.left = hitRect.x + "px";
|
|
2447
|
+
regionNode.style.top = hitRect.y + "px";
|
|
2448
|
+
regionNode.style.width = hitRect.width + "px";
|
|
2449
|
+
regionNode.style.height = hitRect.height + "px";
|
|
2450
|
+
// The gap-handle band keeps its previous always-tintable full-region
|
|
2451
|
+
// background (unaffected by this fix); padding handles no longer paint
|
|
2452
|
+
// the hatch on this node — buildSpacingHandles' dedicated hatchNode above
|
|
2453
|
+
// owns that so it can stay outside the (now much smaller) hit area.
|
|
2454
|
+
regionNode.style.background =
|
|
2455
|
+
handle.kind !== "padding" && active
|
|
2456
|
+
? spacingFill(handle.kind, handle.orientation)
|
|
2457
|
+
: "transparent";
|
|
2458
|
+
regionNode.style.outline =
|
|
2459
|
+
handle.kind !== "padding" && active
|
|
2460
|
+
? "1px solid " + spacingColor(handle.kind)
|
|
2461
|
+
: "0";
|
|
2189
2462
|
regionNode.style.outlineOffset = "-1px";
|
|
2190
2463
|
regionNode.addEventListener(
|
|
2191
2464
|
"pointerdown",
|
|
@@ -2215,18 +2488,28 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2215
2488
|
orientation: string;
|
|
2216
2489
|
} | null)[],
|
|
2217
2490
|
activeGroupKeys: Record<string, boolean>,
|
|
2491
|
+
hoverGroupKeys: Record<string, boolean>,
|
|
2218
2492
|
): void {
|
|
2219
2493
|
handles.forEach(function (handle) {
|
|
2220
2494
|
if (!handle) return;
|
|
2495
|
+
var active = Boolean(activeGroupKeys[handle.groupKey]);
|
|
2496
|
+
var hovered = Boolean(hoverGroupKeys[handle.groupKey]);
|
|
2221
2497
|
var regionNode = spacingHandleNodesByKey[handle.key];
|
|
2222
|
-
if (
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2498
|
+
if (regionNode) {
|
|
2499
|
+
var gapHighlighted = handle.kind !== "padding" && active;
|
|
2500
|
+
(regionNode as HTMLElement).style.background = gapHighlighted
|
|
2501
|
+
? spacingFill(handle.kind, handle.orientation)
|
|
2502
|
+
: "transparent";
|
|
2503
|
+
(regionNode as HTMLElement).style.outline = gapHighlighted
|
|
2504
|
+
? "1px solid " + spacingColor(handle.kind)
|
|
2505
|
+
: "0";
|
|
2506
|
+
}
|
|
2507
|
+
var hatchNode = spacingHatchNodesByKey[handle.key];
|
|
2508
|
+
if (hatchNode) {
|
|
2509
|
+
(hatchNode as HTMLElement).style.background = hovered
|
|
2510
|
+
? spacingFill(handle.kind, handle.orientation)
|
|
2511
|
+
: "transparent";
|
|
2512
|
+
}
|
|
2230
2513
|
});
|
|
2231
2514
|
}
|
|
2232
2515
|
|
|
@@ -2261,6 +2544,39 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2261
2544
|
return activeGroupKeys;
|
|
2262
2545
|
}
|
|
2263
2546
|
|
|
2547
|
+
// Figma-parity: the diagonal hatch fill over the padding band is a
|
|
2548
|
+
// hover-only VISUAL affordance layered on top of handles that are always
|
|
2549
|
+
// mounted and hit-testable for the selected element (mounting itself is
|
|
2550
|
+
// never gated on hover — see buildSpacingHandles/renderSpacingHandle, which
|
|
2551
|
+
// render every handle unconditionally). The hatch must be visible while the
|
|
2552
|
+
// pointer rests over the handle line (so the user can see the full padding
|
|
2553
|
+
// band they are about to resize) and hidden the instant an actual drag
|
|
2554
|
+
// starts — during the drag only the live value badge communicates the
|
|
2555
|
+
// current amount. This is intentionally a separate concept from
|
|
2556
|
+
// activeSpacingGroupKeys (which mirrors the opposite side during an
|
|
2557
|
+
// alt-drag and still applies while dragging) — hover-hatch and drag-mirror
|
|
2558
|
+
// never overlap in time because a drag suppresses hover state (see
|
|
2559
|
+
// startSpacingDrag).
|
|
2560
|
+
function hoverSpacingGroupKeys(
|
|
2561
|
+
handles: ({
|
|
2562
|
+
key: string;
|
|
2563
|
+
groupKey: string;
|
|
2564
|
+
} | null)[],
|
|
2565
|
+
): Record<string, boolean> {
|
|
2566
|
+
var hoverGroupKeys: Record<string, boolean> = {};
|
|
2567
|
+
if (spacingDrag) return hoverGroupKeys;
|
|
2568
|
+
var hoveredKey = hoveredSpacingHandleKey;
|
|
2569
|
+
if (!hoveredKey) return hoverGroupKeys;
|
|
2570
|
+
var handleByKey: Record<string, { groupKey: string }> = {};
|
|
2571
|
+
handles.forEach(function (handle) {
|
|
2572
|
+
if (!handle) return;
|
|
2573
|
+
handleByKey[handle.key] = handle;
|
|
2574
|
+
});
|
|
2575
|
+
var hoveredHandle = handleByKey[hoveredKey];
|
|
2576
|
+
if (hoveredHandle) hoverGroupKeys[hoveredHandle.groupKey] = true;
|
|
2577
|
+
return hoverGroupKeys;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2264
2580
|
function updateSpacingOverlay(el: Element | null): void {
|
|
2265
2581
|
if (el && el !== selectedEl) {
|
|
2266
2582
|
hideSpacingOverlay();
|
|
@@ -2277,6 +2593,9 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2277
2593
|
}
|
|
2278
2594
|
var activeHandle = spacingDrag ? spacingDrag.handle : null;
|
|
2279
2595
|
var activeGroupKeys = activeSpacingGroupKeys(handles, activeHandle);
|
|
2596
|
+
var hoverGroupKeys = hoverSpacingGroupKeys(handles);
|
|
2597
|
+
var badgeHandle =
|
|
2598
|
+
activeHandle || (spacingDrag ? null : hoveredHandleFor(handles));
|
|
2280
2599
|
var nextRenderKey = handles
|
|
2281
2600
|
.map(function (handle) {
|
|
2282
2601
|
return [
|
|
@@ -2297,11 +2616,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2297
2616
|
spacingOverlay.style.display === "block" &&
|
|
2298
2617
|
spacingOverlayRenderKey === nextRenderKey
|
|
2299
2618
|
) {
|
|
2300
|
-
updateSpacingHandleHighlights(handles, activeGroupKeys);
|
|
2301
|
-
if (
|
|
2619
|
+
updateSpacingHandleHighlights(handles, activeGroupKeys, hoverGroupKeys);
|
|
2620
|
+
if (badgeHandle) {
|
|
2302
2621
|
showSpacingBadgeForHandle(
|
|
2303
|
-
|
|
2304
|
-
|
|
2622
|
+
badgeHandle,
|
|
2623
|
+
activeHandle && spacingDrag
|
|
2624
|
+
? spacingDrag.currentValue
|
|
2625
|
+
: badgeHandle.value,
|
|
2305
2626
|
);
|
|
2306
2627
|
} else {
|
|
2307
2628
|
spacingBadge.style.display = "none";
|
|
@@ -2313,19 +2634,41 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2313
2634
|
spacingOverlay.innerHTML = "";
|
|
2314
2635
|
spacingHandleStateByKey = {};
|
|
2315
2636
|
spacingHandleNodesByKey = {};
|
|
2637
|
+
spacingHatchNodesByKey = {};
|
|
2316
2638
|
handles.forEach(function (handle) {
|
|
2317
|
-
renderSpacingHandle(handle, activeGroupKeys);
|
|
2639
|
+
renderSpacingHandle(handle, activeGroupKeys, hoverGroupKeys);
|
|
2318
2640
|
});
|
|
2319
|
-
if (
|
|
2641
|
+
if (badgeHandle) {
|
|
2320
2642
|
showSpacingBadgeForHandle(
|
|
2321
|
-
|
|
2322
|
-
|
|
2643
|
+
badgeHandle,
|
|
2644
|
+
activeHandle && spacingDrag
|
|
2645
|
+
? spacingDrag.currentValue
|
|
2646
|
+
: badgeHandle.value,
|
|
2323
2647
|
);
|
|
2324
2648
|
} else {
|
|
2325
2649
|
spacingBadge.style.display = "none";
|
|
2326
2650
|
}
|
|
2327
2651
|
}
|
|
2328
2652
|
|
|
2653
|
+
// Resolves the handle object matching hoveredSpacingHandleKey, if any — used
|
|
2654
|
+
// to keep the value badge visible on hover (not just during an active drag)
|
|
2655
|
+
// per the padding-handle UX fix: hovering the handle line shows the live
|
|
2656
|
+
// "Npx" readout, dragging keeps showing it with the in-progress value. Note
|
|
2657
|
+
// this only affects which handle drives the *badge* — every handle stays
|
|
2658
|
+
// mounted/hit-testable regardless of hover (see buildSpacingHandles).
|
|
2659
|
+
function hoveredHandleFor(
|
|
2660
|
+
handles: ({ key: string } | null)[],
|
|
2661
|
+
): { key: string } | null {
|
|
2662
|
+
var hoveredKey = hoveredSpacingHandleKey;
|
|
2663
|
+
if (!hoveredKey) return null;
|
|
2664
|
+
var handleByKey: Record<string, { key: string }> = {};
|
|
2665
|
+
handles.forEach(function (handle) {
|
|
2666
|
+
if (!handle) return;
|
|
2667
|
+
handleByKey[handle.key] = handle;
|
|
2668
|
+
});
|
|
2669
|
+
return handleByKey[hoveredKey] || null;
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2329
2672
|
function spacingKeyFromTarget(target: Element | null): string {
|
|
2330
2673
|
var region =
|
|
2331
2674
|
target && target.closest
|
|
@@ -2375,6 +2718,40 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2375
2718
|
activateSpacingHandle(spacingKey);
|
|
2376
2719
|
}
|
|
2377
2720
|
|
|
2721
|
+
// Geometry-based fallback for the padding/gap handle hover: resolves the
|
|
2722
|
+
// handle whose hit rect (line + scaled tolerance zone) contains the given
|
|
2723
|
+
// client point, using the handle state captured at the last overlay
|
|
2724
|
+
// render. The event-target path above (spacingKeyFromTarget) only fires
|
|
2725
|
+
// when the pointermove's target IS the region node — which depends on
|
|
2726
|
+
// overlay z-order and event routing; this direct hit test makes the
|
|
2727
|
+
// hover badge reliable from the shield's pointermove too, so hovering
|
|
2728
|
+
// anywhere on the handle line (with its tolerance zone) always shows the
|
|
2729
|
+
// "Npx" value box.
|
|
2730
|
+
function spacingHandleKeyAtPoint(clientX: number, clientY: number): string {
|
|
2731
|
+
if (!selectedEl || !document.documentElement.contains(selectedEl)) {
|
|
2732
|
+
return "";
|
|
2733
|
+
}
|
|
2734
|
+
var rect = selectedEl.getBoundingClientRect();
|
|
2735
|
+
var localX = clientX - rect.left;
|
|
2736
|
+
var localY = clientY - rect.top;
|
|
2737
|
+
var keys = Object.keys(spacingHandleStateByKey);
|
|
2738
|
+
for (var i = 0; i < keys.length; i += 1) {
|
|
2739
|
+
var handle = spacingHandleStateByKey[keys[i]];
|
|
2740
|
+
if (!handle) continue;
|
|
2741
|
+
var hit = handle.hit || handle.region;
|
|
2742
|
+
if (!hit) continue;
|
|
2743
|
+
if (
|
|
2744
|
+
localX >= hit.x &&
|
|
2745
|
+
localX <= hit.x + hit.width &&
|
|
2746
|
+
localY >= hit.y &&
|
|
2747
|
+
localY <= hit.y + hit.height
|
|
2748
|
+
) {
|
|
2749
|
+
return handle.key;
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
return "";
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2378
2755
|
function spacingRegionFromPoint(
|
|
2379
2756
|
clientX: number,
|
|
2380
2757
|
clientY: number,
|
|
@@ -2450,9 +2827,14 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2450
2827
|
var sx = chromeScaleX();
|
|
2451
2828
|
var sy = chromeScaleY();
|
|
2452
2829
|
var line = chromeLineScale();
|
|
2453
|
-
|
|
2454
|
-
|
|
2830
|
+
// Figma parity: the hover outline is visibly thinner than the selection
|
|
2831
|
+
// outline — hover is a light "you could select this" hint, selection is
|
|
2832
|
+
// the stronger confirmed-state chrome. Matches the overview canvas's
|
|
2833
|
+
// hover (1 * chromeScale) vs selection (1.5 * chromeScale) ratio.
|
|
2834
|
+
highlightOverlay.style.borderWidth = 1 * line + "px";
|
|
2835
|
+
parentAutoLayoutOverlay.style.borderWidth = 1 * line + "px";
|
|
2455
2836
|
selectionOverlay.style.borderWidth = 1.5 * line + "px";
|
|
2837
|
+
marqueeSelectionOverlay.style.borderWidth = 1 * line + "px";
|
|
2456
2838
|
passiveSelectionOverlays.forEach(scalePassiveSelectionOverlay);
|
|
2457
2839
|
if (selectedEl) updateSpacingOverlay(selectedEl);
|
|
2458
2840
|
|
|
@@ -2476,7 +2858,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2476
2858
|
var pos = handle.getAttribute("data-agent-native-edit-handle") || "";
|
|
2477
2859
|
handle.style.width = 7 * sx + "px";
|
|
2478
2860
|
handle.style.height = 7 * sy + "px";
|
|
2479
|
-
handle.style.borderWidth =
|
|
2861
|
+
handle.style.borderWidth = 1 * line + "px";
|
|
2480
2862
|
if (pos.indexOf("n") !== -1) handle.style.top = -4 * sy + "px";
|
|
2481
2863
|
if (pos.indexOf("s") !== -1) handle.style.bottom = -4 * sy + "px";
|
|
2482
2864
|
if (pos.indexOf("w") !== -1) handle.style.left = -4 * sx + "px";
|
|
@@ -2553,7 +2935,9 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2553
2935
|
if (overlay === selectionOverlay) {
|
|
2554
2936
|
applySelectionChrome(el);
|
|
2555
2937
|
updateSpacingOverlay(el);
|
|
2556
|
-
updateComponentTag
|
|
2938
|
+
// `rect` is undefined on the rotated-local-box path; updateComponentTag
|
|
2939
|
+
// falls back to its own read in that case.
|
|
2940
|
+
updateComponentTag(el, rect);
|
|
2557
2941
|
updateParentAutoLayoutOverlay(el);
|
|
2558
2942
|
} else {
|
|
2559
2943
|
applyElementOverlayChrome(overlay, el);
|
|
@@ -2591,6 +2975,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2591
2975
|
var overlay = passiveSelectionOverlays[index];
|
|
2592
2976
|
if (overlay) positionOverlay(overlay, el);
|
|
2593
2977
|
});
|
|
2978
|
+
positionGradientOverlay();
|
|
2594
2979
|
syncOverlayObservers();
|
|
2595
2980
|
}
|
|
2596
2981
|
|
|
@@ -2699,6 +3084,21 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2699
3084
|
var horizontal = Math.abs(x2 - x1) >= Math.abs(y2 - y1);
|
|
2700
3085
|
var line = document.createElement("div");
|
|
2701
3086
|
var labelEl = document.createElement("div");
|
|
3087
|
+
// Constant-screen-size chrome: line thickness, label font/padding, and
|
|
3088
|
+
// label offsets all compensate for the host's iframe scale so the
|
|
3089
|
+
// measurement readout looks identical at any canvas zoom.
|
|
3090
|
+
var scale = chromeLineScale();
|
|
3091
|
+
var lineWidth = 1 * chromeLineScale();
|
|
3092
|
+
var labelChrome =
|
|
3093
|
+
"transform-origin:center;border-radius:" +
|
|
3094
|
+
3 * scale +
|
|
3095
|
+
"px;background:var(--design-editor-measure-color);color:white;padding:" +
|
|
3096
|
+
1 * scale +
|
|
3097
|
+
"px " +
|
|
3098
|
+
4 * scale +
|
|
3099
|
+
"px;font-size:" +
|
|
3100
|
+
11 * scale +
|
|
3101
|
+
"px;";
|
|
2702
3102
|
if (horizontal) {
|
|
2703
3103
|
var left = Math.min(x1, x2);
|
|
2704
3104
|
var width = Math.max(1, Math.abs(x2 - x1));
|
|
@@ -2709,13 +3109,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2709
3109
|
y1 +
|
|
2710
3110
|
"px;width:" +
|
|
2711
3111
|
width +
|
|
2712
|
-
"px;border-top:
|
|
3112
|
+
"px;border-top:" +
|
|
3113
|
+
lineWidth +
|
|
3114
|
+
"px dashed var(--design-editor-measure-color);";
|
|
2713
3115
|
labelEl.style.cssText =
|
|
2714
3116
|
"position:fixed;left:" +
|
|
2715
3117
|
(left + width / 2) +
|
|
2716
3118
|
"px;top:" +
|
|
2717
|
-
(y1 - 9) +
|
|
2718
|
-
"px;transform:translateX(-50%);
|
|
3119
|
+
(y1 - 9 * scale) +
|
|
3120
|
+
"px;transform:translateX(-50%);" +
|
|
3121
|
+
labelChrome;
|
|
2719
3122
|
} else {
|
|
2720
3123
|
var top = Math.min(y1, y2);
|
|
2721
3124
|
var height = Math.max(1, Math.abs(y2 - y1));
|
|
@@ -2726,13 +3129,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2726
3129
|
top +
|
|
2727
3130
|
"px;height:" +
|
|
2728
3131
|
height +
|
|
2729
|
-
"px;border-left:
|
|
3132
|
+
"px;border-left:" +
|
|
3133
|
+
lineWidth +
|
|
3134
|
+
"px dashed var(--design-editor-measure-color);";
|
|
2730
3135
|
labelEl.style.cssText =
|
|
2731
3136
|
"position:fixed;left:" +
|
|
2732
|
-
(x1 + 5) +
|
|
3137
|
+
(x1 + 5 * scale) +
|
|
2733
3138
|
"px;top:" +
|
|
2734
3139
|
(top + height / 2) +
|
|
2735
|
-
"px;transform:translateY(-50%);
|
|
3140
|
+
"px;transform:translateY(-50%);" +
|
|
3141
|
+
labelChrome;
|
|
2736
3142
|
}
|
|
2737
3143
|
labelEl.textContent = label;
|
|
2738
3144
|
measurementOverlay.appendChild(line);
|
|
@@ -2975,7 +3381,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2975
3381
|
var didScroll = scrollElementByWheelDelta(scrollTarget, delta.x, delta.y);
|
|
2976
3382
|
if (!didScroll) return;
|
|
2977
3383
|
stopNativeInteraction(e);
|
|
2978
|
-
|
|
3384
|
+
// Coalesced (not a raw requestAnimationFrame(refreshOverlays)): trackpads
|
|
3385
|
+
// emit several wheel events per frame, and scheduling one full overlay
|
|
3386
|
+
// refresh per event stacks N redundant refreshOverlays() runs into every
|
|
3387
|
+
// frame. With an element selected each run forces multiple synchronous
|
|
3388
|
+
// layout reads, which is exactly the per-event work that froze scrolling
|
|
3389
|
+
// on layout-heavy pages while a selection was active.
|
|
3390
|
+
scheduleRefreshOverlays();
|
|
2979
3391
|
}
|
|
2980
3392
|
|
|
2981
3393
|
function isEditorTypingTarget(target) {
|
|
@@ -2997,6 +3409,13 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
2997
3409
|
var normalized = key && key.length === 1 ? key.toLowerCase() : key;
|
|
2998
3410
|
var primary = e.metaKey || e.ctrlKey;
|
|
2999
3411
|
if (key === "Escape" || key === "Enter") return true;
|
|
3412
|
+
// Space arms Figma-style temporary hand-tool panning while the cursor is
|
|
3413
|
+
// over the preview iframe. Only forward the plain (no-modifier) chord —
|
|
3414
|
+
// the isEditorTypingTarget guard above already keeps this from hijacking
|
|
3415
|
+
// Space while the user is typing in an editable in-iframe target.
|
|
3416
|
+
if (key === " " && e.code === "Space") {
|
|
3417
|
+
return !primary && !e.altKey && !e.shiftKey;
|
|
3418
|
+
}
|
|
3000
3419
|
// Forward Tab only when an element is actively selected so the iframe does
|
|
3001
3420
|
// not intercept Tab when the user is tabbing through browser UI with nothing
|
|
3002
3421
|
// selected (preserves native keyboard accessibility).
|
|
@@ -3538,6 +3957,460 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
3538
3957
|
return Number.isFinite(num) ? num : null;
|
|
3539
3958
|
}
|
|
3540
3959
|
|
|
3960
|
+
// ── Gradient edit overlay: math + minimal linear-gradient CSS parser ────
|
|
3961
|
+
// Ports of MultiScreenCanvas.tsx's exported `gradientLineEndpoints` /
|
|
3962
|
+
// `gradientStopPoints` / `angleFromDraggedEndpoint` /
|
|
3963
|
+
// `stopPercentFromDraggedPoint` (see that file's doc comments for the
|
|
3964
|
+
// full derivation) — duplicated verbatim since this file cannot import.
|
|
3965
|
+
|
|
3966
|
+
function clampGradientT(t: number): number {
|
|
3967
|
+
if (!Number.isFinite(t)) return 0;
|
|
3968
|
+
return Math.max(0, Math.min(1, t));
|
|
3969
|
+
}
|
|
3970
|
+
|
|
3971
|
+
function gradientLineEndpoints(
|
|
3972
|
+
angleDeg: number,
|
|
3973
|
+
width: number,
|
|
3974
|
+
height: number,
|
|
3975
|
+
) {
|
|
3976
|
+
var rad = (angleDeg * Math.PI) / 180;
|
|
3977
|
+
var dx = Math.sin(rad);
|
|
3978
|
+
var dy = -Math.cos(rad);
|
|
3979
|
+
var halfLength = Math.abs((width / 2) * dx) + Math.abs((height / 2) * dy);
|
|
3980
|
+
var center = { x: width / 2, y: height / 2 };
|
|
3981
|
+
return {
|
|
3982
|
+
start: { x: center.x - dx * halfLength, y: center.y - dy * halfLength },
|
|
3983
|
+
end: { x: center.x + dx * halfLength, y: center.y + dy * halfLength },
|
|
3984
|
+
};
|
|
3985
|
+
}
|
|
3986
|
+
|
|
3987
|
+
function gradientStopPoints(
|
|
3988
|
+
angleDeg: number,
|
|
3989
|
+
width: number,
|
|
3990
|
+
height: number,
|
|
3991
|
+
stops: Array<{ position: number }>,
|
|
3992
|
+
) {
|
|
3993
|
+
var line = gradientLineEndpoints(angleDeg, width, height);
|
|
3994
|
+
return stops.map(function (stop) {
|
|
3995
|
+
var t = clampGradientT(stop.position / 100);
|
|
3996
|
+
return {
|
|
3997
|
+
x: line.start.x + (line.end.x - line.start.x) * t,
|
|
3998
|
+
y: line.start.y + (line.end.y - line.start.y) * t,
|
|
3999
|
+
position: stop.position,
|
|
4000
|
+
};
|
|
4001
|
+
});
|
|
4002
|
+
}
|
|
4003
|
+
|
|
4004
|
+
function angleFromDraggedEndpoint(
|
|
4005
|
+
point: { x: number; y: number },
|
|
4006
|
+
width: number,
|
|
4007
|
+
height: number,
|
|
4008
|
+
which: "start" | "end",
|
|
4009
|
+
): number {
|
|
4010
|
+
var center = { x: width / 2, y: height / 2 };
|
|
4011
|
+
var dx = point.x - center.x;
|
|
4012
|
+
var dy = point.y - center.y;
|
|
4013
|
+
if (dx === 0 && dy === 0) return 0;
|
|
4014
|
+
var deg = (Math.atan2(dy, dx) * 180) / Math.PI + 90;
|
|
4015
|
+
if (which === "start") deg += 180;
|
|
4016
|
+
deg = ((deg % 360) + 360) % 360;
|
|
4017
|
+
return deg;
|
|
4018
|
+
}
|
|
4019
|
+
|
|
4020
|
+
function stopPercentFromDraggedPoint(
|
|
4021
|
+
point: { x: number; y: number },
|
|
4022
|
+
angleDeg: number,
|
|
4023
|
+
width: number,
|
|
4024
|
+
height: number,
|
|
4025
|
+
): number {
|
|
4026
|
+
var line = gradientLineEndpoints(angleDeg, width, height);
|
|
4027
|
+
var lineDx = line.end.x - line.start.x;
|
|
4028
|
+
var lineDy = line.end.y - line.start.y;
|
|
4029
|
+
var lengthSquared = lineDx * lineDx + lineDy * lineDy;
|
|
4030
|
+
if (lengthSquared === 0) return 0;
|
|
4031
|
+
var t =
|
|
4032
|
+
((point.x - line.start.x) * lineDx + (point.y - line.start.y) * lineDy) /
|
|
4033
|
+
lengthSquared;
|
|
4034
|
+
return clampGradientT(t) * 100;
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
// Minimal linear-only port of GradientEditor.tsx's parseGradientCss /
|
|
4038
|
+
// gradientToCss (that component owns the canonical parser; this is a
|
|
4039
|
+
// reduced copy scoped to just `linear-gradient(...)`, matching the
|
|
4040
|
+
// MultiScreenCanvas overlay's own linear-only scope).
|
|
4041
|
+
var GRADIENT_LINEAR_RE = /^linear-gradient\s*\(([\s\S]*)\)\s*$/i;
|
|
4042
|
+
var GRADIENT_ANGLE_RE = /(-?\d+(?:\.\d+)?)deg/;
|
|
4043
|
+
function splitGradientTopLevel(input: string): string[] {
|
|
4044
|
+
var parts: string[] = [];
|
|
4045
|
+
var depth = 0;
|
|
4046
|
+
var current = "";
|
|
4047
|
+
for (var i = 0; i < input.length; i += 1) {
|
|
4048
|
+
var char = input.charAt(i);
|
|
4049
|
+
if (char === "(") depth += 1;
|
|
4050
|
+
if (char === ")") depth -= 1;
|
|
4051
|
+
if (char === "," && depth === 0) {
|
|
4052
|
+
parts.push(current.trim());
|
|
4053
|
+
current = "";
|
|
4054
|
+
continue;
|
|
4055
|
+
}
|
|
4056
|
+
current += char;
|
|
4057
|
+
}
|
|
4058
|
+
if (current.trim()) parts.push(current.trim());
|
|
4059
|
+
return parts;
|
|
4060
|
+
}
|
|
4061
|
+
function parseLinearGradientCss(value: string): {
|
|
4062
|
+
angle: number;
|
|
4063
|
+
stops: Array<{ id: string; color: string; position: number }>;
|
|
4064
|
+
} | null {
|
|
4065
|
+
var match = String(value || "")
|
|
4066
|
+
.trim()
|
|
4067
|
+
.match(GRADIENT_LINEAR_RE);
|
|
4068
|
+
if (!match) return null;
|
|
4069
|
+
var segments = splitGradientTopLevel(match[1]);
|
|
4070
|
+
if (segments.length === 0) return null;
|
|
4071
|
+
var angle = 90;
|
|
4072
|
+
var stopStart = 0;
|
|
4073
|
+
var first = segments[0];
|
|
4074
|
+
var angleMatch = first.match(GRADIENT_ANGLE_RE);
|
|
4075
|
+
if (angleMatch) {
|
|
4076
|
+
angle = Number(angleMatch[1]);
|
|
4077
|
+
stopStart = 1;
|
|
4078
|
+
} else if (/to\s+/i.test(first)) {
|
|
4079
|
+
stopStart = 1;
|
|
4080
|
+
}
|
|
4081
|
+
var stopSegments = segments.slice(stopStart);
|
|
4082
|
+
var stops: Array<{ id: string; color: string; position: number }> = [];
|
|
4083
|
+
stopSegments.forEach(function (seg, index) {
|
|
4084
|
+
var posMatch = seg.match(/(-?\d+(?:\.\d+)?)%\s*$/);
|
|
4085
|
+
var color = posMatch ? seg.slice(0, posMatch.index).trim() : seg.trim();
|
|
4086
|
+
if (!color) return;
|
|
4087
|
+
var position = posMatch
|
|
4088
|
+
? Math.max(0, Math.min(100, Number(posMatch[1])))
|
|
4089
|
+
: (index / Math.max(1, stopSegments.length - 1)) * 100;
|
|
4090
|
+
stops.push({ id: "gstop-" + index, color: color, position: position });
|
|
4091
|
+
});
|
|
4092
|
+
if (stops.length < 2) return null;
|
|
4093
|
+
return { angle: angle, stops: stops };
|
|
4094
|
+
}
|
|
4095
|
+
function linearGradientToCss(gradient: {
|
|
4096
|
+
angle: number;
|
|
4097
|
+
stops: Array<{ id: string; color: string; position: number }>;
|
|
4098
|
+
}): string {
|
|
4099
|
+
var sorted = gradient.stops.slice().sort(function (a, b) {
|
|
4100
|
+
return a.position - b.position;
|
|
4101
|
+
});
|
|
4102
|
+
var stopsCss = sorted
|
|
4103
|
+
.map(function (stop) {
|
|
4104
|
+
return stop.color + " " + Math.round(stop.position * 100) / 100 + "%";
|
|
4105
|
+
})
|
|
4106
|
+
.join(", ");
|
|
4107
|
+
return (
|
|
4108
|
+
"linear-gradient(" +
|
|
4109
|
+
Math.round(gradient.angle * 100) / 100 +
|
|
4110
|
+
"deg, " +
|
|
4111
|
+
stopsCss +
|
|
4112
|
+
")"
|
|
4113
|
+
);
|
|
4114
|
+
}
|
|
4115
|
+
|
|
4116
|
+
// gradientEditOverlayTarget doc / parent wiring contract:
|
|
4117
|
+
//
|
|
4118
|
+
// This bridge only RENDERS the overlay + emits drag deltas; it has no idea
|
|
4119
|
+
// which element on the host side "is" the gradient-edited node beyond the
|
|
4120
|
+
// `nodeId` the parent gives it. The parent (DesignEditor.tsx, NOT owned by
|
|
4121
|
+
// this change — see the report) is expected to:
|
|
4122
|
+
//
|
|
4123
|
+
// 1. Keep its existing `gradientEditTarget` state (already threaded into
|
|
4124
|
+
// MultiScreenCanvas's board/screen-frame overlay) as the single
|
|
4125
|
+
// source of truth for "is a gradient edit session active, for which
|
|
4126
|
+
// node, with which CSS value".
|
|
4127
|
+
// 2. Whenever that target refers to an element *inside* the active
|
|
4128
|
+
// screen's iframe content (as opposed to a board/draft primitive
|
|
4129
|
+
// MultiScreenCanvas already draws chrome for directly), postMessage
|
|
4130
|
+
// `{ type: "gradient-edit-target", nodeId, cssValue }` into that
|
|
4131
|
+
// screen's iframe — `nodeId` being the element's
|
|
4132
|
+
// `data-agent-native-node-id`. Post `{ type: "gradient-edit-clear" }`
|
|
4133
|
+
// when the session ends (selection changes, popover closes, or the
|
|
4134
|
+
// target moves to a different screen/board node).
|
|
4135
|
+
// 3. Listen for this bridge's `{ type: "gradient-edit-change", nodeId,
|
|
4136
|
+
// cssValue, phase }` postMessages and route them through the same
|
|
4137
|
+
// style-apply path `visual-style-change` already uses (phase
|
|
4138
|
+
// "preview" for live feedback, "commit" once on release — mirroring
|
|
4139
|
+
// GradientEditOverlayTarget's own onChange contract in
|
|
4140
|
+
// MultiScreenCanvas.tsx).
|
|
4141
|
+
//
|
|
4142
|
+
// Until that parent-side wiring lands, this bridge simply never receives
|
|
4143
|
+
// `gradient-edit-target` and stays fully inert (see the early-return checks
|
|
4144
|
+
// below), so this is a strictly additive, zero-behavior-change surface
|
|
4145
|
+
// until wired up.
|
|
4146
|
+
var gradientEditTarget: { nodeId: string; cssValue: string } | null = null;
|
|
4147
|
+
var gradientDrag: {
|
|
4148
|
+
kind: "endpoint" | "stop";
|
|
4149
|
+
which?: "start" | "end";
|
|
4150
|
+
stopId?: string;
|
|
4151
|
+
pointerId: number;
|
|
4152
|
+
} | null = null;
|
|
4153
|
+
|
|
4154
|
+
function gradientEditTargetElement(): HTMLElement | null {
|
|
4155
|
+
if (!gradientEditTarget) return null;
|
|
4156
|
+
return document.querySelector(
|
|
4157
|
+
'[data-agent-native-node-id="' +
|
|
4158
|
+
String(gradientEditTarget.nodeId)
|
|
4159
|
+
.replace(/\\/g, "\\\\")
|
|
4160
|
+
.replace(/"/g, '\\"') +
|
|
4161
|
+
'"]',
|
|
4162
|
+
) as HTMLElement | null;
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
function hideGradientOverlay(): void {
|
|
4166
|
+
gradientOverlay.style.display = "none";
|
|
4167
|
+
while (gradientOverlay.querySelectorAll("[data-gradient-stop]").length) {
|
|
4168
|
+
var stopEl = gradientOverlay.querySelector("[data-gradient-stop]");
|
|
4169
|
+
if (stopEl && stopEl.parentNode) stopEl.parentNode.removeChild(stopEl);
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
|
|
4173
|
+
function positionGradientOverlay(): void {
|
|
4174
|
+
var target = gradientEditTarget;
|
|
4175
|
+
if (!target) {
|
|
4176
|
+
hideGradientOverlay();
|
|
4177
|
+
return;
|
|
4178
|
+
}
|
|
4179
|
+
var el = gradientEditTargetElement();
|
|
4180
|
+
if (!el || !document.documentElement.contains(el)) {
|
|
4181
|
+
hideGradientOverlay();
|
|
4182
|
+
return;
|
|
4183
|
+
}
|
|
4184
|
+
var gradient = parseLinearGradientCss(target.cssValue);
|
|
4185
|
+
if (!gradient) {
|
|
4186
|
+
// Non-linear/unparseable — render nothing (linear-only scope, matches
|
|
4187
|
+
// MultiScreenCanvas's GradientEditOverlay contract exactly).
|
|
4188
|
+
hideGradientOverlay();
|
|
4189
|
+
return;
|
|
4190
|
+
}
|
|
4191
|
+
var rect = el.getBoundingClientRect();
|
|
4192
|
+
var width = Math.max(1, rect.width);
|
|
4193
|
+
var height = Math.max(1, rect.height);
|
|
4194
|
+
gradientOverlay.style.display = "block";
|
|
4195
|
+
gradientOverlay.style.left = rect.left + "px";
|
|
4196
|
+
gradientOverlay.style.top = rect.top + "px";
|
|
4197
|
+
gradientOverlay.style.width = width + "px";
|
|
4198
|
+
gradientOverlay.style.height = height + "px";
|
|
4199
|
+
|
|
4200
|
+
var line = gradientLineEndpoints(gradient.angle, width, height);
|
|
4201
|
+
var stopPoints = gradientStopPoints(
|
|
4202
|
+
gradient.angle,
|
|
4203
|
+
width,
|
|
4204
|
+
height,
|
|
4205
|
+
gradient.stops,
|
|
4206
|
+
);
|
|
4207
|
+
|
|
4208
|
+
var line1 = chromeLineScale();
|
|
4209
|
+
var lineStrokeWidth = 1.5 * line1;
|
|
4210
|
+
gradientOverlaySvg.setAttribute("viewBox", "0 0 " + width + " " + height);
|
|
4211
|
+
[gradientOverlayLineOutline, gradientOverlayLine].forEach(
|
|
4212
|
+
function (lineEl, index) {
|
|
4213
|
+
lineEl.setAttribute("x1", String(line.start.x));
|
|
4214
|
+
lineEl.setAttribute("y1", String(line.start.y));
|
|
4215
|
+
lineEl.setAttribute("x2", String(line.end.x));
|
|
4216
|
+
lineEl.setAttribute("y2", String(line.end.y));
|
|
4217
|
+
lineEl.setAttribute(
|
|
4218
|
+
"stroke-width",
|
|
4219
|
+
String(index === 0 ? lineStrokeWidth + 1.5 * line1 : lineStrokeWidth),
|
|
4220
|
+
);
|
|
4221
|
+
},
|
|
4222
|
+
);
|
|
4223
|
+
|
|
4224
|
+
var endpointSize = 10 * line1;
|
|
4225
|
+
var endpointBorderWidth = 1.5 * line1;
|
|
4226
|
+
[
|
|
4227
|
+
{ el: gradientOverlayStartHandle, point: line.start, which: "start" },
|
|
4228
|
+
{ el: gradientOverlayEndHandle, point: line.end, which: "end" },
|
|
4229
|
+
].forEach(function (entry) {
|
|
4230
|
+
entry.el.style.left = entry.point.x - endpointSize / 2 + "px";
|
|
4231
|
+
entry.el.style.top = entry.point.y - endpointSize / 2 + "px";
|
|
4232
|
+
entry.el.style.width = endpointSize + "px";
|
|
4233
|
+
entry.el.style.height = endpointSize + "px";
|
|
4234
|
+
entry.el.style.borderWidth = endpointBorderWidth + "px";
|
|
4235
|
+
entry.el.setAttribute(
|
|
4236
|
+
"aria-valuenow",
|
|
4237
|
+
String(Math.round(gradient.angle)),
|
|
4238
|
+
);
|
|
4239
|
+
});
|
|
4240
|
+
|
|
4241
|
+
// Stop markers are rebuilt each render (cheap: 2-8 stops typical) rather
|
|
4242
|
+
// than pooled, matching the overlay's overall "small + self-contained"
|
|
4243
|
+
// design (see the doc comment on MultiScreenCanvas's GradientEditOverlay).
|
|
4244
|
+
hideGradientOverlayStops();
|
|
4245
|
+
var stopSize = 12 * line1;
|
|
4246
|
+
var stopBorderWidth = 2 * line1;
|
|
4247
|
+
stopPoints.forEach(function (point, index) {
|
|
4248
|
+
var stop = gradient.stops[index];
|
|
4249
|
+
if (!stop) return;
|
|
4250
|
+
var stopEl = document.createElement("span");
|
|
4251
|
+
stopEl.setAttribute("data-gradient-stop", stop.id);
|
|
4252
|
+
stopEl.setAttribute("role", "slider");
|
|
4253
|
+
stopEl.setAttribute(
|
|
4254
|
+
"aria-label",
|
|
4255
|
+
stop.color + " at " + Math.round(stop.position) + "%",
|
|
4256
|
+
);
|
|
4257
|
+
stopEl.setAttribute("aria-valuenow", String(Math.round(stop.position)));
|
|
4258
|
+
stopEl.style.cssText =
|
|
4259
|
+
"position:absolute;pointer-events:auto;cursor:grab;border-radius:999px;box-sizing:border-box;border:1px solid white;box-shadow:0 0 0 1px rgba(0,0,0,0.25);";
|
|
4260
|
+
stopEl.style.left = point.x - stopSize / 2 + "px";
|
|
4261
|
+
stopEl.style.top = point.y - stopSize / 2 + "px";
|
|
4262
|
+
stopEl.style.width = stopSize + "px";
|
|
4263
|
+
stopEl.style.height = stopSize + "px";
|
|
4264
|
+
stopEl.style.borderWidth = stopBorderWidth + "px";
|
|
4265
|
+
stopEl.style.backgroundColor = stop.color;
|
|
4266
|
+
stopEl.addEventListener("pointerdown", function (ev: PointerEvent) {
|
|
4267
|
+
beginGradientDrag(ev, { kind: "stop", stopId: stop.id });
|
|
4268
|
+
});
|
|
4269
|
+
gradientOverlay.appendChild(stopEl);
|
|
4270
|
+
});
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
function hideGradientOverlayStops(): void {
|
|
4274
|
+
Array.prototype.slice
|
|
4275
|
+
.call(gradientOverlay.querySelectorAll("[data-gradient-stop]"))
|
|
4276
|
+
.forEach(function (node: Element) {
|
|
4277
|
+
if (node.parentNode) node.parentNode.removeChild(node);
|
|
4278
|
+
});
|
|
4279
|
+
}
|
|
4280
|
+
|
|
4281
|
+
function gradientOverlayLocalPoint(event: PointerEvent): {
|
|
4282
|
+
x: number;
|
|
4283
|
+
y: number;
|
|
4284
|
+
} {
|
|
4285
|
+
var rect = gradientOverlay.getBoundingClientRect();
|
|
4286
|
+
return { x: event.clientX - rect.left, y: event.clientY - rect.top };
|
|
4287
|
+
}
|
|
4288
|
+
|
|
4289
|
+
function emitGradientChange(
|
|
4290
|
+
gradient: {
|
|
4291
|
+
angle: number;
|
|
4292
|
+
stops: Array<{ id: string; color: string; position: number }>;
|
|
4293
|
+
},
|
|
4294
|
+
phase: "preview" | "commit",
|
|
4295
|
+
): void {
|
|
4296
|
+
if (!gradientEditTarget) return;
|
|
4297
|
+
(window.parent as Window).postMessage(
|
|
4298
|
+
{
|
|
4299
|
+
type: "gradient-edit-change",
|
|
4300
|
+
nodeId: gradientEditTarget.nodeId,
|
|
4301
|
+
cssValue: linearGradientToCss(gradient),
|
|
4302
|
+
phase: phase,
|
|
4303
|
+
},
|
|
4304
|
+
"*",
|
|
4305
|
+
);
|
|
4306
|
+
// Keep the local target's cssValue in sync so a subsequent drag tick (or
|
|
4307
|
+
// a re-render triggered by scroll/resize) reflects the in-progress value
|
|
4308
|
+
// instead of waiting for the parent to round-trip a fresh
|
|
4309
|
+
// gradient-edit-target message.
|
|
4310
|
+
gradientEditTarget = {
|
|
4311
|
+
nodeId: gradientEditTarget.nodeId,
|
|
4312
|
+
cssValue: linearGradientToCss(gradient),
|
|
4313
|
+
};
|
|
4314
|
+
}
|
|
4315
|
+
|
|
4316
|
+
function beginGradientDrag(
|
|
4317
|
+
event: PointerEvent,
|
|
4318
|
+
kind:
|
|
4319
|
+
| { kind: "endpoint"; which: "start" | "end" }
|
|
4320
|
+
| { kind: "stop"; stopId: string },
|
|
4321
|
+
): void {
|
|
4322
|
+
event.stopPropagation();
|
|
4323
|
+
event.preventDefault();
|
|
4324
|
+
gradientDrag = {
|
|
4325
|
+
kind: kind.kind,
|
|
4326
|
+
which: kind.kind === "endpoint" ? kind.which : undefined,
|
|
4327
|
+
stopId: kind.kind === "stop" ? kind.stopId : undefined,
|
|
4328
|
+
pointerId: event.pointerId,
|
|
4329
|
+
};
|
|
4330
|
+
var handleEl = event.currentTarget as Element;
|
|
4331
|
+
if (handleEl && (handleEl as HTMLElement).setPointerCapture) {
|
|
4332
|
+
(handleEl as HTMLElement).setPointerCapture(event.pointerId);
|
|
4333
|
+
}
|
|
4334
|
+
document.addEventListener("pointermove", onGradientDragMove, true);
|
|
4335
|
+
document.addEventListener("pointerup", onGradientDragEnd, true);
|
|
4336
|
+
document.addEventListener("pointercancel", onGradientDragEnd, true);
|
|
4337
|
+
}
|
|
4338
|
+
|
|
4339
|
+
function onGradientDragMove(event: PointerEvent): void {
|
|
4340
|
+
var drag = gradientDrag;
|
|
4341
|
+
var target = gradientEditTarget;
|
|
4342
|
+
var el = gradientEditTargetElement();
|
|
4343
|
+
if (!drag || drag.pointerId !== event.pointerId || !target || !el) return;
|
|
4344
|
+
var gradient = parseLinearGradientCss(target.cssValue);
|
|
4345
|
+
if (!gradient) return;
|
|
4346
|
+
var rect = el.getBoundingClientRect();
|
|
4347
|
+
var width = Math.max(1, rect.width);
|
|
4348
|
+
var height = Math.max(1, rect.height);
|
|
4349
|
+
var local = gradientOverlayLocalPoint(event);
|
|
4350
|
+
if (drag.kind === "endpoint" && drag.which) {
|
|
4351
|
+
var nextAngle = angleFromDraggedEndpoint(
|
|
4352
|
+
local,
|
|
4353
|
+
width,
|
|
4354
|
+
height,
|
|
4355
|
+
drag.which,
|
|
4356
|
+
);
|
|
4357
|
+
emitGradientChange(
|
|
4358
|
+
{ angle: nextAngle, stops: gradient.stops },
|
|
4359
|
+
"preview",
|
|
4360
|
+
);
|
|
4361
|
+
positionGradientOverlay();
|
|
4362
|
+
return;
|
|
4363
|
+
}
|
|
4364
|
+
if (drag.kind === "stop" && drag.stopId) {
|
|
4365
|
+
var nextPosition = stopPercentFromDraggedPoint(
|
|
4366
|
+
local,
|
|
4367
|
+
gradient.angle,
|
|
4368
|
+
width,
|
|
4369
|
+
height,
|
|
4370
|
+
);
|
|
4371
|
+
var stopId = drag.stopId;
|
|
4372
|
+
emitGradientChange(
|
|
4373
|
+
{
|
|
4374
|
+
angle: gradient.angle,
|
|
4375
|
+
stops: gradient.stops.map(function (stop) {
|
|
4376
|
+
return stop.id === stopId
|
|
4377
|
+
? { id: stop.id, color: stop.color, position: nextPosition }
|
|
4378
|
+
: stop;
|
|
4379
|
+
}),
|
|
4380
|
+
},
|
|
4381
|
+
"preview",
|
|
4382
|
+
);
|
|
4383
|
+
positionGradientOverlay();
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4387
|
+
function onGradientDragEnd(event: PointerEvent): void {
|
|
4388
|
+
var drag = gradientDrag;
|
|
4389
|
+
if (!drag || drag.pointerId !== event.pointerId) return;
|
|
4390
|
+
gradientDrag = null;
|
|
4391
|
+
document.removeEventListener("pointermove", onGradientDragMove, true);
|
|
4392
|
+
document.removeEventListener("pointerup", onGradientDragEnd, true);
|
|
4393
|
+
document.removeEventListener("pointercancel", onGradientDragEnd, true);
|
|
4394
|
+
var target = gradientEditTarget;
|
|
4395
|
+
if (!target) return;
|
|
4396
|
+
var gradient = parseLinearGradientCss(target.cssValue);
|
|
4397
|
+
if (!gradient) return;
|
|
4398
|
+
emitGradientChange(gradient, "commit");
|
|
4399
|
+
}
|
|
4400
|
+
|
|
4401
|
+
gradientOverlayStartHandle.addEventListener(
|
|
4402
|
+
"pointerdown",
|
|
4403
|
+
function (ev: PointerEvent) {
|
|
4404
|
+
beginGradientDrag(ev, { kind: "endpoint", which: "start" });
|
|
4405
|
+
},
|
|
4406
|
+
);
|
|
4407
|
+
gradientOverlayEndHandle.addEventListener(
|
|
4408
|
+
"pointerdown",
|
|
4409
|
+
function (ev: PointerEvent) {
|
|
4410
|
+
beginGradientDrag(ev, { kind: "endpoint", which: "end" });
|
|
4411
|
+
},
|
|
4412
|
+
);
|
|
4413
|
+
|
|
3541
4414
|
function currentRotation(el) {
|
|
3542
4415
|
var transform =
|
|
3543
4416
|
el.style.transform || window.getComputedStyle(el).transform || "";
|
|
@@ -3663,6 +4536,10 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
3663
4536
|
currentValue: originValue,
|
|
3664
4537
|
mirrorOpposite: !!e.altKey,
|
|
3665
4538
|
};
|
|
4539
|
+
// Hide the hover-only hatch fill the instant the drag begins (Figma-style:
|
|
4540
|
+
// hatch communicates "this is the resizable band" on hover; once dragging,
|
|
4541
|
+
// only the live value badge should be visible over the padding band).
|
|
4542
|
+
updateSpacingOverlay(selectedEl);
|
|
3666
4543
|
showSpacingBadgeForHandle(handle, originValue);
|
|
3667
4544
|
|
|
3668
4545
|
function updateSpacingDragMirrorState(mirrorOpposite: boolean) {
|
|
@@ -4002,10 +4879,18 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4002
4879
|
clientX: number,
|
|
4003
4880
|
clientY: number,
|
|
4004
4881
|
): void {
|
|
4882
|
+
// Constant-screen-size chrome (see showSpacingBadgeForHandle): scale the
|
|
4883
|
+
// label's intrinsic sizes by chromeLineScale() so the move/resize badge
|
|
4884
|
+
// reads the same at any canvas zoom.
|
|
4885
|
+
var line = chromeLineScale();
|
|
4005
4886
|
transformBadge.textContent = text;
|
|
4006
4887
|
transformBadge.style.display = "block";
|
|
4007
|
-
transformBadge.style.
|
|
4008
|
-
transformBadge.style.
|
|
4888
|
+
transformBadge.style.fontSize = 11 * line + "px";
|
|
4889
|
+
transformBadge.style.padding = 3 * line + "px " + 5 * line + "px";
|
|
4890
|
+
transformBadge.style.borderRadius = 4 * line + "px";
|
|
4891
|
+
transformBadge.style.borderWidth = 1 * line + "px";
|
|
4892
|
+
transformBadge.style.left = clientX + 12 * line + "px";
|
|
4893
|
+
transformBadge.style.top = clientY + 12 * line + "px";
|
|
4009
4894
|
}
|
|
4010
4895
|
|
|
4011
4896
|
function hideTransformBadge(): void {
|
|
@@ -4045,6 +4930,84 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4045
4930
|
return true;
|
|
4046
4931
|
}
|
|
4047
4932
|
|
|
4933
|
+
// Multi-select group move: when the user drags an element that is a member
|
|
4934
|
+
// of the current multi-selection (primary selectedEl + the passive
|
|
4935
|
+
// shift-click/marquee set), the whole group moves together — Figma
|
|
4936
|
+
// behavior. Returns the full member list in DOCUMENT ORDER (so a group
|
|
4937
|
+
// flow-insert lands the members consecutively in their existing visual
|
|
4938
|
+
// order) when gestureEl belongs to a 2+ selection, or just [gestureEl]
|
|
4939
|
+
// otherwise. Members nested inside another member are dropped: moving the
|
|
4940
|
+
// ancestor already moves them, and double-applying the delta would fling
|
|
4941
|
+
// them.
|
|
4942
|
+
function collectMoveGroupMembers(gestureEl: Element): Element[] {
|
|
4943
|
+
if (!gestureEl) return [];
|
|
4944
|
+
var raw: Element[] = [];
|
|
4945
|
+
if (selectedEl) raw.push(selectedEl);
|
|
4946
|
+
for (var i = 0; i < passiveSelectionEls.length; i += 1) {
|
|
4947
|
+
raw.push(passiveSelectionEls[i]);
|
|
4948
|
+
}
|
|
4949
|
+
var members: Element[] = [];
|
|
4950
|
+
for (var j = 0; j < raw.length; j += 1) {
|
|
4951
|
+
var candidate = raw[j];
|
|
4952
|
+
if (
|
|
4953
|
+
!candidate ||
|
|
4954
|
+
candidate === document.body ||
|
|
4955
|
+
candidate === document.documentElement ||
|
|
4956
|
+
!document.documentElement.contains(candidate) ||
|
|
4957
|
+
isLayerInteractionBlocked(candidate) ||
|
|
4958
|
+
members.indexOf(candidate) !== -1
|
|
4959
|
+
) {
|
|
4960
|
+
continue;
|
|
4961
|
+
}
|
|
4962
|
+
members.push(candidate);
|
|
4963
|
+
}
|
|
4964
|
+
// Drop members contained by another member.
|
|
4965
|
+
members = members.filter(function (member) {
|
|
4966
|
+
return !members.some(function (other) {
|
|
4967
|
+
return other !== member && other.contains(member);
|
|
4968
|
+
});
|
|
4969
|
+
});
|
|
4970
|
+
var gestureMember: Element | null = null;
|
|
4971
|
+
for (var k = 0; k < members.length; k += 1) {
|
|
4972
|
+
if (
|
|
4973
|
+
members[k] === gestureEl ||
|
|
4974
|
+
(members[k].contains && members[k].contains(gestureEl))
|
|
4975
|
+
) {
|
|
4976
|
+
gestureMember = members[k];
|
|
4977
|
+
break;
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
if (!gestureMember || members.length < 2) return [gestureEl];
|
|
4981
|
+
members.sort(function (a, b) {
|
|
4982
|
+
var position = a.compareDocumentPosition(b);
|
|
4983
|
+
if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;
|
|
4984
|
+
if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;
|
|
4985
|
+
return 0;
|
|
4986
|
+
});
|
|
4987
|
+
return members;
|
|
4988
|
+
}
|
|
4989
|
+
|
|
4990
|
+
// Resolves the group member that owns a drag gesture's target element (the
|
|
4991
|
+
// member itself or an ancestor member), or null when the target is not
|
|
4992
|
+
// part of the current multi-selection.
|
|
4993
|
+
function groupMemberForGestureTarget(target: Element | null): Element | null {
|
|
4994
|
+
if (!target) return null;
|
|
4995
|
+
var raw: Element[] = selectedEl
|
|
4996
|
+
? [selectedEl].concat(passiveSelectionEls)
|
|
4997
|
+
: passiveSelectionEls.slice();
|
|
4998
|
+
for (var i = 0; i < raw.length; i += 1) {
|
|
4999
|
+
var member = raw[i];
|
|
5000
|
+
if (
|
|
5001
|
+
member &&
|
|
5002
|
+
document.documentElement.contains(member) &&
|
|
5003
|
+
(member === target || (member.contains && member.contains(target)))
|
|
5004
|
+
) {
|
|
5005
|
+
return member;
|
|
5006
|
+
}
|
|
5007
|
+
}
|
|
5008
|
+
return null;
|
|
5009
|
+
}
|
|
5010
|
+
|
|
4048
5011
|
function isAutoLayoutElement(el: Element | null): boolean {
|
|
4049
5012
|
if (!el) return false;
|
|
4050
5013
|
var cs = window.getComputedStyle(el);
|
|
@@ -4068,41 +5031,217 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4068
5031
|
return cs.position === "absolute" || cs.position === "fixed";
|
|
4069
5032
|
}
|
|
4070
5033
|
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
5034
|
+
// Figma-parity "drop into a frame" conversion: when a plain rect/div drop
|
|
5035
|
+
// target isn't already auto-layout, dropping a child into it turns it into
|
|
5036
|
+
// one. Direction/gap are inferred from the container's existing children
|
|
5037
|
+
// (same spread-axis heuristic as inferAutoLayoutFromChildren in
|
|
5038
|
+
// DesignEditor.tsx: whichever axis the children's bounding boxes spread
|
|
5039
|
+
// further along wins) when it already has other children; an empty
|
|
5040
|
+
// container defaults to flex-direction:column, matching the product
|
|
5041
|
+
// decision for a fresh single-child drop with no layout signal yet.
|
|
5042
|
+
function inferAutoLayoutConversionForContainer(
|
|
5043
|
+
container: Element,
|
|
5044
|
+
excludeEls: Element[],
|
|
5045
|
+
): { direction: "row" | "column"; gap: number } {
|
|
5046
|
+
var siblings = draggableElementChildren(container).filter(function (child) {
|
|
5047
|
+
return excludeEls.indexOf(child) === -1;
|
|
5048
|
+
});
|
|
5049
|
+
if (siblings.length === 0) {
|
|
5050
|
+
return { direction: "column", gap: 10 };
|
|
5051
|
+
}
|
|
5052
|
+
var rects = siblings.map(function (child) {
|
|
5053
|
+
return child.getBoundingClientRect();
|
|
5054
|
+
});
|
|
5055
|
+
var minX = Math.min.apply(
|
|
5056
|
+
null,
|
|
5057
|
+
rects.map(function (r) {
|
|
5058
|
+
return r.left;
|
|
5059
|
+
}),
|
|
5060
|
+
);
|
|
5061
|
+
var maxX = Math.max.apply(
|
|
5062
|
+
null,
|
|
5063
|
+
rects.map(function (r) {
|
|
5064
|
+
return r.left + r.width;
|
|
5065
|
+
}),
|
|
5066
|
+
);
|
|
5067
|
+
var minY = Math.min.apply(
|
|
5068
|
+
null,
|
|
5069
|
+
rects.map(function (r) {
|
|
5070
|
+
return r.top;
|
|
5071
|
+
}),
|
|
5072
|
+
);
|
|
5073
|
+
var maxY = Math.max.apply(
|
|
5074
|
+
null,
|
|
5075
|
+
rects.map(function (r) {
|
|
5076
|
+
return r.top + r.height;
|
|
5077
|
+
}),
|
|
5078
|
+
);
|
|
5079
|
+
var direction: "row" | "column" =
|
|
5080
|
+
maxX - minX >= maxY - minY ? "row" : "column";
|
|
5081
|
+
if (rects.length < 2) {
|
|
5082
|
+
return { direction: direction, gap: 10 };
|
|
5083
|
+
}
|
|
5084
|
+
var sorted = rects.slice().sort(function (a, b) {
|
|
5085
|
+
return direction === "row" ? a.left - b.left : a.top - b.top;
|
|
5086
|
+
});
|
|
5087
|
+
var gaps: number[] = [];
|
|
5088
|
+
for (var i = 1; i < sorted.length; i += 1) {
|
|
5089
|
+
var prev = sorted[i - 1];
|
|
5090
|
+
var current = sorted[i];
|
|
5091
|
+
var gapValue =
|
|
5092
|
+
direction === "row"
|
|
5093
|
+
? current.left - (prev.left + prev.width)
|
|
5094
|
+
: current.top - (prev.top + prev.height);
|
|
5095
|
+
if (isFinite(gapValue) && gapValue > 0) gaps.push(gapValue);
|
|
5096
|
+
}
|
|
5097
|
+
if (gaps.length === 0) {
|
|
5098
|
+
return { direction: direction, gap: 10 };
|
|
5099
|
+
}
|
|
5100
|
+
gaps.sort(function (a, b) {
|
|
5101
|
+
return a - b;
|
|
5102
|
+
});
|
|
5103
|
+
var mid = Math.floor(gaps.length / 2);
|
|
5104
|
+
var median =
|
|
5105
|
+
gaps.length % 2 === 0 ? (gaps[mid - 1] + gaps[mid]) / 2 : gaps[mid];
|
|
5106
|
+
return { direction: direction, gap: Math.round(median) };
|
|
5107
|
+
}
|
|
5108
|
+
|
|
5109
|
+
// Applies the flex conversion to `container` and posts it to the host as a
|
|
5110
|
+
// normal visual-style-change for that container's own selector/elementInfo
|
|
5111
|
+
// — the same message shape the style panel already uses, just targeting
|
|
5112
|
+
// the drop-target anchor instead of the current selection, so no host-side
|
|
5113
|
+
// routing changes are needed. Runs BEFORE the moved-element's own
|
|
5114
|
+
// visual-structure-change post so the host's synchronous same-tick content
|
|
5115
|
+
// refs (see DesignEditor.tsx's getFreshActiveContent) compose the two
|
|
5116
|
+
// edits in order: container becomes flex, then the child moves into it.
|
|
5117
|
+
// `excludeEls`: the dragged element(s) — every member of a group drag —
|
|
5118
|
+
// so the direction/gap inference only looks at the container's existing
|
|
5119
|
+
// children, never the incoming ones.
|
|
5120
|
+
function applyAutoLayoutConversionForDrop(
|
|
5121
|
+
container: Element,
|
|
5122
|
+
excludeEls: Element[],
|
|
5123
|
+
): void {
|
|
5124
|
+
var inferred = inferAutoLayoutConversionForContainer(container, excludeEls);
|
|
5125
|
+
var el = container as HTMLElement;
|
|
5126
|
+
el.style.display = "flex";
|
|
5127
|
+
el.style.flexDirection = inferred.direction;
|
|
5128
|
+
el.style.gap = inferred.gap + "px";
|
|
5129
|
+
(window.parent as Window).postMessage(
|
|
5130
|
+
{
|
|
5131
|
+
type: "visual-style-change",
|
|
5132
|
+
selector: getSelector(container),
|
|
5133
|
+
styles: {
|
|
5134
|
+
display: "flex",
|
|
5135
|
+
"flex-direction": inferred.direction,
|
|
5136
|
+
gap: inferred.gap + "px",
|
|
5137
|
+
},
|
|
5138
|
+
payload: getElementInfo(container),
|
|
5139
|
+
},
|
|
5140
|
+
"*",
|
|
5141
|
+
);
|
|
5142
|
+
}
|
|
5143
|
+
|
|
5144
|
+
// ── Board-text auto-color adaptation on nest ─────────────────────────────
|
|
5145
|
+
//
|
|
5146
|
+
// Board-drawn text on the dark infinite canvas gets an explicit inline
|
|
5147
|
+
// default `color:#ffffff` (+ Inter) from DesignEditor's
|
|
5148
|
+
// appendCanvasPrimitiveToHtml — necessary there because "currentColor"
|
|
5149
|
+
// would inherit the unstyled document's black and vanish on the dark
|
|
5150
|
+
// board. But when that text is later dragged INTO a (typically light)
|
|
5151
|
+
// container, the stale inline white makes it white-on-white invisible.
|
|
5152
|
+
//
|
|
5153
|
+
// On re-parent into a different container, adapt: if the text's inline
|
|
5154
|
+
// color is the auto-applied board default (marker present, or —
|
|
5155
|
+
// pre-marker content — exactly the default white AND the destination is
|
|
5156
|
+
// light), switch it to `color:inherit` so it picks up the container's
|
|
5157
|
+
// effective text color. A color the user explicitly set is NEVER touched:
|
|
5158
|
+
// DesignEditor should stamp `data-an-auto-text-color` when IT auto-picks
|
|
5159
|
+
// the color at creation and remove the marker on any explicit color edit;
|
|
5160
|
+
// when the marker is present the color is definitely auto (always safe to
|
|
5161
|
+
// adapt), and when absent the conservative default-white + light-target
|
|
5162
|
+
// heuristic below only fires in the exact case where the text would be
|
|
5163
|
+
// invisible anyway.
|
|
5164
|
+
var BOARD_TEXT_AUTO_COLOR_MARKER = "data-an-auto-text-color";
|
|
5165
|
+
|
|
5166
|
+
function parseCssRgb(
|
|
5167
|
+
value: string,
|
|
5168
|
+
): { r: number; g: number; b: number; a: number } | null {
|
|
5169
|
+
var match = /^rgba?\(([^)]+)\)$/.exec((value || "").trim());
|
|
5170
|
+
if (!match) return null;
|
|
5171
|
+
var parts = match[1].split(",").map(function (part) {
|
|
5172
|
+
return parseFloat(part.trim());
|
|
5173
|
+
});
|
|
5174
|
+
if (parts.length < 3 || parts.some(isNaN)) return null;
|
|
5175
|
+
return {
|
|
5176
|
+
r: parts[0],
|
|
5177
|
+
g: parts[1],
|
|
5178
|
+
b: parts[2],
|
|
5179
|
+
a: parts.length > 3 ? parts[3] : 1,
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
5182
|
+
|
|
5183
|
+
// Walks up from the container until it finds a non-transparent computed
|
|
5184
|
+
// background and reports whether it is light (relative-luminance
|
|
5185
|
+
// threshold). An unstyled chain means the default white page background.
|
|
5186
|
+
function containerBackgroundIsLight(container: Element): boolean {
|
|
5187
|
+
var cursor: Element | null = container;
|
|
5188
|
+
while (cursor && cursor !== document.documentElement) {
|
|
5189
|
+
var bg = window.getComputedStyle(cursor).backgroundColor;
|
|
5190
|
+
var rgb = parseCssRgb(bg);
|
|
5191
|
+
if (rgb && rgb.a > 0.01) {
|
|
5192
|
+
var luminance = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
|
|
5193
|
+
return luminance > 150;
|
|
4097
5194
|
}
|
|
4098
|
-
|
|
4099
|
-
anchor: candidate,
|
|
4100
|
-
placement: "inside",
|
|
4101
|
-
axis: "y",
|
|
4102
|
-
dropMode: "absolute-container",
|
|
4103
|
-
};
|
|
5195
|
+
cursor = cursor.parentElement;
|
|
4104
5196
|
}
|
|
4105
|
-
return
|
|
5197
|
+
return true;
|
|
5198
|
+
}
|
|
5199
|
+
|
|
5200
|
+
function adaptAutoTextColorForNest(
|
|
5201
|
+
member: Element,
|
|
5202
|
+
container: Element | null,
|
|
5203
|
+
): void {
|
|
5204
|
+
if (!container || member.parentElement === container) return;
|
|
5205
|
+
var kind = (
|
|
5206
|
+
member.getAttribute("data-an-primitive") ||
|
|
5207
|
+
member.getAttribute("data-agent-native-primitive") ||
|
|
5208
|
+
""
|
|
5209
|
+
).toLowerCase();
|
|
5210
|
+
if (kind !== "text") return;
|
|
5211
|
+
var el = member as HTMLElement;
|
|
5212
|
+
var inline = el.style.color;
|
|
5213
|
+
if (!inline || inline === "inherit" || inline === "currentcolor") return;
|
|
5214
|
+
var hasAutoMarker = member.hasAttribute(BOARD_TEXT_AUTO_COLOR_MARKER);
|
|
5215
|
+
if (!hasAutoMarker) {
|
|
5216
|
+
var normalized = inline.replace(/\s+/g, "").toLowerCase();
|
|
5217
|
+
var isDefaultWhite =
|
|
5218
|
+
normalized === "#ffffff" ||
|
|
5219
|
+
normalized === "#fff" ||
|
|
5220
|
+
normalized === "rgb(255,255,255)" ||
|
|
5221
|
+
normalized === "white";
|
|
5222
|
+
if (!isDefaultWhite) return;
|
|
5223
|
+
if (!containerBackgroundIsLight(container)) return;
|
|
5224
|
+
}
|
|
5225
|
+
el.style.color = "inherit";
|
|
5226
|
+
(window.parent as Window).postMessage(
|
|
5227
|
+
{
|
|
5228
|
+
type: "visual-style-change",
|
|
5229
|
+
selector: getSelector(member),
|
|
5230
|
+
styles: { color: "inherit" },
|
|
5231
|
+
payload: getElementInfo(member),
|
|
5232
|
+
},
|
|
5233
|
+
"*",
|
|
5234
|
+
);
|
|
5235
|
+
}
|
|
5236
|
+
|
|
5237
|
+
// Resolves the actual container element a drop target lands the moved
|
|
5238
|
+
// element(s) in: the anchor itself for "inside" placement, otherwise the
|
|
5239
|
+
// anchor's parent.
|
|
5240
|
+
function dropContainerForTarget(target): Element | null {
|
|
5241
|
+
if (!target || !target.anchor) return null;
|
|
5242
|
+
return target.placement === "inside"
|
|
5243
|
+
? target.anchor
|
|
5244
|
+
: target.anchor.parentElement;
|
|
4106
5245
|
}
|
|
4107
5246
|
|
|
4108
5247
|
function isOutsideIframeViewport(clientX: number, clientY: number): boolean {
|
|
@@ -4278,14 +5417,95 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4278
5417
|
return "y";
|
|
4279
5418
|
}
|
|
4280
5419
|
|
|
4281
|
-
|
|
5420
|
+
// Resolves a between-children insertion inside `container` from the
|
|
5421
|
+
// pointer position: the nearest visible child (by flow-axis center)
|
|
5422
|
+
// becomes the anchor with before/after placement, which renders as the
|
|
5423
|
+
// Figma-style insertion LINE between children. Returns null when the
|
|
5424
|
+
// container has no eligible children (caller falls back to "inside").
|
|
5425
|
+
//
|
|
5426
|
+
// This is the B5-4 fix: hovering the container's own background — its
|
|
5427
|
+
// padding, or the gaps BETWEEN children, which is where the pointer
|
|
5428
|
+
// naturally sits when dropping "between two cards" — used to resolve to
|
|
5429
|
+
// placement "inside" (appendChild = always lands after the LAST child,
|
|
5430
|
+
// with the container-fill affordance instead of an insertion line). Both
|
|
5431
|
+
// in-screen drag paths now route container-background hits through this
|
|
5432
|
+
// helper so dropping between children works and shows the line.
|
|
5433
|
+
function nearestChildInsertionTarget(
|
|
5434
|
+
container: Element,
|
|
5435
|
+
clientX: number,
|
|
5436
|
+
clientY: number,
|
|
5437
|
+
excludeEls?: Element[],
|
|
5438
|
+
) {
|
|
5439
|
+
var excluded: Element[] = excludeEls || [];
|
|
5440
|
+
function isExcluded(node) {
|
|
5441
|
+
for (var i = 0; i < excluded.length; i += 1) {
|
|
5442
|
+
var member = excluded[i];
|
|
5443
|
+
if (
|
|
5444
|
+
member &&
|
|
5445
|
+
(member === node || (member.contains && member.contains(node)))
|
|
5446
|
+
) {
|
|
5447
|
+
return true;
|
|
5448
|
+
}
|
|
5449
|
+
}
|
|
5450
|
+
return false;
|
|
5451
|
+
}
|
|
5452
|
+
var children = draggableElementChildren(container).filter(function (child) {
|
|
5453
|
+
return !isExcluded(child);
|
|
5454
|
+
});
|
|
5455
|
+
if (!children.length) return null;
|
|
5456
|
+
var axis = parentFlowAxis(container);
|
|
5457
|
+
var best: Element | null = null;
|
|
5458
|
+
var bestDistance = Infinity;
|
|
5459
|
+
var placement = "after";
|
|
5460
|
+
for (var j = 0; j < children.length; j += 1) {
|
|
5461
|
+
var rect = children[j].getBoundingClientRect();
|
|
5462
|
+
// Skip zero-size children (e.g. Alpine <template> nodes, hidden
|
|
5463
|
+
// elements) — they are not visible slots.
|
|
5464
|
+
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
5465
|
+
var center =
|
|
5466
|
+
axis === "x" ? rect.left + rect.width / 2 : rect.top + rect.height / 2;
|
|
5467
|
+
var pointer = axis === "x" ? clientX : clientY;
|
|
5468
|
+
var distance = Math.abs(pointer - center);
|
|
5469
|
+
if (distance < bestDistance) {
|
|
5470
|
+
bestDistance = distance;
|
|
5471
|
+
best = children[j];
|
|
5472
|
+
placement = pointer < center ? "before" : "after";
|
|
5473
|
+
}
|
|
5474
|
+
}
|
|
5475
|
+
if (!best) return null;
|
|
5476
|
+
return {
|
|
5477
|
+
anchor: best,
|
|
5478
|
+
placement: placement,
|
|
5479
|
+
axis: axis,
|
|
5480
|
+
dropMode: "flow-insert",
|
|
5481
|
+
};
|
|
5482
|
+
}
|
|
5483
|
+
|
|
5484
|
+
// `excludeEls` (optional): other members of a multi-select group drag.
|
|
5485
|
+
// They can never be the anchor/target of their own group's reorder (that
|
|
5486
|
+
// would insert the group relative to an element that is itself about to
|
|
5487
|
+
// move), so hits on them fall through to the sibling-scan fallback and the
|
|
5488
|
+
// sibling scan skips them.
|
|
5489
|
+
function reorderTargetForPoint(el, clientX, clientY, excludeEls) {
|
|
4282
5490
|
if (!el || !el.parentElement) return null;
|
|
5491
|
+
var dragged: Element[] = [el].concat(excludeEls || []);
|
|
5492
|
+
function isDraggedOrInsideDragged(node) {
|
|
5493
|
+
for (var di = 0; di < dragged.length; di += 1) {
|
|
5494
|
+
var member = dragged[di];
|
|
5495
|
+
if (
|
|
5496
|
+
member &&
|
|
5497
|
+
(member === node || (member.contains && member.contains(node)))
|
|
5498
|
+
) {
|
|
5499
|
+
return true;
|
|
5500
|
+
}
|
|
5501
|
+
}
|
|
5502
|
+
return false;
|
|
5503
|
+
}
|
|
4283
5504
|
var hit = elementFromEditorPoint(clientX, clientY);
|
|
4284
5505
|
if (
|
|
4285
5506
|
hit &&
|
|
4286
5507
|
hit !== document.documentElement &&
|
|
4287
|
-
hit
|
|
4288
|
-
!el.contains(hit) &&
|
|
5508
|
+
!isDraggedOrInsideDragged(hit) &&
|
|
4289
5509
|
!isOverlayElement(hit)
|
|
4290
5510
|
) {
|
|
4291
5511
|
if (isContainerDropTarget(hit)) {
|
|
@@ -4300,6 +5520,19 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4300
5520
|
clientY,
|
|
4301
5521
|
);
|
|
4302
5522
|
if (!edgePlacement) {
|
|
5523
|
+
// B5-4: the pointer is over the container's inner area — its
|
|
5524
|
+
// padding or the gap BETWEEN children (a direct child under the
|
|
5525
|
+
// pointer would have been the hit instead). Resolve to the
|
|
5526
|
+
// nearest child slot so the drop lands between children with the
|
|
5527
|
+
// insertion LINE, instead of the old placement:"inside" append-
|
|
5528
|
+
// after-last with only the container-fill affordance.
|
|
5529
|
+
var betweenChildren = nearestChildInsertionTarget(
|
|
5530
|
+
hit,
|
|
5531
|
+
clientX,
|
|
5532
|
+
clientY,
|
|
5533
|
+
dragged,
|
|
5534
|
+
);
|
|
5535
|
+
if (betweenChildren) return betweenChildren;
|
|
4303
5536
|
return {
|
|
4304
5537
|
anchor: hit,
|
|
4305
5538
|
placement: "inside",
|
|
@@ -4336,7 +5569,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4336
5569
|
var parent = el.parentElement;
|
|
4337
5570
|
var axis = parentFlowAxis(parent);
|
|
4338
5571
|
var siblings = draggableElementChildren(parent).filter(function (child) {
|
|
4339
|
-
return child
|
|
5572
|
+
return !isDraggedOrInsideDragged(child);
|
|
4340
5573
|
});
|
|
4341
5574
|
if (!siblings.length) return null;
|
|
4342
5575
|
var beforeTarget = null;
|
|
@@ -4360,41 +5593,99 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4360
5593
|
};
|
|
4361
5594
|
}
|
|
4362
5595
|
|
|
5596
|
+
// Accepts a single element or an array (group drags temporarily disable
|
|
5597
|
+
// pointer-events on EVERY dragged member so the hit test sees what's
|
|
5598
|
+
// underneath the whole group, not a sibling member riding along under the
|
|
5599
|
+
// pointer).
|
|
4363
5600
|
function elementFromEditorPointIgnoring(
|
|
4364
5601
|
clientX: number,
|
|
4365
5602
|
clientY: number,
|
|
4366
|
-
ignore: Element | null,
|
|
5603
|
+
ignore: Element | Element[] | null,
|
|
4367
5604
|
): Element | null {
|
|
4368
|
-
var
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
5605
|
+
var ignoreList: HTMLElement[] = [];
|
|
5606
|
+
var previousPointerEvents: string[] = [];
|
|
5607
|
+
(Array.isArray(ignore) ? ignore : ignore ? [ignore] : []).forEach(
|
|
5608
|
+
function (item) {
|
|
5609
|
+
if (item && item instanceof HTMLElement) {
|
|
5610
|
+
ignoreList.push(item);
|
|
5611
|
+
previousPointerEvents.push(item.style.pointerEvents);
|
|
5612
|
+
item.style.pointerEvents = "none";
|
|
5613
|
+
}
|
|
5614
|
+
},
|
|
5615
|
+
);
|
|
4373
5616
|
var hit = elementFromEditorPoint(clientX, clientY);
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
}
|
|
5617
|
+
ignoreList.forEach(function (item, index) {
|
|
5618
|
+
item.style.pointerEvents = previousPointerEvents[index] ?? "";
|
|
5619
|
+
});
|
|
4377
5620
|
return hit;
|
|
4378
5621
|
}
|
|
4379
5622
|
|
|
4380
|
-
|
|
4381
|
-
|
|
5623
|
+
// Item 8 — re-parent policy for absolute-position drags (this function
|
|
5624
|
+
// feeds onMove's currentAutoLayoutTarget for the isFlowReorderCandidate
|
|
5625
|
+
// === false path, i.e. plain absolute-positioned elements/shapes, NOT flow
|
|
5626
|
+
// children — see reorderTargetForPoint above for that separate case).
|
|
5627
|
+
//
|
|
5628
|
+
// PRODUCT DECISION (supersedes the old "genuine auto-layout only" policy
|
|
5629
|
+
// below): dragging one element onto another must nest it as a child with
|
|
5630
|
+
// auto-layout, exactly like dropping an element into a frame in Figma —
|
|
5631
|
+
// this applies to plain rectangles/divs too, not just existing flex/grid
|
|
5632
|
+
// containers. `isContainerDropTarget` (below) is the single nestable-
|
|
5633
|
+
// container test shared with reorderTargetForPoint's flow-reorder path and
|
|
5634
|
+
// the overview canvas's getPrimitiveDropTargetForPoint, so in-screen and
|
|
5635
|
+
// cross-screen drag agree on what counts as a container: any block-level
|
|
5636
|
+
// element that can hold children (plain divs/sections/etc. included),
|
|
5637
|
+
// excluding text/leaf tags, overlay chrome, and the dragged element's own
|
|
5638
|
+
// descendants (cycle guard). When the resolved container is not already an
|
|
5639
|
+
// auto-layout (flex/grid) display, the caller (onUp) converts it to
|
|
5640
|
+
// display:flex on drop — see needsAutoLayoutConversion below and
|
|
5641
|
+
// applyAutoLayoutConversionForDrop.
|
|
5642
|
+
//
|
|
5643
|
+
// (Historical note: an earlier revision of this policy matched ANY
|
|
5644
|
+
// absolute-positioned rect primitive as a drop-into target purely from
|
|
5645
|
+
// pointer overlap, with no leaf/text exclusion and no cycle guard, which
|
|
5646
|
+
// caused two merely-overlapping absolute elements to silently adopt one
|
|
5647
|
+
// another. isContainerDropTarget's tag/role checks plus the cursor
|
|
5648
|
+
// ancestor-walk's cycle guard below are what keep this version scoped to
|
|
5649
|
+
// Figma's actual "drop into a frame" behavior instead of that regression.)
|
|
5650
|
+
// `excludeEls` (optional): additional dragged elements to treat exactly
|
|
5651
|
+
// like `el` — used by multi-select group drags so no member of the moving
|
|
5652
|
+
// group is hit-tested, walked through, or offered as a nesting container
|
|
5653
|
+
// for its own group.
|
|
5654
|
+
function autoLayoutInsertionTargetForPoint(el, clientX, clientY, excludeEls) {
|
|
5655
|
+
var dragged: Element[] = [el].concat(excludeEls || []);
|
|
5656
|
+
function isDraggedOrInsideDragged(node) {
|
|
5657
|
+
for (var i = 0; i < dragged.length; i += 1) {
|
|
5658
|
+
var member = dragged[i];
|
|
5659
|
+
if (
|
|
5660
|
+
member &&
|
|
5661
|
+
(member === node || (member.contains && member.contains(node)))
|
|
5662
|
+
) {
|
|
5663
|
+
return true;
|
|
5664
|
+
}
|
|
5665
|
+
}
|
|
5666
|
+
return false;
|
|
5667
|
+
}
|
|
5668
|
+
var hit = elementFromEditorPointIgnoring(clientX, clientY, dragged);
|
|
4382
5669
|
if (!hit || hit === document.documentElement || hit === document.body) {
|
|
4383
5670
|
return null;
|
|
4384
5671
|
}
|
|
4385
5672
|
var cursor = hit;
|
|
4386
5673
|
while (cursor && cursor !== document.body) {
|
|
4387
5674
|
if (
|
|
4388
|
-
cursor
|
|
4389
|
-
(el && el.contains && el.contains(cursor)) ||
|
|
5675
|
+
isDraggedOrInsideDragged(cursor) ||
|
|
4390
5676
|
isOverlayElement(cursor) ||
|
|
4391
5677
|
isLayerInteractionBlocked(cursor)
|
|
4392
5678
|
) {
|
|
4393
5679
|
cursor = cursor.parentElement;
|
|
4394
5680
|
continue;
|
|
4395
5681
|
}
|
|
5682
|
+
// document.body is excluded as a nesting target here (both branches
|
|
5683
|
+
// below): it is the screen root, not a Figma-style frame, so hovering
|
|
5684
|
+
// loose background — including hovering a leaf like an image or text
|
|
5685
|
+
// node whose parent happens to be body — must fall through to a plain
|
|
5686
|
+
// absolute placement instead of silently wrapping body in auto-layout.
|
|
4396
5687
|
var parent = cursor.parentElement;
|
|
4397
|
-
if (parent &&
|
|
5688
|
+
if (parent && parent !== document.body && isContainerDropTarget(parent)) {
|
|
4398
5689
|
var parentAxis = parentFlowAxis(parent);
|
|
4399
5690
|
var childRect = cursor.getBoundingClientRect();
|
|
4400
5691
|
var childCenter =
|
|
@@ -4407,27 +5698,44 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4407
5698
|
placement: childPointer < childCenter ? "before" : "after",
|
|
4408
5699
|
axis: parentAxis,
|
|
4409
5700
|
dropMode: "flow-insert",
|
|
5701
|
+
needsAutoLayoutConversion: !isAutoLayoutElement(parent),
|
|
5702
|
+
conversionTarget: parent,
|
|
4410
5703
|
};
|
|
4411
5704
|
}
|
|
4412
|
-
if (
|
|
5705
|
+
if (cursor !== document.body && isContainerDropTarget(cursor)) {
|
|
5706
|
+
// B5-4 (absolute path): pointer over the container's own background
|
|
5707
|
+
// — its padding or a gap between children. Prefer the nearest child
|
|
5708
|
+
// slot (insertion line at the pointer index) over plain "inside"
|
|
5709
|
+
// (which appends after the last child); fall back to "inside" only
|
|
5710
|
+
// for containers with no visible children.
|
|
5711
|
+
var betweenContainerChildren = nearestChildInsertionTarget(
|
|
5712
|
+
cursor,
|
|
5713
|
+
clientX,
|
|
5714
|
+
clientY,
|
|
5715
|
+
dragged,
|
|
5716
|
+
);
|
|
5717
|
+
if (betweenContainerChildren) {
|
|
5718
|
+
return {
|
|
5719
|
+
anchor: betweenContainerChildren.anchor,
|
|
5720
|
+
placement: betweenContainerChildren.placement,
|
|
5721
|
+
axis: betweenContainerChildren.axis,
|
|
5722
|
+
dropMode: "flow-insert",
|
|
5723
|
+
needsAutoLayoutConversion: !isAutoLayoutElement(cursor),
|
|
5724
|
+
conversionTarget: cursor,
|
|
5725
|
+
};
|
|
5726
|
+
}
|
|
4413
5727
|
return {
|
|
4414
5728
|
anchor: cursor,
|
|
4415
5729
|
placement: "inside",
|
|
4416
5730
|
axis: parentFlowAxis(cursor),
|
|
4417
5731
|
dropMode: "flow-insert",
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
if (isAbsolutePrimitiveContainer(cursor)) {
|
|
4421
|
-
return {
|
|
4422
|
-
anchor: cursor,
|
|
4423
|
-
placement: "inside",
|
|
4424
|
-
axis: "y",
|
|
4425
|
-
dropMode: "absolute-container",
|
|
5732
|
+
needsAutoLayoutConversion: !isAutoLayoutElement(cursor),
|
|
5733
|
+
conversionTarget: cursor,
|
|
4426
5734
|
};
|
|
4427
5735
|
}
|
|
4428
5736
|
cursor = parent;
|
|
4429
5737
|
}
|
|
4430
|
-
return
|
|
5738
|
+
return null;
|
|
4431
5739
|
}
|
|
4432
5740
|
|
|
4433
5741
|
function showInsertionGuideFor(target) {
|
|
@@ -4435,6 +5743,16 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4435
5743
|
hideInsertionGuide();
|
|
4436
5744
|
return;
|
|
4437
5745
|
}
|
|
5746
|
+
// Compensate for the host's inverse-scale chrome model (see
|
|
5747
|
+
// applyEditorChromeScale / chromeLineScale): the host shrinks this iframe
|
|
5748
|
+
// via a CSS transform at low canvas zoom, so a hardcoded "2px" line here
|
|
5749
|
+
// would render sub-pixel (effectively invisible) at typical overview zoom
|
|
5750
|
+
// levels — this was the actual regression, not a missing code path. Every
|
|
5751
|
+
// other chrome line/border in this file (selection border, spacing lines,
|
|
5752
|
+
// handle borders) already scales by chromeLineScale(); the insertion
|
|
5753
|
+
// guide must match so it stays a visible bright line at any zoom.
|
|
5754
|
+
var line = 2 * chromeLineScale();
|
|
5755
|
+
var insideBorder = 2 * chromeLineScale();
|
|
4438
5756
|
var rect = target.anchor.getBoundingClientRect();
|
|
4439
5757
|
insertionGuide.style.display = "block";
|
|
4440
5758
|
insertionGuide.style.background = "var(--design-editor-accent-color)";
|
|
@@ -4450,23 +5768,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4450
5768
|
insertionGuide.style.background =
|
|
4451
5769
|
"color-mix(in srgb, var(--design-editor-accent-color) 14%, transparent)";
|
|
4452
5770
|
insertionGuide.style.border =
|
|
4453
|
-
"
|
|
5771
|
+
insideBorder + "px solid var(--design-editor-accent-color)";
|
|
4454
5772
|
insertionGuide.style.borderRadius = "2px";
|
|
4455
5773
|
insertionGuide.style.boxShadow = "none";
|
|
4456
5774
|
return;
|
|
4457
5775
|
}
|
|
4458
5776
|
if (target.axis === "x") {
|
|
4459
5777
|
var x = target.placement === "before" ? rect.left : rect.right;
|
|
4460
|
-
insertionGuide.style.left = x + "px";
|
|
5778
|
+
insertionGuide.style.left = x - line / 2 + "px";
|
|
4461
5779
|
insertionGuide.style.top = rect.top + "px";
|
|
4462
|
-
insertionGuide.style.width = "
|
|
5780
|
+
insertionGuide.style.width = line + "px";
|
|
4463
5781
|
insertionGuide.style.height = rect.height + "px";
|
|
4464
5782
|
} else {
|
|
4465
5783
|
var y = target.placement === "before" ? rect.top : rect.bottom;
|
|
4466
5784
|
insertionGuide.style.left = rect.left + "px";
|
|
4467
|
-
insertionGuide.style.top = y + "px";
|
|
5785
|
+
insertionGuide.style.top = y - line / 2 + "px";
|
|
4468
5786
|
insertionGuide.style.width = rect.width + "px";
|
|
4469
|
-
insertionGuide.style.height = "
|
|
5787
|
+
insertionGuide.style.height = line + "px";
|
|
4470
5788
|
}
|
|
4471
5789
|
}
|
|
4472
5790
|
|
|
@@ -4532,9 +5850,231 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4532
5850
|
);
|
|
4533
5851
|
}
|
|
4534
5852
|
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
5853
|
+
// Multi-select group drop: land every member of the group CONSECUTIVELY at
|
|
5854
|
+
// the drop target, preserving their existing document order (standard
|
|
5855
|
+
// design-tool group-drop semantics). `members` must already be in document
|
|
5856
|
+
// order (collectMoveGroupMembers guarantees it). The first member takes the
|
|
5857
|
+
// real drop target; each subsequent member chains "after" the previous one
|
|
5858
|
+
// so the group stays contiguous regardless of the target placement mode.
|
|
5859
|
+
// Persistence reuses the existing per-element visual-structure-change
|
|
5860
|
+
// message — one per member, posted in order, which the host composes
|
|
5861
|
+
// sequentially against its synchronous same-tick content refs (the same
|
|
5862
|
+
// established multi-message pattern as the auto-layout conversion +
|
|
5863
|
+
// structure change pairing in onUp). The host handler collapses its
|
|
5864
|
+
// selection to each moved node as it processes each message, so a final
|
|
5865
|
+
// marquee-selection message restores the full multi-selection afterwards
|
|
5866
|
+
// (requirement: selection stays intact after a group drop).
|
|
5867
|
+
function applyGroupStructureDrop(members: Element[], target, ev): void {
|
|
5868
|
+
var container = dropContainerForTarget(target);
|
|
5869
|
+
var previous: Element | null = null;
|
|
5870
|
+
for (var i = 0; i < members.length; i += 1) {
|
|
5871
|
+
var member = members[i];
|
|
5872
|
+
var memberTarget =
|
|
5873
|
+
i === 0
|
|
5874
|
+
? target
|
|
5875
|
+
: target.dropMode === "absolute-container"
|
|
5876
|
+
? target
|
|
5877
|
+
: {
|
|
5878
|
+
anchor: previous,
|
|
5879
|
+
placement: "after",
|
|
5880
|
+
axis: target.axis,
|
|
5881
|
+
dropMode: "flow-insert",
|
|
5882
|
+
};
|
|
5883
|
+
var prevParent = member.parentElement;
|
|
5884
|
+
var prevNextSibling = member.nextSibling;
|
|
5885
|
+
// Board-text auto-color: adapt before the DOM move so the re-parent
|
|
5886
|
+
// check sees the ORIGINAL parent (see adaptAutoTextColorForNest).
|
|
5887
|
+
adaptAutoTextColorForNest(member, container);
|
|
5888
|
+
applyRuntimeReorder(member, memberTarget);
|
|
5889
|
+
postVisualStructureChange(member, memberTarget, {
|
|
5890
|
+
prevParent: prevParent,
|
|
5891
|
+
prevNextSibling: prevNextSibling,
|
|
5892
|
+
});
|
|
5893
|
+
previous = member;
|
|
5894
|
+
}
|
|
5895
|
+
postElementMarqueeSelect(members, false, ev);
|
|
5896
|
+
}
|
|
5897
|
+
|
|
5898
|
+
// ── Alignment / smart-guide snapping (Figma parity) ───────────────────────
|
|
5899
|
+
//
|
|
5900
|
+
// Minimal, dependency-free port of the overview canvas's edge/center snap
|
|
5901
|
+
// routine (shared/canvas-math.ts computeMoveSnap) for in-iframe element
|
|
5902
|
+
// dragging. The bridge's pointer coordinates and getBoundingClientRect()
|
|
5903
|
+
// values are already in the same iframe-local, zoom-normalized coordinate
|
|
5904
|
+
// space (the host CSS-scales the whole iframe, not individual elements),
|
|
5905
|
+
// so — unlike the overview canvas, which divides a screen-px threshold by
|
|
5906
|
+
// its own camera zoom — no extra scale correction is needed here.
|
|
5907
|
+
var SNAP_THRESHOLD_PX = 6;
|
|
5908
|
+
var SNAP_CANDIDATE_CAP = 200;
|
|
5909
|
+
|
|
5910
|
+
// Accepts either a real DOMRect (getBoundingClientRect()) or a plain
|
|
5911
|
+
// {left, top, width, height} object (the moving element's live drag rect,
|
|
5912
|
+
// which doesn't have its own DOMRect during the drag since it's derived
|
|
5913
|
+
// from pointer deltas) — right/bottom are always derived from
|
|
5914
|
+
// left/top/width/height so both shapes work identically.
|
|
5915
|
+
function rectBounds(rect) {
|
|
5916
|
+
return {
|
|
5917
|
+
left: rect.left,
|
|
5918
|
+
top: rect.top,
|
|
5919
|
+
right: rect.left + rect.width,
|
|
5920
|
+
bottom: rect.top + rect.height,
|
|
5921
|
+
centerX: rect.left + rect.width / 2,
|
|
5922
|
+
centerY: rect.top + rect.height / 2,
|
|
5923
|
+
};
|
|
5924
|
+
}
|
|
5925
|
+
|
|
5926
|
+
// Collects candidate rects to snap against: visible sibling elements that
|
|
5927
|
+
// share the dragged element's offsetParent, plus the parent's own content
|
|
5928
|
+
// box. Capped and computed once (one getBoundingClientRect pass) at drag
|
|
5929
|
+
// start rather than per move event, per the perf requirement below.
|
|
5930
|
+
// `excludeEls` (optional): other members of a multi-select group drag —
|
|
5931
|
+
// they move together with dragEl, so snapping against them would chase a
|
|
5932
|
+
// moving target.
|
|
5933
|
+
function collectSnapCandidateRects(dragEl, excludeEls) {
|
|
5934
|
+
var rects = [];
|
|
5935
|
+
var excluded: Element[] = excludeEls || [];
|
|
5936
|
+
var parent = dragEl && dragEl.parentElement;
|
|
5937
|
+
if (parent) {
|
|
5938
|
+
var parentRect = parent.getBoundingClientRect();
|
|
5939
|
+
if (parentRect.width > 0 && parentRect.height > 0) {
|
|
5940
|
+
rects.push(rectBounds(parentRect));
|
|
5941
|
+
}
|
|
5942
|
+
}
|
|
5943
|
+
var offsetParent = dragEl && (dragEl as HTMLElement).offsetParent;
|
|
5944
|
+
if (parent) {
|
|
5945
|
+
var siblings = Array.prototype.slice.call(parent.children);
|
|
5946
|
+
for (
|
|
5947
|
+
var i = 0;
|
|
5948
|
+
i < siblings.length && rects.length < SNAP_CANDIDATE_CAP;
|
|
5949
|
+
i += 1
|
|
5950
|
+
) {
|
|
5951
|
+
var sibling = siblings[i];
|
|
5952
|
+
if (
|
|
5953
|
+
!sibling ||
|
|
5954
|
+
sibling === dragEl ||
|
|
5955
|
+
excluded.indexOf(sibling) !== -1 ||
|
|
5956
|
+
sibling.nodeType !== 1 ||
|
|
5957
|
+
isOverlayElement(sibling)
|
|
5958
|
+
) {
|
|
5959
|
+
continue;
|
|
5960
|
+
}
|
|
5961
|
+
if (
|
|
5962
|
+
offsetParent &&
|
|
5963
|
+
(sibling as HTMLElement).offsetParent !== offsetParent
|
|
5964
|
+
) {
|
|
5965
|
+
continue;
|
|
5966
|
+
}
|
|
5967
|
+
var cs = window.getComputedStyle(sibling);
|
|
5968
|
+
if (cs.display === "none" || cs.visibility === "hidden") continue;
|
|
5969
|
+
var rect = sibling.getBoundingClientRect();
|
|
5970
|
+
if (rect.width <= 0 || rect.height <= 0) continue;
|
|
5971
|
+
rects.push(rectBounds(rect));
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
return rects;
|
|
5975
|
+
}
|
|
5976
|
+
|
|
5977
|
+
// Mirrors getAxisSnapCandidates/getBestCandidate/getVerticalGuide/
|
|
5978
|
+
// getHorizontalGuide from shared/canvas-math.ts: for each axis, compare the
|
|
5979
|
+
// moving rect's left/center/right (or top/center/bottom) against every
|
|
5980
|
+
// candidate's same three values and keep the closest match within
|
|
5981
|
+
// threshold. Exported off the IIFE closure via the return-value shape below
|
|
5982
|
+
// so tests can exercise it directly (see the "extractable pure logic"
|
|
5983
|
+
// convention used by motion-preview.bridge.ts).
|
|
5984
|
+
function computeMoveSnapOffset(movingRect, candidates, threshold) {
|
|
5985
|
+
var moving = rectBounds(movingRect);
|
|
5986
|
+
var bestX = null;
|
|
5987
|
+
var bestY = null;
|
|
5988
|
+
for (var i = 0; i < candidates.length; i += 1) {
|
|
5989
|
+
var candidate = candidates[i];
|
|
5990
|
+
var xValues = [moving.left, moving.centerX, moving.right];
|
|
5991
|
+
var xTargets = [candidate.left, candidate.centerX, candidate.right];
|
|
5992
|
+
for (var xi = 0; xi < xValues.length; xi += 1) {
|
|
5993
|
+
for (var xj = 0; xj < xTargets.length; xj += 1) {
|
|
5994
|
+
var offsetX = xTargets[xj] - xValues[xi];
|
|
5995
|
+
var distanceX = Math.abs(offsetX);
|
|
5996
|
+
if (distanceX > threshold) continue;
|
|
5997
|
+
if (!bestX || distanceX < bestX.distance) {
|
|
5998
|
+
bestX = {
|
|
5999
|
+
distance: distanceX,
|
|
6000
|
+
offset: offsetX,
|
|
6001
|
+
guide: {
|
|
6002
|
+
position: xTargets[xj],
|
|
6003
|
+
start: Math.min(moving.top, candidate.top),
|
|
6004
|
+
end: Math.max(moving.bottom, candidate.bottom),
|
|
6005
|
+
},
|
|
6006
|
+
};
|
|
6007
|
+
}
|
|
6008
|
+
}
|
|
6009
|
+
}
|
|
6010
|
+
var yValues = [moving.top, moving.centerY, moving.bottom];
|
|
6011
|
+
var yTargets = [candidate.top, candidate.centerY, candidate.bottom];
|
|
6012
|
+
for (var yi = 0; yi < yValues.length; yi += 1) {
|
|
6013
|
+
for (var yj = 0; yj < yTargets.length; yj += 1) {
|
|
6014
|
+
var offsetY = yTargets[yj] - yValues[yi];
|
|
6015
|
+
var distanceY = Math.abs(offsetY);
|
|
6016
|
+
if (distanceY > threshold) continue;
|
|
6017
|
+
if (!bestY || distanceY < bestY.distance) {
|
|
6018
|
+
bestY = {
|
|
6019
|
+
distance: distanceY,
|
|
6020
|
+
offset: offsetY,
|
|
6021
|
+
guide: {
|
|
6022
|
+
position: yTargets[yj],
|
|
6023
|
+
start: Math.min(moving.left, candidate.left),
|
|
6024
|
+
end: Math.max(moving.right, candidate.right),
|
|
6025
|
+
},
|
|
6026
|
+
};
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
}
|
|
6031
|
+
return {
|
|
6032
|
+
dx: bestX ? bestX.offset : 0,
|
|
6033
|
+
dy: bestY ? bestY.offset : 0,
|
|
6034
|
+
guideV: bestX ? bestX.guide : null,
|
|
6035
|
+
guideH: bestY ? bestY.guide : null,
|
|
6036
|
+
};
|
|
6037
|
+
}
|
|
6038
|
+
|
|
6039
|
+
function showSnapGuides(guideV, guideH) {
|
|
6040
|
+
// Constant-screen-size chrome: guide THICKNESS compensates for the
|
|
6041
|
+
// host's iframe scale (chromeLineScale) so the line stays a crisp 1px
|
|
6042
|
+
// on screen at any zoom; the guide's span/position stays in content
|
|
6043
|
+
// coordinates.
|
|
6044
|
+
var line = 1 * chromeLineScale();
|
|
6045
|
+
if (guideV) {
|
|
6046
|
+
snapGuideV.style.display = "block";
|
|
6047
|
+
snapGuideV.style.width = line + "px";
|
|
6048
|
+
snapGuideV.style.left = Math.round(guideV.position) + "px";
|
|
6049
|
+
snapGuideV.style.top = Math.round(guideV.start) + "px";
|
|
6050
|
+
snapGuideV.style.height = Math.max(1, guideV.end - guideV.start) + "px";
|
|
6051
|
+
} else {
|
|
6052
|
+
snapGuideV.style.display = "none";
|
|
6053
|
+
}
|
|
6054
|
+
if (guideH) {
|
|
6055
|
+
snapGuideH.style.display = "block";
|
|
6056
|
+
snapGuideH.style.height = line + "px";
|
|
6057
|
+
snapGuideH.style.top = Math.round(guideH.position) + "px";
|
|
6058
|
+
snapGuideH.style.left = Math.round(guideH.start) + "px";
|
|
6059
|
+
snapGuideH.style.width = Math.max(1, guideH.end - guideH.start) + "px";
|
|
6060
|
+
} else {
|
|
6061
|
+
snapGuideH.style.display = "none";
|
|
6062
|
+
}
|
|
6063
|
+
}
|
|
6064
|
+
|
|
6065
|
+
function hideSnapGuides() {
|
|
6066
|
+
snapGuideV.style.display = "none";
|
|
6067
|
+
snapGuideH.style.display = "none";
|
|
6068
|
+
}
|
|
6069
|
+
|
|
6070
|
+
// `gestureElParam` (optional): the specific multi-selection member the
|
|
6071
|
+
// pointer went down on when this drag preserves a 2+ selection instead of
|
|
6072
|
+
// collapsing to one element (see beginPotentialShieldDrag's group branch).
|
|
6073
|
+
// Defaults to selectedEl — the selection-overlay drag path.
|
|
6074
|
+
function startMove(e, gestureElParam?: Element) {
|
|
6075
|
+
var gestureEl = gestureElParam || selectedEl;
|
|
6076
|
+
if (!gestureEl) return;
|
|
6077
|
+
if (isLayerInteractionBlocked(gestureEl)) return;
|
|
4538
6078
|
e.preventDefault();
|
|
4539
6079
|
e.stopPropagation();
|
|
4540
6080
|
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
|
|
@@ -4543,6 +6083,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4543
6083
|
var duplicatedForDrag = false;
|
|
4544
6084
|
if (
|
|
4545
6085
|
e.altKey &&
|
|
6086
|
+
selectedEl &&
|
|
4546
6087
|
selectedEl !== document.body &&
|
|
4547
6088
|
selectedEl !== document.documentElement
|
|
4548
6089
|
) {
|
|
@@ -4551,17 +6092,38 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4551
6092
|
selectedEl.parentElement.insertBefore(clone, selectedEl.nextSibling);
|
|
4552
6093
|
selectedEl = clone;
|
|
4553
6094
|
duplicatedForDrag = true;
|
|
6095
|
+
gestureEl = clone;
|
|
4554
6096
|
positionOverlay(selectionOverlay, selectedEl);
|
|
4555
6097
|
postElementSelect(selectedEl, e);
|
|
4556
6098
|
}
|
|
4557
|
-
|
|
6099
|
+
// Multi-select group move: every member of the current 2+ selection moves
|
|
6100
|
+
// with the gesture when the drag started on a member. Alt-drag duplicates
|
|
6101
|
+
// stay single-element (the clone is never part of a selection group).
|
|
6102
|
+
var groupEls: Element[] =
|
|
6103
|
+
duplicatedForDrag || e.altKey
|
|
6104
|
+
? [gestureEl]
|
|
6105
|
+
: collectMoveGroupMembers(gestureEl);
|
|
6106
|
+
if (groupEls.indexOf(gestureEl) === -1) groupEls = [gestureEl];
|
|
6107
|
+
var isGroupDrag = groupEls.length > 1;
|
|
6108
|
+
var groupOthers = groupEls.filter(function (member) {
|
|
6109
|
+
return member !== gestureEl;
|
|
6110
|
+
});
|
|
6111
|
+
if (isGroupDrag) {
|
|
6112
|
+
// beginPotentialShieldDrag armed the host's cross-screen drag state for
|
|
6113
|
+
// a single element before this group drag was detected; clear it. Group
|
|
6114
|
+
// drags stay in-iframe — the host's cross-screen drop only knows how to
|
|
6115
|
+
// move one element, which would tear the group apart.
|
|
6116
|
+
postCrossScreenDrag("cancel");
|
|
6117
|
+
}
|
|
6118
|
+
if (isFlowReorderCandidate(gestureEl)) {
|
|
4558
6119
|
// Snapshot the element being reordered so a concurrent select-element or
|
|
4559
6120
|
// clear-selection postMessage cannot mutate the wrong element mid-drag.
|
|
4560
|
-
var reorderEl =
|
|
6121
|
+
var reorderEl = gestureEl;
|
|
4561
6122
|
var currentTarget = reorderTargetForPoint(
|
|
4562
6123
|
reorderEl,
|
|
4563
6124
|
e.clientX,
|
|
4564
6125
|
e.clientY,
|
|
6126
|
+
groupOthers,
|
|
4565
6127
|
);
|
|
4566
6128
|
showInsertionGuideFor(currentTarget);
|
|
4567
6129
|
// Cross-screen drag state: true when the pointer is outside this iframe's
|
|
@@ -4584,30 +6146,34 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4584
6146
|
var outside = cx < 0 || cy < 0 || cx > vw || cy > vh;
|
|
4585
6147
|
pointerOutsideIframe = outside;
|
|
4586
6148
|
// Always notify the host frame so it can track the cursor position,
|
|
4587
|
-
// render the ghost, and highlight the target screen.
|
|
4588
|
-
(
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
6149
|
+
// render the ghost, and highlight the target screen. Group drags stay
|
|
6150
|
+
// in-iframe (the host's cross-screen drop moves a single element and
|
|
6151
|
+
// would tear the group apart), so they never arm the host.
|
|
6152
|
+
if (!isGroupDrag) {
|
|
6153
|
+
(window.parent as Window).postMessage(
|
|
6154
|
+
{
|
|
6155
|
+
type: "agent-native:cross-screen-drag",
|
|
6156
|
+
phase: "move",
|
|
6157
|
+
selector: reorderSelector,
|
|
6158
|
+
sourceId: reorderSourceId,
|
|
6159
|
+
iframeX: cx,
|
|
6160
|
+
iframeY: cy,
|
|
6161
|
+
viewportW: vw,
|
|
6162
|
+
viewportH: vh,
|
|
6163
|
+
pointerOffset: reorderPointerOffset,
|
|
6164
|
+
styleSnapshot: reorderStyleSnapshot,
|
|
6165
|
+
},
|
|
6166
|
+
"*",
|
|
6167
|
+
);
|
|
6168
|
+
}
|
|
6169
|
+
if (outside && !isGroupDrag) {
|
|
4604
6170
|
// Cursor left this iframe — hide the in-iframe insertion guide so
|
|
4605
6171
|
// it does not render while the host shows a cross-screen drop target.
|
|
4606
6172
|
hideInsertionGuide();
|
|
4607
6173
|
showTransformBadge("Move layer", cx, cy);
|
|
4608
6174
|
} else {
|
|
4609
6175
|
// Cursor is inside this iframe — use existing in-iframe behavior.
|
|
4610
|
-
currentTarget = reorderTargetForPoint(reorderEl, cx, cy);
|
|
6176
|
+
currentTarget = reorderTargetForPoint(reorderEl, cx, cy, groupOthers);
|
|
4611
6177
|
showInsertionGuideFor(currentTarget);
|
|
4612
6178
|
showTransformBadge(currentTarget ? "Move layer" : "Move", cx, cy);
|
|
4613
6179
|
}
|
|
@@ -4657,24 +6223,30 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4657
6223
|
var cy = ev ? ev.clientY : 0;
|
|
4658
6224
|
var outsideOnDrop = cx < 0 || cy < 0 || cx > vw || cy > vh;
|
|
4659
6225
|
// Post the end message so the host can finalize a cross-screen drop.
|
|
4660
|
-
(
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
6226
|
+
// Group drags never armed the host (see onReorderMove), so posting
|
|
6227
|
+
// end here would trigger a bogus single-element cross-screen move.
|
|
6228
|
+
if (!isGroupDrag) {
|
|
6229
|
+
(window.parent as Window).postMessage(
|
|
6230
|
+
{
|
|
6231
|
+
type: "agent-native:cross-screen-drag",
|
|
6232
|
+
phase: "end",
|
|
6233
|
+
selector: reorderSelector,
|
|
6234
|
+
sourceId: reorderSourceId,
|
|
6235
|
+
iframeX: cx,
|
|
6236
|
+
iframeY: cy,
|
|
6237
|
+
viewportW: vw,
|
|
6238
|
+
viewportH: vh,
|
|
6239
|
+
pointerOffset: reorderPointerOffset,
|
|
6240
|
+
styleSnapshot: reorderStyleSnapshot,
|
|
6241
|
+
},
|
|
6242
|
+
"*",
|
|
6243
|
+
);
|
|
6244
|
+
}
|
|
4675
6245
|
// When the pointer is outside this iframe at release, the host owns the
|
|
4676
6246
|
// move (cross-screen drop). Do NOT apply the in-iframe reorder so we
|
|
4677
|
-
// avoid a ghost element left in screen A's DOM.
|
|
6247
|
+
// avoid a ghost element left in screen A's DOM. For group drags an
|
|
6248
|
+
// outside release is simply a no-op (nothing moved during a flow
|
|
6249
|
+
// reorder drag, so there is nothing to restore).
|
|
4678
6250
|
// Use outsideOnDrop only — pointerOutsideIframe is stale when the user
|
|
4679
6251
|
// briefly exits the iframe and re-enters before releasing. The host
|
|
4680
6252
|
// already clears cross-screen state on re-entry so checking the
|
|
@@ -4703,11 +6275,17 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4703
6275
|
reorderEl,
|
|
4704
6276
|
currentTarget,
|
|
4705
6277
|
);
|
|
6278
|
+
} else if (isGroupDrag) {
|
|
6279
|
+
applyGroupStructureDrop(groupEls, currentTarget, ev);
|
|
4706
6280
|
} else {
|
|
4707
6281
|
// Capture the pre-drag DOM anchor so we can revert if the parent
|
|
4708
6282
|
// reports applied===false on the structure-ack.
|
|
4709
6283
|
var prevParent = reorderEl.parentElement;
|
|
4710
6284
|
var prevNextSibling = reorderEl.nextSibling;
|
|
6285
|
+
adaptAutoTextColorForNest(
|
|
6286
|
+
reorderEl,
|
|
6287
|
+
dropContainerForTarget(currentTarget),
|
|
6288
|
+
);
|
|
4711
6289
|
// Optimistically apply the reorder in the DOM for immediate
|
|
4712
6290
|
// visual feedback; the visual-structure-ack handler will confirm
|
|
4713
6291
|
// or revert once the parent processes the change.
|
|
@@ -4724,28 +6302,63 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4724
6302
|
setActiveDragCancel(onReorderEscape);
|
|
4725
6303
|
return;
|
|
4726
6304
|
}
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
var
|
|
4733
|
-
|
|
4734
|
-
|
|
6305
|
+
// Per-member drag state: inline-style snapshots (for escape-cancel
|
|
6306
|
+
// restore) plus each member's own drag origin. Single-element drags have
|
|
6307
|
+
// exactly one entry; group drags get one per multi-selection member so
|
|
6308
|
+
// the SAME delta can be applied to every member each tick, preserving
|
|
6309
|
+
// the group's relative offsets (Figma group-move semantics).
|
|
6310
|
+
var memberStates = groupEls.map(function (member) {
|
|
6311
|
+
var m = member as HTMLElement;
|
|
6312
|
+
var snapshot = {
|
|
6313
|
+
el: m,
|
|
6314
|
+
originalPosition: m.style.position,
|
|
6315
|
+
originalLeft: m.style.left,
|
|
6316
|
+
originalTop: m.style.top,
|
|
6317
|
+
originalOpacity: m.style.opacity,
|
|
6318
|
+
originLeft: 0,
|
|
6319
|
+
originTop: 0,
|
|
6320
|
+
};
|
|
6321
|
+
ensurePositionable(m);
|
|
6322
|
+
var mcs = window.getComputedStyle(m);
|
|
6323
|
+
snapshot.originLeft = readPx(m.style.left || mcs.left);
|
|
6324
|
+
snapshot.originTop = readPx(m.style.top || mcs.top);
|
|
6325
|
+
return snapshot;
|
|
6326
|
+
});
|
|
6327
|
+
var gestureState =
|
|
6328
|
+
memberStates[groupEls.indexOf(gestureEl)] || memberStates[0];
|
|
6329
|
+
var originalInlineOpacity = gestureState.originalOpacity;
|
|
6330
|
+
var originLeft = gestureState.originLeft;
|
|
6331
|
+
var originTop = gestureState.originTop;
|
|
6332
|
+
function setMembersOpacity(value: string | null): void {
|
|
6333
|
+
memberStates.forEach(function (state) {
|
|
6334
|
+
state.el.style.opacity = value === null ? state.originalOpacity : value;
|
|
6335
|
+
});
|
|
6336
|
+
}
|
|
4735
6337
|
var startX = e.clientX;
|
|
4736
6338
|
var startY = e.clientY;
|
|
4737
6339
|
// Snapshot the element being moved so that a concurrent select-element or
|
|
4738
6340
|
// clear-selection postMessage cannot swap selectedEl mid-drag and cause
|
|
4739
6341
|
// mutations on the wrong element or a null-deref in onUp.
|
|
4740
|
-
var dragEl =
|
|
6342
|
+
var dragEl = gestureEl;
|
|
4741
6343
|
var moved = false;
|
|
4742
6344
|
var DRAG_THRESHOLD = 3;
|
|
4743
6345
|
var currentAutoLayoutTarget: {
|
|
4744
6346
|
anchor: Element;
|
|
4745
6347
|
placement: string;
|
|
4746
6348
|
axis?: string;
|
|
6349
|
+
needsAutoLayoutConversion?: boolean;
|
|
6350
|
+
conversionTarget?: Element;
|
|
4747
6351
|
} | null = null;
|
|
4748
|
-
|
|
6352
|
+
// Snap candidates (siblings + parent content box) are computed once at
|
|
6353
|
+
// drag start — a single getBoundingClientRect pass per candidate — not
|
|
6354
|
+
// recomputed on every move event. Other group members are excluded: they
|
|
6355
|
+
// move with the drag, so snapping against them would chase a moving
|
|
6356
|
+
// target.
|
|
6357
|
+
var snapCandidateRects = collectSnapCandidateRects(dragEl, groupOthers);
|
|
6358
|
+
var dragElStartRect = (dragEl as HTMLElement).getBoundingClientRect();
|
|
6359
|
+
var dragElStartWidth = dragElStartRect.width;
|
|
6360
|
+
var dragElStartHeight = dragElStartRect.height;
|
|
6361
|
+
if (!duplicatedForDrag && !isGroupDrag) {
|
|
4749
6362
|
postCrossScreenDrag("start", dragEl, e);
|
|
4750
6363
|
}
|
|
4751
6364
|
function onMove(ev) {
|
|
@@ -4755,11 +6368,53 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4755
6368
|
) {
|
|
4756
6369
|
moved = true;
|
|
4757
6370
|
}
|
|
4758
|
-
var
|
|
4759
|
-
var
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
6371
|
+
var rawDx = ev.clientX - startX;
|
|
6372
|
+
var rawDy = ev.clientY - startY;
|
|
6373
|
+
// Figma dominant-axis lock: while Shift is held, zero out whichever
|
|
6374
|
+
// delta has the smaller magnitude so the element only moves along one
|
|
6375
|
+
// axis. Read live off the move event (not cached at drag start) so
|
|
6376
|
+
// pressing/releasing Shift mid-drag re-evaluates every event, matching
|
|
6377
|
+
// how the resize path reads ev.shiftKey per-move rather than once.
|
|
6378
|
+
if (ev.shiftKey) {
|
|
6379
|
+
if (Math.abs(rawDx) > Math.abs(rawDy)) {
|
|
6380
|
+
rawDy = 0;
|
|
6381
|
+
} else {
|
|
6382
|
+
rawDx = 0;
|
|
6383
|
+
}
|
|
6384
|
+
}
|
|
6385
|
+
var nextLeft = originLeft + rawDx;
|
|
6386
|
+
var nextTop = originTop + rawDy;
|
|
6387
|
+
// Alignment/smart-guide snapping: disabled while Cmd/Ctrl is held
|
|
6388
|
+
// (Figma behavior) and while an auto-layout flow-insert is about to
|
|
6389
|
+
// happen instead of a free absolute placement (handled below once
|
|
6390
|
+
// currentAutoLayoutTarget is known for this tick).
|
|
6391
|
+
var snapBypass = Boolean(ev.metaKey || ev.ctrlKey);
|
|
6392
|
+
var snapResult =
|
|
6393
|
+
!snapBypass && !duplicatedForDrag
|
|
6394
|
+
? computeMoveSnapOffset(
|
|
6395
|
+
{
|
|
6396
|
+
left: nextLeft,
|
|
6397
|
+
top: nextTop,
|
|
6398
|
+
width: dragElStartWidth,
|
|
6399
|
+
height: dragElStartHeight,
|
|
6400
|
+
},
|
|
6401
|
+
snapCandidateRects,
|
|
6402
|
+
SNAP_THRESHOLD_PX,
|
|
6403
|
+
)
|
|
6404
|
+
: { dx: 0, dy: 0, guideV: null, guideH: null };
|
|
6405
|
+
nextLeft += snapResult.dx;
|
|
6406
|
+
nextTop += snapResult.dy;
|
|
6407
|
+
// Apply the SAME delta to every member (one entry for single drags)
|
|
6408
|
+
// so relative offsets within a multi-selection are preserved. For the
|
|
6409
|
+
// gesture member this reduces exactly to the previous
|
|
6410
|
+
// Math.round(nextLeft/nextTop) single-element behavior.
|
|
6411
|
+
var appliedDx = nextLeft - originLeft;
|
|
6412
|
+
var appliedDy = nextTop - originTop;
|
|
6413
|
+
memberStates.forEach(function (state) {
|
|
6414
|
+
state.el.style.left = Math.round(state.originLeft + appliedDx) + "px";
|
|
6415
|
+
state.el.style.top = Math.round(state.originTop + appliedDy) + "px";
|
|
6416
|
+
});
|
|
6417
|
+
if (!duplicatedForDrag && !isGroupDrag) {
|
|
4763
6418
|
postCrossScreenDrag("move", dragEl, ev);
|
|
4764
6419
|
}
|
|
4765
6420
|
if (
|
|
@@ -4768,27 +6423,51 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4768
6423
|
) {
|
|
4769
6424
|
currentAutoLayoutTarget = null;
|
|
4770
6425
|
hideInsertionGuide();
|
|
4771
|
-
(
|
|
6426
|
+
setMembersOpacity(null);
|
|
4772
6427
|
} else {
|
|
4773
6428
|
currentAutoLayoutTarget = !duplicatedForDrag
|
|
4774
|
-
? autoLayoutInsertionTargetForPoint(
|
|
6429
|
+
? autoLayoutInsertionTargetForPoint(
|
|
6430
|
+
dragEl,
|
|
6431
|
+
ev.clientX,
|
|
6432
|
+
ev.clientY,
|
|
6433
|
+
groupOthers,
|
|
6434
|
+
)
|
|
4775
6435
|
: null;
|
|
4776
6436
|
if (currentAutoLayoutTarget) {
|
|
4777
6437
|
showInsertionGuideFor(currentAutoLayoutTarget);
|
|
4778
|
-
(
|
|
6438
|
+
setMembersOpacity("0.4");
|
|
4779
6439
|
} else {
|
|
4780
6440
|
hideInsertionGuide();
|
|
4781
|
-
(
|
|
6441
|
+
setMembersOpacity(null);
|
|
4782
6442
|
}
|
|
4783
6443
|
}
|
|
4784
|
-
|
|
6444
|
+
// Snap guides only make sense for a free absolute placement — never at
|
|
6445
|
+
// once alongside the auto-layout flow-insert indicator (the element is
|
|
6446
|
+
// about to be reflowed into a flex/grid slot, not placed at an x/y
|
|
6447
|
+
// coordinate), and never while the pointer has left the iframe (the
|
|
6448
|
+
// host owns a cross-screen drop at that point).
|
|
6449
|
+
if (
|
|
6450
|
+
currentAutoLayoutTarget ||
|
|
6451
|
+
(!duplicatedForDrag && isOutsideIframeViewport(ev.clientX, ev.clientY))
|
|
6452
|
+
) {
|
|
6453
|
+
hideSnapGuides();
|
|
6454
|
+
} else {
|
|
6455
|
+
showSnapGuides(snapResult.guideV, snapResult.guideH);
|
|
6456
|
+
}
|
|
6457
|
+
showTransformBadge(
|
|
6458
|
+
Math.round(nextLeft) + ", " + Math.round(nextTop),
|
|
6459
|
+
ev.clientX,
|
|
6460
|
+
ev.clientY,
|
|
6461
|
+
);
|
|
4785
6462
|
refreshOverlays();
|
|
4786
6463
|
}
|
|
4787
6464
|
function restoreSourceDragPosition(): void {
|
|
4788
|
-
(
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
6465
|
+
memberStates.forEach(function (state) {
|
|
6466
|
+
state.el.style.position = state.originalPosition;
|
|
6467
|
+
state.el.style.left = state.originalLeft;
|
|
6468
|
+
state.el.style.top = state.originalTop;
|
|
6469
|
+
state.el.style.opacity = state.originalOpacity;
|
|
6470
|
+
});
|
|
4792
6471
|
selectedEl = originalSelectedEl;
|
|
4793
6472
|
positionOverlay(selectionOverlay, selectedEl);
|
|
4794
6473
|
}
|
|
@@ -4802,6 +6481,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4802
6481
|
cleanupMoveDrag();
|
|
4803
6482
|
hideTransformBadge();
|
|
4804
6483
|
hideInsertionGuide();
|
|
6484
|
+
hideSnapGuides();
|
|
4805
6485
|
currentAutoLayoutTarget = null;
|
|
4806
6486
|
if (duplicatedForDrag) {
|
|
4807
6487
|
if (dragEl && dragEl.parentElement) {
|
|
@@ -4812,7 +6492,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4812
6492
|
postElementSelect(selectedEl);
|
|
4813
6493
|
} else if (dragEl && document.documentElement.contains(dragEl)) {
|
|
4814
6494
|
restoreSourceDragPosition();
|
|
4815
|
-
postCrossScreenDrag("cancel");
|
|
6495
|
+
if (!isGroupDrag) postCrossScreenDrag("cancel");
|
|
4816
6496
|
}
|
|
4817
6497
|
suppressNextShieldClickBriefly();
|
|
4818
6498
|
refreshOverlays();
|
|
@@ -4827,6 +6507,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4827
6507
|
cleanupMoveDrag();
|
|
4828
6508
|
hideTransformBadge();
|
|
4829
6509
|
hideInsertionGuide();
|
|
6510
|
+
hideSnapGuides();
|
|
4830
6511
|
if (!dragEl) return;
|
|
4831
6512
|
var outsideOnDrop = ev
|
|
4832
6513
|
? isOutsideIframeViewport(ev.clientX, ev.clientY)
|
|
@@ -4834,11 +6515,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4834
6515
|
if (
|
|
4835
6516
|
ev &&
|
|
4836
6517
|
!duplicatedForDrag &&
|
|
6518
|
+
!isGroupDrag &&
|
|
4837
6519
|
(outsideOnDrop || designCanvasBoardSurface)
|
|
4838
6520
|
) {
|
|
4839
6521
|
postCrossScreenDrag("end", dragEl, ev);
|
|
4840
6522
|
}
|
|
4841
6523
|
if (ev && !duplicatedForDrag && outsideOnDrop) {
|
|
6524
|
+
// Outside release: the host owns a single-element cross-screen drop;
|
|
6525
|
+
// group drags never armed the host, so an outside release simply
|
|
6526
|
+
// restores every member (cancel semantics).
|
|
4842
6527
|
restoreSourceDragPosition();
|
|
4843
6528
|
return;
|
|
4844
6529
|
}
|
|
@@ -4847,6 +6532,7 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4847
6532
|
dragEl,
|
|
4848
6533
|
ev.clientX,
|
|
4849
6534
|
ev.clientY,
|
|
6535
|
+
groupOthers,
|
|
4850
6536
|
);
|
|
4851
6537
|
if (finalAutoLayoutTarget) {
|
|
4852
6538
|
currentAutoLayoutTarget = finalAutoLayoutTarget;
|
|
@@ -4863,30 +6549,62 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4863
6549
|
if (duplicatedForDrag) {
|
|
4864
6550
|
postVisualDuplicateChange(originalSelectedEl, dragEl);
|
|
4865
6551
|
} else if (currentAutoLayoutTarget) {
|
|
4866
|
-
(
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
6552
|
+
setMembersOpacity(null);
|
|
6553
|
+
// Figma-parity nest-on-drop: the target container may be a plain
|
|
6554
|
+
// rect/div that isn't auto-layout yet (see
|
|
6555
|
+
// autoLayoutInsertionTargetForPoint's needsAutoLayoutConversion).
|
|
6556
|
+
// Convert it to flex BEFORE reparenting/posting the move so the
|
|
6557
|
+
// host applies the two edits in the right order against its
|
|
6558
|
+
// synchronous same-tick content refs (container becomes flex, then
|
|
6559
|
+
// the child moves into it and loses absolute positioning via the
|
|
6560
|
+
// existing "flow-insert" dropMode handling). For group drags the
|
|
6561
|
+
// conversion fires ONCE for the container; every member then nests
|
|
6562
|
+
// consecutively via applyGroupStructureDrop.
|
|
6563
|
+
if (
|
|
6564
|
+
currentAutoLayoutTarget.needsAutoLayoutConversion &&
|
|
6565
|
+
currentAutoLayoutTarget.conversionTarget
|
|
6566
|
+
) {
|
|
6567
|
+
applyAutoLayoutConversionForDrop(
|
|
6568
|
+
currentAutoLayoutTarget.conversionTarget,
|
|
6569
|
+
groupEls,
|
|
6570
|
+
);
|
|
6571
|
+
}
|
|
6572
|
+
if (isGroupDrag) {
|
|
6573
|
+
applyGroupStructureDrop(groupEls, currentAutoLayoutTarget, ev);
|
|
6574
|
+
} else {
|
|
6575
|
+
var prevParent = dragEl.parentElement;
|
|
6576
|
+
var prevNextSibling = dragEl.nextSibling;
|
|
6577
|
+
adaptAutoTextColorForNest(
|
|
6578
|
+
dragEl,
|
|
6579
|
+
dropContainerForTarget(currentAutoLayoutTarget),
|
|
6580
|
+
);
|
|
6581
|
+
applyRuntimeReorder(dragEl, currentAutoLayoutTarget);
|
|
6582
|
+
postVisualStructureChange(dragEl, currentAutoLayoutTarget, {
|
|
6583
|
+
prevParent: prevParent,
|
|
6584
|
+
prevNextSibling: prevNextSibling,
|
|
6585
|
+
});
|
|
6586
|
+
}
|
|
4874
6587
|
} else {
|
|
4875
|
-
(
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
6588
|
+
setMembersOpacity(null);
|
|
6589
|
+
// Free absolute placement: one style-change message per member, in
|
|
6590
|
+
// order — the host composes them against its synchronous same-tick
|
|
6591
|
+
// content refs exactly like multi-property style commits.
|
|
6592
|
+
memberStates.forEach(function (state) {
|
|
6593
|
+
(window.parent as Window).postMessage(
|
|
6594
|
+
{
|
|
6595
|
+
type: "visual-style-change",
|
|
6596
|
+
selector: getSelector(state.el),
|
|
6597
|
+
styles: {
|
|
6598
|
+
position: state.el.style.position,
|
|
6599
|
+
left: state.el.style.left,
|
|
6600
|
+
top: state.el.style.top,
|
|
6601
|
+
},
|
|
6602
|
+
payload: getElementInfo(state.el),
|
|
4884
6603
|
},
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
);
|
|
4889
|
-
postCrossScreenDrag("cancel");
|
|
6604
|
+
"*",
|
|
6605
|
+
);
|
|
6606
|
+
});
|
|
6607
|
+
if (!isGroupDrag) postCrossScreenDrag("cancel");
|
|
4890
6608
|
}
|
|
4891
6609
|
}
|
|
4892
6610
|
document.addEventListener(events.move, onMove, true);
|
|
@@ -4909,6 +6627,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4909
6627
|
var originalInlineTop = resizeEl.style.top;
|
|
4910
6628
|
var originalInlineWidth = resizeEl.style.width;
|
|
4911
6629
|
var originalInlineHeight = resizeEl.style.height;
|
|
6630
|
+
var originalInlineBorderWidth = resizeEl.style.borderWidth;
|
|
6631
|
+
var originalInlineFontSize = resizeEl.style.fontSize;
|
|
4912
6632
|
ensurePositionable(resizeEl);
|
|
4913
6633
|
var cs = window.getComputedStyle(resizeEl);
|
|
4914
6634
|
// Bug fix: use CSS width/height (not getBoundingClientRect) for the resize
|
|
@@ -4916,6 +6636,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
4916
6636
|
// axis-aligned bounding box as the starting size.
|
|
4917
6637
|
var originW = readPx(resizeEl.style.width || cs.width);
|
|
4918
6638
|
var originH = readPx(resizeEl.style.height || cs.height);
|
|
6639
|
+
// K-scale (Figma "Scale" tool) parity: capture the element's own border
|
|
6640
|
+
// width and font size once at drag-start so a uniform per-tick scale
|
|
6641
|
+
// factor (derived from width/height growth, see nextRect below) can
|
|
6642
|
+
// multiply them proportionally, exactly like Figma's Scale tool resizes
|
|
6643
|
+
// stroke weight and text size along with the box — a *normal* resize
|
|
6644
|
+
// (scaleToolEnabled false) never touches either. Uses the CSS
|
|
6645
|
+
// borderWidth/fontSize shorthand (not per-side border-*-width) since
|
|
6646
|
+
// canvas-primitive-style.ts / appendCanvasPrimitiveToHtml only ever set a
|
|
6647
|
+
// uniform border on these elements; a hand-authored per-side border is
|
|
6648
|
+
// left untouched (readPx on the shorthand returns 0 for mixed values,
|
|
6649
|
+
// which multiplies to 0 — an explicit non-goal edge case, not silently
|
|
6650
|
+
// wrong: scaleToolEnabled is opt-in and mixed-width borders on a
|
|
6651
|
+
// draggable primitive are not part of this app's authored shapes).
|
|
6652
|
+
var originBorderWidth = readPx(
|
|
6653
|
+
resizeEl.style.borderWidth || cs.borderWidth,
|
|
6654
|
+
);
|
|
6655
|
+
var originFontSize = readPx(resizeEl.style.fontSize || cs.fontSize);
|
|
4919
6656
|
var origin = {
|
|
4920
6657
|
left: readPx(resizeEl.style.left || cs.left),
|
|
4921
6658
|
top: readPx(resizeEl.style.top || cs.top),
|
|
@@ -5014,6 +6751,25 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5014
6751
|
resizeEl.style.top = Math.round(rect.top) + "px";
|
|
5015
6752
|
resizeEl.style.width = Math.round(rect.width) + "px";
|
|
5016
6753
|
resizeEl.style.height = Math.round(rect.height) + "px";
|
|
6754
|
+
if (scaleToolEnabled) {
|
|
6755
|
+
// Uniform scale factor: scaleToolEnabled already forces the
|
|
6756
|
+
// aspect-ratio lock above (nextRect), so width/origin.width and
|
|
6757
|
+
// height/origin.height agree (barring the min-size clamp's rounding)
|
|
6758
|
+
// — width is the simpler, always-defined choice.
|
|
6759
|
+
var kScaleFactor = rect.width / Math.max(1, origin.width);
|
|
6760
|
+
if (originBorderWidth > 0) {
|
|
6761
|
+
resizeEl.style.borderWidth =
|
|
6762
|
+
Math.max(
|
|
6763
|
+
0,
|
|
6764
|
+
Math.round(originBorderWidth * kScaleFactor * 100) / 100,
|
|
6765
|
+
) + "px";
|
|
6766
|
+
}
|
|
6767
|
+
if (originFontSize > 0) {
|
|
6768
|
+
resizeEl.style.fontSize =
|
|
6769
|
+
Math.max(1, Math.round(originFontSize * kScaleFactor * 100) / 100) +
|
|
6770
|
+
"px";
|
|
6771
|
+
}
|
|
6772
|
+
}
|
|
5017
6773
|
showTransformBadge(
|
|
5018
6774
|
Math.round(rect.width) + " x " + Math.round(rect.height),
|
|
5019
6775
|
ev.clientX,
|
|
@@ -5036,6 +6792,8 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5036
6792
|
resizeEl.style.top = originalInlineTop;
|
|
5037
6793
|
resizeEl.style.width = originalInlineWidth;
|
|
5038
6794
|
resizeEl.style.height = originalInlineHeight;
|
|
6795
|
+
resizeEl.style.borderWidth = originalInlineBorderWidth;
|
|
6796
|
+
resizeEl.style.fontSize = originalInlineFontSize;
|
|
5039
6797
|
selectedEl = resizeEl;
|
|
5040
6798
|
positionOverlay(selectionOverlay, selectedEl);
|
|
5041
6799
|
}
|
|
@@ -5052,17 +6810,28 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5052
6810
|
cleanupResizeDrag();
|
|
5053
6811
|
hideTransformBadge();
|
|
5054
6812
|
if (!resizeEl) return;
|
|
6813
|
+
var styles: Record<string, string> = {
|
|
6814
|
+
position: resizeEl.style.position,
|
|
6815
|
+
left: resizeEl.style.left,
|
|
6816
|
+
top: resizeEl.style.top,
|
|
6817
|
+
width: resizeEl.style.width,
|
|
6818
|
+
height: resizeEl.style.height,
|
|
6819
|
+
};
|
|
6820
|
+
// Only include borderWidth/fontSize when the K-scale tool actually
|
|
6821
|
+
// changed them (originBorderWidth/originFontSize > 0 AND
|
|
6822
|
+
// scaleToolEnabled) — a normal resize must never introduce these keys,
|
|
6823
|
+
// matching the "normal resize unchanged" requirement.
|
|
6824
|
+
if (scaleToolEnabled && originBorderWidth > 0) {
|
|
6825
|
+
styles.borderWidth = resizeEl.style.borderWidth;
|
|
6826
|
+
}
|
|
6827
|
+
if (scaleToolEnabled && originFontSize > 0) {
|
|
6828
|
+
styles.fontSize = resizeEl.style.fontSize;
|
|
6829
|
+
}
|
|
5055
6830
|
(window.parent as Window).postMessage(
|
|
5056
6831
|
{
|
|
5057
6832
|
type: "visual-style-change",
|
|
5058
6833
|
selector: getSelector(resizeEl),
|
|
5059
|
-
styles:
|
|
5060
|
-
position: resizeEl.style.position,
|
|
5061
|
-
left: resizeEl.style.left,
|
|
5062
|
-
top: resizeEl.style.top,
|
|
5063
|
-
width: resizeEl.style.width,
|
|
5064
|
-
height: resizeEl.style.height,
|
|
5065
|
-
},
|
|
6834
|
+
styles: styles,
|
|
5066
6835
|
payload: getElementInfo(resizeEl),
|
|
5067
6836
|
},
|
|
5068
6837
|
"*",
|
|
@@ -5232,6 +7001,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5232
7001
|
if (Math.hypot(ev.clientX - startX, ev.clientY - startY) <= 3) return;
|
|
5233
7002
|
clearPendingShieldDrag();
|
|
5234
7003
|
didStartDrag = true;
|
|
7004
|
+
// Multi-select group move: when the drag starts on a member of the
|
|
7005
|
+
// current 2+ selection, PRESERVE the whole selection (no
|
|
7006
|
+
// selectTarget collapse) and move the group together — Figma
|
|
7007
|
+
// behavior. A plain click (no drag) still collapses to the clicked
|
|
7008
|
+
// element via onUp below (existing disambiguation). Alt-drag
|
|
7009
|
+
// duplication stays single-element.
|
|
7010
|
+
var groupGestureMember = !e.altKey
|
|
7011
|
+
? groupMemberForGestureTarget(dragTarget)
|
|
7012
|
+
: null;
|
|
7013
|
+
if (
|
|
7014
|
+
groupGestureMember &&
|
|
7015
|
+
collectMoveGroupMembers(groupGestureMember).length > 1
|
|
7016
|
+
) {
|
|
7017
|
+
suppressNextShieldClickBriefly();
|
|
7018
|
+
startMove(ev, groupGestureMember);
|
|
7019
|
+
return;
|
|
7020
|
+
}
|
|
5235
7021
|
selectTarget(dragTarget, ev);
|
|
5236
7022
|
suppressNextShieldClickBriefly();
|
|
5237
7023
|
startMove(ev);
|
|
@@ -5400,6 +7186,27 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5400
7186
|
true,
|
|
5401
7187
|
);
|
|
5402
7188
|
|
|
7189
|
+
// Space-pan release: keydown forwarding above arms the parent's temporary
|
|
7190
|
+
// hand tool (see postDesignHotkey/"design-hotkey"), but the parent also
|
|
7191
|
+
// needs the matching keyup to release it — without this, holding Space
|
|
7192
|
+
// inside the preview iframe would arm panning but never let go. Forwarded
|
|
7193
|
+
// as its own message (not reusing "design-hotkey", which the parent only
|
|
7194
|
+
// ever re-dispatches as a synthetic keydown) so the parent can drive its
|
|
7195
|
+
// real keyup-driven release logic.
|
|
7196
|
+
document.addEventListener(
|
|
7197
|
+
"keyup",
|
|
7198
|
+
function (e) {
|
|
7199
|
+
if (e.key !== " " || e.code !== "Space") return;
|
|
7200
|
+
if (activeTextEditEl || isEditorTypingTarget(e.target)) return;
|
|
7201
|
+
stopNativeInteraction(e);
|
|
7202
|
+
(window.parent as Window).postMessage(
|
|
7203
|
+
{ type: "design-hotkey-up", key: e.key, code: e.code },
|
|
7204
|
+
"*",
|
|
7205
|
+
);
|
|
7206
|
+
},
|
|
7207
|
+
true,
|
|
7208
|
+
);
|
|
7209
|
+
|
|
5403
7210
|
function hasFigmaClipboardPayload(value) {
|
|
5404
7211
|
return /<[^>]+\sdata-(metadata|buffer)=["'][^"']*\((figmeta|figma)\)[^"']*["']/i.test(
|
|
5405
7212
|
String(value || ""),
|
|
@@ -5507,11 +7314,56 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5507
7314
|
programmaticFlag && !isRejectedRawTextEditTarget(eventTarget)
|
|
5508
7315
|
? eventTarget
|
|
5509
7316
|
: null;
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
7317
|
+
// Programmatic edits (e.g. begin-text-edit on a just-created text node)
|
|
7318
|
+
// already carry the exact node to edit as e.target. A freshly-created text
|
|
7319
|
+
// node is 0×0, so elementFromEditorPoint at its synthesized edge point
|
|
7320
|
+
// resolves to whatever is underneath — the parent screen container
|
|
7321
|
+
// (<main>) — and editing would bind to the ENTIRE screen instead of the new
|
|
7322
|
+
// node (keystrokes land in the wrong element, the node stays empty, focus is
|
|
7323
|
+
// lost). So for the programmatic path, honor the explicit target first and
|
|
7324
|
+
// never re-resolve from a point.
|
|
7325
|
+
var target = programmaticFlag
|
|
7326
|
+
? // Prefer the raw explicit node (rawTargetFallback === eventTarget) over
|
|
7327
|
+
// findTextEditTarget, which climbs UP to the highest inline-editable
|
|
7328
|
+
// ancestor (→ <main>) and would put the whole screen into edit mode.
|
|
7329
|
+
rawTargetFallback || findTextEditTarget(eventTarget)
|
|
7330
|
+
: findTextEditTarget(elementFromEditorPoint(e.clientX, e.clientY)) ||
|
|
7331
|
+
findTextEditTarget(eventTarget) ||
|
|
7332
|
+
rawTargetFallback;
|
|
7333
|
+
if (!target || target.nodeType !== 1) {
|
|
7334
|
+
// Figma parity: double-clicking a non-text element descends one level
|
|
7335
|
+
// into the current selection instead of doing nothing — select the
|
|
7336
|
+
// hit-tested element under the pointer (selectionTargetForHit already
|
|
7337
|
+
// returns the raw, deeper hit when it falls inside the current
|
|
7338
|
+
// selection, and climbs to the nearest stable-source ancestor
|
|
7339
|
+
// otherwise, so this reuses the same selection-filtering rules a
|
|
7340
|
+
// normal click uses). Skip this for the programmatic path: there is no
|
|
7341
|
+
// real pointer position to hit-test, and we already tried the explicit
|
|
7342
|
+
// target above.
|
|
7343
|
+
if (!programmaticFlag) {
|
|
7344
|
+
var descendHit = elementFromEditorPoint(e.clientX, e.clientY);
|
|
7345
|
+
if (
|
|
7346
|
+
descendHit &&
|
|
7347
|
+
descendHit !== document.body &&
|
|
7348
|
+
descendHit !== document.documentElement &&
|
|
7349
|
+
!isLayerInteractionBlocked(descendHit)
|
|
7350
|
+
) {
|
|
7351
|
+
var previousSelectedElForDescend = selectedEl;
|
|
7352
|
+
var descendTarget = selectionTargetForHit(descendHit);
|
|
7353
|
+
if (descendTarget && !isLayerInteractionBlocked(descendTarget)) {
|
|
7354
|
+
selectedEl = descendTarget;
|
|
7355
|
+
positionOverlay(selectionOverlay, selectedEl);
|
|
7356
|
+
preservePreviousSelectedElementForShiftClick(
|
|
7357
|
+
previousSelectedElForDescend,
|
|
7358
|
+
selectedEl,
|
|
7359
|
+
e,
|
|
7360
|
+
);
|
|
7361
|
+
postElementSelect(selectedEl, e);
|
|
7362
|
+
}
|
|
7363
|
+
}
|
|
7364
|
+
}
|
|
7365
|
+
return;
|
|
7366
|
+
}
|
|
5515
7367
|
// Anchor the selection identity to the nearest source-backed element. Text
|
|
5516
7368
|
// editing still operates on the actual target text node, but a later
|
|
5517
7369
|
// style edit posts from selectedEl, so it must point at a patchable
|
|
@@ -5665,10 +7517,10 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5665
7517
|
scheduleTextEditingChromeUpdate();
|
|
5666
7518
|
return;
|
|
5667
7519
|
}
|
|
5668
|
-
// T21: forward Cmd/Ctrl+B and Cmd/Ctrl+
|
|
5669
|
-
// execCommand bold/italic on the current selection.
|
|
5670
|
-
// (T12) cleans up any span nesting
|
|
5671
|
-
// session commits.
|
|
7520
|
+
// T21: forward Cmd/Ctrl+B, Cmd/Ctrl+I, and Cmd/Ctrl+U within the edit
|
|
7521
|
+
// session to execCommand bold/italic/underline on the current selection.
|
|
7522
|
+
// normalizeNestedIdenticalSpans (T12) cleans up any span nesting
|
|
7523
|
+
// execCommand leaves behind when the session commits.
|
|
5672
7524
|
var metaOrCtrl = ev.metaKey || ev.ctrlKey;
|
|
5673
7525
|
if (metaOrCtrl && !ev.altKey && ev.key.toLowerCase() === "b") {
|
|
5674
7526
|
ev.preventDefault();
|
|
@@ -5682,6 +7534,12 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5682
7534
|
scheduleTextEditingChromeUpdate();
|
|
5683
7535
|
return;
|
|
5684
7536
|
}
|
|
7537
|
+
if (metaOrCtrl && !ev.altKey && ev.key.toLowerCase() === "u") {
|
|
7538
|
+
ev.preventDefault();
|
|
7539
|
+
document.execCommand("underline");
|
|
7540
|
+
scheduleTextEditingChromeUpdate();
|
|
7541
|
+
return;
|
|
7542
|
+
}
|
|
5685
7543
|
}
|
|
5686
7544
|
|
|
5687
7545
|
function onPaste(ev) {
|
|
@@ -5708,7 +7566,23 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5708
7566
|
target.addEventListener("mouseup", onSelectionChange, true);
|
|
5709
7567
|
document.addEventListener("selectionchange", onSelectionChange);
|
|
5710
7568
|
target.focus();
|
|
5711
|
-
|
|
7569
|
+
if (programmaticTextEdit) {
|
|
7570
|
+
// The synthesized point sits at the (0×0) node's edge and resolves to the
|
|
7571
|
+
// parent element, so caretRangeFromPoint would drop the caret OUTSIDE the
|
|
7572
|
+
// editable node. Collapse to the end of the target's own contents instead.
|
|
7573
|
+
try {
|
|
7574
|
+
var progRange = document.createRange();
|
|
7575
|
+
progRange.selectNodeContents(target);
|
|
7576
|
+
progRange.collapse(false);
|
|
7577
|
+
var progSel = window.getSelection();
|
|
7578
|
+
progSel.removeAllRanges();
|
|
7579
|
+
progSel.addRange(progRange);
|
|
7580
|
+
} catch {
|
|
7581
|
+
/* selection APIs unavailable — focus() alone still enables typing */
|
|
7582
|
+
}
|
|
7583
|
+
} else {
|
|
7584
|
+
placeTextCaretFromPoint(target, e.clientX, e.clientY);
|
|
7585
|
+
}
|
|
5712
7586
|
}
|
|
5713
7587
|
|
|
5714
7588
|
shieldOverlay.addEventListener("dblclick", beginTextEditingFromEvent, true);
|
|
@@ -5751,7 +7625,21 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5751
7625
|
if (hoveringSelectedSpacingSurface) {
|
|
5752
7626
|
clearSpacingHoverTimer();
|
|
5753
7627
|
selectedSpacingHovered = true;
|
|
7628
|
+
lastSpacingPointerPoint = { x: e.clientX, y: e.clientY };
|
|
5754
7629
|
updateSpacingOverlay(selectedEl);
|
|
7630
|
+
// Reliable padding/gap hover: hit-test the handle geometry
|
|
7631
|
+
// directly from the pointer position instead of depending on the
|
|
7632
|
+
// pointermove's event target being the region node (see
|
|
7633
|
+
// spacingHandleKeyAtPoint). Shows/updates the "Npx" value box
|
|
7634
|
+
// while hovering the handle line; clears it when the pointer
|
|
7635
|
+
// leaves the tolerance zone.
|
|
7636
|
+
var pointSpacingKey = spacingHandleKeyAtPoint(e.clientX, e.clientY);
|
|
7637
|
+
if (pointSpacingKey) {
|
|
7638
|
+
activateSpacingHandle(pointSpacingKey);
|
|
7639
|
+
} else if (hoveredSpacingHandleKey) {
|
|
7640
|
+
hoveredSpacingHandleKey = "";
|
|
7641
|
+
updateSpacingOverlay(selectedEl);
|
|
7642
|
+
}
|
|
5755
7643
|
} else {
|
|
5756
7644
|
scheduleSpacingHoverClear(e);
|
|
5757
7645
|
}
|
|
@@ -5897,10 +7785,12 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5897
7785
|
'"]',
|
|
5898
7786
|
);
|
|
5899
7787
|
if (!nodeTarget || nodeTarget.nodeType !== 1) return;
|
|
5900
|
-
//
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
7788
|
+
// Edit the EXACT node identified by nodeId. Do NOT run it through
|
|
7789
|
+
// findTextEditTarget here — that helper climbs UP to the highest
|
|
7790
|
+
// inline-editable ancestor, which for a text node inside a text-heavy
|
|
7791
|
+
// screen resolves all the way to <main>, putting the ENTIRE screen into
|
|
7792
|
+
// edit mode instead of this node (keystrokes land in the wrong element).
|
|
7793
|
+
var textTarget: HTMLElement | null = nodeTarget as HTMLElement;
|
|
5904
7794
|
if (!textTarget || textTarget.nodeType !== 1) return;
|
|
5905
7795
|
// If we are already editing this element, do nothing.
|
|
5906
7796
|
if (activeTextEditEl && activeTextEditEl === textTarget) return;
|
|
@@ -5941,6 +7831,76 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
5941
7831
|
scaleToolEnabled = !!e.data.enabled;
|
|
5942
7832
|
return;
|
|
5943
7833
|
}
|
|
7834
|
+
// gradient-edit-target / gradient-edit-clear: see the gradientEditTarget
|
|
7835
|
+
// doc comment above (near parseLinearGradientCss) for the full parent
|
|
7836
|
+
// wiring contract. `nodeId` must be a `data-agent-native-node-id` value;
|
|
7837
|
+
// `cssValue` is the live gradient CSS (only linear-gradient(...) renders
|
|
7838
|
+
// handles — anything else is accepted but draws nothing).
|
|
7839
|
+
if (e.data.type === "gradient-edit-target") {
|
|
7840
|
+
var gradientTargetNodeId =
|
|
7841
|
+
typeof e.data.nodeId === "string" ? e.data.nodeId : "";
|
|
7842
|
+
var gradientTargetCssValue =
|
|
7843
|
+
typeof e.data.cssValue === "string" ? e.data.cssValue : "";
|
|
7844
|
+
if (!gradientTargetNodeId || !gradientTargetCssValue) {
|
|
7845
|
+
gradientEditTarget = null;
|
|
7846
|
+
hideGradientOverlay();
|
|
7847
|
+
return;
|
|
7848
|
+
}
|
|
7849
|
+
gradientEditTarget = {
|
|
7850
|
+
nodeId: gradientTargetNodeId,
|
|
7851
|
+
cssValue: gradientTargetCssValue,
|
|
7852
|
+
};
|
|
7853
|
+
positionGradientOverlay();
|
|
7854
|
+
return;
|
|
7855
|
+
}
|
|
7856
|
+
if (e.data.type === "gradient-edit-clear") {
|
|
7857
|
+
gradientEditTarget = null;
|
|
7858
|
+
hideGradientOverlay();
|
|
7859
|
+
return;
|
|
7860
|
+
}
|
|
7861
|
+
// state-preview: force-render one element's interaction-state styling by
|
|
7862
|
+
// setting/removing the `data-an-state-preview="<state>"` attribute — see
|
|
7863
|
+
// `shared/interaction-states.ts`'s "Forced-preview mechanism" doc comment
|
|
7864
|
+
// for the full contract this implements. This bridge does ZERO CSS work:
|
|
7865
|
+
// the twin `[data-agent-native-node-id="…"][data-an-state-preview="…"]`
|
|
7866
|
+
// rule already lives in the persisted `<style data-agent-native-states>`
|
|
7867
|
+
// block (written by `duplicateStatePreviewRules`), so setting the
|
|
7868
|
+
// attribute is the entire preview mechanism. `state: null` (or a missing/
|
|
7869
|
+
// empty `nodeId`) clears any currently-previewing element.
|
|
7870
|
+
if (e.data.type === "state-preview") {
|
|
7871
|
+
var statePreviewTargetNodeId =
|
|
7872
|
+
typeof e.data.nodeId === "string" ? e.data.nodeId : "";
|
|
7873
|
+
var statePreviewState =
|
|
7874
|
+
typeof e.data.state === "string" ? e.data.state : "";
|
|
7875
|
+
// Clear the PREVIOUS target first — only one element force-previews a
|
|
7876
|
+
// state at a time, and the new message may target a different node
|
|
7877
|
+
// (e.g. the selection changed) or clear entirely.
|
|
7878
|
+
if (statePreviewNodeId) {
|
|
7879
|
+
var previousStatePreviewEl = document.querySelector(
|
|
7880
|
+
'[data-agent-native-node-id="' +
|
|
7881
|
+
String(statePreviewNodeId)
|
|
7882
|
+
.replace(/\\/g, "\\\\")
|
|
7883
|
+
.replace(/"/g, '\\"') +
|
|
7884
|
+
'"]',
|
|
7885
|
+
) as HTMLElement | null;
|
|
7886
|
+
if (previousStatePreviewEl) {
|
|
7887
|
+
previousStatePreviewEl.removeAttribute("data-an-state-preview");
|
|
7888
|
+
}
|
|
7889
|
+
statePreviewNodeId = null;
|
|
7890
|
+
}
|
|
7891
|
+
if (!statePreviewTargetNodeId || !statePreviewState) return;
|
|
7892
|
+
var statePreviewEl = document.querySelector(
|
|
7893
|
+
'[data-agent-native-node-id="' +
|
|
7894
|
+
String(statePreviewTargetNodeId)
|
|
7895
|
+
.replace(/\\/g, "\\\\")
|
|
7896
|
+
.replace(/"/g, '\\"') +
|
|
7897
|
+
'"]',
|
|
7898
|
+
) as HTMLElement | null;
|
|
7899
|
+
if (!statePreviewEl) return;
|
|
7900
|
+
statePreviewEl.setAttribute("data-an-state-preview", statePreviewState);
|
|
7901
|
+
statePreviewNodeId = statePreviewTargetNodeId;
|
|
7902
|
+
return;
|
|
7903
|
+
}
|
|
5944
7904
|
if (e.data.type === "agent-native:cancel-active-drag") {
|
|
5945
7905
|
cancelActiveBridgeDrag();
|
|
5946
7906
|
return;
|
|
@@ -6083,8 +8043,18 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6083
8043
|
} catch (_err) {}
|
|
6084
8044
|
}
|
|
6085
8045
|
if (!target) return;
|
|
6086
|
-
|
|
6087
|
-
|
|
8046
|
+
// Only reset the spacing hover state when the replay actually CHANGES
|
|
8047
|
+
// the selection. The host re-sends select-element on every
|
|
8048
|
+
// application-state poll tick (~1-2s) even when nothing changed; the
|
|
8049
|
+
// old unconditional reset silently killed the padding/gap hover state
|
|
8050
|
+
// — the "Npx" value box and the hatch band vanished within a poll tick
|
|
8051
|
+
// whenever the cursor RESTED on a handle (the user only ever saw the
|
|
8052
|
+
// badge flash, i.e. "the value box never shows"). A same-element
|
|
8053
|
+
// replay must be a no-op for hover state.
|
|
8054
|
+
if (target !== selectedEl) {
|
|
8055
|
+
selectedSpacingHovered = false;
|
|
8056
|
+
hoveredSpacingHandleKey = "";
|
|
8057
|
+
}
|
|
6088
8058
|
selectedEl = target;
|
|
6089
8059
|
positionOverlay(selectionOverlay, target);
|
|
6090
8060
|
if (hoveredEl === selectedEl) highlightOverlay.style.display = "none";
|
|
@@ -6308,8 +8278,15 @@ declare var __DESIGN_CANVAS_BOARD_SURFACE__: boolean;
|
|
|
6308
8278
|
}
|
|
6309
8279
|
});
|
|
6310
8280
|
|
|
6311
|
-
|
|
6312
|
-
|
|
8281
|
+
// rAF-coalesced on purpose: scroll events can fire several times per frame
|
|
8282
|
+
// (nested scrollers, high-rate trackpads), and running the full overlay
|
|
8283
|
+
// pipeline synchronously per event — with its interleaved rect reads and
|
|
8284
|
+
// overlay style writes — forces repeated synchronous reflows while a
|
|
8285
|
+
// selection is active. Coalescing to one refreshOverlays() per frame keeps
|
|
8286
|
+
// overlays visually locked to the content (rAF runs before paint) while
|
|
8287
|
+
// bounding the per-frame cost regardless of the incoming event rate.
|
|
8288
|
+
window.addEventListener("scroll", scheduleRefreshOverlays, true);
|
|
8289
|
+
window.addEventListener("resize", scheduleRefreshOverlays);
|
|
6313
8290
|
applyEditorChromeScale();
|
|
6314
8291
|
|
|
6315
8292
|
// One-time ready signal: tells the host that every message listener above is
|