@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.
Files changed (346) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +26 -0
  3. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +41 -0
  4. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +51 -0
  5. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +50 -0
  6. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +49 -0
  7. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +44 -0
  8. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +46 -0
  9. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +44 -0
  10. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +48 -0
  11. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +36 -0
  12. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +38 -0
  13. package/corpus/core/docs/content/template-design.mdx +45 -0
  14. package/corpus/core/package.json +2 -1
  15. package/corpus/core/src/agent/engine/index.ts +1 -0
  16. package/corpus/core/src/agent/engine/registry.ts +40 -0
  17. package/corpus/core/src/agent/production-agent.ts +6 -2
  18. package/corpus/core/src/cli/design-connect.ts +490 -0
  19. package/corpus/core/src/cli/index.ts +16 -0
  20. package/corpus/core/src/cli/skills.ts +126 -3
  21. package/corpus/core/src/client/AssistantChat.tsx +43 -7
  22. package/corpus/core/src/client/agent-chat.ts +27 -3
  23. package/corpus/core/src/client/blocks/library/MermaidBlock.tsx +1 -1
  24. package/corpus/core/src/client/blocks/library/diagram.tsx +2 -2
  25. package/corpus/core/src/client/blocks/library/wireframe.tsx +1 -1
  26. package/corpus/core/src/client/chat/message-components.tsx +1 -1
  27. package/corpus/core/src/client/chat-model-groups.ts +1 -2
  28. package/corpus/core/src/client/components/LiveCursorOverlay.tsx +33 -66
  29. package/corpus/core/src/client/composer/TiptapComposer.tsx +99 -24
  30. package/corpus/core/src/client/composer/useVoiceDictation.ts +429 -260
  31. package/corpus/core/src/client/frame-protocol.ts +17 -2
  32. package/corpus/core/src/client/rich-markdown-editor/RegistryBlockNode.tsx +2 -2
  33. package/corpus/core/src/client/sharing/ShareButton.tsx +20 -15
  34. package/corpus/core/src/client/use-agent-engine-configured.ts +80 -28
  35. package/corpus/core/src/server/transcribe-voice.ts +57 -22
  36. package/corpus/core/src/shared/mcp-embed-headers.ts +23 -1
  37. package/corpus/core/src/styles/agent-native.css +56 -7
  38. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
  39. package/corpus/core/src/vite/client.ts +2 -0
  40. package/corpus/core/src/voice/index.ts +18 -0
  41. package/corpus/core/src/voice/voice-cleanup-prompt.ts +38 -0
  42. package/corpus/core/src/voice/voice-context.ts +217 -0
  43. package/corpus/core/src/voice/voice-replacements.ts +75 -0
  44. package/corpus/templates/analytics/AGENTS.md +4 -3
  45. package/corpus/templates/analytics/actions/list-session-recordings.ts +6 -1
  46. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +557 -400
  47. package/corpus/templates/analytics/app/global.css +0 -4
  48. package/corpus/templates/analytics/app/i18n/zh-TW.ts +2 -0
  49. package/corpus/templates/analytics/app/i18n-data.ts +20 -0
  50. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +33 -8
  51. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +7 -0
  52. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +7 -0
  53. package/corpus/templates/analytics/changelog/2026-06-28-ask-tab-now-matches-the-darker-app-background.md +6 -0
  54. package/corpus/templates/analytics/changelog/2026-06-28-dashboard-email-reports-now-use-cleaner-light-mode-snapshots.md +6 -0
  55. package/corpus/templates/analytics/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  56. package/corpus/templates/analytics/changelog/2026-06-28-sessions-now-show-only-signed-in-recordings-with-playable-re.md +6 -0
  57. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +3 -3
  58. package/corpus/templates/analytics/server/lib/dashboard-report.ts +29 -7
  59. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +5 -2
  60. package/corpus/templates/analytics/server/lib/session-replay.ts +24 -4
  61. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -4
  62. package/corpus/templates/assets/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  63. package/corpus/templates/brain/app/components/layout/Layout.tsx +29 -2
  64. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +131 -42
  65. package/corpus/templates/brain/app/i18n/zh-TW.ts +2 -0
  66. package/corpus/templates/brain/app/i18n-data.ts +20 -0
  67. package/corpus/templates/brain/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  68. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +29 -1
  69. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +417 -299
  70. package/corpus/templates/calendar/app/i18n/zh-TW.ts +2 -0
  71. package/corpus/templates/calendar/app/i18n-data.ts +20 -0
  72. package/corpus/templates/calendar/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  73. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +6 -19
  74. package/corpus/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  75. package/corpus/templates/clips/AGENTS.md +4 -0
  76. package/corpus/templates/clips/actions/cleanup-transcript.ts +39 -9
  77. package/corpus/templates/clips/actions/finalize-meeting.ts +81 -12
  78. package/corpus/templates/clips/actions/lib/agents-md-context.ts +4 -1
  79. package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -3
  80. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +1 -1
  81. package/corpus/templates/clips/app/components/player/share-dialog.tsx +2 -2
  82. package/corpus/templates/clips/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
  83. package/corpus/templates/clips/desktop/src/lib/personal-vocabulary.ts +13 -8
  84. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +60 -1
  85. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +1 -1
  86. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +1 -1
  87. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -5
  88. package/corpus/templates/content/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  89. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +137 -0
  90. package/corpus/templates/design/AGENTS.md +95 -5
  91. package/corpus/templates/design/DESIGN.md +82 -0
  92. package/corpus/templates/design/DEVELOPING.md +43 -0
  93. package/corpus/templates/design/actions/add-localhost-screens.ts +426 -0
  94. package/corpus/templates/design/actions/apply-shader.ts +277 -0
  95. package/corpus/templates/design/actions/apply-visual-edit.ts +303 -0
  96. package/corpus/templates/design/actions/connect-localhost.ts +183 -0
  97. package/corpus/templates/design/actions/edit-design.ts +195 -42
  98. package/corpus/templates/design/actions/generate-design.ts +71 -1
  99. package/corpus/templates/design/actions/generate-screens.ts +193 -0
  100. package/corpus/templates/design/actions/get-code-layer-projection.ts +191 -0
  101. package/corpus/templates/design/actions/get-shader.ts +119 -0
  102. package/corpus/templates/design/actions/list-localhost-connections.ts +95 -0
  103. package/corpus/templates/design/actions/navigate.ts +55 -2
  104. package/corpus/templates/design/actions/view-screen.ts +68 -1
  105. package/corpus/templates/design/agent-native.app-skill.json +10 -0
  106. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +761 -0
  107. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +2057 -103
  108. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +74 -38
  109. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +399 -0
  110. package/corpus/templates/design/app/components/design/EditPanel.tsx +3185 -439
  111. package/corpus/templates/design/app/components/design/LayersPanel.tsx +1124 -0
  112. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +3116 -147
  113. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +700 -13
  114. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +107 -63
  115. package/corpus/templates/design/app/components/design/index.ts +0 -5
  116. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +286 -0
  117. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +699 -0
  118. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +461 -0
  119. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +166 -0
  120. package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +1420 -0
  121. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +420 -0
  122. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +215 -0
  123. package/corpus/templates/design/app/components/design/inspector/ShaderControls.stories.tsx +71 -0
  124. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +518 -0
  125. package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +106 -0
  126. package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +361 -0
  127. package/corpus/templates/design/app/components/design/inspector/index.ts +60 -0
  128. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +201 -0
  129. package/corpus/templates/design/app/components/design/types.ts +23 -0
  130. package/corpus/templates/design/app/components/layout/Layout.tsx +24 -13
  131. package/corpus/templates/design/app/components/layout/Sidebar.tsx +4 -4
  132. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +474 -36
  133. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +22 -5
  134. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +24 -11
  135. package/corpus/templates/design/app/global.css +121 -3
  136. package/corpus/templates/design/app/hooks/use-agent-generating.ts +4 -0
  137. package/corpus/templates/design/app/hooks/use-navigation-state.ts +130 -8
  138. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +329 -0
  139. package/corpus/templates/design/app/i18n/zh-TW.ts +264 -0
  140. package/corpus/templates/design/app/i18n-data.ts +2790 -1
  141. package/corpus/templates/design/app/pages/DesignEditor.tsx +5108 -851
  142. package/corpus/templates/design/changelog/2026-06-28-annotate-mode-now-combines-drawing-and-comment-pins-in-one-c.md +6 -0
  143. package/corpus/templates/design/changelog/2026-06-28-design-editor-panel-borders-are-cleaner-so-the-canvas-edge-s.md +6 -0
  144. package/corpus/templates/design/changelog/2026-06-28-drawing-prompts-now-reliably-open-and-submit-to-the-design-a.md +6 -0
  145. package/corpus/templates/design/changelog/2026-06-28-editor-app-menus-now-use-an-outline-agent-native-mark-with-b.md +6 -0
  146. package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +6 -0
  147. package/corpus/templates/design/changelog/2026-06-28-layer-selection-now-updates-chat-context-without-opening-the.md +6 -0
  148. package/corpus/templates/design/changelog/2026-06-28-layer-selections-now-preserve-hidden-and-locked-canvas-state.md +6 -0
  149. package/corpus/templates/design/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  150. package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +6 -0
  151. package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +6 -0
  152. package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-handles-zooming-drawing-opening-and-dele.md +6 -0
  153. package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-selects-screens-on-click-and-opens-them-.md +6 -0
  154. package/corpus/templates/design/changelog/2026-06-28-screen-overview-zooms-into-the-active-screen-with-smoother-e.md +6 -0
  155. package/corpus/templates/design/changelog/2026-06-28-selected-element-chips-in-chat-are-shorter-and-easier-to-sca.md +6 -0
  156. package/corpus/templates/design/changelog/2026-06-28-the-design-editor-toolbar-is-more-compact-and-the-app-menu-m.md +6 -0
  157. package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-at-the-top-of-the-design-sidebar-instead-of-.md +6 -0
  158. package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-in-the-right-inspector-beside-the-design-tab.md +6 -0
  159. package/corpus/templates/design/changelog/2026-06-29-active-sidebar-rows-use-soft-highlights.md +6 -0
  160. package/corpus/templates/design/changelog/2026-06-29-agents-can-now-open-screen-overview-focus-screens-and-add.md +6 -0
  161. package/corpus/templates/design/changelog/2026-06-29-design-agents-now-edit-broad-copy-changes-more-reliably.md +6 -0
  162. package/corpus/templates/design/changelog/2026-06-29-design-editor-extensions-now-run-inline-beside-the-artboard.md +6 -0
  163. package/corpus/templates/design/changelog/2026-06-29-sidebars-use-darker-chrome-and-chat-warns-immediately.md +6 -0
  164. package/corpus/templates/design/changelog/2026-06-29-visual-edit-can-open-localhost-routes-as-url-backed-screens.md +6 -0
  165. package/corpus/templates/design/package.json +1 -0
  166. package/corpus/templates/design/server/db/schema.ts +26 -0
  167. package/corpus/templates/design/server/lib/design-snapshot.ts +1 -1
  168. package/corpus/templates/design/server/plugins/agent-chat.ts +3 -0
  169. package/corpus/templates/design/shared/api.ts +27 -0
  170. package/corpus/templates/design/shared/canvas-frames.ts +103 -0
  171. package/corpus/templates/design/shared/canvas-math.ts +1058 -0
  172. package/corpus/templates/design/shared/code-layer.ts +2212 -0
  173. package/corpus/templates/design/shared/color-utils.ts +180 -0
  174. package/corpus/templates/design/shared/generation-session.ts +61 -0
  175. package/corpus/templates/design/shared/shader-presets.ts +782 -0
  176. package/corpus/templates/design/shared/shader-safety.ts +137 -0
  177. package/corpus/templates/design/shared/source-mode.ts +237 -0
  178. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +149 -6
  179. package/corpus/templates/forms/app/i18n/ar-SA.ts +2 -0
  180. package/corpus/templates/forms/app/i18n/de-DE.ts +2 -0
  181. package/corpus/templates/forms/app/i18n/en-US.ts +2 -0
  182. package/corpus/templates/forms/app/i18n/es-ES.ts +2 -0
  183. package/corpus/templates/forms/app/i18n/fr-FR.ts +2 -0
  184. package/corpus/templates/forms/app/i18n/hi-IN.ts +2 -0
  185. package/corpus/templates/forms/app/i18n/ja-JP.ts +2 -0
  186. package/corpus/templates/forms/app/i18n/ko-KR.ts +2 -0
  187. package/corpus/templates/forms/app/i18n/pt-BR.ts +2 -0
  188. package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
  189. package/corpus/templates/forms/app/i18n/zh-TW.ts +2 -0
  190. package/corpus/templates/forms/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  191. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +2 -2
  192. package/corpus/templates/macros/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
  193. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +408 -247
  194. package/corpus/templates/mail/app/i18n/ar-SA.ts +4 -0
  195. package/corpus/templates/mail/app/i18n/de-DE.ts +4 -0
  196. package/corpus/templates/mail/app/i18n/en-US.ts +4 -0
  197. package/corpus/templates/mail/app/i18n/es-ES.ts +4 -0
  198. package/corpus/templates/mail/app/i18n/fr-FR.ts +4 -0
  199. package/corpus/templates/mail/app/i18n/hi-IN.ts +4 -0
  200. package/corpus/templates/mail/app/i18n/ja-JP.ts +4 -0
  201. package/corpus/templates/mail/app/i18n/ko-KR.ts +4 -0
  202. package/corpus/templates/mail/app/i18n/pt-BR.ts +4 -0
  203. package/corpus/templates/mail/app/i18n/zh-CN.ts +4 -0
  204. package/corpus/templates/mail/app/i18n/zh-TW.ts +4 -0
  205. package/corpus/templates/mail/changelog/2026-06-28-pinned-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  206. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +6 -8
  207. package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +1 -1
  208. package/corpus/templates/plan/app/global.css +61 -78
  209. package/corpus/templates/plan/app/pages/PlansPage.tsx +8 -0
  210. package/corpus/templates/plan/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  211. package/corpus/templates/plan/changelog/2026-06-28-plans-use-the-shared-neutral-light-and-dark-theme.md +6 -0
  212. package/corpus/templates/plan/changelog/2026-06-28-sending-open-feedback-from-a-plan-now-opens-the-inline-plan-.md +6 -0
  213. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +5 -5
  214. package/corpus/templates/slides/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  215. package/corpus/templates/videos/app/components/Sidebar.tsx +2 -2
  216. package/corpus/templates/videos/app/components/layout/Layout.tsx +29 -2
  217. package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +113 -30
  218. package/corpus/templates/videos/app/i18n/ar-SA.ts +2 -0
  219. package/corpus/templates/videos/app/i18n/de-DE.ts +2 -0
  220. package/corpus/templates/videos/app/i18n/en-US.ts +2 -0
  221. package/corpus/templates/videos/app/i18n/es-ES.ts +2 -0
  222. package/corpus/templates/videos/app/i18n/fr-FR.ts +2 -0
  223. package/corpus/templates/videos/app/i18n/hi-IN.ts +2 -0
  224. package/corpus/templates/videos/app/i18n/ja-JP.ts +2 -0
  225. package/corpus/templates/videos/app/i18n/ko-KR.ts +2 -0
  226. package/corpus/templates/videos/app/i18n/pt-BR.ts +2 -0
  227. package/corpus/templates/videos/app/i18n/zh-CN.ts +2 -0
  228. package/corpus/templates/videos/app/i18n/zh-TW.ts +2 -0
  229. package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  230. package/dist/agent/engine/index.d.ts +1 -1
  231. package/dist/agent/engine/index.d.ts.map +1 -1
  232. package/dist/agent/engine/index.js +1 -1
  233. package/dist/agent/engine/index.js.map +1 -1
  234. package/dist/agent/engine/registry.d.ts +9 -0
  235. package/dist/agent/engine/registry.d.ts.map +1 -1
  236. package/dist/agent/engine/registry.js +37 -0
  237. package/dist/agent/engine/registry.js.map +1 -1
  238. package/dist/agent/production-agent.d.ts.map +1 -1
  239. package/dist/agent/production-agent.js +4 -3
  240. package/dist/agent/production-agent.js.map +1 -1
  241. package/dist/cli/design-connect.d.ts +52 -0
  242. package/dist/cli/design-connect.d.ts.map +1 -0
  243. package/dist/cli/design-connect.js +409 -0
  244. package/dist/cli/design-connect.js.map +1 -0
  245. package/dist/cli/index.js +15 -0
  246. package/dist/cli/index.js.map +1 -1
  247. package/dist/cli/skills.d.ts +3 -0
  248. package/dist/cli/skills.d.ts.map +1 -1
  249. package/dist/cli/skills.js +121 -3
  250. package/dist/cli/skills.js.map +1 -1
  251. package/dist/client/AssistantChat.d.ts.map +1 -1
  252. package/dist/client/AssistantChat.js +38 -8
  253. package/dist/client/AssistantChat.js.map +1 -1
  254. package/dist/client/agent-chat.d.ts +6 -0
  255. package/dist/client/agent-chat.d.ts.map +1 -1
  256. package/dist/client/agent-chat.js +17 -3
  257. package/dist/client/agent-chat.js.map +1 -1
  258. package/dist/client/blocks/library/MermaidBlock.js +1 -1
  259. package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
  260. package/dist/client/blocks/library/diagram.js +1 -1
  261. package/dist/client/blocks/library/diagram.js.map +1 -1
  262. package/dist/client/blocks/library/wireframe.js +1 -1
  263. package/dist/client/blocks/library/wireframe.js.map +1 -1
  264. package/dist/client/chat/message-components.js +1 -1
  265. package/dist/client/chat/message-components.js.map +1 -1
  266. package/dist/client/chat-model-groups.d.ts.map +1 -1
  267. package/dist/client/chat-model-groups.js +1 -2
  268. package/dist/client/chat-model-groups.js.map +1 -1
  269. package/dist/client/components/LiveCursorOverlay.d.ts +1 -2
  270. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
  271. package/dist/client/components/LiveCursorOverlay.js +19 -40
  272. package/dist/client/components/LiveCursorOverlay.js.map +1 -1
  273. package/dist/client/composer/TiptapComposer.d.ts +3 -1
  274. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  275. package/dist/client/composer/TiptapComposer.js +86 -24
  276. package/dist/client/composer/TiptapComposer.js.map +1 -1
  277. package/dist/client/composer/useVoiceDictation.d.ts +9 -0
  278. package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
  279. package/dist/client/composer/useVoiceDictation.js +154 -32
  280. package/dist/client/composer/useVoiceDictation.js.map +1 -1
  281. package/dist/client/frame-protocol.d.ts +17 -2
  282. package/dist/client/frame-protocol.d.ts.map +1 -1
  283. package/dist/client/frame-protocol.js.map +1 -1
  284. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +2 -2
  285. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
  286. package/dist/client/sharing/ShareButton.d.ts +2 -0
  287. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  288. package/dist/client/sharing/ShareButton.js +7 -6
  289. package/dist/client/sharing/ShareButton.js.map +1 -1
  290. package/dist/client/use-agent-engine-configured.d.ts +5 -0
  291. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  292. package/dist/client/use-agent-engine-configured.js +51 -25
  293. package/dist/client/use-agent-engine-configured.js.map +1 -1
  294. package/dist/collab/awareness.d.ts +2 -2
  295. package/dist/collab/awareness.d.ts.map +1 -1
  296. package/dist/collab/routes.d.ts +1 -1
  297. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  298. package/dist/notifications/routes.d.ts +3 -3
  299. package/dist/observability/routes.d.ts +8 -8
  300. package/dist/progress/routes.d.ts +1 -1
  301. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  302. package/dist/server/transcribe-voice.d.ts.map +1 -1
  303. package/dist/server/transcribe-voice.js +40 -24
  304. package/dist/server/transcribe-voice.js.map +1 -1
  305. package/dist/shared/mcp-embed-headers.d.ts +1 -0
  306. package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
  307. package/dist/shared/mcp-embed-headers.js +20 -1
  308. package/dist/shared/mcp-embed-headers.js.map +1 -1
  309. package/dist/styles/agent-native.css +56 -7
  310. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
  311. package/dist/vite/client.d.ts.map +1 -1
  312. package/dist/vite/client.js +2 -0
  313. package/dist/vite/client.js.map +1 -1
  314. package/dist/voice/index.d.ts +5 -0
  315. package/dist/voice/index.d.ts.map +1 -0
  316. package/dist/voice/index.js +4 -0
  317. package/dist/voice/index.js.map +1 -0
  318. package/dist/voice/voice-cleanup-prompt.d.ts +6 -0
  319. package/dist/voice/voice-cleanup-prompt.d.ts.map +1 -0
  320. package/dist/voice/voice-cleanup-prompt.js +26 -0
  321. package/dist/voice/voice-cleanup-prompt.js.map +1 -0
  322. package/dist/voice/voice-context.d.ts +25 -0
  323. package/dist/voice/voice-context.d.ts.map +1 -0
  324. package/dist/voice/voice-context.js +156 -0
  325. package/dist/voice/voice-context.js.map +1 -0
  326. package/dist/voice/voice-replacements.d.ts +4 -0
  327. package/dist/voice/voice-replacements.d.ts.map +1 -0
  328. package/dist/voice/voice-replacements.js +52 -0
  329. package/dist/voice/voice-replacements.js.map +1 -0
  330. package/docs/content/locales/ar-SA/template-design.mdx +41 -0
  331. package/docs/content/locales/de-DE/template-design.mdx +51 -0
  332. package/docs/content/locales/es-ES/template-design.mdx +50 -0
  333. package/docs/content/locales/fr-FR/template-design.mdx +49 -0
  334. package/docs/content/locales/hi-IN/template-design.mdx +44 -0
  335. package/docs/content/locales/ja-JP/template-design.mdx +46 -0
  336. package/docs/content/locales/ko-KR/template-design.mdx +44 -0
  337. package/docs/content/locales/pt-BR/template-design.mdx +48 -0
  338. package/docs/content/locales/zh-CN/template-design.mdx +36 -0
  339. package/docs/content/locales/zh-TW/template-design.mdx +38 -0
  340. package/docs/content/template-design.mdx +45 -0
  341. package/package.json +2 -1
  342. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
  343. package/corpus/templates/design/app/components/design/DesignToolbar.tsx +0 -339
  344. package/corpus/templates/design/app/components/design/PresentMode.tsx +0 -68
  345. package/corpus/templates/design/app/components/design/ViewportTabs.tsx +0 -149
  346. 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, "&#39;");
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
+ });