@agent-native/core 0.131.5 → 0.131.7
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 +3 -3
- package/corpus/core/CHANGELOG.md +70 -0
- package/corpus/core/docs/content/agent-native-toolkit.mdx +13 -6
- package/corpus/core/docs/content/cloneable-saas.mdx +5 -5
- package/corpus/core/docs/content/creating-templates.mdx +9 -0
- package/corpus/core/docs/content/deployment.mdx +18 -12
- package/corpus/core/docs/content/doctor.mdx +8 -1
- package/corpus/core/docs/content/drop-in-agent.mdx +1 -1
- package/corpus/core/docs/content/extensions.mdx +18 -18
- package/corpus/core/docs/content/faq.mdx +4 -4
- package/corpus/core/docs/content/key-concepts.mdx +30 -21
- package/corpus/core/docs/content/security.mdx +2 -2
- package/corpus/core/docs/content/template-brain-developers.mdx +4 -1
- package/corpus/core/docs/content/template-mail-developers.mdx +4 -1
- package/corpus/core/docs/content/what-is-agent-native.mdx +6 -6
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +449 -66
- package/corpus/core/src/a2a/correlation.ts +31 -0
- package/corpus/core/src/a2a/invoke.ts +4 -2
- package/corpus/core/src/a2a/types.ts +4 -0
- package/corpus/core/src/action.ts +3 -0
- package/corpus/core/src/agent/action-continuation-guidance.ts +2 -1
- package/corpus/core/src/agent/engine/registry.ts +98 -6
- package/corpus/core/src/agent/production-agent.ts +151 -18
- package/corpus/core/src/agent/run-loop-with-resume.ts +190 -3
- package/corpus/core/src/agent/run-manager.ts +117 -41
- package/corpus/core/src/agent/thread-debug-history.ts +86 -0
- package/corpus/core/src/agent/types.ts +3 -1
- package/corpus/core/src/cli/create.ts +11 -1
- package/corpus/core/src/cli/templates-meta.ts +2 -2
- package/corpus/core/src/client/agent-chat-adapter.ts +91 -13
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +11 -9
- package/corpus/core/src/client/sse-event-processor.ts +17 -3
- package/corpus/core/src/db/client.ts +57 -17
- package/corpus/core/src/guards/no-env-credentials.ts +2 -0
- package/corpus/core/src/integrations/adapters/slack.ts +8 -3
- package/corpus/core/src/localization/default-messages.ts +44 -0
- package/corpus/core/src/observability/traces.ts +78 -5
- package/corpus/core/src/scripts/call-agent.ts +294 -11
- package/corpus/core/src/scripts/describe-workspace-apps.ts +2 -2
- package/corpus/core/src/secrets/crypto.ts +156 -35
- package/corpus/core/src/secrets/storage.ts +64 -26
- package/corpus/core/src/server/agent-capabilities.ts +1 -1
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +113 -7
- package/corpus/core/src/server/agent-chat/context-tools.ts +2 -0
- package/corpus/core/src/server/agent-chat/plugin-options.ts +34 -1
- package/corpus/core/src/server/agent-chat/prompt-resources.ts +1 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +91 -59
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
- package/corpus/core/src/vite/client.ts +1 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +5 -4
- package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +6 -4
- package/corpus/templates/analytics/AGENTS.md +7 -4
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -3
- package/corpus/templates/analytics/changelog/2026-07-30-delegated-analytics-questions-now-return-a-useful-current-me.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-30-scheduled-dashboard-emails-complete-every-panel-without-pool.md +6 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +28 -1
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +18 -11
- package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +4 -4
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -5
- package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +6 -2
- package/corpus/templates/analytics/server/lib/dashboard-panel-source-resolver.ts +11 -4
- package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +49 -31
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +16 -8
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +9 -3
- package/corpus/templates/analytics/server/lib/production-serverless-runtime.ts +11 -0
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +10 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +3 -2
- package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +2 -1
- package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +2 -1
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -12
- package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +54 -6
- package/corpus/templates/brain/.agents/skills/brain/SKILL.md +20 -9
- package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +33 -1
- package/corpus/templates/brain/AGENTS.md +3 -1
- package/corpus/templates/brain/README.md +4 -1
- package/corpus/templates/brain/actions/_schemas.ts +30 -8
- package/corpus/templates/brain/actions/ask-brain.ts +124 -14
- package/corpus/templates/brain/actions/create-source.ts +18 -2
- package/corpus/templates/brain/actions/enqueue-captures-distillation.ts +8 -123
- package/corpus/templates/brain/actions/enqueue-distillation.ts +6 -127
- package/corpus/templates/brain/actions/import-capture.ts +17 -5
- package/corpus/templates/brain/actions/import-transcript.ts +17 -5
- package/corpus/templates/brain/actions/update-source.ts +25 -5
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/brain/changelog/2026-07-30-blessed-resources-can-feed-cited-company-answers.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-30-the-left-sidebar-no-longer-shows-a-blank-organization-placeh.md +6 -0
- package/corpus/templates/brain/server/lib/brain.ts +49 -19
- package/corpus/templates/brain/server/lib/distillation-queue.ts +147 -0
- package/corpus/templates/brain/server/lib/source-policy.ts +264 -0
- package/corpus/templates/brain/server/plugins/agent-chat.ts +3 -2
- package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +116 -19
- package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +51 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +3 -1
- package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +6 -0
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +72 -8
- package/corpus/templates/content/changelog/2026-07-30-the-slash-command-hint-now-stays-on-only-the-active-empty-li.md +6 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +506 -11
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +148 -17
- package/corpus/templates/design/app/components/design/canvas-interactions/design-canvas-interactions.ts +357 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +7 -466
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +15 -311
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +19 -997
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +8 -1216
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +31 -38
- package/corpus/templates/design/package.json +0 -1
- package/corpus/templates/dispatch/AGENTS.md +8 -0
- package/corpus/templates/dispatch/DEVELOPING.md +29 -0
- package/corpus/templates/dispatch/actions/view-screen.ts +34 -2
- package/corpus/templates/dispatch/app/i18n-data.ts +457 -0
- package/corpus/templates/dispatch/changelog/2026-07-30-delegated-dispatch-work-continues-reliably-in-the-background.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-30-find-failed-runs-across-apps.md +6 -0
- package/corpus/templates/dispatch/netlify.toml +3 -0
- package/corpus/templates/plan/actions/get-visual-plan.ts +10 -6
- package/corpus/templates/plan/actions/patch-visual-plan-source.ts +117 -1
- package/corpus/templates/plan/actions/update-visual-plan.ts +56 -14
- package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -1
- package/corpus/templates/plan/changelog/2026-07-30-plan-agents-recover-with-targeted-writes.md +6 -0
- package/corpus/templates/plan/shared/plan-content.ts +35 -0
- package/corpus/templates/slides/.agents/skills/analytics-data-for-decks/SKILL.md +8 -12
- package/corpus/templates/slides/AGENTS.md +4 -4
- package/corpus/templates/slides/actions/create-deck.ts +1 -1
- package/corpus/templates/slides/actions/duplicate-deck.ts +19 -4
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -10
- package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +12 -1
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +30 -13
- package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +35 -0
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +63 -56
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -76
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1110 -292
- package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +53 -0
- package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +235 -18
- package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +4 -1
- package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +11 -0
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +129 -0
- package/corpus/templates/slides/app/components/editor/selection-overlay-measurement.ts +70 -0
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +519 -65
- package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +36 -426
- package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +19 -458
- package/corpus/templates/slides/app/context/DeckContext.tsx +179 -72
- package/corpus/templates/slides/app/global.css +105 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +8 -0
- package/corpus/templates/slides/app/lib/deck-editor-loading.ts +26 -0
- package/corpus/templates/slides/app/lib/mermaid-blocks.ts +27 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +71 -38
- package/corpus/templates/slides/app/pages/Index.tsx +98 -41
- package/corpus/templates/slides/changelog/2026-07-30-deck-loading-no-longer-flashes-an-unavailable-message.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-duplicating-a-deck-from-the-deck-list-now-opens-the-copy-imm.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-layout-overflow-warning-is-readable-and-dismissible.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-new-decks-now-start-with-an-empty-slide-list-when-no-slides-.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-shared-canvas-annotations.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-slide-objects-can-be-moved-as-a-group-copied-and-pasted-with.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-slide-text-editing-and-object-controls-now-match-google-slides.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-style-panel-now-scrolls-with-speaker-notes-open-shows-the-sl.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-top-toolbar-controls-now-use-consistent-sizing-with-undo-and.md +6 -0
- package/corpus/templates/slides/netlify.toml +3 -1
- package/corpus/templates/slides/package.json +0 -1
- package/corpus/templates/slides/server/lib/chat-attachments.ts +18 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/slides/vite.config.ts +0 -1
- package/corpus/toolkit/CHANGELOG.md +13 -0
- package/corpus/toolkit/README.md +19 -0
- package/corpus/toolkit/agent-native.eject.json +38 -0
- package/corpus/toolkit/package.json +29 -1
- package/corpus/toolkit/src/canvas-annotations/CanvasCommentPins.tsx +861 -0
- package/corpus/toolkit/src/canvas-annotations/DrawOverlay.tsx +1233 -0
- package/corpus/toolkit/src/canvas-annotations/index.ts +15 -0
- package/corpus/toolkit/src/canvas-annotations/types.ts +14 -0
- package/corpus/toolkit/src/canvas-interactions/canvas-interactions.ts +933 -0
- package/corpus/toolkit/src/canvas-interactions/index.ts +67 -0
- package/corpus/toolkit/src/design-tweaks/scrub-input-utils.ts +311 -0
- package/corpus/toolkit/src/design-tweaks/scrub-input.tsx +491 -0
- package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +26 -171
- package/corpus/toolkit/src/index.ts +1 -0
- package/dist/a2a/client.d.ts +23 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +309 -44
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/correlation.d.ts +1 -0
- package/dist/a2a/correlation.d.ts.map +1 -1
- package/dist/a2a/correlation.js +27 -0
- package/dist/a2a/correlation.js.map +1 -1
- package/dist/a2a/invoke.d.ts.map +1 -1
- package/dist/a2a/invoke.js +4 -2
- package/dist/a2a/invoke.js.map +1 -1
- package/dist/a2a/types.d.ts +4 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/action.d.ts +3 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/action-continuation-guidance.d.ts.map +1 -1
- package/dist/agent/action-continuation-guidance.js +2 -1
- package/dist/agent/action-continuation-guidance.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +82 -8
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts +40 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +113 -15
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +21 -0
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +163 -4
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-manager.d.ts +16 -9
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +93 -42
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-debug-history.d.ts +10 -0
- package/dist/agent/thread-debug-history.d.ts.map +1 -0
- package/dist/agent/thread-debug-history.js +68 -0
- package/dist/agent/thread-debug-history.js.map +1 -0
- package/dist/agent/types.d.ts +3 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +6 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/templates-meta.js +2 -2
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +76 -13
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +1 -1
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +12 -3
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +48 -17
- package/dist/db/client.js.map +1 -1
- package/dist/guards/no-env-credentials.d.ts.map +1 -1
- package/dist/guards/no-env-credentials.js +2 -0
- package/dist/guards/no-env-credentials.js.map +1 -1
- package/dist/integrations/adapters/slack.js +7 -3
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/localization/default-messages.d.ts +43 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +43 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/observability/traces.d.ts +3 -1
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +73 -4
- package/dist/observability/traces.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +237 -12
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/describe-workspace-apps.js +2 -2
- package/dist/scripts/describe-workspace-apps.js.map +1 -1
- package/dist/secrets/crypto.d.ts +36 -21
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +134 -33
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/storage.d.ts +6 -6
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +43 -26
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/agent-capabilities.js +1 -1
- package/dist/server/agent-capabilities.js.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +17 -3
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +94 -3
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
- package/dist/server/agent-chat/context-tools.js +2 -0
- package/dist/server/agent-chat/context-tools.js.map +1 -1
- package/dist/server/agent-chat/plugin-options.d.ts +23 -0
- package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
- package/dist/server/agent-chat/plugin-options.js +4 -1
- package/dist/server/agent-chat/plugin-options.js.map +1 -1
- package/dist/server/agent-chat/prompt-resources.js +1 -1
- package/dist/server/agent-chat/prompt-resources.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +2 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +79 -56
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +19 -7
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +1 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/agent-native-toolkit.mdx +13 -6
- package/docs/content/cloneable-saas.mdx +5 -5
- package/docs/content/creating-templates.mdx +9 -0
- package/docs/content/deployment.mdx +18 -12
- package/docs/content/doctor.mdx +8 -1
- package/docs/content/drop-in-agent.mdx +1 -1
- package/docs/content/extensions.mdx +18 -18
- package/docs/content/faq.mdx +4 -4
- package/docs/content/key-concepts.mdx +30 -21
- package/docs/content/security.mdx +2 -2
- package/docs/content/template-brain-developers.mdx +4 -1
- package/docs/content/template-mail-developers.mdx +4 -1
- package/docs/content/what-is-agent-native.mdx +6 -6
- package/package.json +3 -3
- package/src/a2a/client.ts +449 -66
- package/src/a2a/correlation.ts +31 -0
- package/src/a2a/invoke.ts +4 -2
- package/src/a2a/types.ts +4 -0
- package/src/action.ts +3 -0
- package/src/agent/action-continuation-guidance.ts +2 -1
- package/src/agent/engine/registry.ts +98 -6
- package/src/agent/production-agent.ts +151 -18
- package/src/agent/run-loop-with-resume.ts +190 -3
- package/src/agent/run-manager.ts +117 -41
- package/src/agent/thread-debug-history.ts +86 -0
- package/src/agent/types.ts +3 -1
- package/src/cli/create.ts +11 -1
- package/src/cli/templates-meta.ts +2 -2
- package/src/client/agent-chat-adapter.ts +91 -13
- package/src/client/resources/McpIntegrationDialog.tsx +11 -9
- package/src/client/sse-event-processor.ts +17 -3
- package/src/db/client.ts +57 -17
- package/src/guards/no-env-credentials.ts +2 -0
- package/src/integrations/adapters/slack.ts +8 -3
- package/src/localization/default-messages.ts +44 -0
- package/src/observability/traces.ts +78 -5
- package/src/scripts/call-agent.ts +294 -11
- package/src/scripts/describe-workspace-apps.ts +2 -2
- package/src/secrets/crypto.ts +156 -35
- package/src/secrets/storage.ts +64 -26
- package/src/server/agent-capabilities.ts +1 -1
- package/src/server/agent-chat/action-filters-a2a.ts +113 -7
- package/src/server/agent-chat/context-tools.ts +2 -0
- package/src/server/agent-chat/plugin-options.ts +34 -1
- package/src/server/agent-chat/prompt-resources.ts +1 -1
- package/src/server/agent-chat-plugin.ts +91 -59
- package/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
- package/src/vite/client.ts +1 -0
|
@@ -1,23 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import {
|
|
2
|
+
clientPointToCanvasPoint,
|
|
3
|
+
resizeCanvasRect,
|
|
4
|
+
type CanvasResizeHandle,
|
|
5
|
+
} from "@agent-native/toolkit/canvas-interactions";
|
|
2
6
|
|
|
3
|
-
export
|
|
7
|
+
export const MIN_SLIDE_OBJECT_SIZE = 24;
|
|
4
8
|
|
|
5
|
-
export type
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Resolve a selected freeform object's drag affordance without conflating a
|
|
9
|
-
* normal body click (which still enters inline text editing) with a drag.
|
|
10
|
-
*/
|
|
11
|
-
export function getSelectedObjectDragStart({
|
|
12
|
-
targetWithinSelectedObject,
|
|
13
|
-
pointerWithinMoveBand,
|
|
14
|
-
}: {
|
|
15
|
-
targetWithinSelectedObject: boolean;
|
|
16
|
-
pointerWithinMoveBand: boolean;
|
|
17
|
-
}): SelectedObjectDragStart {
|
|
18
|
-
if (pointerWithinMoveBand) return "perimeter";
|
|
19
|
-
return targetWithinSelectedObject ? "body" : null;
|
|
20
|
-
}
|
|
9
|
+
export type ResizeHandle = CanvasResizeHandle;
|
|
21
10
|
|
|
22
11
|
export interface SlideObjectGeometry {
|
|
23
12
|
x: number;
|
|
@@ -41,6 +30,23 @@ export interface SlideObjectLayoutSnapshot {
|
|
|
41
30
|
alignSelf: string;
|
|
42
31
|
}
|
|
43
32
|
|
|
33
|
+
export interface SlideObjectTextPresentationSnapshot {
|
|
34
|
+
color: string;
|
|
35
|
+
direction: string;
|
|
36
|
+
fontFamily: string;
|
|
37
|
+
fontSize: string;
|
|
38
|
+
fontStyle: string;
|
|
39
|
+
fontWeight: string;
|
|
40
|
+
letterSpacing: string;
|
|
41
|
+
lineHeight: string;
|
|
42
|
+
textAlign: string;
|
|
43
|
+
textDecoration: string;
|
|
44
|
+
textShadow: string;
|
|
45
|
+
textTransform: string;
|
|
46
|
+
whiteSpace: string;
|
|
47
|
+
wordSpacing: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
44
50
|
export interface ResizeOptions {
|
|
45
51
|
handle: ResizeHandle;
|
|
46
52
|
dx: number;
|
|
@@ -202,6 +208,109 @@ export function resolveSlideObjectContainingBlock(
|
|
|
202
208
|
return slideLayer;
|
|
203
209
|
}
|
|
204
210
|
|
|
211
|
+
export interface SlideTextBoxCanvas {
|
|
212
|
+
fmdSlide: HTMLElement;
|
|
213
|
+
positioningLayer: HTMLElement;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Text objects need the same persisted coordinate root as other freeform
|
|
218
|
+
* objects. Markdown initially renders straight into `.slide-content`, so the
|
|
219
|
+
* first placement promotes that live flow DOM into an fmd-slide before it is
|
|
220
|
+
* saved. Copying the canvas layout values keeps the current visual alignment
|
|
221
|
+
* when the raw HTML renderer owns it after the edit is committed.
|
|
222
|
+
*/
|
|
223
|
+
export function ensureSlideTextBoxCanvas(
|
|
224
|
+
editorRoot: HTMLElement,
|
|
225
|
+
): SlideTextBoxCanvas | null {
|
|
226
|
+
const existing = editorRoot.querySelector<HTMLElement>(".fmd-slide");
|
|
227
|
+
if (existing) {
|
|
228
|
+
const positioningLayer =
|
|
229
|
+
Array.from(existing.children).find(
|
|
230
|
+
(child): child is HTMLElement =>
|
|
231
|
+
child instanceof HTMLElement &&
|
|
232
|
+
child.hasAttribute("data-fmd-autofit-content"),
|
|
233
|
+
) ?? existing;
|
|
234
|
+
return { fmdSlide: existing, positioningLayer };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const slideContents = Array.from(
|
|
238
|
+
editorRoot.querySelectorAll<HTMLElement>(".slide-content"),
|
|
239
|
+
);
|
|
240
|
+
// A two-column Markdown slide renders one independent content root per
|
|
241
|
+
// column. Promoting only the first and serializing it would discard the
|
|
242
|
+
// other column, so decline until both roots have a shared raw-HTML canvas.
|
|
243
|
+
if (slideContents.length !== 1) return null;
|
|
244
|
+
const slideContent = slideContents[0];
|
|
245
|
+
const canvas = slideContent?.closest<HTMLElement>("[data-slide-canvas]");
|
|
246
|
+
if (!slideContent || !canvas) return null;
|
|
247
|
+
|
|
248
|
+
const canvasStyle = window.getComputedStyle(canvas);
|
|
249
|
+
const fmdSlide = document.createElement("div");
|
|
250
|
+
fmdSlide.className = "fmd-slide";
|
|
251
|
+
fmdSlide.style.justifyContent = canvasStyle.justifyContent;
|
|
252
|
+
fmdSlide.style.alignItems = canvasStyle.alignItems;
|
|
253
|
+
fmdSlide.style.padding = canvasStyle.padding;
|
|
254
|
+
fmdSlide.style.textAlign = canvasStyle.textAlign;
|
|
255
|
+
fmdSlide.style.color = canvasStyle.color;
|
|
256
|
+
fmdSlide.style.fontFamily = canvasStyle.fontFamily;
|
|
257
|
+
fmdSlide.append(...Array.from(slideContent.childNodes));
|
|
258
|
+
slideContent.append(fmdSlide);
|
|
259
|
+
|
|
260
|
+
return { fmdSlide, positioningLayer: fmdSlide };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function hasUsableTextColor(color: string) {
|
|
264
|
+
return (
|
|
265
|
+
Boolean(color) &&
|
|
266
|
+
color !== "transparent" &&
|
|
267
|
+
!/rgba\([^)]*,\s*0\)$/.test(color)
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function isTextRun(element: HTMLElement) {
|
|
272
|
+
return Boolean(element.textContent?.trim());
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function isDarkColor(color: string) {
|
|
276
|
+
const channels = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
|
|
277
|
+
if (!channels) return false;
|
|
278
|
+
const [, red, green, blue] = channels.map(Number);
|
|
279
|
+
return red * 0.2126 + green * 0.7152 + blue * 0.0722 < 140;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Choose rendered text before generic canvas shells, then keep the fallback legible. */
|
|
283
|
+
export function getSlideTextBoxDefaultColor(
|
|
284
|
+
target: HTMLElement | null,
|
|
285
|
+
positioningLayer: HTMLElement,
|
|
286
|
+
): string {
|
|
287
|
+
const candidates = [
|
|
288
|
+
...Array.from(
|
|
289
|
+
positioningLayer.querySelectorAll<HTMLElement>(
|
|
290
|
+
"h1, h2, h3, h4, h5, h6, p, li, span",
|
|
291
|
+
),
|
|
292
|
+
).filter(isTextRun),
|
|
293
|
+
target?.matches("h1, h2, h3, h4, h5, h6, p, li, span") && isTextRun(target)
|
|
294
|
+
? target
|
|
295
|
+
: null,
|
|
296
|
+
];
|
|
297
|
+
for (const candidate of candidates) {
|
|
298
|
+
if (!candidate) continue;
|
|
299
|
+
const color = window.getComputedStyle(candidate).color;
|
|
300
|
+
if (hasUsableTextColor(color)) {
|
|
301
|
+
return color;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const canvas = positioningLayer.closest<HTMLElement>("[data-slide-canvas]");
|
|
306
|
+
const canvasStyle = canvas ? window.getComputedStyle(canvas) : null;
|
|
307
|
+
const designSystemText = canvasStyle?.getPropertyValue("--ds-text").trim();
|
|
308
|
+
if (designSystemText) return designSystemText;
|
|
309
|
+
|
|
310
|
+
const background = canvasStyle?.backgroundColor ?? "";
|
|
311
|
+
return isDarkColor(background) ? "#ffffff" : "#111827";
|
|
312
|
+
}
|
|
313
|
+
|
|
205
314
|
export function removeTransientBuilderIds(element: HTMLElement): void {
|
|
206
315
|
element.removeAttribute("data-builder-id");
|
|
207
316
|
element.querySelectorAll("[data-builder-id]").forEach((node) => {
|
|
@@ -209,9 +318,99 @@ export function removeTransientBuilderIds(element: HTMLElement): void {
|
|
|
209
318
|
});
|
|
210
319
|
}
|
|
211
320
|
|
|
321
|
+
const ID_REFERENCE_ATTRIBUTES = [
|
|
322
|
+
"aria-activedescendant",
|
|
323
|
+
"aria-controls",
|
|
324
|
+
"aria-describedby",
|
|
325
|
+
"aria-details",
|
|
326
|
+
"aria-errormessage",
|
|
327
|
+
"aria-flowto",
|
|
328
|
+
"aria-labelledby",
|
|
329
|
+
"aria-owns",
|
|
330
|
+
] as const;
|
|
331
|
+
|
|
332
|
+
function escapeRegExp(value: string): string {
|
|
333
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function createSlideObjectDomId(occupiedIds: Set<string>): string {
|
|
337
|
+
let id = `slide-object-dom-${createSlideObjectId()}`;
|
|
338
|
+
while (occupiedIds.has(id)) {
|
|
339
|
+
id = `slide-object-dom-${createSlideObjectId()}`;
|
|
340
|
+
}
|
|
341
|
+
occupiedIds.add(id);
|
|
342
|
+
return id;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function remintSlideObjectDomIds(
|
|
346
|
+
element: HTMLElement,
|
|
347
|
+
occupiedIds: Set<string> = new Set(
|
|
348
|
+
Array.from(element.ownerDocument.querySelectorAll<HTMLElement>("[id]")).map(
|
|
349
|
+
(node) => node.id,
|
|
350
|
+
),
|
|
351
|
+
),
|
|
352
|
+
): void {
|
|
353
|
+
const idMap = new Map<string, string>();
|
|
354
|
+
const elements = [element, ...element.querySelectorAll<HTMLElement>("[id]")];
|
|
355
|
+
|
|
356
|
+
for (const node of elements) {
|
|
357
|
+
const id = node.getAttribute("id");
|
|
358
|
+
if (!id) continue;
|
|
359
|
+
const remintedId = createSlideObjectDomId(occupiedIds);
|
|
360
|
+
// Duplicate source ids are already ambiguous. Keep internal references
|
|
361
|
+
// pointed at the first occurrence, matching document.getElementById().
|
|
362
|
+
if (!idMap.has(id)) idMap.set(id, remintedId);
|
|
363
|
+
node.id = remintedId;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (idMap.size === 0) return;
|
|
367
|
+
|
|
368
|
+
const remapIdReferences = (value: string): string =>
|
|
369
|
+
value
|
|
370
|
+
.split(/\s+/)
|
|
371
|
+
.map((id) => idMap.get(id) ?? id)
|
|
372
|
+
.join(" ");
|
|
373
|
+
const remapUrlReferences = (value: string): string => {
|
|
374
|
+
let result = value;
|
|
375
|
+
for (const [id, remintedId] of idMap) {
|
|
376
|
+
result = result.replace(
|
|
377
|
+
new RegExp(`url\\(\\s*#${escapeRegExp(id)}\\s*\\)`, "g"),
|
|
378
|
+
`url(#${remintedId})`,
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return result;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
for (const node of [element, ...element.querySelectorAll<HTMLElement>("*")]) {
|
|
385
|
+
const labelFor = node.getAttribute("for");
|
|
386
|
+
if (labelFor) node.setAttribute("for", idMap.get(labelFor) ?? labelFor);
|
|
387
|
+
|
|
388
|
+
for (const attribute of ID_REFERENCE_ATTRIBUTES) {
|
|
389
|
+
const value = node.getAttribute(attribute);
|
|
390
|
+
if (value) node.setAttribute(attribute, remapIdReferences(value));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
for (const attribute of Array.from(node.attributes)) {
|
|
394
|
+
if (attribute.name === "id") continue;
|
|
395
|
+
if (attribute.name === "href" || attribute.name === "xlink:href") {
|
|
396
|
+
const remintedId = attribute.value.startsWith("#")
|
|
397
|
+
? idMap.get(attribute.value.slice(1))
|
|
398
|
+
: undefined;
|
|
399
|
+
if (remintedId) node.setAttribute(attribute.name, `#${remintedId}`);
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
const remapped = remapUrlReferences(attribute.value);
|
|
403
|
+
if (remapped !== attribute.value) {
|
|
404
|
+
node.setAttribute(attribute.name, remapped);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
212
410
|
export function cloneSlideObject(element: HTMLElement): HTMLElement {
|
|
213
411
|
const clone = element.cloneNode(true) as HTMLElement;
|
|
214
412
|
removeTransientBuilderIds(clone);
|
|
413
|
+
remintSlideObjectDomIds(clone);
|
|
215
414
|
clone.setAttribute("data-slide-object-id", createSlideObjectId());
|
|
216
415
|
// Nested freeform objects are independently addressable after a clone. Each
|
|
217
416
|
// one needs a new persisted identity so selector-based edits cannot resolve
|
|
@@ -233,6 +432,7 @@ export function freezeSlideElementForFreeform(
|
|
|
233
432
|
element: HTMLElement,
|
|
234
433
|
geometry: SlideObjectGeometry,
|
|
235
434
|
layout: SlideObjectLayoutSnapshot,
|
|
435
|
+
textPresentation?: SlideObjectTextPresentationSnapshot,
|
|
236
436
|
): HTMLElement {
|
|
237
437
|
const objectId = ensureSlideObjectId(element);
|
|
238
438
|
const spacer = element.cloneNode(false) as HTMLElement;
|
|
@@ -254,9 +454,12 @@ export function freezeSlideElementForFreeform(
|
|
|
254
454
|
spacer.style.minHeight = "0";
|
|
255
455
|
spacer.style.maxWidth = "none";
|
|
256
456
|
spacer.style.maxHeight = "none";
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
457
|
+
// `geometry` is the already-laid-out border box. Letting the replacement
|
|
458
|
+
// flex item shrink that measured size a second time changes the autofit
|
|
459
|
+
// transform and makes the object jump as soon as it becomes absolute.
|
|
460
|
+
spacer.style.flexGrow = "0";
|
|
461
|
+
spacer.style.flexShrink = "0";
|
|
462
|
+
spacer.style.flexBasis = "auto";
|
|
260
463
|
spacer.style.alignSelf = layout.alignSelf;
|
|
261
464
|
// An inline placeholder cannot reserve a measured block's height. Preserve
|
|
262
465
|
// inline text flow with inline-block while retaining block/grid displays.
|
|
@@ -274,6 +477,31 @@ export function freezeSlideElementForFreeform(
|
|
|
274
477
|
// left/top describe the visible border box. Leaving flow margins on the
|
|
275
478
|
// absolute element would offset it from the measured pre-freeze rect.
|
|
276
479
|
element.style.margin = "0";
|
|
480
|
+
if (textPresentation) {
|
|
481
|
+
const properties: Array<
|
|
482
|
+
[keyof SlideObjectTextPresentationSnapshot, string]
|
|
483
|
+
> = [
|
|
484
|
+
["color", "color"],
|
|
485
|
+
["direction", "direction"],
|
|
486
|
+
["fontFamily", "font-family"],
|
|
487
|
+
["fontSize", "font-size"],
|
|
488
|
+
["fontStyle", "font-style"],
|
|
489
|
+
["fontWeight", "font-weight"],
|
|
490
|
+
["letterSpacing", "letter-spacing"],
|
|
491
|
+
["lineHeight", "line-height"],
|
|
492
|
+
["textAlign", "text-align"],
|
|
493
|
+
["textDecoration", "text-decoration"],
|
|
494
|
+
["textShadow", "text-shadow"],
|
|
495
|
+
["textTransform", "text-transform"],
|
|
496
|
+
["whiteSpace", "white-space"],
|
|
497
|
+
["wordSpacing", "word-spacing"],
|
|
498
|
+
];
|
|
499
|
+
for (const [key, property] of properties) {
|
|
500
|
+
if (textPresentation[key] && !element.style.getPropertyValue(property)) {
|
|
501
|
+
element.style.setProperty(property, textPresentation[key]);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
277
505
|
return spacer;
|
|
278
506
|
}
|
|
279
507
|
|
|
@@ -301,13 +529,13 @@ export function clientPointToSlideCoordinates(
|
|
|
301
529
|
slideWidth: number,
|
|
302
530
|
slideHeight: number,
|
|
303
531
|
): { x: number; y: number } {
|
|
304
|
-
const x
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
532
|
+
const point = clientPointToCanvasPoint({ x: clientX, y: clientY }, rect, {
|
|
533
|
+
width: slideWidth,
|
|
534
|
+
height: slideHeight,
|
|
535
|
+
});
|
|
308
536
|
return {
|
|
309
|
-
x: Math.round(x),
|
|
310
|
-
y: Math.round(y),
|
|
537
|
+
x: Math.round(point.x),
|
|
538
|
+
y: Math.round(point.y),
|
|
311
539
|
};
|
|
312
540
|
}
|
|
313
541
|
|
|
@@ -321,48 +549,274 @@ export function resizeSlideObject(
|
|
|
321
549
|
minSize = MIN_SLIDE_OBJECT_SIZE,
|
|
322
550
|
}: ResizeOptions,
|
|
323
551
|
): SlideObjectGeometry {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
552
|
+
return resizeCanvasRect(start, {
|
|
553
|
+
handle,
|
|
554
|
+
delta: { x: dx, y: dy },
|
|
555
|
+
preserveAspectRatio,
|
|
556
|
+
minWidth: minSize,
|
|
557
|
+
minHeight: minSize,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
export type SlideObjectZOrderTarget = "front" | "back";
|
|
562
|
+
|
|
563
|
+
function readSlideObjectZIndex(element: HTMLElement): number {
|
|
564
|
+
const raw = element.style.zIndex || window.getComputedStyle(element).zIndex;
|
|
565
|
+
const value = Number(raw);
|
|
566
|
+
return Number.isFinite(value) ? value : 0;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export interface SlideObjectZOrderChange {
|
|
570
|
+
/** z-index to assign to the moved element. */
|
|
571
|
+
value: number;
|
|
572
|
+
/** Peers that must be shifted up to make room, when there was none below. */
|
|
573
|
+
shiftPeers: { element: HTMLElement; value: number }[];
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function isEditableFreeformSlideObject(element: HTMLElement): boolean {
|
|
577
|
+
return (
|
|
578
|
+
element.hasAttribute("data-slide-object-id") &&
|
|
579
|
+
(element.style.position || window.getComputedStyle(element).position) ===
|
|
580
|
+
"absolute"
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function createsSlideObjectStackingContext(element: HTMLElement): boolean {
|
|
585
|
+
const style = window.getComputedStyle(element);
|
|
586
|
+
const inline = element.style;
|
|
587
|
+
const position = inline.position || style.position || "static";
|
|
588
|
+
const zIndex = inline.zIndex || style.zIndex;
|
|
589
|
+
const containment = inline.contain || style.contain || "";
|
|
590
|
+
const willChange = inline.willChange || style.willChange || "";
|
|
591
|
+
const opacity = Number.parseFloat(inline.opacity || style.opacity || "1");
|
|
592
|
+
const transform = inline.transform || style.transform;
|
|
593
|
+
const perspective = inline.perspective || style.perspective;
|
|
594
|
+
const filter = inline.filter || style.filter;
|
|
595
|
+
const backdropFilter = inline.backdropFilter || style.backdropFilter;
|
|
596
|
+
const isolation = inline.isolation || style.isolation;
|
|
597
|
+
const mixBlendMode = inline.mixBlendMode || style.mixBlendMode;
|
|
598
|
+
|
|
599
|
+
return (
|
|
600
|
+
position === "fixed" ||
|
|
601
|
+
position === "sticky" ||
|
|
602
|
+
(position !== "static" && zIndex !== "" && zIndex !== "auto") ||
|
|
603
|
+
(Number.isFinite(opacity) && opacity < 1) ||
|
|
604
|
+
(transform !== "" && transform !== "none") ||
|
|
605
|
+
(perspective !== "" && perspective !== "none") ||
|
|
606
|
+
(filter !== "" && filter !== "none") ||
|
|
607
|
+
(backdropFilter !== "" && backdropFilter !== "none") ||
|
|
608
|
+
isolation === "isolate" ||
|
|
609
|
+
(mixBlendMode !== "" && mixBlendMode !== "normal") ||
|
|
610
|
+
/(?:^|\s)(?:layout|paint|strict|content)(?:\s|$)/.test(containment) ||
|
|
611
|
+
/(?:^|,\s*)(?:transform|opacity|filter|perspective)(?:,\s*|$)/.test(
|
|
612
|
+
willChange,
|
|
613
|
+
)
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function resolveSlideObjectStackingContext(
|
|
618
|
+
element: HTMLElement,
|
|
619
|
+
container: HTMLElement,
|
|
620
|
+
): HTMLElement {
|
|
621
|
+
let ancestor = element.parentElement;
|
|
622
|
+
while (ancestor && ancestor !== container) {
|
|
623
|
+
if (createsSlideObjectStackingContext(ancestor)) return ancestor;
|
|
624
|
+
ancestor = ancestor.parentElement;
|
|
625
|
+
}
|
|
626
|
+
return container;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
interface SlideObjectZOrderPeer {
|
|
630
|
+
element: HTMLElement;
|
|
631
|
+
zIndex: number;
|
|
632
|
+
order: number;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function getSlideObjectZOrderPeers(
|
|
636
|
+
element: HTMLElement,
|
|
637
|
+
container: HTMLElement,
|
|
638
|
+
): SlideObjectZOrderPeer[] {
|
|
639
|
+
const containingBlock = resolveSlideObjectContainingBlock(element, container);
|
|
640
|
+
const stackingContext = resolveSlideObjectStackingContext(element, container);
|
|
641
|
+
|
|
642
|
+
return Array.from(
|
|
643
|
+
container.querySelectorAll<HTMLElement>("[data-slide-object-id]"),
|
|
644
|
+
).flatMap((peer, order) => {
|
|
645
|
+
if (
|
|
646
|
+
peer === element ||
|
|
647
|
+
element.contains(peer) ||
|
|
648
|
+
!isEditableFreeformSlideObject(peer) ||
|
|
649
|
+
resolveSlideObjectContainingBlock(peer, container) !== containingBlock ||
|
|
650
|
+
resolveSlideObjectStackingContext(peer, container) !== stackingContext
|
|
651
|
+
) {
|
|
652
|
+
return [];
|
|
653
|
+
}
|
|
654
|
+
const zIndex = readSlideObjectZIndex(peer);
|
|
655
|
+
// Negative layers are reserved for slide backgrounds. They must remain
|
|
656
|
+
// below editable objects, never be pulled up by send-to-back normalization.
|
|
657
|
+
if (zIndex < 0) return [];
|
|
658
|
+
return [{ element: peer, zIndex, order }];
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Compute the z-index change that puts `element` in front of / behind every
|
|
664
|
+
* other freeform object inside `container`. Returns null when nothing needs
|
|
665
|
+
* to change.
|
|
666
|
+
*/
|
|
667
|
+
export function computeSlideObjectZOrder(
|
|
668
|
+
element: HTMLElement,
|
|
669
|
+
container: HTMLElement,
|
|
670
|
+
target: SlideObjectZOrderTarget,
|
|
671
|
+
): SlideObjectZOrderChange | null {
|
|
672
|
+
if (!isEditableFreeformSlideObject(element)) return null;
|
|
673
|
+
const peers = getSlideObjectZOrderPeers(element, container);
|
|
674
|
+
|
|
675
|
+
if (peers.length === 0) return null;
|
|
676
|
+
|
|
677
|
+
const peerZIndexes = peers.map((peer) => peer.zIndex);
|
|
678
|
+
const currentValue = readSlideObjectZIndex(element);
|
|
679
|
+
|
|
680
|
+
if (target === "front") {
|
|
681
|
+
const value = Math.max(...peerZIndexes) + 1;
|
|
682
|
+
return value === currentValue ? null : { value, shiftPeers: [] };
|
|
351
683
|
}
|
|
352
684
|
|
|
353
|
-
|
|
354
|
-
|
|
685
|
+
const minPeer = Math.min(...peerZIndexes);
|
|
686
|
+
const hasTiedPeers = new Set(peerZIndexes).size !== peers.length;
|
|
687
|
+
if (minPeer - 1 >= 0 && !hasTiedPeers) {
|
|
688
|
+
const value = minPeer - 1;
|
|
689
|
+
return value === currentValue ? null : { value, shiftPeers: [] };
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// No room below zero, or an existing tie needs repair. Keep reserved negative
|
|
693
|
+
// layers untouched and give every editable peer a stable unique position.
|
|
694
|
+
const orderedPeers = [...peers].sort(
|
|
695
|
+
(left, right) => left.zIndex - right.zIndex || left.order - right.order,
|
|
696
|
+
);
|
|
697
|
+
return {
|
|
698
|
+
value: 0,
|
|
699
|
+
shiftPeers: orderedPeers.map((peer, index) => ({
|
|
700
|
+
element: peer.element,
|
|
701
|
+
value: index + 1,
|
|
702
|
+
})),
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
export interface SlideObjectMoveMember {
|
|
707
|
+
objectId: string;
|
|
708
|
+
element: HTMLElement;
|
|
709
|
+
start: SlideObjectGeometry;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function normalizeSlideObjectRoots(elements: HTMLElement[]): HTMLElement[] {
|
|
713
|
+
const uniqueElements = Array.from(new Set(elements));
|
|
714
|
+
return uniqueElements.filter(
|
|
715
|
+
(element) =>
|
|
716
|
+
!uniqueElements.some(
|
|
717
|
+
(candidate) => candidate !== element && candidate.contains(element),
|
|
718
|
+
),
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Snapshot the movable members of a multi-selection, keyed by durable object id.
|
|
724
|
+
* Elements that are not absolutely positioned cannot move and are excluded.
|
|
725
|
+
*/
|
|
726
|
+
export function collectMovableSlideObjects(
|
|
727
|
+
elements: HTMLElement[],
|
|
728
|
+
getGeometry: (element: HTMLElement) => SlideObjectGeometry,
|
|
729
|
+
): SlideObjectMoveMember[] {
|
|
730
|
+
const seen = new Set<string>();
|
|
731
|
+
const members: SlideObjectMoveMember[] = [];
|
|
732
|
+
for (const element of normalizeSlideObjectRoots(elements)) {
|
|
733
|
+
const objectId = element.getAttribute("data-slide-object-id");
|
|
734
|
+
if (!objectId || seen.has(objectId)) continue;
|
|
735
|
+
if (
|
|
736
|
+
(element.style.position || window.getComputedStyle(element).position) !==
|
|
737
|
+
"absolute"
|
|
738
|
+
) {
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
741
|
+
seen.add(objectId);
|
|
742
|
+
members.push({ objectId, element, start: getGeometry(element) });
|
|
743
|
+
}
|
|
744
|
+
return members;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/** Apply one shared delta to every member, relative to its captured start. */
|
|
748
|
+
export function applySlideObjectMoveDelta(
|
|
749
|
+
members: SlideObjectMoveMember[],
|
|
750
|
+
deltaX: number,
|
|
751
|
+
deltaY: number,
|
|
752
|
+
applyGeometry: (element: HTMLElement, geometry: SlideObjectGeometry) => void,
|
|
753
|
+
): void {
|
|
754
|
+
for (const member of members) {
|
|
755
|
+
applyGeometry(member.element, {
|
|
756
|
+
...member.start,
|
|
757
|
+
x: member.start.x + deltaX,
|
|
758
|
+
y: member.start.y + deltaY,
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
export interface CopiedSlideObjects {
|
|
764
|
+
html: string[];
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
export function copySlideObjects(elements: HTMLElement[]): CopiedSlideObjects {
|
|
355
768
|
return {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
769
|
+
html: normalizeSlideObjectRoots(elements).map((element) => {
|
|
770
|
+
const clone = cloneSlideObject(element);
|
|
771
|
+
return clone.outerHTML;
|
|
772
|
+
}),
|
|
360
773
|
};
|
|
361
774
|
}
|
|
362
775
|
|
|
363
|
-
export
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
776
|
+
export const SLIDE_OBJECT_PASTE_OFFSET = 16;
|
|
777
|
+
|
|
778
|
+
function offsetInlinePx(
|
|
779
|
+
element: HTMLElement,
|
|
780
|
+
property: "left" | "top",
|
|
781
|
+
offset: number,
|
|
782
|
+
): void {
|
|
783
|
+
const value = Number.parseFloat(element.style[property]);
|
|
784
|
+
if (!Number.isFinite(value)) return;
|
|
785
|
+
element.style[property] = `${value + offset}px`;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Rebuild pasted objects from stored HTML with fresh ids and a cascade offset,
|
|
790
|
+
* so a paste never collides exactly with its source.
|
|
791
|
+
*/
|
|
792
|
+
export function buildPastedSlideObjects(
|
|
793
|
+
copied: CopiedSlideObjects,
|
|
794
|
+
doc: Document,
|
|
795
|
+
offset: number = SLIDE_OBJECT_PASTE_OFFSET,
|
|
796
|
+
): HTMLElement[] {
|
|
797
|
+
const pasted: HTMLElement[] = [];
|
|
798
|
+
const occupiedDomIds = new Set(
|
|
799
|
+
Array.from(doc.querySelectorAll<HTMLElement>("[id]")).map(
|
|
800
|
+
(element) => element.id,
|
|
801
|
+
),
|
|
802
|
+
);
|
|
803
|
+
for (const html of copied.html) {
|
|
804
|
+
const template = doc.createElement("template");
|
|
805
|
+
template.innerHTML = html;
|
|
806
|
+
const element = template.content.firstElementChild;
|
|
807
|
+
if (!(element instanceof HTMLElement)) continue;
|
|
808
|
+
remintSlideObjectDomIds(element, occupiedDomIds);
|
|
809
|
+
// Remint every persisted id (root + nested) the same way cloneSlideObject
|
|
810
|
+
// does, so a pasted object never resolves to its source via selector.
|
|
811
|
+
element.setAttribute("data-slide-object-id", createSlideObjectId());
|
|
812
|
+
element
|
|
813
|
+
.querySelectorAll<HTMLElement>("[data-slide-object-id]")
|
|
814
|
+
.forEach((descendant) => {
|
|
815
|
+
descendant.setAttribute("data-slide-object-id", createSlideObjectId());
|
|
816
|
+
});
|
|
817
|
+
offsetInlinePx(element, "left", offset);
|
|
818
|
+
offsetInlinePx(element, "top", offset);
|
|
819
|
+
pasted.push(element);
|
|
820
|
+
}
|
|
821
|
+
return pasted;
|
|
368
822
|
}
|