@agent-native/core 0.102.1 → 0.103.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 (295) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +15 -0
  3. package/corpus/core/docs/content/local-file-mode.mdx +99 -489
  4. package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  5. package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
  6. package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  7. package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
  8. package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  9. package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
  10. package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  11. package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
  12. package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  13. package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
  14. package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  15. package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
  16. package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  17. package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
  18. package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  19. package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
  20. package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  21. package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
  22. package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  23. package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
  24. package/corpus/core/docs/content/template-content.mdx +26 -42
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/cli/content-local.ts +86 -18
  27. package/corpus/core/src/cli/index.ts +2 -2
  28. package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
  29. package/corpus/core/src/cli/skills.ts +47 -13
  30. package/corpus/core/src/client/AgentPanel.tsx +7 -0
  31. package/corpus/core/src/client/AssistantChat.tsx +7 -0
  32. package/corpus/core/src/client/agent-chat-adapter.ts +7 -1
  33. package/corpus/core/src/client/chat/message-components.tsx +17 -3
  34. package/corpus/core/src/client/composer/ComposerPlusMenu.tsx +96 -6
  35. package/corpus/core/src/client/composer/asset-picker-url.ts +80 -0
  36. package/corpus/core/src/client/error-format.ts +24 -0
  37. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +8 -1
  38. package/corpus/core/src/local-artifacts/index.ts +65 -0
  39. package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
  40. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
  41. package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
  42. package/corpus/templates/assets/app/i18n-data.ts +215 -0
  43. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
  44. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
  45. package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
  46. package/corpus/templates/assets/app/routes/library.tsx +69 -0
  47. package/corpus/templates/assets/changelog/2026-07-15-fixed-top-level-image-picker-selections-returning-to-the-com.md +6 -0
  48. package/corpus/templates/clips/actions/disconnect-calendar.ts +57 -9
  49. package/corpus/templates/clips/actions/generate-workflow.ts +102 -59
  50. package/corpus/templates/clips/actions/get-recording-player-data.ts +64 -1
  51. package/corpus/templates/clips/actions/regenerate-summary.ts +7 -0
  52. package/corpus/templates/clips/actions/regenerate-title.ts +29 -11
  53. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +34 -13
  54. package/corpus/templates/clips/app/components/editor/thumbnail-picker.tsx +87 -9
  55. package/corpus/templates/clips/app/components/editor/trim-handles.tsx +15 -0
  56. package/corpus/templates/clips/app/components/editor/waveform.tsx +21 -8
  57. package/corpus/templates/clips/app/components/library/folder-tree.tsx +52 -0
  58. package/corpus/templates/clips/app/components/player/settings-panel.tsx +116 -58
  59. package/corpus/templates/clips/app/components/player/share-dialog.tsx +43 -37
  60. package/corpus/templates/clips/app/components/player/video-player.tsx +50 -2
  61. package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
  62. package/corpus/templates/clips/app/i18n/ar-SA.ts +5 -2
  63. package/corpus/templates/clips/app/i18n/de-DE.ts +6 -2
  64. package/corpus/templates/clips/app/i18n/en-US.ts +5 -2
  65. package/corpus/templates/clips/app/i18n/es-ES.ts +6 -2
  66. package/corpus/templates/clips/app/i18n/fr-FR.ts +5 -2
  67. package/corpus/templates/clips/app/i18n/hi-IN.ts +5 -2
  68. package/corpus/templates/clips/app/i18n/ja-JP.ts +6 -1
  69. package/corpus/templates/clips/app/i18n/ko-KR.ts +5 -2
  70. package/corpus/templates/clips/app/i18n/pt-BR.ts +6 -2
  71. package/corpus/templates/clips/app/i18n/zh-CN.ts +5 -2
  72. package/corpus/templates/clips/app/i18n/zh-TW.ts +5 -2
  73. package/corpus/templates/clips/app/lib/timestamp-mapping.ts +14 -0
  74. package/corpus/templates/clips/app/routes/_app.library._index.tsx +1 -2
  75. package/corpus/templates/clips/app/routes/_index.tsx +1 -2
  76. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +98 -32
  77. package/corpus/templates/clips/app/routes/share.$shareId.tsx +4 -1
  78. package/corpus/templates/clips/changelog/2026-07-15-ai-workflow-generation-now-stays-visible-avoids-duplicate-re.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-07-15-clip-playback-and-cta-controls-remain-interactive-through-ed.md +6 -0
  80. package/corpus/templates/clips/changelog/2026-07-15-clip-titles-no-longer-describe-the-product-as-a-loom-alterna.md +6 -0
  81. package/corpus/templates/clips/changelog/2026-07-15-description-regeneration-now-opens-the-agent-chat-so-progres.md +6 -0
  82. package/corpus/templates/clips/changelog/2026-07-15-direct-recording-urls-now-respect-share-link-and-password-ac.md +6 -0
  83. package/corpus/templates/clips/changelog/2026-07-15-disconnecting-a-calendar-now-clears-unrecorded-synced-meetin.md +6 -0
  84. package/corpus/templates/clips/changelog/2026-07-15-folders-now-expose-rename-and-delete-actions-directly-in-the.md +6 -0
  85. package/corpus/templates/clips/changelog/2026-07-15-recording-pages-now-explain-sign-in-access-and-avoid-showing.md +6 -0
  86. package/corpus/templates/clips/changelog/2026-07-15-the-clips-extension-quick-actions-toolbar-can-be-moved-out-o.md +6 -0
  87. package/corpus/templates/clips/changelog/2026-07-15-the-editor-now-skips-cut-sections-during-preview-and-keeps-t.md +6 -0
  88. package/corpus/templates/clips/changelog/2026-07-15-thumbnail-capture-now-updates-previews-while-scrubbing-and-l.md +6 -0
  89. package/corpus/templates/clips/changelog/2026-07-15-windows-permission-links-open-the-matching-settings-page.md +6 -0
  90. package/corpus/templates/clips/chrome-extension/src/content-script.ts +117 -2
  91. package/corpus/templates/clips/chrome-extension/src/overlay.ts +27 -0
  92. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +1 -1
  93. package/corpus/templates/clips/server/lib/calendar-event-meetings.ts +139 -101
  94. package/corpus/templates/clips/server/lib/recording-page-access.ts +35 -0
  95. package/corpus/templates/clips/server/lib/share-password.ts +6 -3
  96. package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
  97. package/corpus/templates/content/AGENTS.md +25 -30
  98. package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
  99. package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
  100. package/corpus/templates/content/actions/_content-files.ts +338 -0
  101. package/corpus/templates/content/actions/_content-space-access.ts +135 -0
  102. package/corpus/templates/content/actions/_content-spaces.ts +640 -0
  103. package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
  104. package/corpus/templates/content/actions/_database-utils.ts +7 -1
  105. package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
  106. package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
  107. package/corpus/templates/content/actions/_property-utils.ts +9 -4
  108. package/corpus/templates/content/actions/add-database-item.ts +108 -65
  109. package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
  110. package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
  111. package/corpus/templates/content/actions/create-content-database.ts +129 -3
  112. package/corpus/templates/content/actions/create-document.ts +65 -55
  113. package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
  114. package/corpus/templates/content/actions/delete-content-database.ts +3 -0
  115. package/corpus/templates/content/actions/delete-document.ts +7 -11
  116. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
  117. package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
  118. package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
  119. package/corpus/templates/content/actions/edit-document.ts +2 -28
  120. package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
  121. package/corpus/templates/content/actions/export-content-source.ts +47 -9
  122. package/corpus/templates/content/actions/get-document.ts +0 -14
  123. package/corpus/templates/content/actions/import-content-source.ts +43 -3
  124. package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
  125. package/corpus/templates/content/actions/list-documents.ts +12 -9
  126. package/corpus/templates/content/actions/move-document.ts +3 -14
  127. package/corpus/templates/content/actions/pull-document.ts +0 -23
  128. package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
  129. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
  130. package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
  131. package/corpus/templates/content/actions/search-documents.ts +0 -34
  132. package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
  133. package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
  134. package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
  135. package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
  136. package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
  137. package/corpus/templates/content/actions/update-document.ts +0 -15
  138. package/corpus/templates/content/actions/view-screen.ts +2 -50
  139. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
  140. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
  141. package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
  142. package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
  143. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  144. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
  145. package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
  146. package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
  147. package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
  148. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
  149. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
  150. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
  151. package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
  152. package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
  153. package/corpus/templates/content/app/i18n-data.ts +75 -0
  154. package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
  155. package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
  156. package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
  157. package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
  158. package/corpus/templates/content/parity/matrix.md +2 -1
  159. package/corpus/templates/content/parity/matrix.ts +37 -1
  160. package/corpus/templates/content/scripts/dev-database.mjs +7 -5
  161. package/corpus/templates/content/server/db/schema.ts +113 -40
  162. package/corpus/templates/content/server/lib/document-context.ts +15 -1
  163. package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
  164. package/corpus/templates/content/server/plugins/db.ts +153 -0
  165. package/corpus/templates/content/shared/api.ts +17 -3
  166. package/corpus/templates/content/shared/builder-mdx.ts +163 -16
  167. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
  168. package/corpus/templates/design/AGENTS.md +6 -1
  169. package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
  170. package/corpus/templates/design/actions/list-design-templates.ts +69 -21
  171. package/corpus/templates/design/actions/navigate.ts +2 -2
  172. package/corpus/templates/design/actions/view-screen.ts +15 -2
  173. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  174. package/corpus/templates/design/app/components/design/design-canvas/external-preview.ts +7 -3
  175. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
  176. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
  177. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
  178. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
  179. package/corpus/templates/design/app/i18n-data.ts +21 -2
  180. package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
  181. package/corpus/templates/design/app/pages/Index.tsx +195 -24
  182. package/corpus/templates/design/app/pages/Templates.tsx +144 -55
  183. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
  184. package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
  185. package/corpus/templates/design/changelog/2026-07-15-prototype-print-and-download-buttons-now-work-in-preview.md +6 -0
  186. package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
  187. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  188. package/corpus/templates/mail/AGENTS.md +23 -18
  189. package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
  190. package/corpus/templates/mail/actions/mark-read.ts +131 -20
  191. package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
  192. package/corpus/templates/mail/actions/move-email.ts +30 -24
  193. package/corpus/templates/mail/actions/send-email.ts +73 -68
  194. package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
  195. package/corpus/templates/mail/server/handlers/emails.ts +218 -209
  196. package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
  197. package/corpus/templates/mail/server/lib/email-state.ts +198 -113
  198. package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
  199. package/corpus/templates/mail/server/lib/jobs.ts +69 -74
  200. package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
  201. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  202. package/corpus/templates/slides/actions/extract-pdf.ts +1 -1
  203. package/corpus/templates/slides/actions/import-file.ts +4 -1
  204. package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +1 -1
  205. package/corpus/templates/slides/app/components/editor/ImageSearchPanel.tsx +9 -12
  206. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +29 -21
  207. package/corpus/templates/slides/app/i18n/ar-SA.ts +1 -0
  208. package/corpus/templates/slides/app/i18n/de-DE.ts +1 -0
  209. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  210. package/corpus/templates/slides/app/i18n/es-ES.ts +1 -0
  211. package/corpus/templates/slides/app/i18n/fr-FR.ts +1 -0
  212. package/corpus/templates/slides/app/i18n/hi-IN.ts +1 -0
  213. package/corpus/templates/slides/app/i18n/ja-JP.ts +1 -0
  214. package/corpus/templates/slides/app/i18n/ko-KR.ts +1 -0
  215. package/corpus/templates/slides/app/i18n/pt-BR.ts +1 -0
  216. package/corpus/templates/slides/app/i18n/zh-CN.ts +1 -0
  217. package/corpus/templates/slides/app/i18n/zh-TW.ts +1 -0
  218. package/corpus/templates/slides/changelog/2026-07-15-image-search-and-generation-controls-now-use-the-registered-.md +6 -0
  219. package/corpus/templates/slides/changelog/2026-07-15-pdf-deck-references-can-be-imported-for-slide-inspiration-ag.md +6 -0
  220. package/corpus/templates/slides/changelog/2026-07-15-stable-style-inspector-dock.md +6 -0
  221. package/dist/cli/content-local.d.ts +1 -0
  222. package/dist/cli/content-local.d.ts.map +1 -1
  223. package/dist/cli/content-local.js +75 -17
  224. package/dist/cli/content-local.js.map +1 -1
  225. package/dist/cli/index.js +2 -2
  226. package/dist/cli/index.js.map +1 -1
  227. package/dist/cli/skills-content/content-skill.d.ts +1 -1
  228. package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
  229. package/dist/cli/skills-content/content-skill.js +36 -25
  230. package/dist/cli/skills-content/content-skill.js.map +1 -1
  231. package/dist/cli/skills.d.ts.map +1 -1
  232. package/dist/cli/skills.js +47 -14
  233. package/dist/cli/skills.js.map +1 -1
  234. package/dist/client/AgentPanel.d.ts.map +1 -1
  235. package/dist/client/AgentPanel.js +5 -0
  236. package/dist/client/AgentPanel.js.map +1 -1
  237. package/dist/client/AssistantChat.d.ts.map +1 -1
  238. package/dist/client/AssistantChat.js +7 -0
  239. package/dist/client/AssistantChat.js.map +1 -1
  240. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  241. package/dist/client/agent-chat-adapter.js +8 -1
  242. package/dist/client/agent-chat-adapter.js.map +1 -1
  243. package/dist/client/chat/message-components.d.ts +4 -0
  244. package/dist/client/chat/message-components.d.ts.map +1 -1
  245. package/dist/client/chat/message-components.js +10 -3
  246. package/dist/client/chat/message-components.js.map +1 -1
  247. package/dist/client/composer/ComposerPlusMenu.d.ts.map +1 -1
  248. package/dist/client/composer/ComposerPlusMenu.js +58 -6
  249. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  250. package/dist/client/composer/asset-picker-url.d.ts +16 -0
  251. package/dist/client/composer/asset-picker-url.d.ts.map +1 -0
  252. package/dist/client/composer/asset-picker-url.js +64 -0
  253. package/dist/client/composer/asset-picker-url.js.map +1 -0
  254. package/dist/client/error-format.d.ts.map +1 -1
  255. package/dist/client/error-format.js +17 -0
  256. package/dist/client/error-format.js.map +1 -1
  257. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  258. package/dist/client/resources/McpIntegrationDialog.js +6 -1
  259. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  260. package/dist/collab/awareness.d.ts +2 -2
  261. package/dist/collab/awareness.d.ts.map +1 -1
  262. package/dist/collab/struct-routes.d.ts +1 -1
  263. package/dist/local-artifacts/index.d.ts +10 -0
  264. package/dist/local-artifacts/index.d.ts.map +1 -1
  265. package/dist/local-artifacts/index.js +39 -0
  266. package/dist/local-artifacts/index.js.map +1 -1
  267. package/dist/notifications/routes.d.ts +1 -1
  268. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  269. package/dist/resources/handlers.d.ts +3 -3
  270. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  271. package/dist/server/transcribe-voice.d.ts +1 -1
  272. package/docs/content/local-file-mode.mdx +99 -489
  273. package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  274. package/docs/content/locales/ar-SA/template-content.mdx +10 -24
  275. package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  276. package/docs/content/locales/de-DE/template-content.mdx +10 -24
  277. package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  278. package/docs/content/locales/es-ES/template-content.mdx +10 -24
  279. package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  280. package/docs/content/locales/fr-FR/template-content.mdx +10 -24
  281. package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  282. package/docs/content/locales/hi-IN/template-content.mdx +10 -24
  283. package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  284. package/docs/content/locales/ja-JP/template-content.mdx +10 -24
  285. package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  286. package/docs/content/locales/ko-KR/template-content.mdx +10 -24
  287. package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  288. package/docs/content/locales/pt-BR/template-content.mdx +10 -24
  289. package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  290. package/docs/content/locales/zh-CN/template-content.mdx +9 -24
  291. package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  292. package/docs/content/locales/zh-TW/template-content.mdx +9 -24
  293. package/docs/content/template-content.mdx +26 -42
  294. package/package.json +1 -1
  295. package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
