@agent-native/core 0.79.27 → 0.80.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 +26 -0
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +41 -0
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +51 -0
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +50 -0
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +49 -0
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +44 -0
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +46 -0
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +44 -0
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +48 -0
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +36 -0
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +38 -0
- package/corpus/core/docs/content/template-design.mdx +45 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/agent/engine/index.ts +1 -0
- package/corpus/core/src/agent/engine/registry.ts +40 -0
- package/corpus/core/src/agent/production-agent.ts +6 -2
- package/corpus/core/src/cli/design-connect.ts +490 -0
- package/corpus/core/src/cli/index.ts +16 -0
- package/corpus/core/src/cli/skills.ts +126 -3
- package/corpus/core/src/client/AssistantChat.tsx +43 -7
- package/corpus/core/src/client/agent-chat.ts +27 -3
- package/corpus/core/src/client/blocks/library/MermaidBlock.tsx +1 -1
- package/corpus/core/src/client/blocks/library/diagram.tsx +2 -2
- package/corpus/core/src/client/blocks/library/wireframe.tsx +1 -1
- package/corpus/core/src/client/chat/message-components.tsx +1 -1
- package/corpus/core/src/client/chat-model-groups.ts +1 -2
- package/corpus/core/src/client/components/LiveCursorOverlay.tsx +33 -66
- package/corpus/core/src/client/composer/TiptapComposer.tsx +99 -24
- package/corpus/core/src/client/composer/useVoiceDictation.ts +429 -260
- package/corpus/core/src/client/frame-protocol.ts +17 -2
- package/corpus/core/src/client/rich-markdown-editor/RegistryBlockNode.tsx +2 -2
- package/corpus/core/src/client/sharing/ShareButton.tsx +20 -15
- package/corpus/core/src/client/use-agent-engine-configured.ts +80 -28
- package/corpus/core/src/server/transcribe-voice.ts +57 -22
- package/corpus/core/src/shared/mcp-embed-headers.ts +23 -1
- package/corpus/core/src/styles/agent-native.css +56 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/corpus/core/src/vite/client.ts +2 -0
- package/corpus/core/src/voice/index.ts +18 -0
- package/corpus/core/src/voice/voice-cleanup-prompt.ts +38 -0
- package/corpus/core/src/voice/voice-context.ts +217 -0
- package/corpus/core/src/voice/voice-replacements.ts +75 -0
- package/corpus/templates/analytics/AGENTS.md +4 -3
- package/corpus/templates/analytics/actions/list-session-recordings.ts +6 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +557 -400
- package/corpus/templates/analytics/app/global.css +0 -4
- package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/analytics/app/i18n-data.ts +20 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +33 -8
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +7 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +7 -0
- package/corpus/templates/analytics/changelog/2026-06-28-ask-tab-now-matches-the-darker-app-background.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-dashboard-email-reports-now-use-cleaner-light-mode-snapshots.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-28-sessions-now-show-only-signed-in-recordings-with-playable-re.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +3 -3
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +29 -7
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +5 -2
- package/corpus/templates/analytics/server/lib/session-replay.ts +24 -4
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -4
- package/corpus/templates/assets/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/brain/app/components/layout/Layout.tsx +29 -2
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +131 -42
- package/corpus/templates/brain/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/brain/app/i18n-data.ts +20 -0
- package/corpus/templates/brain/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +29 -1
- package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +417 -299
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/calendar/app/i18n-data.ts +20 -0
- package/corpus/templates/calendar/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +6 -19
- package/corpus/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/cleanup-transcript.ts +39 -9
- package/corpus/templates/clips/actions/finalize-meeting.ts +81 -12
- package/corpus/templates/clips/actions/lib/agents-md-context.ts +4 -1
- package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -3
- package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +1 -1
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +2 -2
- package/corpus/templates/clips/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/personal-vocabulary.ts +13 -8
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +60 -1
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +1 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +1 -1
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -5
- package/corpus/templates/content/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +137 -0
- package/corpus/templates/design/AGENTS.md +95 -5
- package/corpus/templates/design/DESIGN.md +82 -0
- package/corpus/templates/design/DEVELOPING.md +43 -0
- package/corpus/templates/design/actions/add-localhost-screens.ts +426 -0
- package/corpus/templates/design/actions/apply-shader.ts +277 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +303 -0
- package/corpus/templates/design/actions/connect-localhost.ts +183 -0
- package/corpus/templates/design/actions/edit-design.ts +195 -42
- package/corpus/templates/design/actions/generate-design.ts +71 -1
- package/corpus/templates/design/actions/generate-screens.ts +193 -0
- package/corpus/templates/design/actions/get-code-layer-projection.ts +191 -0
- package/corpus/templates/design/actions/get-shader.ts +119 -0
- package/corpus/templates/design/actions/list-localhost-connections.ts +95 -0
- package/corpus/templates/design/actions/navigate.ts +55 -2
- package/corpus/templates/design/actions/view-screen.ts +68 -1
- package/corpus/templates/design/agent-native.app-skill.json +10 -0
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +761 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +2057 -103
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +74 -38
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +399 -0
- package/corpus/templates/design/app/components/design/EditPanel.tsx +3185 -439
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +1124 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +3116 -147
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +700 -13
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +107 -63
- package/corpus/templates/design/app/components/design/index.ts +0 -5
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +286 -0
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +699 -0
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +461 -0
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +166 -0
- package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +1420 -0
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +420 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +215 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.stories.tsx +71 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +518 -0
- package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +106 -0
- package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +361 -0
- package/corpus/templates/design/app/components/design/inspector/index.ts +60 -0
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +201 -0
- package/corpus/templates/design/app/components/design/types.ts +23 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +24 -13
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +4 -4
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +474 -36
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +22 -5
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +24 -11
- package/corpus/templates/design/app/global.css +121 -3
- package/corpus/templates/design/app/hooks/use-agent-generating.ts +4 -0
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +130 -8
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +329 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +264 -0
- package/corpus/templates/design/app/i18n-data.ts +2790 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +5108 -851
- package/corpus/templates/design/changelog/2026-06-28-annotate-mode-now-combines-drawing-and-comment-pins-in-one-c.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-design-editor-panel-borders-are-cleaner-so-the-canvas-edge-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-drawing-prompts-now-reliably-open-and-submit-to-the-design-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-editor-app-menus-now-use-an-outline-agent-native-mark-with-b.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-layer-selection-now-updates-chat-context-without-opening-the.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-layer-selections-now-preserve-hidden-and-locked-canvas-state.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-handles-zooming-drawing-opening-and-dele.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-selects-screens-on-click-and-opens-them-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-screen-overview-zooms-into-the-active-screen-with-smoother-e.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-selected-element-chips-in-chat-are-shorter-and-easier-to-sca.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-the-design-editor-toolbar-is-more-compact-and-the-app-menu-m.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-at-the-top-of-the-design-sidebar-instead-of-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-in-the-right-inspector-beside-the-design-tab.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-active-sidebar-rows-use-soft-highlights.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-agents-can-now-open-screen-overview-focus-screens-and-add.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-agents-now-edit-broad-copy-changes-more-reliably.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-editor-extensions-now-run-inline-beside-the-artboard.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-sidebars-use-darker-chrome-and-chat-warns-immediately.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-visual-edit-can-open-localhost-routes-as-url-backed-screens.md +6 -0
- package/corpus/templates/design/package.json +1 -0
- package/corpus/templates/design/server/db/schema.ts +26 -0
- package/corpus/templates/design/server/lib/design-snapshot.ts +1 -1
- package/corpus/templates/design/server/plugins/agent-chat.ts +3 -0
- package/corpus/templates/design/shared/api.ts +27 -0
- package/corpus/templates/design/shared/canvas-frames.ts +103 -0
- package/corpus/templates/design/shared/canvas-math.ts +1058 -0
- package/corpus/templates/design/shared/code-layer.ts +2212 -0
- package/corpus/templates/design/shared/color-utils.ts +180 -0
- package/corpus/templates/design/shared/generation-session.ts +61 -0
- package/corpus/templates/design/shared/shader-presets.ts +782 -0
- package/corpus/templates/design/shared/shader-safety.ts +137 -0
- package/corpus/templates/design/shared/source-mode.ts +237 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +149 -6
- package/corpus/templates/forms/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/forms/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/forms/app/i18n/en-US.ts +2 -0
- package/corpus/templates/forms/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/forms/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/forms/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/forms/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/forms/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/forms/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/forms/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/forms/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/macros/app/components/layout/AppLayout.tsx +2 -2
- package/corpus/templates/macros/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +408 -247
- package/corpus/templates/mail/app/i18n/ar-SA.ts +4 -0
- package/corpus/templates/mail/app/i18n/de-DE.ts +4 -0
- package/corpus/templates/mail/app/i18n/en-US.ts +4 -0
- package/corpus/templates/mail/app/i18n/es-ES.ts +4 -0
- package/corpus/templates/mail/app/i18n/fr-FR.ts +4 -0
- package/corpus/templates/mail/app/i18n/hi-IN.ts +4 -0
- package/corpus/templates/mail/app/i18n/ja-JP.ts +4 -0
- package/corpus/templates/mail/app/i18n/ko-KR.ts +4 -0
- package/corpus/templates/mail/app/i18n/pt-BR.ts +4 -0
- package/corpus/templates/mail/app/i18n/zh-CN.ts +4 -0
- package/corpus/templates/mail/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/mail/changelog/2026-06-28-pinned-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +6 -8
- package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +1 -1
- package/corpus/templates/plan/app/global.css +61 -78
- package/corpus/templates/plan/app/pages/PlansPage.tsx +8 -0
- package/corpus/templates/plan/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-28-plans-use-the-shared-neutral-light-and-dark-theme.md +6 -0
- package/corpus/templates/plan/changelog/2026-06-28-sending-open-feedback-from-a-plan-now-opens-the-inline-plan-.md +6 -0
- package/corpus/templates/slides/app/components/layout/Sidebar.tsx +5 -5
- package/corpus/templates/slides/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
- package/corpus/templates/videos/app/components/Sidebar.tsx +2 -2
- package/corpus/templates/videos/app/components/layout/Layout.tsx +29 -2
- package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +113 -30
- package/corpus/templates/videos/app/i18n/ar-SA.ts +2 -0
- package/corpus/templates/videos/app/i18n/de-DE.ts +2 -0
- package/corpus/templates/videos/app/i18n/en-US.ts +2 -0
- package/corpus/templates/videos/app/i18n/es-ES.ts +2 -0
- package/corpus/templates/videos/app/i18n/fr-FR.ts +2 -0
- package/corpus/templates/videos/app/i18n/hi-IN.ts +2 -0
- package/corpus/templates/videos/app/i18n/ja-JP.ts +2 -0
- package/corpus/templates/videos/app/i18n/ko-KR.ts +2 -0
- package/corpus/templates/videos/app/i18n/pt-BR.ts +2 -0
- package/corpus/templates/videos/app/i18n/zh-CN.ts +2 -0
- package/corpus/templates/videos/app/i18n/zh-TW.ts +2 -0
- package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
- package/dist/agent/engine/index.d.ts +1 -1
- package/dist/agent/engine/index.d.ts.map +1 -1
- package/dist/agent/engine/index.js +1 -1
- package/dist/agent/engine/index.js.map +1 -1
- package/dist/agent/engine/registry.d.ts +9 -0
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +37 -0
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -3
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/design-connect.d.ts +52 -0
- package/dist/cli/design-connect.d.ts.map +1 -0
- package/dist/cli/design-connect.js +409 -0
- package/dist/cli/design-connect.js.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -0
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +121 -3
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +38 -8
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts +6 -0
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +17 -3
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.js +1 -1
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/blocks/library/wireframe.js +1 -1
- package/dist/client/blocks/library/wireframe.js.map +1 -1
- package/dist/client/chat/message-components.js +1 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +1 -2
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/components/LiveCursorOverlay.d.ts +1 -2
- package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
- package/dist/client/components/LiveCursorOverlay.js +19 -40
- package/dist/client/components/LiveCursorOverlay.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +3 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +86 -24
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/useVoiceDictation.d.ts +9 -0
- package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
- package/dist/client/composer/useVoiceDictation.js +154 -32
- package/dist/client/composer/useVoiceDictation.js.map +1 -1
- package/dist/client/frame-protocol.d.ts +17 -2
- package/dist/client/frame-protocol.d.ts.map +1 -1
- package/dist/client/frame-protocol.js.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +2 -2
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +2 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +7 -6
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts +5 -0
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +51 -25
- package/dist/client/use-agent-engine-configured.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/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +8 -8
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +40 -24
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +1 -0
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +20 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/styles/agent-native.css +56 -7
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +2 -0
- package/dist/vite/client.js.map +1 -1
- package/dist/voice/index.d.ts +5 -0
- package/dist/voice/index.d.ts.map +1 -0
- package/dist/voice/index.js +4 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/voice-cleanup-prompt.d.ts +6 -0
- package/dist/voice/voice-cleanup-prompt.d.ts.map +1 -0
- package/dist/voice/voice-cleanup-prompt.js +26 -0
- package/dist/voice/voice-cleanup-prompt.js.map +1 -0
- package/dist/voice/voice-context.d.ts +25 -0
- package/dist/voice/voice-context.d.ts.map +1 -0
- package/dist/voice/voice-context.js +156 -0
- package/dist/voice/voice-context.js.map +1 -0
- package/dist/voice/voice-replacements.d.ts +4 -0
- package/dist/voice/voice-replacements.d.ts.map +1 -0
- package/dist/voice/voice-replacements.js +52 -0
- package/dist/voice/voice-replacements.js.map +1 -0
- package/docs/content/locales/ar-SA/template-design.mdx +41 -0
- package/docs/content/locales/de-DE/template-design.mdx +51 -0
- package/docs/content/locales/es-ES/template-design.mdx +50 -0
- package/docs/content/locales/fr-FR/template-design.mdx +49 -0
- package/docs/content/locales/hi-IN/template-design.mdx +44 -0
- package/docs/content/locales/ja-JP/template-design.mdx +46 -0
- package/docs/content/locales/ko-KR/template-design.mdx +44 -0
- package/docs/content/locales/pt-BR/template-design.mdx +48 -0
- package/docs/content/locales/zh-CN/template-design.mdx +36 -0
- package/docs/content/locales/zh-TW/template-design.mdx +38 -0
- package/docs/content/template-design.mdx +45 -0
- package/package.json +2 -1
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
- package/corpus/templates/design/app/components/design/DesignToolbar.tsx +0 -339
- package/corpus/templates/design/app/components/design/PresentMode.tsx +0 -68
- package/corpus/templates/design/app/components/design/ViewportTabs.tsx +0 -149
- package/corpus/templates/design/app/components/design/ZoomControls.tsx +0 -157
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
import { defineAction, embedApp } from "@agent-native/core";
|
|
2
|
+
import {
|
|
3
|
+
applyText,
|
|
4
|
+
hasCollabState,
|
|
5
|
+
seedFromText,
|
|
6
|
+
} from "@agent-native/core/collab";
|
|
7
|
+
import { buildDeepLink } from "@agent-native/core/server";
|
|
8
|
+
import {
|
|
9
|
+
getRequestOrgId,
|
|
10
|
+
getRequestUserEmail,
|
|
11
|
+
} from "@agent-native/core/server/request-context";
|
|
12
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
13
|
+
import { and, desc, eq, isNull } from "drizzle-orm";
|
|
14
|
+
import { nanoid } from "nanoid";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
|
|
17
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
18
|
+
import {
|
|
19
|
+
mergeCanvasFramePlacements,
|
|
20
|
+
type CanvasFramePlacement,
|
|
21
|
+
} from "../shared/canvas-frames.js";
|
|
22
|
+
import {
|
|
23
|
+
makeLocalhostRouteId,
|
|
24
|
+
titleFromRoutePath,
|
|
25
|
+
type LocalhostDesignRouteManifest,
|
|
26
|
+
} from "../shared/source-mode.js";
|
|
27
|
+
|
|
28
|
+
const routeInputSchema = z.object({
|
|
29
|
+
routeId: z.string().optional(),
|
|
30
|
+
path: z.string().optional(),
|
|
31
|
+
url: z.string().optional(),
|
|
32
|
+
title: z.string().optional(),
|
|
33
|
+
width: z.number().positive().optional(),
|
|
34
|
+
height: z.number().positive().optional(),
|
|
35
|
+
x: z.number().optional(),
|
|
36
|
+
y: z.number().optional(),
|
|
37
|
+
z: z.number().optional(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
type LocalhostScreenInput = z.infer<typeof routeInputSchema>;
|
|
41
|
+
|
|
42
|
+
function designDeepLink(designId: string): string {
|
|
43
|
+
return buildDeepLink({
|
|
44
|
+
app: "design",
|
|
45
|
+
view: "editor",
|
|
46
|
+
params: { designId, editorView: "overview" },
|
|
47
|
+
to: `/design/${encodeURIComponent(designId)}`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseJson<T>(value: string | null | undefined, fallback: T): T {
|
|
52
|
+
if (!value) return fallback;
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(value) as T;
|
|
55
|
+
} catch {
|
|
56
|
+
return fallback;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
61
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeBaseUrl(value: string): string {
|
|
65
|
+
const parsed = new URL(value);
|
|
66
|
+
parsed.hash = "";
|
|
67
|
+
return parsed.toString().replace(/\/$/, "");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function routeUrl(baseUrl: string, route: { path?: string; url?: string }) {
|
|
71
|
+
const raw = route.url ?? route.path ?? "/";
|
|
72
|
+
const parsed = new URL(raw, `${baseUrl}/`);
|
|
73
|
+
parsed.hash = "";
|
|
74
|
+
return parsed.toString();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function pathFromUrl(baseUrl: string, url: string, fallback?: string) {
|
|
78
|
+
try {
|
|
79
|
+
const parsed = new URL(url);
|
|
80
|
+
const base = new URL(baseUrl);
|
|
81
|
+
if (parsed.origin === base.origin) {
|
|
82
|
+
return `${parsed.pathname}${parsed.search}` || "/";
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
// Fall through to the provided fallback.
|
|
86
|
+
}
|
|
87
|
+
return fallback ?? "/";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function slugForPath(pathOrUrl: string) {
|
|
91
|
+
const parsed = (() => {
|
|
92
|
+
try {
|
|
93
|
+
return new URL(pathOrUrl).pathname + new URL(pathOrUrl).search;
|
|
94
|
+
} catch {
|
|
95
|
+
return pathOrUrl;
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
const slug = parsed
|
|
99
|
+
.replace(/^\/+/, "")
|
|
100
|
+
.replace(/[^a-zA-Z0-9]+/g, "-")
|
|
101
|
+
.replace(/^-+|-+$/g, "")
|
|
102
|
+
.toLowerCase();
|
|
103
|
+
return (slug || "home").slice(0, 80);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function uniqueFilename(
|
|
107
|
+
pathOrUrl: string,
|
|
108
|
+
used: Set<string>,
|
|
109
|
+
preferred?: string,
|
|
110
|
+
) {
|
|
111
|
+
const base = preferred ?? `localhost-${slugForPath(pathOrUrl)}.html`;
|
|
112
|
+
const [stem, extension = "html"] = base.split(/\.(?=[^.]+$)/);
|
|
113
|
+
let filename = `${stem}.${extension}`;
|
|
114
|
+
let suffix = 2;
|
|
115
|
+
while (used.has(filename)) {
|
|
116
|
+
filename = `${stem}-${suffix}.${extension}`;
|
|
117
|
+
suffix += 1;
|
|
118
|
+
}
|
|
119
|
+
used.add(filename);
|
|
120
|
+
return filename;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default defineAction({
|
|
124
|
+
description:
|
|
125
|
+
"Create or refresh URL-backed localhost screens in a design project. " +
|
|
126
|
+
"Use after connect-localhost to place local app routes on the overview " +
|
|
127
|
+
"canvas as iframe-backed artboards with editable URL metadata.",
|
|
128
|
+
schema: z.object({
|
|
129
|
+
designId: z.string().describe("Design project ID to add screens to."),
|
|
130
|
+
connectionId: z
|
|
131
|
+
.string()
|
|
132
|
+
.optional()
|
|
133
|
+
.describe(
|
|
134
|
+
"Localhost connection ID from connect-localhost. Omit to use the latest connection.",
|
|
135
|
+
),
|
|
136
|
+
routes: z
|
|
137
|
+
.preprocess(
|
|
138
|
+
(value) => (typeof value === "string" ? JSON.parse(value) : value),
|
|
139
|
+
z.array(routeInputSchema).optional(),
|
|
140
|
+
)
|
|
141
|
+
.describe(
|
|
142
|
+
"Routes or URL states to place. Each may include path, url, title, width, height, x/y/z.",
|
|
143
|
+
),
|
|
144
|
+
paths: z
|
|
145
|
+
.preprocess(
|
|
146
|
+
(value) => (typeof value === "string" ? JSON.parse(value) : value),
|
|
147
|
+
z.array(z.string()).optional(),
|
|
148
|
+
)
|
|
149
|
+
.describe("Shortcut for routes when only paths/URLs are needed."),
|
|
150
|
+
defaultWidth: z
|
|
151
|
+
.number()
|
|
152
|
+
.positive()
|
|
153
|
+
.optional()
|
|
154
|
+
.default(1280)
|
|
155
|
+
.describe("Default iframe viewport width."),
|
|
156
|
+
defaultHeight: z
|
|
157
|
+
.number()
|
|
158
|
+
.positive()
|
|
159
|
+
.optional()
|
|
160
|
+
.default(900)
|
|
161
|
+
.describe("Default iframe viewport height."),
|
|
162
|
+
startX: z.number().optional().default(0),
|
|
163
|
+
startY: z.number().optional().default(0),
|
|
164
|
+
gap: z.number().optional().default(160),
|
|
165
|
+
}),
|
|
166
|
+
mcpApp: {
|
|
167
|
+
compactCatalog: true,
|
|
168
|
+
resource: embedApp({
|
|
169
|
+
title: "Local visual edit",
|
|
170
|
+
description: "Open local URL-backed screens in Design overview mode.",
|
|
171
|
+
iframeTitle: "Agent-Native Design",
|
|
172
|
+
openLabel: "Open overview",
|
|
173
|
+
height: 680,
|
|
174
|
+
}),
|
|
175
|
+
},
|
|
176
|
+
run: async ({
|
|
177
|
+
designId,
|
|
178
|
+
connectionId,
|
|
179
|
+
routes,
|
|
180
|
+
paths,
|
|
181
|
+
defaultWidth,
|
|
182
|
+
defaultHeight,
|
|
183
|
+
startX,
|
|
184
|
+
startY,
|
|
185
|
+
gap,
|
|
186
|
+
}) => {
|
|
187
|
+
await assertAccess("design", designId, "editor");
|
|
188
|
+
const ownerEmail = getRequestUserEmail();
|
|
189
|
+
if (!ownerEmail) throw new Error("no authenticated user");
|
|
190
|
+
const orgId = getRequestOrgId() ?? null;
|
|
191
|
+
const db = getDb();
|
|
192
|
+
|
|
193
|
+
const connectionClauses = [
|
|
194
|
+
eq(schema.designLocalhostConnections.ownerEmail, ownerEmail),
|
|
195
|
+
orgId
|
|
196
|
+
? eq(schema.designLocalhostConnections.orgId, orgId)
|
|
197
|
+
: isNull(schema.designLocalhostConnections.orgId),
|
|
198
|
+
];
|
|
199
|
+
if (connectionId) {
|
|
200
|
+
connectionClauses.push(
|
|
201
|
+
eq(schema.designLocalhostConnections.id, connectionId),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const [connection] = await db
|
|
206
|
+
.select()
|
|
207
|
+
.from(schema.designLocalhostConnections)
|
|
208
|
+
.where(and(...connectionClauses))
|
|
209
|
+
.orderBy(desc(schema.designLocalhostConnections.updatedAt))
|
|
210
|
+
.limit(1);
|
|
211
|
+
|
|
212
|
+
if (!connection) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
connectionId
|
|
215
|
+
? `No localhost connection found for ${connectionId}.`
|
|
216
|
+
: "No localhost connection found. Run connect-localhost first.",
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const devServerUrl = normalizeBaseUrl(connection.devServerUrl);
|
|
221
|
+
const manifest = parseJson<LocalhostDesignRouteManifest>(
|
|
222
|
+
connection.routeManifest,
|
|
223
|
+
{
|
|
224
|
+
version: 1,
|
|
225
|
+
sourceType: "localhost",
|
|
226
|
+
devServerUrl,
|
|
227
|
+
rootPath: connection.rootPath ?? undefined,
|
|
228
|
+
routes: [],
|
|
229
|
+
generatedAt: connection.updatedAt ?? new Date(0).toISOString(),
|
|
230
|
+
},
|
|
231
|
+
);
|
|
232
|
+
const manifestByPath = new Map(
|
|
233
|
+
manifest.routes.map((route) => [route.path, route]),
|
|
234
|
+
);
|
|
235
|
+
const manifestById = new Map(
|
|
236
|
+
manifest.routes.map((route) => [route.id, route]),
|
|
237
|
+
);
|
|
238
|
+
const requestedRoutes: LocalhostScreenInput[] = routes?.length
|
|
239
|
+
? routes
|
|
240
|
+
: paths?.length
|
|
241
|
+
? paths.map((path) => ({ path }))
|
|
242
|
+
: manifest.routes.map((route) => ({
|
|
243
|
+
routeId: route.id,
|
|
244
|
+
path: route.path,
|
|
245
|
+
title: route.title,
|
|
246
|
+
width:
|
|
247
|
+
typeof route.metadata?.width === "number"
|
|
248
|
+
? route.metadata.width
|
|
249
|
+
: undefined,
|
|
250
|
+
height:
|
|
251
|
+
typeof route.metadata?.height === "number"
|
|
252
|
+
? route.metadata.height
|
|
253
|
+
: undefined,
|
|
254
|
+
}));
|
|
255
|
+
|
|
256
|
+
if (requestedRoutes.length === 0) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
"No routes were provided and the localhost manifest has no routes.",
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const [design] = await db
|
|
263
|
+
.select({ data: schema.designs.data })
|
|
264
|
+
.from(schema.designs)
|
|
265
|
+
.where(eq(schema.designs.id, designId))
|
|
266
|
+
.limit(1);
|
|
267
|
+
const existingFiles = await db
|
|
268
|
+
.select()
|
|
269
|
+
.from(schema.designFiles)
|
|
270
|
+
.where(eq(schema.designFiles.designId, designId));
|
|
271
|
+
const existingByFilename = new Map(
|
|
272
|
+
existingFiles.map((file) => [file.filename, file]),
|
|
273
|
+
);
|
|
274
|
+
const usedFilenames = new Set(existingFiles.map((file) => file.filename));
|
|
275
|
+
const now = new Date().toISOString();
|
|
276
|
+
const savedScreens: Array<{
|
|
277
|
+
id: string;
|
|
278
|
+
filename: string;
|
|
279
|
+
title: string;
|
|
280
|
+
path: string;
|
|
281
|
+
url: string;
|
|
282
|
+
routeId: string;
|
|
283
|
+
width: number;
|
|
284
|
+
height: number;
|
|
285
|
+
}> = [];
|
|
286
|
+
const placements: CanvasFramePlacement[] = [];
|
|
287
|
+
|
|
288
|
+
for (let index = 0; index < requestedRoutes.length; index += 1) {
|
|
289
|
+
const input = requestedRoutes[index]!;
|
|
290
|
+
const manifestRoute =
|
|
291
|
+
(input.routeId ? manifestById.get(input.routeId) : undefined) ??
|
|
292
|
+
(input.path ? manifestByPath.get(input.path) : undefined);
|
|
293
|
+
const url = routeUrl(devServerUrl, {
|
|
294
|
+
path: input.path ?? manifestRoute?.path,
|
|
295
|
+
url: input.url,
|
|
296
|
+
});
|
|
297
|
+
const path = pathFromUrl(
|
|
298
|
+
devServerUrl,
|
|
299
|
+
url,
|
|
300
|
+
input.path ?? manifestRoute?.path ?? "/",
|
|
301
|
+
);
|
|
302
|
+
const routeId =
|
|
303
|
+
input.routeId ?? manifestRoute?.id ?? makeLocalhostRouteId(path);
|
|
304
|
+
const title =
|
|
305
|
+
input.title ?? manifestRoute?.title ?? titleFromRoutePath(path);
|
|
306
|
+
const width = input.width ?? defaultWidth;
|
|
307
|
+
const height = input.height ?? defaultHeight;
|
|
308
|
+
const preferredFilename = `localhost-${slugForPath(path)}.html`;
|
|
309
|
+
const existing = existingByFilename.get(preferredFilename);
|
|
310
|
+
const filename =
|
|
311
|
+
existing?.filename ??
|
|
312
|
+
uniqueFilename(path, usedFilenames, preferredFilename);
|
|
313
|
+
const fileId = existing?.id ?? nanoid();
|
|
314
|
+
|
|
315
|
+
if (existing) {
|
|
316
|
+
await db
|
|
317
|
+
.update(schema.designFiles)
|
|
318
|
+
.set({ content: url, fileType: "html", updatedAt: now })
|
|
319
|
+
.where(eq(schema.designFiles.id, existing.id));
|
|
320
|
+
if (await hasCollabState(existing.id)) {
|
|
321
|
+
await applyText(existing.id, url, "content", "agent");
|
|
322
|
+
} else {
|
|
323
|
+
await seedFromText(existing.id, url);
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
await db.insert(schema.designFiles).values({
|
|
327
|
+
id: fileId,
|
|
328
|
+
designId,
|
|
329
|
+
filename,
|
|
330
|
+
fileType: "html",
|
|
331
|
+
content: url,
|
|
332
|
+
createdAt: now,
|
|
333
|
+
updatedAt: now,
|
|
334
|
+
});
|
|
335
|
+
await seedFromText(fileId, url);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
savedScreens.push({
|
|
339
|
+
id: fileId,
|
|
340
|
+
filename,
|
|
341
|
+
title,
|
|
342
|
+
path,
|
|
343
|
+
url,
|
|
344
|
+
routeId,
|
|
345
|
+
width,
|
|
346
|
+
height,
|
|
347
|
+
});
|
|
348
|
+
placements.push({
|
|
349
|
+
fileId,
|
|
350
|
+
filename,
|
|
351
|
+
x: input.x ?? startX + index * (width + gap),
|
|
352
|
+
y: input.y ?? startY,
|
|
353
|
+
width,
|
|
354
|
+
height,
|
|
355
|
+
z: input.z ?? index,
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const prevData = parseJson<Record<string, unknown>>(design?.data, {});
|
|
360
|
+
const mergedFrames = mergeCanvasFramePlacements({
|
|
361
|
+
existing: prevData.canvasFrames,
|
|
362
|
+
placements,
|
|
363
|
+
resolveFileId: (placement) => placement.fileId,
|
|
364
|
+
});
|
|
365
|
+
const previousMetadata = isRecord(prevData.screenMetadata)
|
|
366
|
+
? { ...prevData.screenMetadata }
|
|
367
|
+
: {};
|
|
368
|
+
const previousLocalhostScreens = isRecord(prevData.localhostScreens)
|
|
369
|
+
? { ...prevData.localhostScreens }
|
|
370
|
+
: {};
|
|
371
|
+
for (const screen of savedScreens) {
|
|
372
|
+
const metadata = {
|
|
373
|
+
sourceType: "localhost",
|
|
374
|
+
previewState: "live",
|
|
375
|
+
title: screen.title,
|
|
376
|
+
width: screen.width,
|
|
377
|
+
height: screen.height,
|
|
378
|
+
url: screen.url,
|
|
379
|
+
previewUrl: screen.url,
|
|
380
|
+
connectionId: connection.id,
|
|
381
|
+
routeId: screen.routeId,
|
|
382
|
+
path: screen.path,
|
|
383
|
+
bridgeUrl: connection.bridgeUrl ?? undefined,
|
|
384
|
+
};
|
|
385
|
+
previousMetadata[screen.id] = metadata;
|
|
386
|
+
previousLocalhostScreens[screen.id] = metadata;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
await db
|
|
390
|
+
.update(schema.designs)
|
|
391
|
+
.set({
|
|
392
|
+
data: JSON.stringify({
|
|
393
|
+
...prevData,
|
|
394
|
+
sourceMode: "localhost",
|
|
395
|
+
canvasFrames: mergedFrames.canvasFrames,
|
|
396
|
+
screenMetadata: previousMetadata,
|
|
397
|
+
localhostScreens: previousLocalhostScreens,
|
|
398
|
+
updatedAt: now,
|
|
399
|
+
}),
|
|
400
|
+
updatedAt: now,
|
|
401
|
+
})
|
|
402
|
+
.where(eq(schema.designs.id, designId));
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
designId,
|
|
406
|
+
connectionId: connection.id,
|
|
407
|
+
devServerUrl,
|
|
408
|
+
bridgeUrl: connection.bridgeUrl ?? null,
|
|
409
|
+
screenCount: savedScreens.length,
|
|
410
|
+
screens: savedScreens,
|
|
411
|
+
placedFrames: mergedFrames.placedFrames,
|
|
412
|
+
overview: true,
|
|
413
|
+
urlPath: `/design/${designId}`,
|
|
414
|
+
};
|
|
415
|
+
},
|
|
416
|
+
link: ({ result }) => {
|
|
417
|
+
if (!result || typeof result !== "object") return null;
|
|
418
|
+
const designId = (result as { designId?: string }).designId;
|
|
419
|
+
if (!designId) return null;
|
|
420
|
+
return {
|
|
421
|
+
url: designDeepLink(designId),
|
|
422
|
+
label: "Open overview",
|
|
423
|
+
view: "editor",
|
|
424
|
+
};
|
|
425
|
+
},
|
|
426
|
+
});
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
SHADER_PRESET_MAP,
|
|
6
|
+
type ShaderDescriptor,
|
|
7
|
+
type ShaderPresetName,
|
|
8
|
+
validateDescriptor,
|
|
9
|
+
} from "../shared/shader-presets.js";
|
|
10
|
+
|
|
11
|
+
// Zod v4 enum values must be an array
|
|
12
|
+
const PRESET_NAMES = Object.keys(SHADER_PRESET_MAP) as [
|
|
13
|
+
ShaderPresetName,
|
|
14
|
+
...ShaderPresetName[],
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const sourceSchema = z.object({
|
|
18
|
+
kind: z.enum(["design-file", "inline-html"]).default("design-file"),
|
|
19
|
+
designId: z.string().optional(),
|
|
20
|
+
fileId: z.string().optional(),
|
|
21
|
+
html: z.string().optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const targetSchema = z.object({
|
|
25
|
+
nodeId: z.string().optional(),
|
|
26
|
+
selector: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const descriptorSchema = z.object({
|
|
30
|
+
preset: z
|
|
31
|
+
.enum(PRESET_NAMES)
|
|
32
|
+
.describe(
|
|
33
|
+
"One of the 8 shader presets: MeshGradient, GrainGradient, Voronoi, Metaballs, Warp, GodRays, Dithering, PaperTexture.",
|
|
34
|
+
),
|
|
35
|
+
params: z
|
|
36
|
+
.record(z.string(), z.union([z.number(), z.boolean(), z.string()]))
|
|
37
|
+
.optional()
|
|
38
|
+
.default({}),
|
|
39
|
+
colors: z.array(z.string()).optional(),
|
|
40
|
+
speed: z.number().optional(),
|
|
41
|
+
frame: z.number().optional(),
|
|
42
|
+
fit: z.enum(["none", "contain", "cover"]).optional(),
|
|
43
|
+
scale: z.number().optional(),
|
|
44
|
+
rotation: z.number().optional(),
|
|
45
|
+
offsetX: z.number().optional(),
|
|
46
|
+
offsetY: z.number().optional(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Serialize a JSX prop value to a JSX-safe string.
|
|
51
|
+
* Strings become `"value"`, numbers and booleans become `{value}`.
|
|
52
|
+
*/
|
|
53
|
+
function serializePropValue(val: number | boolean | string): string {
|
|
54
|
+
if (typeof val === "string") return `"${val}"`;
|
|
55
|
+
return `{${val}}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Build the JSX snippet + import line for a descriptor.
|
|
60
|
+
* The result is copy-pasteable into a React/JSX source file.
|
|
61
|
+
*/
|
|
62
|
+
function buildJsxSnippet(
|
|
63
|
+
descriptor: ShaderDescriptor,
|
|
64
|
+
surface: "fill" | "effect",
|
|
65
|
+
): { importLine: string; jsxSnippet: string } {
|
|
66
|
+
const presetDef = SHADER_PRESET_MAP[descriptor.preset];
|
|
67
|
+
const componentName = descriptor.preset;
|
|
68
|
+
|
|
69
|
+
const zIndex = surface === "fill" ? 0 : 2;
|
|
70
|
+
const pointerEvents = surface === "effect" ? "none" : undefined;
|
|
71
|
+
|
|
72
|
+
const styleEntries: string[] = [
|
|
73
|
+
`position: "absolute"`,
|
|
74
|
+
`inset: 0`,
|
|
75
|
+
`zIndex: ${zIndex}`,
|
|
76
|
+
`width: "100%"`,
|
|
77
|
+
`height: "100%"`,
|
|
78
|
+
];
|
|
79
|
+
if (pointerEvents) {
|
|
80
|
+
styleEntries.push(`pointerEvents: "${pointerEvents}"`);
|
|
81
|
+
}
|
|
82
|
+
const styleStr = styleEntries.join(", ");
|
|
83
|
+
|
|
84
|
+
const propLines: string[] = [];
|
|
85
|
+
|
|
86
|
+
// Colors — use provided colors or fall back to preset defaults
|
|
87
|
+
if (descriptor.colors && descriptor.colors.length > 0) {
|
|
88
|
+
propLines.push(` colors={${JSON.stringify(descriptor.colors)}}`);
|
|
89
|
+
} else if (presetDef.defaultColors && presetDef.defaultColors.length > 0) {
|
|
90
|
+
propLines.push(` colors={${JSON.stringify(presetDef.defaultColors)}}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Single-color params from preset defaults when not in colors array
|
|
94
|
+
if (presetDef.defaultColorBack && !presetDef.defaultColors) {
|
|
95
|
+
propLines.push(` colorBack="${presetDef.defaultColorBack}"`);
|
|
96
|
+
}
|
|
97
|
+
if (presetDef.defaultColorFront && !presetDef.defaultColors) {
|
|
98
|
+
propLines.push(` colorFront="${presetDef.defaultColorFront}"`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Shader-specific params — merge preset defaults with descriptor overrides.
|
|
102
|
+
// ParamDef.default can be string[] for "colors" kind params; skip those here.
|
|
103
|
+
const defaultParamValues = Object.fromEntries(
|
|
104
|
+
presetDef.params
|
|
105
|
+
.filter((p) => !Array.isArray(p.default))
|
|
106
|
+
.map((p) => [p.key, p.default as number | boolean | string]),
|
|
107
|
+
);
|
|
108
|
+
const mergedParams: Record<string, number | boolean | string> = {
|
|
109
|
+
...defaultParamValues,
|
|
110
|
+
...(descriptor.params ?? {}),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
for (const [key, val] of Object.entries(mergedParams)) {
|
|
114
|
+
propLines.push(` ${key}=${serializePropValue(val)}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Universal sizing/animation params from top-level descriptor fields
|
|
118
|
+
if (descriptor.speed !== undefined)
|
|
119
|
+
propLines.push(` speed={${descriptor.speed}}`);
|
|
120
|
+
if (descriptor.frame !== undefined)
|
|
121
|
+
propLines.push(` frame={${descriptor.frame}}`);
|
|
122
|
+
if (descriptor.fit !== undefined) propLines.push(` fit="${descriptor.fit}"`);
|
|
123
|
+
if (descriptor.scale !== undefined)
|
|
124
|
+
propLines.push(` scale={${descriptor.scale}}`);
|
|
125
|
+
if (descriptor.rotation !== undefined)
|
|
126
|
+
propLines.push(` rotation={${descriptor.rotation}}`);
|
|
127
|
+
if (descriptor.offsetX !== undefined)
|
|
128
|
+
propLines.push(` offsetX={${descriptor.offsetX}}`);
|
|
129
|
+
if (descriptor.offsetY !== undefined)
|
|
130
|
+
propLines.push(` offsetY={${descriptor.offsetY}}`);
|
|
131
|
+
|
|
132
|
+
propLines.push(` style={{ ${styleStr} }}`);
|
|
133
|
+
|
|
134
|
+
const jsxSnippet = `<${componentName}\n${propLines.join("\n")}\n/>`;
|
|
135
|
+
const importLine = `import { ${componentName} } from "@paper-design/shaders-react";`;
|
|
136
|
+
|
|
137
|
+
return { importLine, jsxSnippet };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Build the vanilla <canvas> data-shader element for inline HTML artboards.
|
|
142
|
+
* The design runtime reads `data-shader` and mounts the WebGL shader into the canvas.
|
|
143
|
+
*/
|
|
144
|
+
function buildBridgeMount(
|
|
145
|
+
descriptor: ShaderDescriptor,
|
|
146
|
+
surface: "fill" | "effect",
|
|
147
|
+
): string {
|
|
148
|
+
const zIndex = surface === "fill" ? 0 : 2;
|
|
149
|
+
const pointerEvents = surface === "effect" ? "none" : "auto";
|
|
150
|
+
// Escape single quotes in the JSON so the attribute is safe in single-quoted HTML
|
|
151
|
+
const dataShader = JSON.stringify(descriptor).replace(/'/g, "'");
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
`<canvas\n` +
|
|
155
|
+
` data-shader='${dataShader}'\n` +
|
|
156
|
+
` style="position:absolute;inset:0;width:100%;height:100%;` +
|
|
157
|
+
`z-index:${zIndex};pointer-events:${pointerEvents};"\n` +
|
|
158
|
+
`></canvas>`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export default defineAction({
|
|
163
|
+
description: `
|
|
164
|
+
Apply a GPU shader effect to a design element by validating params and returning the exact code
|
|
165
|
+
to insert. This is a planning/validation action — it does not write files itself; the agent
|
|
166
|
+
uses the returned snippets to make the actual edit via edit-design or apply-visual-edit.
|
|
167
|
+
|
|
168
|
+
Available presets (8 total):
|
|
169
|
+
- MeshGradient: Smooth flowing color spots with distortion + swirl. Best for hero/card fills. Accepts up to 10 colors.
|
|
170
|
+
- GrainGradient: Noisy gradient in 7 shape modes (wave, dots, truchet, corners, ripple, blob, sphere). Up to 7 colors + colorBack.
|
|
171
|
+
- Voronoi: Animated Voronoi cells with glow and gap colors. Good for geometric/abstract backgrounds.
|
|
172
|
+
- Metaballs: Organic blobs that merge and split. Playful, great for marketing backgrounds.
|
|
173
|
+
- Warp: Domain-warped distortion in several shape patterns (checks, cross, circle, star, waves, spiral). Abstract / high-energy.
|
|
174
|
+
- GodRays: Volumetric light rays from a configurable source. Dramatic lighting effects.
|
|
175
|
+
- Dithering: Ordered dithering overlay (composites over the layer beneath). Use surface="effect".
|
|
176
|
+
- PaperTexture: Procedural paper with grain, fiber, crumples, and folds. Static by default (no animation).
|
|
177
|
+
|
|
178
|
+
surface="fill" places the shader behind content (z-index 0, pointer-events auto).
|
|
179
|
+
surface="effect" places it in front (z-index 2, pointer-events none) — use for overlays like Dithering.
|
|
180
|
+
|
|
181
|
+
Framework artboards (React/JSX files): paste the returned importLine at the top and jsxSnippet
|
|
182
|
+
inside the container element. The agent should call edit-design with the exact text.
|
|
183
|
+
|
|
184
|
+
Inline/Alpine artboards (HTML files): inject the returned bridgeMount <canvas> element at the
|
|
185
|
+
top of the container. The design runtime automatically mounts the shader.
|
|
186
|
+
`.trim(),
|
|
187
|
+
schema: z.object({
|
|
188
|
+
source: sourceSchema
|
|
189
|
+
.optional()
|
|
190
|
+
.describe(
|
|
191
|
+
"Design source for context. kind=design-file uses the SQL-backed file; kind=inline-html operates on inline HTML.",
|
|
192
|
+
),
|
|
193
|
+
target: targetSchema
|
|
194
|
+
.optional()
|
|
195
|
+
.describe(
|
|
196
|
+
"Target element by nodeId or CSS selector. When omitted, the shader targets the root artboard container.",
|
|
197
|
+
),
|
|
198
|
+
surface: z
|
|
199
|
+
.enum(["fill", "effect"])
|
|
200
|
+
.default("fill")
|
|
201
|
+
.describe(
|
|
202
|
+
'fill: shader sits behind content (z-index 0). effect: shader composites over content (z-index 2, pointer-events none). Use "effect" for Dithering.',
|
|
203
|
+
),
|
|
204
|
+
descriptor: descriptorSchema.describe(
|
|
205
|
+
"Shader preset + params to apply. Use get-shader first to see available presets and current state.",
|
|
206
|
+
),
|
|
207
|
+
}),
|
|
208
|
+
readOnly: true,
|
|
209
|
+
run: async ({ descriptor, surface, target }) => {
|
|
210
|
+
// Cast to ShaderDescriptor for the shared helpers
|
|
211
|
+
const desc: ShaderDescriptor = {
|
|
212
|
+
preset: descriptor.preset as ShaderPresetName,
|
|
213
|
+
params: descriptor.params ?? {},
|
|
214
|
+
colors: descriptor.colors,
|
|
215
|
+
speed: descriptor.speed,
|
|
216
|
+
frame: descriptor.frame,
|
|
217
|
+
fit: descriptor.fit,
|
|
218
|
+
scale: descriptor.scale,
|
|
219
|
+
rotation: descriptor.rotation,
|
|
220
|
+
offsetX: descriptor.offsetX,
|
|
221
|
+
offsetY: descriptor.offsetY,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const validation = validateDescriptor(desc);
|
|
225
|
+
if (!validation.valid) {
|
|
226
|
+
return {
|
|
227
|
+
ok: false,
|
|
228
|
+
errors: validation.errors,
|
|
229
|
+
descriptor: desc,
|
|
230
|
+
availablePresets: Object.keys(SHADER_PRESET_MAP),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const { importLine, jsxSnippet } = buildJsxSnippet(desc, surface);
|
|
235
|
+
const bridgeMount = buildBridgeMount(desc, surface);
|
|
236
|
+
|
|
237
|
+
const targetDescription = target?.nodeId
|
|
238
|
+
? `node with id="${target.nodeId}"`
|
|
239
|
+
: target?.selector
|
|
240
|
+
? `element matching selector "${target.selector}"`
|
|
241
|
+
: "the root artboard container element";
|
|
242
|
+
|
|
243
|
+
const instructions = [
|
|
244
|
+
`To apply the "${desc.preset}" shader as a ${surface} to ${targetDescription}:`,
|
|
245
|
+
"",
|
|
246
|
+
"== For React/JSX (framework artboard) ==",
|
|
247
|
+
`1. Add this import near the top of the component file (if not already present):`,
|
|
248
|
+
` ${importLine}`,
|
|
249
|
+
`2. Inside ${targetDescription}, insert as the ${surface === "fill" ? "first" : "last"} child:`,
|
|
250
|
+
jsxSnippet
|
|
251
|
+
.split("\n")
|
|
252
|
+
.map((l) => ` ${l}`)
|
|
253
|
+
.join("\n"),
|
|
254
|
+
` The parent element must have position:relative (or position:absolute/fixed) for the absolute positioning to work.`,
|
|
255
|
+
"",
|
|
256
|
+
"== For inline HTML artboard ==",
|
|
257
|
+
`1. Inside ${targetDescription}, insert as the ${surface === "fill" ? "first" : "last"} child:`,
|
|
258
|
+
bridgeMount
|
|
259
|
+
.split("\n")
|
|
260
|
+
.map((l) => ` ${l}`)
|
|
261
|
+
.join("\n"),
|
|
262
|
+
` The parent element must have position:relative for the absolute positioning to work.`,
|
|
263
|
+
"",
|
|
264
|
+
"Use edit-design for JSX files (structural insert) or apply-visual-edit for inline HTML.",
|
|
265
|
+
].join("\n");
|
|
266
|
+
|
|
267
|
+
return {
|
|
268
|
+
ok: true,
|
|
269
|
+
descriptor: desc,
|
|
270
|
+
surface,
|
|
271
|
+
importLine,
|
|
272
|
+
jsxSnippet,
|
|
273
|
+
bridgeMount,
|
|
274
|
+
instructions,
|
|
275
|
+
};
|
|
276
|
+
},
|
|
277
|
+
});
|