@agent-native/core 0.129.1 → 0.130.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +78 -0
- package/corpus/core/docs/content/cloneable-saas.mdx +114 -0
- package/corpus/core/docs/content/creating-templates.mdx +28 -2
- package/corpus/core/docs/content/syncing-template-changes.mdx +7 -0
- package/corpus/core/docs/content/template-clips-developers.mdx +12 -5
- package/corpus/core/docs/content/template-dispatch-developers.mdx +1 -1
- package/corpus/core/docs/content/template-dispatch.mdx +1 -1
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/action-ui.ts +1 -0
- package/corpus/core/src/action.ts +5 -1
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +15 -5
- package/corpus/core/src/agent/harness/runner.ts +5 -0
- package/corpus/core/src/agent/production-agent.ts +33 -0
- package/corpus/core/src/agent/run-loop-with-resume.ts +5 -0
- package/corpus/core/src/agent/run-manager.ts +126 -1
- package/corpus/core/src/agent/thread-data-builder.ts +47 -2
- package/corpus/core/src/agent/types.ts +2 -0
- package/corpus/core/src/chat-threads/store.ts +4 -5
- package/corpus/core/src/cli/create.ts +1139 -65
- package/corpus/core/src/cli/design-connect.ts +3 -7
- package/corpus/core/src/cli/index.ts +5 -3
- package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +83 -47
- package/corpus/core/src/client/AssistantChat.tsx +27 -16
- package/corpus/core/src/client/agent-chat-adapter.ts +24 -0
- package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +17 -2
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/message-components.tsx +104 -1
- package/corpus/core/src/client/chat/run-recovery.tsx +15 -3
- package/corpus/core/src/client/chat/widgets/DataTableWidget.tsx +10 -29
- package/corpus/core/src/client/chat/widgets/WorkspaceFileWidget.tsx +71 -0
- package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +22 -0
- package/corpus/core/src/client/chat/widgets/workspace-file-result.ts +49 -0
- package/corpus/core/src/client/embed-auth.ts +2 -0
- package/corpus/core/src/client/org/InvitationBanner.tsx +14 -1
- package/corpus/core/src/client/org/OrgSwitcher.tsx +23 -0
- package/corpus/core/src/client/org/TeamPage.tsx +130 -0
- package/corpus/core/src/client/org/WorkspaceNotice.tsx +85 -0
- package/corpus/core/src/client/org/hooks.ts +14 -0
- package/corpus/core/src/client/org/index.ts +2 -0
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +27 -40
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +134 -51
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +7 -0
- package/corpus/core/src/client/resources/use-resources.ts +6 -0
- package/corpus/core/src/client/use-action.ts +3 -1
- package/corpus/core/src/client/widgets/index.ts +1 -0
- package/corpus/core/src/coding-tools/run-code.ts +2 -1
- package/corpus/core/src/db/client.ts +28 -4
- package/corpus/core/src/deploy/build.ts +31 -3
- package/corpus/core/src/eject/remote-mcp-presets.ts +1 -1
- package/corpus/core/src/index.browser.ts +1 -0
- package/corpus/core/src/index.ts +1 -0
- package/corpus/core/src/integrations/google-docs-poller.ts +3 -0
- package/corpus/core/src/integrations/webhook-handler.ts +28 -11
- package/corpus/core/src/jobs/scheduler.ts +4 -0
- package/corpus/core/src/localization/default-messages.ts +3 -2
- package/corpus/core/src/mcp/build-server.ts +20 -2
- package/corpus/core/src/observability/traces.ts +48 -12
- package/corpus/core/src/org/handlers.ts +52 -1
- package/corpus/core/src/org/migrations.ts +4 -0
- package/corpus/core/src/org/plugin.ts +14 -0
- package/corpus/core/src/org/schema.ts +1 -0
- package/corpus/core/src/org/types.ts +8 -0
- package/corpus/core/src/org/workspace-url.ts +74 -0
- package/corpus/core/src/resources/handlers.ts +35 -5
- package/corpus/core/src/scripts/runner.ts +183 -5
- package/corpus/core/src/server/action-routes.ts +32 -2
- package/corpus/core/src/server/agent-chat-plugin.ts +29 -0
- package/corpus/core/src/server/agent-teams.ts +10 -1
- package/corpus/core/src/server/auth.ts +33 -7
- package/corpus/core/src/server/embed-route.ts +9 -0
- package/corpus/core/src/server/embed-session.ts +62 -3
- package/corpus/core/src/server/open-route.ts +20 -6
- package/corpus/core/src/server/prompts/framework-core-compact.ts +1 -1
- package/corpus/core/src/server/prompts/framework-core.ts +1 -0
- package/corpus/core/src/server/request-context.ts +36 -1
- package/corpus/core/src/shared/embed-auth.ts +1 -0
- package/corpus/core/src/shared/html-document.ts +115 -0
- package/corpus/core/src/shared/index.ts +1 -0
- package/corpus/core/src/sharing/access.ts +8 -1
- package/corpus/core/src/sharing/registry.ts +11 -2
- package/corpus/core/src/workspace-files/actions.ts +100 -0
- package/corpus/core/src/workspace-files/tool.ts +4 -0
- package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +4 -0
- package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +9 -1
- package/corpus/templates/analytics/AGENTS.md +17 -24
- package/corpus/templates/analytics/actions/data-source-status.ts +11 -1
- package/corpus/templates/analytics/app/i18n-data.ts +34 -0
- package/corpus/templates/analytics/app/lib/data-source-status.ts +86 -0
- package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +9 -0
- package/corpus/templates/analytics/app/pages/DataSources.tsx +184 -20
- package/corpus/templates/analytics/changelog/2026-07-28-native-analytics-data-sources-no-longer-show-duplicate-mc.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-exports-now-appear-in-chat-with-a-direct-download-instead-of.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-queued-chat-prompts-no-longer-resend-after-analytics-navigat.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-29-scheduled-dashboard-emails-load-every-panel-reliably-without.md +6 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +6 -3
- package/corpus/templates/analytics/server/lib/credential-keys.ts +1 -1
- package/corpus/templates/analytics/server/lib/hubspot.ts +1 -1
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +55 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +123 -27
- package/corpus/templates/analytics/server/routes/[...page].get.ts +2 -7
- package/corpus/templates/analytics/vite.config.ts +7 -0
- package/corpus/templates/brain/actions/list-connection-providers.ts +100 -2
- package/corpus/templates/brain/app/lib/brain.ts +10 -0
- package/corpus/templates/brain/app/routes/sources.tsx +15 -4
- package/corpus/templates/brain/changelog/2026-07-29-brain-automatically-retries-failed-source-syncs-and-guides-y.md +6 -0
- package/corpus/templates/brain/jobs/process-ingest-queue.ts +13 -0
- package/corpus/templates/brain/server/jobs/sync-sources.ts +27 -22
- package/corpus/templates/brain/server/plugins/agent-chat.ts +2 -1
- package/corpus/templates/brain/server/routes/api/brain/queue/run.post.ts +7 -1
- package/corpus/templates/clips/AGENTS.md +1 -1
- package/corpus/templates/clips/actions/create-recording-agent-link.ts +11 -1
- package/corpus/templates/clips/actions/update-comment.ts +68 -0
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +0 -1
- package/corpus/templates/clips/app/components/player/comments-panel.tsx +221 -65
- package/corpus/templates/clips/app/components/player/video-player.tsx +101 -27
- package/corpus/templates/clips/app/i18n/en-US.ts +2 -1
- package/corpus/templates/clips/app/routes/bug-report.done.tsx +48 -27
- package/corpus/templates/clips/app/routes/bug-report.tsx +43 -4
- package/corpus/templates/clips/app/routes/record.tsx +2 -0
- package/corpus/templates/clips/changelog/2026-07-29-clip-playback-now-recovers-instead-of-getting-stuck-when-a-b.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-29-comments-can-now-be-edited-after-posting.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-29-download-actions-now-use-a-shorter-clearer-label.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-29-embedded-bug-reports-now-return-a-temporary-agent-readable.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
- package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +21 -1
- package/corpus/templates/clips/shared/bug-report.ts +99 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +7 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +5 -2
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +20 -8
- package/corpus/templates/content/changelog/2026-07-29-missing-databases-no-longer-trigger-repeated-personal-view-e.md +6 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +83 -47
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +474 -55
- package/corpus/templates/design/.generated/bridge/embedded-wheel.generated.ts +3 -0
- package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +18 -1
- package/corpus/templates/design/.generated/bridge/source-location.generated.ts +348 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +2 -7
- package/corpus/templates/design/actions/apply-visual-edit.ts +7 -1
- package/corpus/templates/design/actions/connect-localhost.ts +5 -9
- package/corpus/templates/design/actions/create-component.ts +16 -13
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +2 -7
- package/corpus/templates/design/actions/list-design-source-capabilities.ts +4 -7
- package/corpus/templates/design/actions/list-local-files.ts +26 -50
- package/corpus/templates/design/actions/list-localhost-connections.ts +2 -7
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +2 -2
- package/corpus/templates/design/actions/open-visual-edit.ts +234 -135
- package/corpus/templates/design/actions/read-local-file.ts +26 -50
- package/corpus/templates/design/actions/request-localhost-write-consent.ts +2 -7
- package/corpus/templates/design/actions/write-local-file.ts +31 -73
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +307 -133
- package/corpus/templates/design/app/components/design/EditPanel.tsx +94 -22
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +137 -60
- package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +75 -14
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +755 -97
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +8 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +24 -1
- package/corpus/templates/design/app/components/design/bridge/source-location.bridge.ts +530 -0
- package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbenchLoader.tsx +278 -0
- package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +32 -12
- package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-setup.ts +14 -3
- package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +87 -24
- package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +36 -0
- package/corpus/templates/design/app/components/design/code-workbench/store.tsx +2 -1
- package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +48 -8
- package/corpus/templates/design/app/components/design/design-canvas/content-size-report.ts +3 -13
- package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +4 -2
- package/corpus/templates/design/app/components/design/design-canvas/hit-test.ts +3 -13
- package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +3 -1
- package/corpus/templates/design/app/components/design/edit-panel/inspect-code-source.ts +93 -0
- package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +4 -2
- package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +35 -0
- package/corpus/templates/design/app/components/design/multi-screen/iframe-targeting.ts +24 -0
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -0
- package/corpus/templates/design/app/components/design/types.ts +29 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +7 -4
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +153 -46
- package/corpus/templates/design/app/i18n-responsive-interact.ts +12 -12
- package/corpus/templates/design/app/lib/design-editor-route.ts +23 -0
- package/corpus/templates/design/app/pages/VisualEdit.tsx +19 -6
- package/corpus/templates/design/app/pages/design-editor/action-error.ts +19 -0
- package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +29 -0
- package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +6 -0
- package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +277 -15
- package/corpus/templates/design/app/pages/design-editor/editor-state.ts +75 -9
- package/corpus/templates/design/app/pages/design-editor/layer-state-scope.ts +28 -0
- package/corpus/templates/design/app/pages/design-editor/live-screen-layer-drop.ts +41 -0
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +496 -41
- package/corpus/templates/design/app/pages/design-editor/pending-live-edit-unload-guard.ts +27 -0
- package/corpus/templates/design/app/pages/design-editor/pending-structure-verification.ts +10 -0
- package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +168 -9
- package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +41 -3
- package/corpus/templates/design/app/pages/design-editor/screen-command-utils.ts +5 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +42 -13
- package/corpus/templates/design/app/pages/design-editor/source-location.ts +180 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +17 -0
- package/corpus/templates/design/app/public-routes.ts +1 -0
- package/corpus/templates/design/app/routes/visual-edit.$id.tsx +15 -0
- package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +1 -1
- package/corpus/templates/design/changelog/2026-07-28-deleting-a-selected-element-now-removes-it-from-the-live-pre.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-deleting-an-element-on-a-connected-localhost-screen-now-queu.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-shows-a-responsive-device-preview-with-pre.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-layer-hide-and-lock-now-apply-to-the-live-preview-instead-of.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-layers-panel-selections-on-a-connected-localhost-screen-can-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-locked-layers-now-show-a-subtle-dashed-outline-in-the-live-p.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-the-apply-design-updates-button-now-counts-pending-live-text.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-the-design-canvas-no-longer-stalls-on-a-render-loop-while-a-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-visual-edit-on-a-local-dev-server-no-longer-requires-signing.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-visual-edits-on-a-react-19-app-no-longer-report-a-source-lin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-an-edit-that-cannot-be-written-to-source-now-names-the-reaso.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-applying-a-primitive-dropped-onto-a-connected-localhost-scre.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-changing-a-style-in-the-inspector-now-updates-a-connected-lo.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-choosing-edit-or-annotate-from-a-focused-screen-now-returns-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-concurrent-source-edits-now-return-a-clear-conflict-response.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-creating-a-component-from-an-element-that-repeats-in-the-sou.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-deleting-an-element-you-just-dropped-onto-a-live-screen-no-l.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-dragging-a-layer-across-screens-no-longer-dies-when-the-poin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-dragging-an-element-from-a-stored-screen-into-a-live-localho.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-drawing-a-box-on-the-canvas-and-dragging-it-into-a-connected.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-fixed-a-loading-screen-flicker-when-reopening-designs-with-a.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-interact-mode-has-edit-and-annotate-buttons-again-so-you-can.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-layer-groups-can-be-dropped-onto-live-localhost-screens.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-localhost-source-errors-now-say-what-actually-went-wrong-whi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-opening-or-refreshing-a-live-localhost-screen-in-single-scre.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-redo-after-undoing-a-drop-onto-a-live-screen-puts-the-elemen.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-shapes-can-be-drawn-directly-onto-live-localhost-screens.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-shapes-drawn-on-the-canvas-board-now-appear-immediately-inst.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-style-edits-on-a-running-localhost-screen-now-preview-live-a.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-the-apply-design-updates-control-now-appears-as-a-clean-conn.md +6 -0
- package/corpus/templates/design/changelog/2026-07-29-visual-edits-on-a-mapped-component-instance-now-tell-the-age.md +6 -0
- package/corpus/templates/design/server/lib/design-data-access.ts +21 -7
- package/corpus/templates/design/server/lib/localhost-connection.ts +240 -0
- package/corpus/templates/design/server/routes/[...page].get.ts +2 -5
- package/corpus/templates/design/server/source-workspace.ts +2 -0
- package/corpus/templates/design/shared/code-layer.ts +43 -0
- package/corpus/templates/design/shared/html-content.ts +26 -0
- package/corpus/templates/design/shared/html-integrity.ts +21 -1
- package/corpus/templates/design/shared/local-jsx-visual-edit.ts +15 -0
- package/corpus/templates/design/shared/source-mode.ts +76 -0
- package/corpus/templates/dispatch/changelog/2026-07-29-workspace-members-without-admin-access-now-see-a-clear-permi.md +6 -0
- package/corpus/templates/dispatch/server/lib/usage-metrics.ts +2 -1
- package/corpus/templates/plan/app/lib/plan-annotation-runtime.ts +3 -4
- package/corpus/templates/plan/server/routes/[...page].get.ts +2 -5
- package/corpus/templates/slides/actions/patch-deck.ts +29 -17
- package/corpus/toolkit/CHANGELOG.md +6 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +3 -2
- package/dist/action-ui.d.ts +1 -0
- package/dist/action-ui.d.ts.map +1 -1
- package/dist/action-ui.js +1 -0
- package/dist/action-ui.js.map +1 -1
- package/dist/action.d.ts +5 -1
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +13 -5
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/harness/runner.d.ts.map +1 -1
- package/dist/agent/harness/runner.js +5 -0
- package/dist/agent/harness/runner.js.map +1 -1
- package/dist/agent/production-agent.d.ts +16 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +18 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +6 -0
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-manager.d.ts +14 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +91 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +3 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +35 -3
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/agent/types.d.ts +2 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +4 -6
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/create.d.ts +66 -6
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +765 -66
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +2 -7
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/cli/index.js +5 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.js +83 -47
- package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +16 -9
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +21 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
- package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe-kit.js +8 -2
- package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +17 -0
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +24 -2
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts +6 -0
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +13 -3
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat/widgets/DataTableWidget.d.ts.map +1 -1
- package/dist/client/chat/widgets/DataTableWidget.js +2 -3
- package/dist/client/chat/widgets/DataTableWidget.js.map +1 -1
- package/dist/client/chat/widgets/WorkspaceFileWidget.d.ts +5 -0
- package/dist/client/chat/widgets/WorkspaceFileWidget.d.ts.map +1 -0
- package/dist/client/chat/widgets/WorkspaceFileWidget.js +34 -0
- package/dist/client/chat/widgets/WorkspaceFileWidget.js.map +1 -0
- package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js +13 -1
- package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/client/chat/widgets/workspace-file-result.d.ts +12 -0
- package/dist/client/chat/widgets/workspace-file-result.d.ts.map +1 -0
- package/dist/client/chat/widgets/workspace-file-result.js +34 -0
- package/dist/client/chat/widgets/workspace-file-result.js.map +1 -0
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +2 -1
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/org/InvitationBanner.d.ts +5 -2
- package/dist/client/org/InvitationBanner.d.ts.map +1 -1
- package/dist/client/org/InvitationBanner.js +9 -2
- package/dist/client/org/InvitationBanner.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +3 -2
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +27 -3
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/org/WorkspaceNotice.d.ts +18 -0
- package/dist/client/org/WorkspaceNotice.d.ts.map +1 -0
- package/dist/client/org/WorkspaceNotice.js +56 -0
- package/dist/client/org/WorkspaceNotice.js.map +1 -0
- package/dist/client/org/hooks.d.ts +1 -0
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/org/hooks.js +12 -0
- package/dist/client/org/hooks.js.map +1 -1
- package/dist/client/org/index.d.ts +2 -1
- package/dist/client/org/index.d.ts.map +1 -1
- package/dist/client/org/index.js +2 -1
- package/dist/client/org/index.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +8 -12
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.d.ts +3 -0
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +49 -20
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +3 -0
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/resources/use-resources.d.ts +1 -0
- package/dist/client/resources/use-resources.d.ts.map +1 -1
- package/dist/client/resources/use-resources.js +3 -0
- package/dist/client/resources/use-resources.js.map +1 -1
- package/dist/client/use-action.d.ts +3 -1
- package/dist/client/use-action.d.ts.map +1 -1
- package/dist/client/use-action.js +3 -1
- package/dist/client/use-action.js.map +1 -1
- package/dist/client/widgets/index.d.ts +1 -1
- package/dist/client/widgets/index.d.ts.map +1 -1
- package/dist/client/widgets/index.js +1 -1
- package/dist/client/widgets/index.js.map +1 -1
- package/dist/coding-tools/run-code.js +2 -1
- package/dist/coding-tools/run-code.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +24 -3
- package/dist/db/client.js.map +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +25 -3
- package/dist/deploy/build.js.map +1 -1
- package/dist/eject/remote-mcp-presets.js +1 -1
- package/dist/eject/remote-mcp-presets.js.map +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.browser.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/google-docs-poller.d.ts.map +1 -1
- package/dist/integrations/google-docs-poller.js +4 -1
- package/dist/integrations/google-docs-poller.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +23 -11
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/jobs/scheduler.js +4 -0
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/localization/default-messages.d.ts +3 -2
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +3 -2
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +10 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +27 -7
- package/dist/observability/traces.js.map +1 -1
- package/dist/org/handlers.d.ts +15 -0
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +45 -1
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +4 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/org/plugin.d.ts +1 -0
- package/dist/org/plugin.d.ts.map +1 -1
- package/dist/org/plugin.js +10 -1
- package/dist/org/plugin.js.map +1 -1
- package/dist/org/schema.d.ts +19 -0
- package/dist/org/schema.d.ts.map +1 -1
- package/dist/org/schema.js +1 -0
- package/dist/org/schema.js.map +1 -1
- package/dist/org/types.d.ts +8 -0
- package/dist/org/types.d.ts.map +1 -1
- package/dist/org/types.js.map +1 -1
- package/dist/org/workspace-url.d.ts +32 -0
- package/dist/org/workspace-url.d.ts.map +1 -0
- package/dist/org/workspace-url.js +63 -0
- package/dist/org/workspace-url.js.map +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
- package/dist/provider-api/actions/provider-api.d.ts +7 -7
- package/dist/resources/handlers.d.ts +2 -3
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +25 -5
- package/dist/resources/handlers.js.map +1 -1
- package/dist/scripts/runner.d.ts +17 -0
- package/dist/scripts/runner.d.ts.map +1 -1
- package/dist/scripts/runner.js +130 -5
- package/dist/scripts/runner.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +24 -2
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +26 -1
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-teams.d.ts.map +1 -1
- package/dist/server/agent-teams.js +10 -1
- package/dist/server/agent-teams.js.map +1 -1
- package/dist/server/auth.d.ts +22 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +33 -7
- package/dist/server/auth.js.map +1 -1
- package/dist/server/embed-route.d.ts.map +1 -1
- package/dist/server/embed-route.js +6 -1
- package/dist/server/embed-route.js.map +1 -1
- package/dist/server/embed-session.d.ts +7 -0
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +46 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/server/open-route.d.ts.map +1 -1
- package/dist/server/open-route.js +17 -7
- package/dist/server/open-route.js.map +1 -1
- package/dist/server/prompts/framework-core-compact.js +1 -1
- package/dist/server/prompts/framework-core-compact.js.map +1 -1
- package/dist/server/prompts/framework-core.d.ts.map +1 -1
- package/dist/server/prompts/framework-core.js +1 -0
- package/dist/server/prompts/framework-core.js.map +1 -1
- package/dist/server/request-context.d.ts +26 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js +18 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/shared/embed-auth.d.ts +1 -0
- package/dist/shared/embed-auth.d.ts.map +1 -1
- package/dist/shared/embed-auth.js +1 -0
- package/dist/shared/embed-auth.js.map +1 -1
- package/dist/shared/html-document.d.ts +12 -0
- package/dist/shared/html-document.d.ts.map +1 -0
- package/dist/shared/html-document.js +92 -0
- package/dist/shared/html-document.js.map +1 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/sharing/access.d.ts +1 -0
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +8 -2
- package/dist/sharing/access.js.map +1 -1
- package/dist/sharing/registry.d.ts +3 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js.map +1 -1
- package/dist/workspace-files/actions.d.ts +28 -0
- package/dist/workspace-files/actions.d.ts.map +1 -0
- package/dist/workspace-files/actions.js +75 -0
- package/dist/workspace-files/actions.js.map +1 -0
- package/dist/workspace-files/tool.d.ts.map +1 -1
- package/dist/workspace-files/tool.js +4 -0
- package/dist/workspace-files/tool.js.map +1 -1
- package/docs/content/cloneable-saas.mdx +114 -0
- package/docs/content/creating-templates.mdx +28 -2
- package/docs/content/syncing-template-changes.mdx +7 -0
- package/docs/content/template-clips-developers.mdx +12 -5
- package/docs/content/template-dispatch-developers.mdx +1 -1
- package/docs/content/template-dispatch.mdx +1 -1
- package/package.json +2 -2
- package/src/action-ui.ts +1 -0
- package/src/action.ts +5 -1
- package/src/agent/engine/ai-sdk-engine.ts +15 -5
- package/src/agent/harness/runner.ts +5 -0
- package/src/agent/production-agent.ts +33 -0
- package/src/agent/run-loop-with-resume.ts +5 -0
- package/src/agent/run-manager.ts +126 -1
- package/src/agent/thread-data-builder.ts +47 -2
- package/src/agent/types.ts +2 -0
- package/src/chat-threads/store.ts +4 -5
- package/src/cli/create.ts +1139 -65
- package/src/cli/design-connect.ts +3 -7
- package/src/cli/index.ts +5 -3
- package/src/cli/skills-content/design-visual-edit-skill.ts +83 -47
- package/src/client/AssistantChat.tsx +27 -16
- package/src/client/agent-chat-adapter.ts +24 -0
- package/src/client/blocks/library/wireframe-kit.tsx +17 -2
- package/src/client/chat/index.ts +1 -0
- package/src/client/chat/message-components.tsx +104 -1
- package/src/client/chat/run-recovery.tsx +15 -3
- package/src/client/chat/widgets/DataTableWidget.tsx +10 -29
- package/src/client/chat/widgets/WorkspaceFileWidget.tsx +71 -0
- package/src/client/chat/widgets/builtin-tool-renderers.tsx +22 -0
- package/src/client/chat/widgets/workspace-file-result.ts +49 -0
- package/src/client/embed-auth.ts +2 -0
- package/src/client/org/InvitationBanner.tsx +14 -1
- package/src/client/org/OrgSwitcher.tsx +23 -0
- package/src/client/org/TeamPage.tsx +130 -0
- package/src/client/org/WorkspaceNotice.tsx +85 -0
- package/src/client/org/hooks.ts +14 -0
- package/src/client/org/index.ts +2 -0
- package/src/client/resources/McpIntegrationDialog.tsx +27 -40
- package/src/client/resources/ResourcesPanel.tsx +134 -51
- package/src/client/resources/mcp-integration-catalog.ts +7 -0
- package/src/client/resources/use-resources.ts +6 -0
- package/src/client/use-action.ts +3 -1
- package/src/client/widgets/index.ts +1 -0
- package/src/coding-tools/run-code.ts +2 -1
- package/src/db/client.ts +28 -4
- package/src/deploy/build.ts +31 -3
- package/src/eject/remote-mcp-presets.ts +1 -1
- package/src/index.browser.ts +1 -0
- package/src/index.ts +1 -0
- package/src/integrations/google-docs-poller.ts +3 -0
- package/src/integrations/webhook-handler.ts +28 -11
- package/src/jobs/scheduler.ts +4 -0
- package/src/localization/default-messages.ts +3 -2
- package/src/mcp/build-server.ts +20 -2
- package/src/observability/traces.ts +48 -12
- package/src/org/handlers.ts +52 -1
- package/src/org/migrations.ts +4 -0
- package/src/org/plugin.ts +14 -0
- package/src/org/schema.ts +1 -0
- package/src/org/types.ts +8 -0
- package/src/org/workspace-url.ts +74 -0
- package/src/resources/handlers.ts +35 -5
- package/src/scripts/runner.ts +183 -5
- package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +2 -1
- package/src/server/action-routes.ts +32 -2
- package/src/server/agent-chat-plugin.ts +29 -0
- package/src/server/agent-teams.ts +10 -1
- package/src/server/auth.ts +33 -7
- package/src/server/embed-route.ts +9 -0
- package/src/server/embed-session.ts +62 -3
- package/src/server/open-route.ts +20 -6
- package/src/server/prompts/framework-core-compact.ts +1 -1
- package/src/server/prompts/framework-core.ts +1 -0
- package/src/server/request-context.ts +36 -1
- package/src/shared/embed-auth.ts +1 -0
- package/src/shared/html-document.ts +115 -0
- package/src/shared/index.ts +1 -0
- package/src/sharing/access.ts +8 -1
- package/src/sharing/registry.ts +11 -2
- package/src/workspace-files/actions.ts +100 -0
- package/src/workspace-files/tool.ts +4 -0
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +0 -6
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +0 -6
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
IconDeviceFloppy,
|
|
9
9
|
IconLayoutGrid,
|
|
10
10
|
} from "@tabler/icons-react";
|
|
11
|
+
import { useEffect, useState } from "react";
|
|
11
12
|
import { Link } from "react-router";
|
|
12
13
|
|
|
13
14
|
import { Button } from "@/components/ui/button";
|
|
@@ -16,10 +17,22 @@ export default function VisualEditPage() {
|
|
|
16
17
|
const t = useT();
|
|
17
18
|
const { session } = useSession();
|
|
18
19
|
const hasSession = Boolean(session?.email);
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const [hasHydrated, setHasHydrated] = useState(false);
|
|
21
|
+
const [signInHref, setSignInHref] = useState<string | null>(null);
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
setHasHydrated(true);
|
|
25
|
+
if (hasSession) return;
|
|
26
|
+
setSignInHref(
|
|
27
|
+
buildSignInReturnHref({ returnTo: "/visual-edit?intent=save" }),
|
|
28
|
+
);
|
|
29
|
+
}, [hasSession]);
|
|
30
|
+
|
|
31
|
+
// Server shells are intentionally anonymous; defer session-specific chrome
|
|
32
|
+
// until after hydration so the public entry never reconciles different links.
|
|
33
|
+
const isSignedIn = hasHydrated && hasSession;
|
|
34
|
+
const primaryHref = isSignedIn ? "/" : (signInHref ?? undefined);
|
|
35
|
+
const primaryAriaLabel = isSignedIn
|
|
23
36
|
? t("visualEdit.openDesign")
|
|
24
37
|
: t("designEditor.signUpToSave");
|
|
25
38
|
|
|
@@ -35,7 +48,7 @@ export default function VisualEditPage() {
|
|
|
35
48
|
</Link>
|
|
36
49
|
<Button asChild variant="outline" size="sm">
|
|
37
50
|
<a href={primaryHref} aria-label={primaryAriaLabel}>
|
|
38
|
-
{
|
|
51
|
+
{isSignedIn
|
|
39
52
|
? t("visualEdit.openDesign")
|
|
40
53
|
: t("designEditor.signUpToSave")}
|
|
41
54
|
<IconArrowUpRight className="size-4" />
|
|
@@ -59,7 +72,7 @@ export default function VisualEditPage() {
|
|
|
59
72
|
<Button asChild size="lg" className="gap-2">
|
|
60
73
|
<a href={primaryHref} aria-label={primaryAriaLabel}>
|
|
61
74
|
<IconDeviceFloppy className="size-4" />
|
|
62
|
-
{
|
|
75
|
+
{isSignedIn
|
|
63
76
|
? t("visualEdit.openDesign")
|
|
64
77
|
: t("designEditor.signUpToSave")}
|
|
65
78
|
</a>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The action HTTP surface echoes a real message only for explicit client
|
|
3
|
+
* errors; an unclassified failure comes back as a 5xx "Internal server error"
|
|
4
|
+
* with the cause server-log-only. Show the former as toast detail and drop the
|
|
5
|
+
* latter, so a toast never reads as if it explained something when it didn't.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** `callAction` prefixes every rejection with `Action <name> failed: `. */
|
|
9
|
+
const CALL_ACTION_PREFIX = /^Action [\w.-]+ failed:\s*/;
|
|
10
|
+
|
|
11
|
+
export function actionErrorDetail(error: unknown): string | undefined {
|
|
12
|
+
const status = (error as { status?: unknown } | null | undefined)?.status;
|
|
13
|
+
if (typeof status !== "number" || status < 400 || status >= 500) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const message = error instanceof Error ? error.message : "";
|
|
17
|
+
const detail = message.replace(CALL_ACTION_PREFIX, "").trim();
|
|
18
|
+
return detail || undefined;
|
|
19
|
+
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "./canvas-primitives";
|
|
14
14
|
import { BOARD_TEXT_AUTO_COLOR_MARKER } from "./cross-screen-text-color";
|
|
15
15
|
import { escapeHtmlAttributeValue, escapeHtmlText } from "./dom-utils";
|
|
16
|
+
import { isStandaloneHttpUrl } from "./editor-state";
|
|
16
17
|
import type { DesignFile } from "./types";
|
|
17
18
|
|
|
18
19
|
export function nextDuplicatedFilename(
|
|
@@ -188,6 +189,11 @@ export function appendCanvasPrimitiveToHtml(
|
|
|
188
189
|
options?: { preserveNegativePosition?: boolean; isBoardTarget?: boolean },
|
|
189
190
|
): string | null {
|
|
190
191
|
if (typeof window === "undefined") return null;
|
|
192
|
+
// A live/localhost screen stores its route URL here, not a document.
|
|
193
|
+
// Appending to it parses the URL as body text and returns a whole HTML file,
|
|
194
|
+
// which the caller then persists OVER the URL — the screen stops being live
|
|
195
|
+
// and the route is gone. There is no correct append for this shape.
|
|
196
|
+
if (isStandaloneHttpUrl(content)) return null;
|
|
191
197
|
try {
|
|
192
198
|
const doc = new DOMParser().parseFromString(content, "text/html");
|
|
193
199
|
if (!doc.body) return null;
|
|
@@ -477,3 +483,26 @@ export function appendCanvasPrimitiveToHtml(
|
|
|
477
483
|
return null;
|
|
478
484
|
}
|
|
479
485
|
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Extract one newly-created primitive from a temporary document as markup the
|
|
489
|
+
* live iframe bridge can insert. URL-backed screens keep their route URL in the
|
|
490
|
+
* Design file, so their creation path must serialize a node without ever
|
|
491
|
+
* rewriting that file content.
|
|
492
|
+
*/
|
|
493
|
+
export function extractCanvasPrimitiveHtml(
|
|
494
|
+
content: string,
|
|
495
|
+
nodeId: string,
|
|
496
|
+
): string | null {
|
|
497
|
+
if (typeof window === "undefined") return null;
|
|
498
|
+
try {
|
|
499
|
+
const doc = new DOMParser().parseFromString(content, "text/html");
|
|
500
|
+
const safeNodeId = nodeId.replace(/["\\]/g, "\\$&");
|
|
501
|
+
return (
|
|
502
|
+
doc.querySelector(`[data-agent-native-node-id="${safeNodeId}"]`)
|
|
503
|
+
?.outerHTML ?? null
|
|
504
|
+
);
|
|
505
|
+
} catch {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { uniqueLayerId } from "./canvas-primitive-insert";
|
|
15
15
|
import { reassignClonedAuthoredIds } from "./clone-idrefs";
|
|
16
16
|
import { queryUniqueSelector } from "./dom-utils";
|
|
17
|
+
import { isStandaloneHttpUrl } from "./editor-state";
|
|
17
18
|
import {
|
|
18
19
|
applyPortableStyles,
|
|
19
20
|
elementAtPortableStylePath,
|
|
@@ -236,6 +237,11 @@ export function insertClonedHtmlLayers(
|
|
|
236
237
|
nodeIdMap: Map<string, string>;
|
|
237
238
|
} | null {
|
|
238
239
|
if (typeof window === "undefined" || layerHtmls.length === 0) return null;
|
|
240
|
+
// Same hazard appendCanvasPrimitiveToHtml guards: a live screen's stored
|
|
241
|
+
// content is its route URL, and returning an "edited document" for it means
|
|
242
|
+
// the caller persists HTML over the URL. Paste, duplicate, and image-drop
|
|
243
|
+
// all land here, so refuse the shape once rather than at each of them.
|
|
244
|
+
if (isStandaloneHttpUrl(content)) return null;
|
|
239
245
|
try {
|
|
240
246
|
const doc = new DOMParser().parseFromString(content, "text/html");
|
|
241
247
|
if (!doc.body) return null;
|
|
@@ -6,6 +6,11 @@ import {
|
|
|
6
6
|
removeCodeLayerNodeFromHtml,
|
|
7
7
|
} from "@shared/code-layer";
|
|
8
8
|
import { isComponentInstance } from "@shared/component-model";
|
|
9
|
+
import {
|
|
10
|
+
ELEMENT_PROVENANCE_METHODS,
|
|
11
|
+
type ElementProvenanceFramework,
|
|
12
|
+
type ElementProvenanceMethod,
|
|
13
|
+
} from "@shared/source-mode";
|
|
9
14
|
export {
|
|
10
15
|
renameFilenamePreservingExtension,
|
|
11
16
|
replaceDataScreenReferences,
|
|
@@ -83,6 +88,69 @@ export function codeLayerSelectorAliases(
|
|
|
83
88
|
);
|
|
84
89
|
}
|
|
85
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Selector candidate groups for a host-initiated removal in the LIVE iframe
|
|
93
|
+
* (the bridge's `delete-element`), one group per node to remove.
|
|
94
|
+
*
|
|
95
|
+
* A live/localhost screen runs two different node-id namespaces: the document
|
|
96
|
+
* carries the ids the injected bridge assigned there, while every host-side
|
|
97
|
+
* projection is built from the fetched source snapshot that
|
|
98
|
+
* `ensureCodeLayerNodeIdsInHtml` stamped separately. A selector taken from the
|
|
99
|
+
* source projection therefore matches nothing in the running document — and
|
|
100
|
+
* `delete-element` is fire-and-forget, so the editor reported success and
|
|
101
|
+
* cleared the selection while the element stayed on screen.
|
|
102
|
+
*
|
|
103
|
+
* Order of trust: the runtime layer model's aliases (Layers-panel selection on
|
|
104
|
+
* a runtime-projected screen), then the source-projection selectors carrying
|
|
105
|
+
* the bridge-reported identities as extra candidates, then those identities
|
|
106
|
+
* alone. `findRuntimeTarget` in the bridge takes the first candidate that
|
|
107
|
+
* resolves, so a group may safely mix namespaces.
|
|
108
|
+
*/
|
|
109
|
+
export function liveDeleteSelectorGroups(args: {
|
|
110
|
+
runtimeAliasGroups: readonly (readonly string[])[];
|
|
111
|
+
liveSelectionSelectors: readonly string[];
|
|
112
|
+
fallbackSelectors: readonly string[];
|
|
113
|
+
}): string[][] {
|
|
114
|
+
const runtimeGroups = args.runtimeAliasGroups
|
|
115
|
+
.map((aliases) => aliases.filter(Boolean))
|
|
116
|
+
.filter((aliases) => aliases.length > 0);
|
|
117
|
+
if (runtimeGroups.length > 0) return runtimeGroups;
|
|
118
|
+
const liveSelectors = Array.from(
|
|
119
|
+
new Set(args.liveSelectionSelectors.filter(Boolean)),
|
|
120
|
+
);
|
|
121
|
+
if (args.fallbackSelectors.length > 0) {
|
|
122
|
+
return args.fallbackSelectors.map((selector) =>
|
|
123
|
+
Array.from(new Set([selector, ...liveSelectors])),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return liveSelectors.length > 0 ? [liveSelectors] : [];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Whether Delete must run the LIVE-screen removal path (delete the running
|
|
131
|
+
* DOM node + queue a pending live edit for the coding agent) instead of
|
|
132
|
+
* rewriting the screen's stored source, mirroring the `localhost` branch
|
|
133
|
+
* handleVisualStructureChange already takes for a drag-move.
|
|
134
|
+
*
|
|
135
|
+
* Deliberately does NOT require a source-snapshot node: a Layers-panel
|
|
136
|
+
* selection on a runtime-projected screen only exists in the runtime id
|
|
137
|
+
* namespace, so gating the delete on a resolved source snapshot meant such a
|
|
138
|
+
* row could be selected and never deleted at all. The runtime alias groups
|
|
139
|
+
* ARE the target here.
|
|
140
|
+
*/
|
|
141
|
+
export function shouldDeleteThroughLiveScreen(args: {
|
|
142
|
+
screenSourceType: string | null | undefined;
|
|
143
|
+
runtimeAliasGroups: readonly (readonly string[])[];
|
|
144
|
+
liveSelectionSelectors: readonly string[];
|
|
145
|
+
}): boolean {
|
|
146
|
+
if (args.screenSourceType !== "localhost") return false;
|
|
147
|
+
return (
|
|
148
|
+
args.runtimeAliasGroups.some((aliases) =>
|
|
149
|
+
aliases.some((alias) => Boolean(alias)),
|
|
150
|
+
) || args.liveSelectionSelectors.some((selector) => Boolean(selector))
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
86
154
|
export function normalizeCodeLayerSelector(selector: string): string {
|
|
87
155
|
return (
|
|
88
156
|
selector
|
|
@@ -326,12 +394,84 @@ function provenanceForCodeLayerNode(
|
|
|
326
394
|
node.dataAttributes["data-source-column"],
|
|
327
395
|
);
|
|
328
396
|
const component = node.dataAttributes["data-component-name"]?.trim();
|
|
329
|
-
|
|
397
|
+
const declaredFramework =
|
|
398
|
+
node.dataAttributes["data-source-framework"]?.trim();
|
|
399
|
+
const framework =
|
|
400
|
+
declaredFramework === "html" ||
|
|
401
|
+
declaredFramework === "react" ||
|
|
402
|
+
declaredFramework === "vue" ||
|
|
403
|
+
declaredFramework === "svelte"
|
|
404
|
+
? (declaredFramework as ElementProvenanceFramework)
|
|
405
|
+
: undefined;
|
|
406
|
+
const ownerSourceFile = node.dataAttributes["data-source-owner-file"]?.trim();
|
|
407
|
+
const ownerLine = positiveIntegerDataAttribute(
|
|
408
|
+
node.dataAttributes["data-source-owner-line"],
|
|
409
|
+
);
|
|
410
|
+
const ownerColumn = positiveIntegerDataAttribute(
|
|
411
|
+
node.dataAttributes["data-source-owner-column"],
|
|
412
|
+
);
|
|
413
|
+
const ownerComponentName =
|
|
414
|
+
node.dataAttributes["data-source-owner-component"]?.trim();
|
|
415
|
+
const ownerKey = node.dataAttributes["data-source-owner-key"]?.trim();
|
|
416
|
+
const declaredMethod = node.dataAttributes["data-source-method"]?.trim();
|
|
417
|
+
const declaredOwnerMethod =
|
|
418
|
+
node.dataAttributes["data-source-owner-method"]?.trim();
|
|
419
|
+
// The owner site has its own tier: an authored `data-attribute` element can
|
|
420
|
+
// still owe its owner line to a transformed React 19 owner stack. Unlike
|
|
421
|
+
// `method` below there is no build-time convention to fall back on — this
|
|
422
|
+
// bridge is the only writer of data-source-owner-*, and it always labels the
|
|
423
|
+
// tier — so an unlabelled owner position stays "unknown" rather than
|
|
424
|
+
// inheriting a claim of authored coordinates.
|
|
425
|
+
const ownerMethod = ELEMENT_PROVENANCE_METHODS.includes(
|
|
426
|
+
declaredOwnerMethod as ElementProvenanceMethod,
|
|
427
|
+
)
|
|
428
|
+
? (declaredOwnerMethod as ElementProvenanceMethod)
|
|
429
|
+
: undefined;
|
|
430
|
+
// The bridge stamps the tier alongside the position; a projection carrying a
|
|
431
|
+
// position with no tier came from a build-time transform's own attributes.
|
|
432
|
+
const method = ELEMENT_PROVENANCE_METHODS.includes(
|
|
433
|
+
declaredMethod as ElementProvenanceMethod,
|
|
434
|
+
)
|
|
435
|
+
? (declaredMethod as ElementProvenanceMethod)
|
|
436
|
+
: sourceFile
|
|
437
|
+
? "data-attribute"
|
|
438
|
+
: undefined;
|
|
439
|
+
const unavailable = node.dataAttributes["data-source-unavailable"]?.trim();
|
|
440
|
+
// The bridge only stamps a reason when the node resolved to no location, so
|
|
441
|
+
// a resolved anchor must never carry one back out of the projection.
|
|
442
|
+
const unavailableReason =
|
|
443
|
+
!sourceFile &&
|
|
444
|
+
(unavailable === "not-framework" ||
|
|
445
|
+
unavailable === "not-react" ||
|
|
446
|
+
unavailable === "no-debug-info")
|
|
447
|
+
? unavailable
|
|
448
|
+
: undefined;
|
|
449
|
+
if (
|
|
450
|
+
!sourceFile &&
|
|
451
|
+
!line &&
|
|
452
|
+
!column &&
|
|
453
|
+
!component &&
|
|
454
|
+
!framework &&
|
|
455
|
+
!ownerSourceFile &&
|
|
456
|
+
!ownerKey &&
|
|
457
|
+
!unavailableReason
|
|
458
|
+
) {
|
|
459
|
+
return undefined;
|
|
460
|
+
}
|
|
330
461
|
return {
|
|
462
|
+
...(framework ? { framework } : {}),
|
|
331
463
|
...(sourceFile ? { sourceFile } : {}),
|
|
332
464
|
...(line ? { line } : {}),
|
|
333
465
|
...(column ? { column } : {}),
|
|
334
466
|
...(component ? { component } : {}),
|
|
467
|
+
...(ownerSourceFile ? { ownerSourceFile } : {}),
|
|
468
|
+
...(ownerLine ? { ownerLine } : {}),
|
|
469
|
+
...(ownerColumn ? { ownerColumn } : {}),
|
|
470
|
+
...(ownerComponentName ? { ownerComponentName } : {}),
|
|
471
|
+
...(ownerKey ? { ownerKey } : {}),
|
|
472
|
+
...(method ? { method } : {}),
|
|
473
|
+
...(ownerMethod ? { ownerMethod } : {}),
|
|
474
|
+
...(unavailableReason ? { unavailableReason } : {}),
|
|
335
475
|
};
|
|
336
476
|
}
|
|
337
477
|
|
|
@@ -465,11 +605,44 @@ export function codeLayerNodeMatchesBridgeTarget(
|
|
|
465
605
|
return codeLayerSelectorMatches(node, selector);
|
|
466
606
|
}
|
|
467
607
|
|
|
468
|
-
|
|
608
|
+
/**
|
|
609
|
+
* `absent` and `ambiguous` must stay distinguishable all the way to the UI —
|
|
610
|
+
* five matching instances is a scoping question, a gone element is a different
|
|
611
|
+
* failure. Collapsing both into `null` is what made a style commit on a repeated
|
|
612
|
+
* component claim the element no longer existed while it was on screen.
|
|
613
|
+
*/
|
|
614
|
+
export type CodeLayerResolution =
|
|
615
|
+
| { status: "resolved"; node: CodeLayerNode }
|
|
616
|
+
| { status: "absent" }
|
|
617
|
+
| { status: "ambiguous"; candidates: CodeLayerNode[] };
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* A client-rendered app serves `<html><body><div id="root">`, so its projection
|
|
621
|
+
* holds none of the markup the user can select and every selection reads as
|
|
622
|
+
* "absent" — a different fact, with a different remedy, from a removed element.
|
|
623
|
+
* The bound is deliberate: an authored page worth editing has more nodes, and a
|
|
624
|
+
* false positive only costs a more specific refusal message.
|
|
625
|
+
*/
|
|
626
|
+
const MOUNT_SHELL_MAX_NODES = 4;
|
|
627
|
+
|
|
628
|
+
export function isClientRenderedMountShell(projection: {
|
|
629
|
+
nodes: CodeLayerNode[];
|
|
630
|
+
}): boolean {
|
|
631
|
+
if (projection.nodes.length > MOUNT_SHELL_MAX_NODES) return false;
|
|
632
|
+
return projection.nodes.every(
|
|
633
|
+
(node) =>
|
|
634
|
+
node.tag === "html" ||
|
|
635
|
+
node.tag === "head" ||
|
|
636
|
+
node.tag === "body" ||
|
|
637
|
+
(node.children.length === 0 && !collapsedElementText(node.textSnippet)),
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export function resolveCodeLayerTargetFromBridge(
|
|
469
642
|
projection: { nodes: CodeLayerNode[] },
|
|
470
643
|
selector?: string,
|
|
471
644
|
sourceId?: string,
|
|
472
|
-
):
|
|
645
|
+
): CodeLayerResolution {
|
|
473
646
|
// Id-based match first, across the WHOLE projection (not just up to
|
|
474
647
|
// whichever node the old combined-predicate `.find()` reached first) — a
|
|
475
648
|
// sourceId identifies one node by its own stable id/data-attribute, which
|
|
@@ -482,7 +655,7 @@ export function resolveCodeLayerNodeFromBridge(
|
|
|
482
655
|
const idMatch = projection.nodes.find((node) =>
|
|
483
656
|
codeLayerNodeMatchesSourceId(node, sourceId),
|
|
484
657
|
);
|
|
485
|
-
if (idMatch) return idMatch;
|
|
658
|
+
if (idMatch) return { status: "resolved", node: idMatch };
|
|
486
659
|
}
|
|
487
660
|
// No sourceId (or no node carries it yet, e.g. a bridge target minted a
|
|
488
661
|
// fresh pending id the projection hasn't picked up) — fall back to the
|
|
@@ -493,29 +666,49 @@ export function resolveCodeLayerNodeFromBridge(
|
|
|
493
666
|
// selector that matches more than one node rather than silently picking
|
|
494
667
|
// the first DOM-order match — mirror that discipline here so a duplicate/
|
|
495
668
|
// move/style edit can never silently land on the wrong sibling instance.
|
|
496
|
-
if (!selector) return
|
|
669
|
+
if (!selector) return { status: "absent" };
|
|
497
670
|
const selectorMatches = projection.nodes.filter((node) =>
|
|
498
671
|
codeLayerSelectorMatches(node, selector),
|
|
499
672
|
);
|
|
500
|
-
|
|
673
|
+
if (selectorMatches.length === 1) {
|
|
674
|
+
return { status: "resolved", node: selectorMatches[0]! };
|
|
675
|
+
}
|
|
676
|
+
return selectorMatches.length === 0
|
|
677
|
+
? { status: "absent" }
|
|
678
|
+
: { status: "ambiguous", candidates: selectorMatches };
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
export function resolveCodeLayerNodeFromBridge(
|
|
682
|
+
projection: { nodes: CodeLayerNode[] },
|
|
683
|
+
selector?: string,
|
|
684
|
+
sourceId?: string,
|
|
685
|
+
): CodeLayerNode | null {
|
|
686
|
+
const resolution = resolveCodeLayerTargetFromBridge(
|
|
687
|
+
projection,
|
|
688
|
+
selector,
|
|
689
|
+
sourceId,
|
|
690
|
+
);
|
|
691
|
+
return resolution.status === "resolved" ? resolution.node : null;
|
|
501
692
|
}
|
|
502
693
|
|
|
503
694
|
export function collapsedElementText(value: string | null | undefined): string {
|
|
504
695
|
return value?.replace(/\s+/g, " ").trim() ?? "";
|
|
505
696
|
}
|
|
506
697
|
|
|
507
|
-
export function
|
|
698
|
+
export function resolveCodeLayerTargetFromElementInfo(
|
|
508
699
|
projection: { nodes: CodeLayerNode[] },
|
|
509
700
|
info: ElementInfo | null | undefined,
|
|
510
|
-
):
|
|
511
|
-
if (!info) return
|
|
512
|
-
const direct =
|
|
701
|
+
): CodeLayerResolution {
|
|
702
|
+
if (!info) return { status: "absent" };
|
|
703
|
+
const direct = resolveCodeLayerTargetFromBridge(
|
|
513
704
|
projection,
|
|
514
705
|
info.selector,
|
|
515
706
|
info.sourceId ?? info.id,
|
|
516
707
|
);
|
|
517
|
-
if (direct) return direct;
|
|
708
|
+
if (direct.status === "resolved") return direct;
|
|
518
709
|
|
|
710
|
+
// Score even an ambiguous selector: text/class/id evidence it does not carry
|
|
711
|
+
// can single out one instance. Ambiguity only survives if scoring ties too.
|
|
519
712
|
const tagName = info.tagName.toLowerCase();
|
|
520
713
|
const text = collapsedElementText(info.textContent);
|
|
521
714
|
const classes = new Set(info.classes);
|
|
@@ -541,11 +734,25 @@ export function resolveCodeLayerNodeFromElementInfo(
|
|
|
541
734
|
.filter((candidate) => candidate.score >= 4)
|
|
542
735
|
.sort((a, b) => b.score - a.score);
|
|
543
736
|
|
|
544
|
-
if (scored.length === 0) return null;
|
|
545
737
|
const [best, next] = scored;
|
|
546
|
-
if (!best) return
|
|
547
|
-
if (next && next.score === best.score)
|
|
548
|
-
|
|
738
|
+
if (!best) return direct;
|
|
739
|
+
if (next && next.score === best.score) {
|
|
740
|
+
return {
|
|
741
|
+
status: "ambiguous",
|
|
742
|
+
candidates: scored
|
|
743
|
+
.filter((candidate) => candidate.score === best.score)
|
|
744
|
+
.map((candidate) => candidate.node),
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
return { status: "resolved", node: best.node };
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
export function resolveCodeLayerNodeFromElementInfo(
|
|
751
|
+
projection: { nodes: CodeLayerNode[] },
|
|
752
|
+
info: ElementInfo | null | undefined,
|
|
753
|
+
): CodeLayerNode | null {
|
|
754
|
+
const resolution = resolveCodeLayerTargetFromElementInfo(projection, info);
|
|
755
|
+
return resolution.status === "resolved" ? resolution.node : null;
|
|
549
756
|
}
|
|
550
757
|
|
|
551
758
|
export function canonicalElementInfoForCodeLayerNode(
|
|
@@ -554,6 +761,11 @@ export function canonicalElementInfoForCodeLayerNode(
|
|
|
554
761
|
): ElementInfo {
|
|
555
762
|
return {
|
|
556
763
|
...info,
|
|
764
|
+
// Keep the bridge's own identity before overwriting it — it is the only
|
|
765
|
+
// one that resolves in a live document. Idempotent: re-canonicalizing an
|
|
766
|
+
// already-canonicalized info must not overwrite it with the source id.
|
|
767
|
+
runtimeSelector: info.runtimeSelector ?? info.selector,
|
|
768
|
+
runtimeSourceId: info.runtimeSourceId ?? info.sourceId,
|
|
557
769
|
sourceId: bridgeSourceIdForCodeLayerNode(node),
|
|
558
770
|
selector: preferredCodeLayerSelector(node),
|
|
559
771
|
classes: node.classes,
|
|
@@ -592,6 +804,56 @@ export function elementInfoIsRuntimeOnly(
|
|
|
592
804
|
);
|
|
593
805
|
}
|
|
594
806
|
|
|
807
|
+
/**
|
|
808
|
+
* Per-node refinement of a file's "layers panel is showing the runtime
|
|
809
|
+
* (localhost-hydrated) tree" flag into an actual "this node has no
|
|
810
|
+
* resolvable source counterpart" signal.
|
|
811
|
+
*
|
|
812
|
+
* `fileIsRuntimeProjected` alone is true for basically every hydrated
|
|
813
|
+
* localhost screen, React or not — see shouldPreferRuntimeLayerProjection's
|
|
814
|
+
* doc comment in design-editor/pending-edits.ts — so callers that gated a
|
|
815
|
+
* React-semantic-handoff requirement on that file-level flag treated every
|
|
816
|
+
* node on every localhost screen as unresolvable, including on a page with
|
|
817
|
+
* no React at all.
|
|
818
|
+
*
|
|
819
|
+
* Match on the stamped `data-agent-native-node-id` attribute, NOT
|
|
820
|
+
* CodeLayerNode.id: `id` is `hashStable(sourceKey:...)` (see nodeIdFor in
|
|
821
|
+
* shared/code-layer.ts), and sourceKey differs between the runtime and
|
|
822
|
+
* source projection parses of the very same content, so `id` never lines up
|
|
823
|
+
* even for the identical element — the stamped DOM attribute does.
|
|
824
|
+
*/
|
|
825
|
+
export function isCodeLayerNodeRuntimeOnly(args: {
|
|
826
|
+
fileIsRuntimeProjected: boolean;
|
|
827
|
+
nodeIdAttr: string | undefined;
|
|
828
|
+
sourceNodeIdAttrs: ReadonlySet<string>;
|
|
829
|
+
}): boolean {
|
|
830
|
+
if (!args.fileIsRuntimeProjected) return false;
|
|
831
|
+
if (!args.nodeIdAttr) return true;
|
|
832
|
+
return !args.sourceNodeIdAttrs.has(args.nodeIdAttr);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Whether a Layers-panel hide/lock toggle on a runtime (localhost-hydrated)
|
|
837
|
+
* node can be handed off to the agent to make durable in React source.
|
|
838
|
+
*
|
|
839
|
+
* The handoff is about DURABILITY — writing `data-agent-native-hidden` /
|
|
840
|
+
* `-locked` into JSX. The visual effect is host state that DesignCanvas
|
|
841
|
+
* mirrors into the iframe as a `layer-states` message; the source attribute
|
|
842
|
+
* has no rendering power of its own. A localhost target with no React
|
|
843
|
+
* debug-source provenance (plain HTML, or a React build without the source
|
|
844
|
+
* plugin) can never resolve an anchor, so gating the preview on the handoff
|
|
845
|
+
* made hide/lock a total no-op there and reported "source anchors still
|
|
846
|
+
* loading" for a load that will never happen. "Nothing to write into" and
|
|
847
|
+
* "source not loaded yet" must stay different answers.
|
|
848
|
+
*/
|
|
849
|
+
export function runtimeLayerStateHandoffMode(args: {
|
|
850
|
+
runtimeOnly: boolean;
|
|
851
|
+
provenanceSourceFile: string | null | undefined;
|
|
852
|
+
}): "handoff" | "preview-only" {
|
|
853
|
+
if (!args.runtimeOnly) return "preview-only";
|
|
854
|
+
return args.provenanceSourceFile?.trim() ? "handoff" : "preview-only";
|
|
855
|
+
}
|
|
856
|
+
|
|
595
857
|
export function codeLayerPatchMessage(
|
|
596
858
|
message: string | null | undefined,
|
|
597
859
|
fallback: string,
|
|
@@ -1,18 +1,32 @@
|
|
|
1
|
+
import { isStandaloneHttpUrl } from "@shared/html-content";
|
|
2
|
+
|
|
1
3
|
import {
|
|
4
|
+
type DesignFile,
|
|
2
5
|
type DesignLeftPanel,
|
|
3
6
|
type DesignTool,
|
|
7
|
+
type EditorMode,
|
|
4
8
|
SHOW_DESIGN_CODE_LEFT_PANEL,
|
|
5
9
|
} from "./types";
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
// Definition moved to shared/ so the server write path and code-layer's
|
|
12
|
+
// document transforms guard on the same predicate. Re-exported here because
|
|
13
|
+
// the editor's URL-backed-screen refusals all import it from this module.
|
|
14
|
+
export { isStandaloneHttpUrl };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A live-screen route intentionally refuses whole-document replacement. That
|
|
18
|
+
* is a successful no-op, unlike an unavailable preview bridge, which callers
|
|
19
|
+
* must recover by rebuilding their render state.
|
|
20
|
+
*/
|
|
21
|
+
export type PreviewContentReplaceResult =
|
|
22
|
+
| "applied"
|
|
23
|
+
| "skipped-live-route"
|
|
24
|
+
| "unavailable";
|
|
25
|
+
|
|
26
|
+
export function previewContentReplaceNeedsRenderFallback(
|
|
27
|
+
result: PreviewContentReplaceResult,
|
|
28
|
+
): boolean {
|
|
29
|
+
return result === "unavailable";
|
|
16
30
|
}
|
|
17
31
|
|
|
18
32
|
/**
|
|
@@ -110,6 +124,7 @@ export function getDesignEditorStateUrlSearch(args: {
|
|
|
110
124
|
leftPanel?: DesignLeftPanel | null;
|
|
111
125
|
zoom?: number | null;
|
|
112
126
|
tool?: DesignTool | null;
|
|
127
|
+
mode?: EditorMode | null;
|
|
113
128
|
}) {
|
|
114
129
|
const params = new URLSearchParams(args.currentSearch);
|
|
115
130
|
const leftPanel =
|
|
@@ -155,6 +170,11 @@ export function getDesignEditorStateUrlSearch(args: {
|
|
|
155
170
|
} else {
|
|
156
171
|
params.delete("tool");
|
|
157
172
|
}
|
|
173
|
+
if (args.viewMode === "single") {
|
|
174
|
+
params.set("mode", "interact");
|
|
175
|
+
} else {
|
|
176
|
+
params.delete("mode");
|
|
177
|
+
}
|
|
158
178
|
const query = params.toString();
|
|
159
179
|
return query ? `?${query}` : "";
|
|
160
180
|
}
|
|
@@ -491,3 +511,49 @@ export function shouldSendKeepalive(
|
|
|
491
511
|
): boolean {
|
|
492
512
|
return hashKnown || !collabLive;
|
|
493
513
|
}
|
|
514
|
+
|
|
515
|
+
const EMPTY_DESIGN_FILES: DesignFile[] = [];
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Identity-stable `design.files` read for the editor render body.
|
|
519
|
+
*
|
|
520
|
+
* `design` is null until the `get-design` query resolves, and a literal `??
|
|
521
|
+
* []` there mints a new array on every render of that window. That identity
|
|
522
|
+
* feeds `files` → `proposalFileIds` → the pending-node-rewrite effect, whose
|
|
523
|
+
* empty-files branch commits a fresh `[]` — a passive-effect update that
|
|
524
|
+
* re-renders, remints the array, and re-fires itself until the query lands
|
|
525
|
+
* ("Maximum update depth exceeded" on cold loads). Return the shared empty
|
|
526
|
+
* array so the no-files render is stable.
|
|
527
|
+
*/
|
|
528
|
+
export function resolveServerFiles(
|
|
529
|
+
design: { files?: DesignFile[] } | null | undefined,
|
|
530
|
+
): DesignFile[] {
|
|
531
|
+
return design?.files ?? EMPTY_DESIGN_FILES;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Pure decision helper (exported for unit testing) for the pending-local-write
|
|
536
|
+
* reconcile: now that `file` arrived from the server, may the editor stop
|
|
537
|
+
* overlaying this file's pending local content and trust the query cache?
|
|
538
|
+
*
|
|
539
|
+
* Matching content alone does not prove the server took the write. Every
|
|
540
|
+
* optimistic writer also mirrors its content into the cached `get-design`
|
|
541
|
+
* payload while deliberately keeping the file's prior server-clock
|
|
542
|
+
* `updatedAt`, so an echo of our own cache write is indistinguishable from an
|
|
543
|
+
* acknowledgement by content. A writer that records `baseUpdatedAt` keeps its
|
|
544
|
+
* overlay until `updatedAt` actually advances; retiring it early leaves the
|
|
545
|
+
* cache as the only carrier of the edit, and a `get-design` response already
|
|
546
|
+
* in flight when the write happened then lands with pre-write content and the
|
|
547
|
+
* edit vanishes until a reload.
|
|
548
|
+
*/
|
|
549
|
+
export function shouldRetirePendingLocalFileContent(
|
|
550
|
+
pending: { content: string; baseUpdatedAt?: string | null } | undefined,
|
|
551
|
+
file: { content?: string | null; updatedAt?: string | null },
|
|
552
|
+
): boolean {
|
|
553
|
+
if (!pending) return false;
|
|
554
|
+
if ((file.content ?? "") !== pending.content) return false;
|
|
555
|
+
return (
|
|
556
|
+
pending.baseUpdatedAt === undefined ||
|
|
557
|
+
file.updatedAt !== pending.baseUpdatedAt
|
|
558
|
+
);
|
|
559
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const LAYER_STATE_SCOPE_SEPARATOR = "\u001f";
|
|
2
|
+
|
|
3
|
+
export function scopedLayerStateId(screenId: string, layerId: string): string {
|
|
4
|
+
return layerId === screenId
|
|
5
|
+
? screenId
|
|
6
|
+
: `${screenId}${LAYER_STATE_SCOPE_SEPARATOR}${layerId}`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function layerStateIdsForScreen(
|
|
10
|
+
stateIds: ReadonlySet<string>,
|
|
11
|
+
screenId: string,
|
|
12
|
+
): Set<string> {
|
|
13
|
+
const prefix = `${screenId}${LAYER_STATE_SCOPE_SEPARATOR}`;
|
|
14
|
+
const result = new Set<string>();
|
|
15
|
+
if (stateIds.has(screenId)) result.add(screenId);
|
|
16
|
+
stateIds.forEach((id) => {
|
|
17
|
+
if (id.startsWith(prefix)) result.add(id.slice(prefix.length));
|
|
18
|
+
});
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function hasScopedLayerState(
|
|
23
|
+
stateIds: ReadonlySet<string>,
|
|
24
|
+
screenId: string,
|
|
25
|
+
layerId: string,
|
|
26
|
+
): boolean {
|
|
27
|
+
return stateIds.has(scopedLayerStateId(screenId, layerId));
|
|
28
|
+
}
|