@@ -1850,10 +1850,14 @@ function nodeSlice(body: string, node: MdxNode) {
1850
1850
 
1851
1851
  function freshTextBlock(markdown: string): unknown {
1852
1852
  const text = markdownToBuilderTextHtml(markdown);
1853
+ return freshTextHtmlBlock(text, markdown);
1854
+ }
1855
+
1856
+ function freshTextHtmlBlock(text: string, stableSource: string): unknown {
1853
1857
  return {
1854
1858
  "@type": "@builder.io/sdk:Element",
1855
1859
  "@version": 2,
1856
- id: `builder-mdx-${stableHash(markdown).slice(0, 16)}`,
1860
+ id: `builder-mdx-${stableHash(stableSource).slice(0, 16)}`,
1857
1861
  component: {
1858
1862
  name: "Text",
1859
1863
  options: { text },
@@ -1868,6 +1872,91 @@ function freshTextBlock(markdown: string): unknown {
1868
1872
  };
1869
1873
  }
1870
1874
 
1875
+ function assertSafeFreshCalloutNode(node: MdxNode) {
1876
+ for (const child of node.children ?? []) {
1877
+ if (
1878
+ child.type === "mdxFlowExpression" ||
1879
+ child.type === "mdxTextExpression" ||
1880
+ child.type === "mdxjsEsm" ||
1881
+ child.type === "mdxJsxFlowElement" ||
1882
+ child.type === "mdxJsxTextElement"
1883
+ ) {
1884
+ throw new Error("Unsupported dynamic syntax inside Builder callout.");
1885
+ }
1886
+ assertSafeFreshCalloutNode(child);
1887
+ }
1888
+ }
1889
+
1890
+ function findMdxOpeningTagEnd(raw: string) {
1891
+ let quote: '"' | "'" | null = null;
1892
+ let escaped = false;
1893
+ for (let index = 0; index < raw.length; index += 1) {
1894
+ const character = raw[index];
1895
+ if (escaped) {
1896
+ escaped = false;
1897
+ continue;
1898
+ }
1899
+ if (character === "\\" && quote) {
1900
+ escaped = true;
1901
+ continue;
1902
+ }
1903
+ if (quote) {
1904
+ if (character === quote) quote = null;
1905
+ continue;
1906
+ }
1907
+ if (character === '"' || character === "'") {
1908
+ quote = character;
1909
+ continue;
1910
+ }
1911
+ if (character === ">") return index;
1912
+ }
1913
+ return -1;
1914
+ }
1915
+
1916
+ function freshCalloutBlock(
1917
+ node: MdxNode,
1918
+ raw: string,
1919
+ occurrence: number,
1920
+ ): unknown {
1921
+ let icon = "💡";
1922
+ for (const attribute of node.attributes ?? []) {
1923
+ if (
1924
+ attribute.type !== "mdxJsxAttribute" ||
1925
+ !attribute.name ||
1926
+ (attribute.value !== null && typeof attribute.value !== "string")
1927
+ ) {
1928
+ throw new Error("Builder callout attributes must be literal values.");
1929
+ }
1930
+ if (attribute.name === "icon") {
1931
+ icon = attribute.value || "💡";
1932
+ continue;
1933
+ }
1934
+ if (attribute.name === "color") continue;
1935
+ throw new Error(
1936
+ `Unsupported Builder callout attribute: ${attribute.name}.`,
1937
+ );
1938
+ }
1939
+ assertSafeFreshCalloutNode(node);
1940
+
1941
+ const openingEnd = findMdxOpeningTagEnd(raw);
1942
+ const closingStart = raw.lastIndexOf("</callout>");
1943
+ if (openingEnd < 0 || closingStart < openingEnd) {
1944
+ throw new Error("Builder callout must have an explicit closing tag.");
1945
+ }
1946
+ const markdown = raw
1947
+ .slice(openingEnd + 1, closingStart)
1948
+ .split(/\r?\n/)
1949
+ .map((line) => line.replace(/^(?:\t| {2})/, ""))
1950
+ .join("\n")
1951
+ .trim();
1952
+ const content = markdownToBuilderTextHtml(markdown);
1953
+ const iconHtml = icon ? `<p><strong>${escapeHtml(icon)}</strong></p>` : "";
1954
+ return freshTextHtmlBlock(
1955
+ `<blockquote>${iconHtml}${content}</blockquote>`,
1956
+ `${raw}\n@source:${node.position?.start?.offset ?? occurrence}`,
1957
+ );
1958
+ }
1959
+
1871
1960
  const BUILDER_TABLE_TAGS = new Set([
1872
1961
  "table",
1873
1962
  "colgroup",
@@ -2510,7 +2599,7 @@ export async function builderMdxBodyToBuilderBlocks(
2510
2599
  ) {
2511
2600
  const root = await parseMdxRoot(body);
2512
2601
  const blocks: unknown[] = [];
2513
- for (const child of root.children ?? []) {
2602
+ for (const [childIndex, child] of (root.children ?? []).entries()) {
2514
2603
  const raw = nodeSlice(body, child);
2515
2604
  if (!raw) continue;
2516
2605
  if (
@@ -2528,6 +2617,10 @@ export async function builderMdxBodyToBuilderBlocks(
2528
2617
  blocks.push(freshTableBlock(child, raw));
2529
2618
  continue;
2530
2619
  }
2620
+ if (child.name === "callout") {
2621
+ blocks.push(freshCalloutBlock(child, raw, childIndex));
2622
+ continue;
2623
+ }
2531
2624
  const block = await blockFromMdxComponent(raw, sidecars);
2532
2625
  if (block) {
2533
2626
  blocks.push(block);
@@ -2775,24 +2868,78 @@ function inlineMarkdownToHtml(value: string) {
2775
2868
  .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
2776
2869
  .replace(/_([^_]+)_/g, "<em>$1</em>")
2777
2870
  .replace(/`([^`]+)`/g, "<code>$1</code>")
2778
- .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
2871
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, label, destination) => {
2872
+ const href = destination.trim();
2873
+ const hasScheme = /^[a-z][a-z\d+.-]*:/i.test(href);
2874
+ const hasSafeScheme = /^(?:https?:\/\/|mailto:|tel:)/i.test(href);
2875
+ const isNetworkPath = /^(?:\/\/|\\\\)/.test(href);
2876
+ const hasControlCharacter = /[\u0000-\u001f\u007f]/.test(href);
2877
+ if (
2878
+ !href ||
2879
+ isNetworkPath ||
2880
+ hasControlCharacter ||
2881
+ (hasScheme && !hasSafeScheme)
2882
+ ) {
2883
+ return label;
2884
+ }
2885
+ return `<a href="${href}">${label}</a>`;
2886
+ });
2887
+ }
2888
+
2889
+ type MarkdownListNode = {
2890
+ type: "ul" | "ol";
2891
+ value: string;
2892
+ children: MarkdownListNode[];
2893
+ };
2894
+
2895
+ function renderMarkdownListNodes(nodes: MarkdownListNode[]): string {
2896
+ const html: string[] = [];
2897
+ for (let index = 0; index < nodes.length; ) {
2898
+ const type = nodes[index].type;
2899
+ const items: string[] = [];
2900
+ while (index < nodes.length && nodes[index].type === type) {
2901
+ const node = nodes[index];
2902
+ items.push(
2903
+ `<li>${inlineMarkdownToHtml(node.value)}${renderMarkdownListNodes(node.children)}</li>`,
2904
+ );
2905
+ index += 1;
2906
+ }
2907
+ html.push(`<${type}>${items.join("")}</${type}>`);
2908
+ }
2909
+ return html.join("");
2779
2910
  }
2780
2911
 
2781
2912
  export function markdownToBuilderTextHtml(markdown: string) {
2782
2913
  const lines = markdown.trim().split(/\r?\n/);
2783
2914
  const html: string[] = [];
2784
- let listItems: string[] = [];
2785
- let listType: "ul" | "ol" | null = null;
2915
+ let listItems: MarkdownListNode[] = [];
2916
+ let listStack: Array<{ indent: number; node: MarkdownListNode }> = [];
2786
2917
  const flushList = () => {
2787
- if (!listItems.length || !listType) return;
2788
- html.push(`<${listType}>${listItems.join("")}</${listType}>`);
2918
+ if (!listItems.length) return;
2919
+ html.push(renderMarkdownListNodes(listItems));
2789
2920
  listItems = [];
2790
- listType = null;
2921
+ listStack = [];
2791
2922
  };
2792
- const appendListItem = (type: "ul" | "ol", value: string) => {
2793
- if (listType && listType !== type) flushList();
2794
- listType = type;
2795
- listItems.push(`<li>${inlineMarkdownToHtml(value)}</li>`);
2923
+ const appendListItem = (
2924
+ type: "ul" | "ol",
2925
+ value: string,
2926
+ whitespace: string,
2927
+ ) => {
2928
+ const indent = [...whitespace].reduce(
2929
+ (width, character) => width + (character === "\t" ? 2 : 1),
2930
+ 0,
2931
+ );
2932
+ while (
2933
+ listStack.length &&
2934
+ listStack[listStack.length - 1].indent >= indent
2935
+ ) {
2936
+ listStack.pop();
2937
+ }
2938
+ const node: MarkdownListNode = { type, value, children: [] };
2939
+ const parent = listStack[listStack.length - 1]?.node;
2940
+ if (parent) parent.children.push(node);
2941
+ else listItems.push(node);
2942
+ listStack.push({ indent, node });
2796
2943
  };
2797
2944
  for (const line of lines) {
2798
2945
  const trimmed = line.trim();
@@ -2815,14 +2962,14 @@ export function markdownToBuilderTextHtml(markdown: string) {
2815
2962
  );
2816
2963
  continue;
2817
2964
  }
2818
- const unorderedList = trimmed.match(/^[-*]\s+(.+)$/);
2965
+ const unorderedList = line.match(/^(\s*)[-*]\s+(.+)$/);
2819
2966
  if (unorderedList) {
2820
- appendListItem("ul", unorderedList[1]);
2967
+ appendListItem("ul", unorderedList[2], unorderedList[1]);
2821
2968
  continue;
2822
2969
  }
2823
- const orderedList = trimmed.match(/^\d+\.\s+(.+)$/);
2970
+ const orderedList = line.match(/^(\s*)\d+\.\s+(.+)$/);
2824
2971
  if (orderedList) {
2825
- appendListItem("ol", orderedList[1]);
2972
+ appendListItem("ol", orderedList[2], orderedList[1]);
2826
2973
  continue;
2827
2974
  }
2828
2975
  flushList();
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: design-templates
3
+ description: >-
4
+ Find, save, copy, and adapt Design templates. Use when the user references a
5
+ template, a prior design, or wants a reusable starting point.
6
+ ---
7
+
8
+ # Design Templates
9
+
10
+ ## Prerequisites
11
+
12
+ - Read `navigation` or call `view-screen` when the current template or design
13
+ is unclear.
14
+ - Treat built-in and user-created entries as one template concept. Their
15
+ ownership and available share/delete actions differ, but creation uses the
16
+ same action.
17
+
18
+ ## Workflow
19
+
20
+ 1. **Resolve the starting point.** When the user mentions a template, past
21
+ design, or prior work, call both `list-design-templates` and `list-designs`
22
+ before generating. Match the requested name or id; ask only when multiple
23
+ plausible results remain.
24
+ 2. **Save a reusable template.** Call `save-design-as-template` with the source
25
+ `designId`. The action snapshots inline screens, canvas dimensions,
26
+ defaults, linked design system, and locked layers.
27
+ 3. **Create from a template.** Call `create-design-from-template` with the
28
+ resolved `templateId`. Pass `designSystemId` only when the user selected an
29
+ override; the action access-checks it and returns the effective linked id.
30
+ 4. **Stop after a pure copy.** If there is no prompt and no pending design-system
31
+ adaptation, open the copied design and do not start generation.
32
+ 5. **Adapt copied screens.** When the user supplied a prompt or chose a
33
+ different accessible design system, call `get-design-snapshot` exactly once,
34
+ then refine the existing unlocked content with `edit-design`. Never call
35
+ `generate-design`, delete the copied screens, or recreate the template from
36
+ scratch.
37
+ 6. **Verify.** Read back the copied design and confirm its screens, canvas
38
+ dimensions, linked design system, and locked-layer boundaries before
39
+ reporting completion.
40
+
41
+ ## Locked Layers
42
+
43
+ `data-agent-native-locked="true"` is authoritative. Keep each locked element
44
+ and all descendants byte-for-byte unchanged during adaptation. If the user
45
+ explicitly wants one changed, ask them to unlock it in the Layers panel first.
46
+
47
+ ## Ownership
48
+
49
+ - Built-in templates cannot be renamed, shared, or deleted.
50
+ - Owned user templates may be shared with the standard resource sharing UI and
51
+ deleted with `delete-design-template`.
52
+ - Do not model templates as ordinary `designs` rows and do not invent alternate
53
+ template action names.
54
+
55
+ ## Related Skills
56
+
57
+ - `design-generation` — creating and refining Design screens.
58
+ - `design-systems` — applying linked systems and tokens.
59
+ - `sharing` — access and sharing rules for user-created templates.
@@ -133,12 +133,15 @@ patterns live in `.agents/skills/`.
133
133
  `generate-design` for new files. For broad rewrites of an existing selected
134
134
  file, use `edit-design` with `mode: "replace-file"` and the exact `fileId`;
135
135
  never resend files you aren't changing.
136
- - For reusable starting points, call `list-design-templates`. Use
136
+ - When the user references a template, prior design, or past work, call both
137
+ `list-design-templates` and `list-designs` before generating so the existing
138
+ starting point is resolved instead of recreated. Use
137
139
  `save-design-as-template` to snapshot an editable inline design, including
138
140
  its screens, canvas dimensions, defaults, and locked layers. Use
139
141
  `create-design-from-template` to instantiate a normal design. If the user
140
142
  supplies a prompt, call `get-design-snapshot` once and refine the copied
141
143
  files with `edit-design`; never regenerate the template from scratch.
144
+ Read the `design-templates` skill for the complete copy/adaptation workflow.
142
145
  - Treat `data-agent-native-locked="true"` as an authoritative template
143
146
  boundary. Locked backgrounds, logos, and their descendants must remain
144
147
  byte-for-byte unchanged during agent edits. The server rejects attempts to
@@ -465,6 +468,8 @@ and requires Builder connected. See `full-app-build` skill for the full flow.
465
468
  Read the relevant skill before deeper work:
466
469
 
467
470
  - `design-generation` for creating/editing prototype HTML and variant flows.
471
+ - `design-templates` for resolving, saving, copying, and adapting templates or
472
+ prior Design work without fresh generation.
468
473
  - `responsive-breakpoints` for Framer-style breakpoint editing (single DOM,
469
474
  cascading width-scoped overrides, the managed breakpoints media block).
470
475
  - `design-systems` for tokens, brand extraction, and linked systems.
@@ -4,7 +4,7 @@ import {
4
4
  getRequestOrgId,
5
5
  getRequestUserEmail,
6
6
  } from "@agent-native/core/server/request-context";
7
- import { assertAccess, resolveAccess } from "@agent-native/core/sharing";
7
+ import { resolveAccess } from "@agent-native/core/sharing";
8
8
  import { eq } from "drizzle-orm";
9
9
  import { nanoid } from "nanoid";
10
10
  import { z } from "zod";
@@ -31,7 +31,7 @@ export default defineAction({
31
31
  "Create an editable design from a reusable template. The action copies the template files, exact dimensions, defaults, and locked layers. " +
32
32
  "When a prompt is supplied, refine the copied files with get-design-snapshot and edit-design; do not replace the template with generate-design.",
33
33
  schema: z.object({
34
- templateId: z.string().min(1).describe("Template or starter template ID"),
34
+ templateId: z.string().min(1).describe("Saved or built-in template ID"),
35
35
  title: z.string().trim().min(1).max(120).optional(),
36
36
  prompt: z
37
37
  .string()
@@ -124,14 +124,20 @@ export default defineAction({
124
124
  let linkedDesignSystemId =
125
125
  designSystemId === undefined ? templateDesignSystemId : designSystemId;
126
126
  if (linkedDesignSystemId) {
127
- try {
128
- await assertAccess("design-system", linkedDesignSystemId, "viewer");
129
- } catch {
130
- if (designSystemId !== undefined)
127
+ const designSystemAccess = await resolveAccess(
128
+ "design-system",
129
+ linkedDesignSystemId,
130
+ );
131
+ if (!designSystemAccess) {
132
+ if (designSystemId !== undefined) {
131
133
  throw new Error("Design system not found");
134
+ }
132
135
  linkedDesignSystemId = null;
133
136
  }
134
137
  }
138
+ const designSystemOverridden =
139
+ designSystemId !== undefined && designSystemId !== templateDesignSystemId;
140
+ const adaptationPending = Boolean(prompt);
135
141
 
136
142
  const ownerEmail = getRequestUserEmail();
137
143
  if (!ownerEmail) throw new Error("Not authenticated");
@@ -149,6 +155,9 @@ export default defineAction({
149
155
  category: templateCategory,
150
156
  templateUpdatedAt,
151
157
  instantiatedAt: now,
158
+ templateDesignSystemId,
159
+ appliedDesignSystemId: linkedDesignSystemId,
160
+ designSystemOverridden,
152
161
  };
153
162
  if (prompt) data.templatePrompt = prompt;
154
163
 
@@ -190,6 +199,8 @@ export default defineAction({
190
199
  title: title ?? templateTitle,
191
200
  templateId,
192
201
  templateTitle,
202
+ designSystemId: linkedDesignSystemId,
203
+ designSystemOverridden,
193
204
  fileCount: files.length,
194
205
  lockedLayerCount: countLockedLayersAcrossFiles(persistedFiles),
195
206
  templateBaselineFiles: persistedFiles.map((file) => ({
@@ -197,7 +208,8 @@ export default defineAction({
197
208
  contentHash: sourceContentHash(file.content),
198
209
  })),
199
210
  promptPending: Boolean(prompt),
200
- nextRequiredAction: prompt
211
+ adaptationPending,
212
+ nextRequiredAction: adaptationPending
201
213
  ? "Call get-design-snapshot, then refine unlocked content with edit-design. Do not call generate-design."
202
214
  : null,
203
215
  };
@@ -15,7 +15,7 @@ const PREVIEW_MAX_BYTES = 50_000;
15
15
 
16
16
  export default defineAction({
17
17
  description:
18
- "List reusable Design templates, including built-in starters plus templates the user can access or publicly discover.",
18
+ "List reusable Design templates, including built-in templates plus templates the user can access or publicly discover.",
19
19
  schema: z.object({
20
20
  category: designTemplateCategorySchema.optional(),
21
21
  includePreview: z.enum(["true", "false"]).optional().default("false"),
@@ -25,10 +25,11 @@ export default defineAction({
25
25
  run: async ({ category, includePreview }) => {
26
26
  const db = getDb();
27
27
  const userEmail = getRequestUserEmail();
28
+ const orgId = getRequestOrgId();
28
29
  const access = accessFilter(
29
30
  schema.designTemplates,
30
31
  schema.designTemplateShares,
31
- { userEmail, orgId: getRequestOrgId() },
32
+ { userEmail, orgId },
32
33
  "viewer",
33
34
  { includePublic: true },
34
35
  );
@@ -41,6 +42,7 @@ export default defineAction({
41
42
  width: schema.designTemplates.width,
42
43
  height: schema.designTemplates.height,
43
44
  lockedLayerCount: schema.designTemplates.lockedLayerCount,
45
+ designSystemId: schema.designTemplates.designSystemId,
44
46
  visibility: schema.designTemplates.visibility,
45
47
  ownerEmail: schema.designTemplates.ownerEmail,
46
48
  createdAt: schema.designTemplates.createdAt,
@@ -54,31 +56,68 @@ export default defineAction({
54
56
  )
55
57
  .orderBy(desc(schema.designTemplates.updatedAt));
56
58
 
57
- const previews = new Map<string, string>();
58
- if (includePreview === "true" && rows.length > 0) {
59
- const files = await db
60
- .select({
61
- templateId: schema.designTemplateFiles.templateId,
62
- filename: schema.designTemplateFiles.filename,
63
- fileType: schema.designTemplateFiles.fileType,
64
- content: sql<string>`substr(${schema.designTemplateFiles.content}, 1, ${PREVIEW_MAX_BYTES})`,
65
- })
66
- .from(schema.designTemplateFiles)
67
- .where(
68
- inArray(
69
- schema.designTemplateFiles.templateId,
70
- rows.map((row) => row.id),
59
+ const linkedDesignSystemIds = Array.from(
60
+ new Set(
61
+ rows
62
+ .map((row) => row.designSystemId)
63
+ .filter(
64
+ (id): id is string => typeof id === "string" && id.length > 0,
71
65
  ),
72
- );
66
+ ),
67
+ );
68
+ const [files, accessibleDesignSystems] = await Promise.all([
69
+ includePreview === "true" && rows.length > 0
70
+ ? db
71
+ .select({
72
+ templateId: schema.designTemplateFiles.templateId,
73
+ filename: schema.designTemplateFiles.filename,
74
+ fileType: schema.designTemplateFiles.fileType,
75
+ content: sql<string>`substr(${schema.designTemplateFiles.content}, 1, ${PREVIEW_MAX_BYTES})`,
76
+ })
77
+ .from(schema.designTemplateFiles)
78
+ .where(
79
+ and(
80
+ inArray(
81
+ schema.designTemplateFiles.templateId,
82
+ rows.map((row) => row.id),
83
+ ),
84
+ eq(schema.designTemplateFiles.fileType, "html"),
85
+ ),
86
+ )
87
+ : Promise.resolve([]),
88
+ linkedDesignSystemIds.length > 0
89
+ ? db
90
+ .select({ id: schema.designSystems.id })
91
+ .from(schema.designSystems)
92
+ .where(
93
+ and(
94
+ inArray(schema.designSystems.id, linkedDesignSystemIds),
95
+ accessFilter(
96
+ schema.designSystems,
97
+ schema.designSystemShares,
98
+ { userEmail, orgId },
99
+ "viewer",
100
+ { includePublic: true },
101
+ ),
102
+ ),
103
+ )
104
+ : Promise.resolve([]),
105
+ ]);
106
+
107
+ const previews = new Map<string, string>();
108
+ if (includePreview === "true") {
73
109
  for (const file of files) {
74
- if (!previews.has(file.templateId) && file.fileType === "html") {
110
+ if (!previews.has(file.templateId)) {
75
111
  previews.set(file.templateId, file.content);
76
112
  }
77
- if (file.filename === "index.html" && file.fileType === "html") {
113
+ if (file.filename === "index.html") {
78
114
  previews.set(file.templateId, file.content);
79
115
  }
80
116
  }
81
117
  }
118
+ const accessibleDesignSystemIds = new Set(
119
+ accessibleDesignSystems.map((system) => system.id),
120
+ );
82
121
 
83
122
  const presets = DESIGN_TEMPLATE_PRESETS.filter(
84
123
  (preset) => !category || preset.category === category,
@@ -90,18 +129,25 @@ export default defineAction({
90
129
  width: preset.width,
91
130
  height: preset.height,
92
131
  lockedLayerCount: 2,
132
+ designSystemId: null,
93
133
  visibility: "public" as const,
94
134
  isOwner: false,
135
+ isBuiltIn: true,
95
136
  source: "starter" as const,
96
137
  createdAt: null,
97
138
  updatedAt: null,
98
139
  ...(includePreview === "true" ? { previewHtml: preset.content } : {}),
99
140
  }));
100
141
 
101
- const saved = rows.map(({ ownerEmail, ...row }) => ({
142
+ const saved = rows.map(({ ownerEmail, designSystemId, ...row }) => ({
102
143
  ...row,
144
+ designSystemId:
145
+ designSystemId && accessibleDesignSystemIds.has(designSystemId)
146
+ ? designSystemId
147
+ : null,
103
148
  isOwner:
104
149
  !!userEmail && ownerEmail.toLowerCase() === userEmail.toLowerCase(),
150
+ isBuiltIn: false,
105
151
  source: "saved" as const,
106
152
  ...(includePreview === "true"
107
153
  ? { previewHtml: previews.get(row.id) ?? null }
@@ -110,9 +156,11 @@ export default defineAction({
110
156
 
111
157
  return {
112
158
  count: presets.length + saved.length,
159
+ builtInCount: presets.length,
160
+ userCount: saved.length,
113
161
  starterCount: presets.length,
114
162
  savedCount: saved.length,
115
- templates: [...presets, ...saved],
163
+ templates: [...saved, ...presets],
116
164
  };
117
165
  },
118
166
  });
@@ -24,7 +24,7 @@
24
24
  * --filename Screen filename to focus in the design editor
25
25
  * --tool Design editor tool to activate
26
26
  * --designSystemId Design system ID (for design-systems view)
27
- * --templateId Saved or starter template ID (for templates view)
27
+ * --templateId Saved or built-in template ID (for templates view)
28
28
  * --path URL path to navigate to
29
29
  */
30
30
 
@@ -122,7 +122,7 @@ export default defineAction({
122
122
  templateId: z
123
123
  .string()
124
124
  .optional()
125
- .describe("Saved or starter template ID for templates view"),
125
+ .describe("Saved or built-in template ID for templates view"),
126
126
  path: z.string().optional().describe("URL path to navigate to"),
127
127
  })
128
128
  .superRefine((args, ctx) => {
@@ -244,7 +244,9 @@ export default defineAction({
244
244
  width: preset.width,
245
245
  height: preset.height,
246
246
  lockedLayerCount: 2,
247
- source: "starter",
247
+ designSystemId: null,
248
+ isBuiltIn: true,
249
+ source: "built-in",
248
250
  };
249
251
  } else {
250
252
  const templateAccess = await resolveAccess(
@@ -253,6 +255,15 @@ export default defineAction({
253
255
  ).catch(() => null);
254
256
  if (templateAccess) {
255
257
  const template = templateAccess.resource;
258
+ const linkedDesignSystemId =
259
+ typeof template.designSystemId === "string"
260
+ ? template.designSystemId
261
+ : null;
262
+ const designSystemAccess = linkedDesignSystemId
263
+ ? await resolveAccess("design-system", linkedDesignSystemId).catch(
264
+ () => null,
265
+ )
266
+ : null;
256
267
  screen.template = {
257
268
  id: templateId,
258
269
  title: template.title ?? null,
@@ -261,8 +272,10 @@ export default defineAction({
261
272
  width: template.width ?? null,
262
273
  height: template.height ?? null,
263
274
  lockedLayerCount: template.lockedLayerCount ?? 0,
275
+ designSystemId: designSystemAccess ? linkedDesignSystemId : null,
264
276
  visibility: template.visibility ?? "private",
265
- source: "saved",
277
+ isBuiltIn: false,
278
+ source: "user",
266
279
  };
267
280
  }
268
281
  }
@@ -37,6 +37,11 @@
37
37
  }
38
38
  ],
39
39
  "skills": [
40
+ {
41
+ "path": ".agents/skills/design-templates",
42
+ "visibility": "both",
43
+ "exportAs": "design-templates"
44
+ },
40
45
  {
41
46
  "path": ".agents/skills/design-generation",
42
47
  "visibility": "both",
@@ -110,12 +110,16 @@ export function sanitizeLocalhostSourceSnapshotHtml(html: string): string {
110
110
  );
111
111
  }
112
112
 
113
+ // Prototypes are still sandboxed, but user-initiated print/download controls
114
+ // need the browser permissions that make `window.print()` and download links
115
+ // work inside an iframe. Without these tokens the browser silently ignores
116
+ // the common "Download / Print PDF" pattern.
113
117
  const EXTERNAL_PREVIEW_IFRAME_SANDBOX =
114
- "allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin";
118
+ "allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads allow-modals allow-same-origin";
115
119
  const EDITABLE_INLINE_IFRAME_SANDBOX =
116
- "allow-scripts allow-popups allow-popups-to-escape-sandbox allow-same-origin";
120
+ "allow-scripts allow-popups allow-popups-to-escape-sandbox allow-downloads allow-modals allow-same-origin";
117
121
  const READ_ONLY_INLINE_IFRAME_SANDBOX =
118
- "allow-scripts allow-popups allow-popups-to-escape-sandbox";
122
+ "allow-scripts allow-popups allow-popups-to-escape-sandbox allow-downloads allow-modals";
119
123
 
120
124
  export function getDesignCanvasIframeSandbox(args: {
121
125
  externalPreview: boolean;