@agent-native/core 0.102.2 → 0.103.1

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 (205) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -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/local-artifacts/index.ts +65 -0
  31. package/corpus/core/src/vite/client.ts +2 -0
  32. package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
  33. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
  34. package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
  35. package/corpus/templates/assets/app/i18n-data.ts +215 -0
  36. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
  37. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
  38. package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
  39. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +12 -2
  40. package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
  41. package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
  42. package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
  43. package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
  44. package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
  45. package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
  46. package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
  47. package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
  48. package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
  49. package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
  50. package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
  51. package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
  52. package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
  53. package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
  54. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
  55. package/corpus/templates/clips/server/lib/share-password.ts +6 -3
  56. package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
  57. package/corpus/templates/content/AGENTS.md +25 -30
  58. package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
  59. package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
  60. package/corpus/templates/content/actions/_content-files.ts +338 -0
  61. package/corpus/templates/content/actions/_content-space-access.ts +135 -0
  62. package/corpus/templates/content/actions/_content-spaces.ts +640 -0
  63. package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
  64. package/corpus/templates/content/actions/_database-utils.ts +7 -1
  65. package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
  66. package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
  67. package/corpus/templates/content/actions/_property-utils.ts +9 -4
  68. package/corpus/templates/content/actions/add-database-item.ts +108 -65
  69. package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
  70. package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
  71. package/corpus/templates/content/actions/create-content-database.ts +129 -3
  72. package/corpus/templates/content/actions/create-document.ts +65 -55
  73. package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
  74. package/corpus/templates/content/actions/delete-content-database.ts +3 -0
  75. package/corpus/templates/content/actions/delete-document.ts +7 -11
  76. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
  77. package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
  78. package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
  79. package/corpus/templates/content/actions/edit-document.ts +2 -28
  80. package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
  81. package/corpus/templates/content/actions/export-content-source.ts +47 -9
  82. package/corpus/templates/content/actions/get-document.ts +0 -14
  83. package/corpus/templates/content/actions/import-content-source.ts +43 -3
  84. package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
  85. package/corpus/templates/content/actions/list-documents.ts +12 -9
  86. package/corpus/templates/content/actions/move-document.ts +3 -14
  87. package/corpus/templates/content/actions/pull-document.ts +0 -23
  88. package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
  89. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
  90. package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
  91. package/corpus/templates/content/actions/search-documents.ts +0 -34
  92. package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
  93. package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
  94. package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
  95. package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
  96. package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
  97. package/corpus/templates/content/actions/update-document.ts +0 -15
  98. package/corpus/templates/content/actions/view-screen.ts +2 -50
  99. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
  100. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
  101. package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
  102. package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
  103. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  104. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
  105. package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
  106. package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
  107. package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
  108. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
  109. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
  110. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
  111. package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
  112. package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
  113. package/corpus/templates/content/app/i18n-data.ts +75 -0
  114. package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
  115. package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
  116. package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
  117. package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
  118. package/corpus/templates/content/parity/matrix.md +2 -1
  119. package/corpus/templates/content/parity/matrix.ts +37 -1
  120. package/corpus/templates/content/scripts/dev-database.mjs +7 -5
  121. package/corpus/templates/content/server/db/schema.ts +113 -40
  122. package/corpus/templates/content/server/lib/document-context.ts +15 -1
  123. package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
  124. package/corpus/templates/content/server/plugins/db.ts +153 -0
  125. package/corpus/templates/content/shared/api.ts +17 -3
  126. package/corpus/templates/content/shared/builder-mdx.ts +163 -16
  127. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
  128. package/corpus/templates/design/AGENTS.md +6 -1
  129. package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
  130. package/corpus/templates/design/actions/list-design-templates.ts +69 -21
  131. package/corpus/templates/design/actions/navigate.ts +2 -2
  132. package/corpus/templates/design/actions/view-screen.ts +15 -2
  133. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  134. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
  135. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
  136. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
  137. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
  138. package/corpus/templates/design/app/i18n-data.ts +21 -2
  139. package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
  140. package/corpus/templates/design/app/pages/Index.tsx +195 -24
  141. package/corpus/templates/design/app/pages/Templates.tsx +144 -55
  142. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
  143. package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
  144. package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
  145. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  146. package/corpus/templates/mail/AGENTS.md +23 -18
  147. package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
  148. package/corpus/templates/mail/actions/mark-read.ts +131 -20
  149. package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
  150. package/corpus/templates/mail/actions/move-email.ts +30 -24
  151. package/corpus/templates/mail/actions/send-email.ts +73 -68
  152. package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
  153. package/corpus/templates/mail/server/handlers/emails.ts +218 -209
  154. package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
  155. package/corpus/templates/mail/server/lib/email-state.ts +198 -113
  156. package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
  157. package/corpus/templates/mail/server/lib/jobs.ts +69 -74
  158. package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
  159. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  160. package/dist/cli/content-local.d.ts +1 -0
  161. package/dist/cli/content-local.d.ts.map +1 -1
  162. package/dist/cli/content-local.js +75 -17
  163. package/dist/cli/content-local.js.map +1 -1
  164. package/dist/cli/index.js +2 -2
  165. package/dist/cli/index.js.map +1 -1
  166. package/dist/cli/skills-content/content-skill.d.ts +1 -1
  167. package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
  168. package/dist/cli/skills-content/content-skill.js +36 -25
  169. package/dist/cli/skills-content/content-skill.js.map +1 -1
  170. package/dist/cli/skills.d.ts.map +1 -1
  171. package/dist/cli/skills.js +47 -14
  172. package/dist/cli/skills.js.map +1 -1
  173. package/dist/collab/struct-routes.d.ts +1 -1
  174. package/dist/local-artifacts/index.d.ts +10 -0
  175. package/dist/local-artifacts/index.d.ts.map +1 -1
  176. package/dist/local-artifacts/index.js +39 -0
  177. package/dist/local-artifacts/index.js.map +1 -1
  178. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  179. package/dist/vite/client.d.ts.map +1 -1
  180. package/dist/vite/client.js +2 -0
  181. package/dist/vite/client.js.map +1 -1
  182. package/docs/content/local-file-mode.mdx +99 -489
  183. package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  184. package/docs/content/locales/ar-SA/template-content.mdx +10 -24
  185. package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  186. package/docs/content/locales/de-DE/template-content.mdx +10 -24
  187. package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  188. package/docs/content/locales/es-ES/template-content.mdx +10 -24
  189. package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  190. package/docs/content/locales/fr-FR/template-content.mdx +10 -24
  191. package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  192. package/docs/content/locales/hi-IN/template-content.mdx +10 -24
  193. package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  194. package/docs/content/locales/ja-JP/template-content.mdx +10 -24
  195. package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  196. package/docs/content/locales/ko-KR/template-content.mdx +10 -24
  197. package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  198. package/docs/content/locales/pt-BR/template-content.mdx +10 -24
  199. package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  200. package/docs/content/locales/zh-CN/template-content.mdx +9 -24
  201. package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  202. package/docs/content/locales/zh-TW/template-content.mdx +9 -24
  203. package/docs/content/template-content.mdx +26 -42
  204. package/package.json +1 -1
  205. package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
@@ -36,6 +36,7 @@ import {
36
36
  AlertDialogHeader,
37
37
  AlertDialogTitle,
38
38
  } from "@/components/ui/alert-dialog";
39
+ import { Badge } from "@/components/ui/badge";
39
40
  import { Button } from "@/components/ui/button";
40
41
  import {
41
42
  DropdownMenu,
@@ -45,6 +46,7 @@ import {
45
46
  } from "@/components/ui/dropdown-menu";
46
47
  import { Input } from "@/components/ui/input";
47
48
  import { Spinner } from "@/components/ui/spinner";
49
+ import { useDesignSystems } from "@/hooks/use-design-systems";
48
50
  import { writePendingGeneration } from "@/lib/pending-generation";
49
51
 
50
52
  type TemplateCategory =
@@ -63,28 +65,26 @@ interface DesignTemplateSummary {
63
65
  width?: number | null;
64
66
  height?: number | null;
65
67
  lockedLayerCount: number;
68
+ designSystemId?: string | null;
66
69
  visibility: "private" | "org" | "public";
67
70
  isOwner: boolean;
71
+ isBuiltIn: boolean;
68
72
  source: "starter" | "saved";
69
73
  previewHtml?: string | null;
70
74
  }
71
75
 
72
- interface TemplatesResult {
73
- count: number;
74
- starterCount: number;
75
- savedCount: number;
76
- templates: DesignTemplateSummary[];
77
- }
78
-
79
76
  export default function Templates() {
80
77
  const t = useT();
81
78
  const navigate = useNavigate();
82
- const [searchParams] = useSearchParams();
79
+ const [searchParams, setSearchParams] = useSearchParams();
83
80
  const queryClient = useQueryClient();
84
81
  const [search, setSearch] = useState("");
85
82
  const [selected, setSelected] = useState<DesignTemplateSummary | null>(null);
86
83
  const [promptOpen, setPromptOpen] = useState(false);
87
84
  const [creating, setCreating] = useState(false);
85
+ const [selectedDesignSystemId, setSelectedDesignSystemId] = useState<
86
+ string | null | undefined
87
+ >(undefined);
88
88
  const [deleteTemplate, setDeleteTemplate] =
89
89
  useState<DesignTemplateSummary | null>(null);
90
90
  const anchorElRef = useRef<HTMLElement | null>(null);
@@ -92,12 +92,19 @@ export default function Templates() {
92
92
  const anchorRef = useRef<HTMLElement | null>(null);
93
93
  anchorRef.current = anchorElRef.current;
94
94
 
95
- const { data, isLoading, isError, isFetching, refetch } =
96
- useActionQuery<TemplatesResult>("list-design-templates", {
95
+ const { data, isLoading, isError, isFetching, refetch } = useActionQuery(
96
+ "list-design-templates",
97
+ {
97
98
  includePreview: "true",
98
- });
99
+ },
100
+ );
99
101
  const createMutation = useActionMutation("create-design-from-template");
100
102
  const deleteMutation = useActionMutation("delete-design-template");
103
+ const {
104
+ designSystems,
105
+ defaultSystem,
106
+ isLoading: designSystemsLoading,
107
+ } = useDesignSystems();
101
108
 
102
109
  const templates = data?.templates ?? [];
103
110
  const linkedTemplateId = searchParams.get("templateId");
@@ -112,8 +119,27 @@ export default function Templates() {
112
119
  )
113
120
  : templates;
114
121
  }, [search, templates]);
115
- const starters = filtered.filter((template) => template.source === "starter");
116
- const saved = filtered.filter((template) => template.source === "saved");
122
+ const builtIns = filtered.filter((template) => template.isBuiltIn);
123
+ const userTemplates = filtered.filter((template) => !template.isBuiltIn);
124
+
125
+ const resolveTemplateDesignSystemId = (
126
+ template: DesignTemplateSummary,
127
+ ): string | null => {
128
+ if (
129
+ template.designSystemId &&
130
+ designSystems.some((system) => system.id === template.designSystemId)
131
+ ) {
132
+ return template.designSystemId;
133
+ }
134
+ return defaultSystem?.id ?? designSystems[0]?.id ?? null;
135
+ };
136
+
137
+ const setSelectedTemplateParam = (templateId: string | null) => {
138
+ const next = new URLSearchParams(searchParams);
139
+ if (templateId) next.set("templateId", templateId);
140
+ else next.delete("templateId");
141
+ setSearchParams(next, { replace: true });
142
+ };
117
143
 
118
144
  useEffect(() => {
119
145
  if (
@@ -125,7 +151,7 @@ export default function Templates() {
125
151
  const template = templates.find(
126
152
  (candidate) => candidate.id === linkedTemplateId,
127
153
  );
128
- if (!template) return;
154
+ if (!template || designSystemsLoading) return;
129
155
 
130
156
  const card = document.getElementById(`design-template-${linkedTemplateId}`);
131
157
  const useButton = card?.querySelector<HTMLElement>(
@@ -135,17 +161,21 @@ export default function Templates() {
135
161
  handledTemplateIdRef.current = linkedTemplateId;
136
162
  setSearch("");
137
163
  setSelected(template);
164
+ setSelectedDesignSystemId(resolveTemplateDesignSystemId(template));
138
165
  setPromptOpen(true);
139
166
  card?.scrollIntoView({ block: "center", behavior: "smooth" });
140
167
  useButton?.focus();
141
- }, [linkedTemplateId, templates]);
168
+ }, [designSystemsLoading, linkedTemplateId, templates]);
142
169
 
143
170
  const openTemplatePrompt = (
144
171
  template: DesignTemplateSummary,
145
172
  element: HTMLElement,
146
173
  ) => {
147
174
  anchorElRef.current = element;
175
+ handledTemplateIdRef.current = template.id;
176
+ setSelectedTemplateParam(template.id);
148
177
  setSelected(template);
178
+ setSelectedDesignSystemId(resolveTemplateDesignSystemId(template));
149
179
  setPromptOpen(true);
150
180
  };
151
181
 
@@ -156,40 +186,58 @@ export default function Templates() {
156
186
  ) => {
157
187
  setCreating(true);
158
188
  try {
159
- const title = prompt?.trim() ? derivePromptTitle(prompt) : template.title;
189
+ const trimmedPrompt = prompt?.trim() ?? "";
190
+ const title = trimmedPrompt
191
+ ? derivePromptTitle(trimmedPrompt)
192
+ : template.title;
193
+ const designSystemId =
194
+ selectedDesignSystemId === undefined
195
+ ? resolveTemplateDesignSystemId(template)
196
+ : selectedDesignSystemId;
160
197
  const result = (await createMutation.mutateAsync({
161
198
  templateId: template.id,
162
199
  title,
163
- ...(prompt?.trim() ? { prompt: prompt.trim() } : {}),
200
+ designSystemId,
201
+ ...(trimmedPrompt ? { prompt: trimmedPrompt } : {}),
164
202
  })) as {
165
- id?: string;
203
+ id: string;
166
204
  title?: string;
205
+ designSystemId?: string | null;
206
+ adaptationPending?: boolean;
167
207
  templateBaselineFiles?: Array<{ id: string; contentHash: string }>;
168
208
  };
169
209
  if (!result.id)
170
210
  throw new Error("Template copy did not return a design ID");
171
- if (prompt?.trim()) {
211
+ if (result.adaptationPending) {
212
+ const effectiveDesignSystemId = result.designSystemId ?? null;
213
+ const effectiveSystemTitle =
214
+ designSystems.find((system) => system.id === effectiveDesignSystemId)
215
+ ?.title ?? t("promptDialog.designSystem");
172
216
  writePendingGeneration(result.id, {
173
- prompt: prompt.trim(),
217
+ prompt:
218
+ trimmedPrompt ||
219
+ t("promptDialog.reskinTemplatePrompt", {
220
+ title: template.title,
221
+ system: effectiveSystemTitle,
222
+ }),
174
223
  title: result.title ?? title,
175
224
  source: template.title,
176
225
  templateId: template.id,
177
226
  templateBaselineFiles: result.templateBaselineFiles,
227
+ designSystemId: effectiveDesignSystemId,
178
228
  skipQuestions: true,
179
229
  ...options,
180
230
  });
181
231
  }
182
- await queryClient.invalidateQueries({
183
- queryKey: ["action", "list-designs"],
184
- });
232
+ void queryClient
233
+ .invalidateQueries({
234
+ queryKey: ["action", "list-designs"],
235
+ })
236
+ .catch(() => {});
185
237
  navigate(`/design/${result.id}`);
186
238
  } catch (error) {
187
239
  setCreating(false);
188
- toast.error(
189
- error instanceof Error
190
- ? error.message
191
- : t("templatesPage.createFailed"),
192
- );
240
+ throw error;
193
241
  }
194
242
  };
195
243
 
@@ -199,7 +247,7 @@ export default function Templates() {
199
247
  options: PromptComposerSubmitOptions,
200
248
  ) => {
201
249
  if (!selected) return;
202
- void createFromTemplate(selected, prompt, options);
250
+ return createFromTemplate(selected, prompt, options);
203
251
  };
204
252
 
205
253
  const handleDelete = async () => {
@@ -254,6 +302,16 @@ export default function Templates() {
254
302
  </p>
255
303
  </div>
256
304
 
305
+ <div className="relative mb-6 md:hidden">
306
+ <IconSearch className="absolute start-2.5 top-1/2 size-3.5 -translate-y-1/2 text-muted-foreground/70" />
307
+ <Input
308
+ value={search}
309
+ onChange={(event) => setSearch(event.target.value)}
310
+ placeholder={t("templatesPage.searchPlaceholder")}
311
+ className="h-9 w-full bg-accent/50 ps-8 text-sm"
312
+ />
313
+ </div>
314
+
257
315
  {isLoading ? (
258
316
  <TemplateGridSkeleton />
259
317
  ) : isError ? (
@@ -264,19 +322,19 @@ export default function Templates() {
264
322
  ) : (
265
323
  <div className="flex flex-col gap-10">
266
324
  <TemplateSection
267
- title={t("templatesPage.starterTemplates")}
268
- templates={starters}
325
+ title={t("templatesPage.yourTemplates")}
326
+ description={t("templatesPage.yourTemplatesDescription")}
327
+ templates={userTemplates}
269
328
  linkedTemplateId={linkedTemplateId}
329
+ empty={t("templatesPage.yourTemplatesEmpty")}
270
330
  onUse={openTemplatePrompt}
331
+ onDelete={setDeleteTemplate}
271
332
  />
272
333
  <TemplateSection
273
- title={t("templatesPage.savedTemplates")}
274
- description={t("templatesPage.savedTemplatesDescription")}
275
- templates={saved}
334
+ title={t("templatesPage.builtInTemplates")}
335
+ templates={builtIns}
276
336
  linkedTemplateId={linkedTemplateId}
277
- empty={t("templatesPage.savedEmpty")}
278
337
  onUse={openTemplatePrompt}
279
- onDelete={setDeleteTemplate}
280
338
  />
281
339
  </div>
282
340
  )}
@@ -286,17 +344,40 @@ export default function Templates() {
286
344
  open={promptOpen}
287
345
  onOpenChange={(open) => {
288
346
  setPromptOpen(open);
289
- if (!open) setSelected(null);
347
+ if (!open) {
348
+ setSelected(null);
349
+ setSelectedDesignSystemId(undefined);
350
+ setSelectedTemplateParam(null);
351
+ }
290
352
  }}
291
353
  title={selected?.title ?? t("templatesPage.useTemplate")}
292
354
  placeholder={t("templatesPage.promptPlaceholder")}
293
- onSkip={() => {
294
- if (selected) void createFromTemplate(selected);
355
+ onSkip={async () => {
356
+ if (!selected) return;
357
+ try {
358
+ await createFromTemplate(selected);
359
+ return false;
360
+ } catch (error) {
361
+ toast.error(
362
+ error instanceof Error
363
+ ? error.message
364
+ : t("templatesPage.createFailed"),
365
+ );
366
+ throw error;
367
+ }
295
368
  }}
296
369
  skipLabel={t("templatesPage.useAsIs")}
297
370
  onSubmit={handleSubmit}
298
371
  anchorRef={anchorRef}
299
372
  loading={creating}
373
+ designSystems={designSystems}
374
+ designSystemsLoading={designSystemsLoading}
375
+ selectedDesignSystemId={selectedDesignSystemId ?? null}
376
+ onDesignSystemChange={setSelectedDesignSystemId}
377
+ onCreateDesignSystem={() => {
378
+ setPromptOpen(false);
379
+ navigate("/design-systems/setup");
380
+ }}
300
381
  />
301
382
 
302
383
  <AlertDialog
@@ -407,14 +488,24 @@ function TemplateCard({
407
488
  <div className="flex min-h-40 flex-col p-4">
408
489
  <div className="flex items-start gap-2">
409
490
  <div className="min-w-0 flex-1">
410
- <h3 className="truncate text-sm font-medium text-foreground/90">
411
- {template.title}
412
- </h3>
491
+ <div className="flex min-w-0 items-center gap-2">
492
+ <h3 className="truncate text-sm font-medium text-foreground/90">
493
+ {template.title}
494
+ </h3>
495
+ {template.isBuiltIn ? (
496
+ <Badge
497
+ variant="secondary"
498
+ className="h-5 shrink-0 px-1.5 text-[10px] font-medium"
499
+ >
500
+ {t("templatesPage.builtIn")}
501
+ </Badge>
502
+ ) : null}
503
+ </div>
413
504
  <p className="mt-1 line-clamp-2 text-xs leading-relaxed text-muted-foreground">
414
505
  {template.description}
415
506
  </p>
416
507
  </div>
417
- {template.source === "saved" ? (
508
+ {!template.isBuiltIn && template.isOwner ? (
418
509
  <DropdownMenu>
419
510
  <DropdownMenuTrigger asChild>
420
511
  <Button
@@ -429,18 +520,16 @@ function TemplateCard({
429
520
  </Button>
430
521
  </DropdownMenuTrigger>
431
522
  <DropdownMenuContent align="end">
432
- {template.isOwner ? (
433
- <div className="p-1">
434
- <ShareButton
435
- resourceType="design-template"
436
- resourceId={template.id}
437
- resourceTitle={template.title}
438
- trigger="label"
439
- triggerClassName="w-full justify-start"
440
- />
441
- </div>
442
- ) : null}
443
- {template.isOwner && onDelete ? (
523
+ <div className="p-1">
524
+ <ShareButton
525
+ resourceType="design-template"
526
+ resourceId={template.id}
527
+ resourceTitle={template.title}
528
+ trigger="label"
529
+ triggerClassName="w-full justify-start"
530
+ />
531
+ </div>
532
+ {onDelete ? (
444
533
  <DropdownMenuItem
445
534
  onClick={() => onDelete(template)}
446
535
  className="text-destructive focus:text-destructive"
@@ -66,6 +66,17 @@ export function designSystemGenerationDirectives(
66
66
  ];
67
67
  }
68
68
 
69
+ export function designSystemTemplateEditDirectives(
70
+ designSystemId?: string | null,
71
+ ): string[] {
72
+ if (!designSystemId) return [];
73
+ return [
74
+ `Use design system id "${designSystemId}" while adapting the copied template.`,
75
+ "Use the selected design system context in this message as mandatory edit input. If details are missing or conflict, call `get-design-system` for that id before editing.",
76
+ "Apply the system through `edit-design` while preserving the copied structure and every locked subtree. Do not call `generate-design`.",
77
+ ];
78
+ }
79
+
69
80
  interface DesignSystemGenerationContextResult {
70
81
  title?: string;
71
82
  agentContext?: string;
@@ -169,7 +180,7 @@ export function designTemplateRefinementDirectives(
169
180
  ): string[] {
170
181
  return [
171
182
  `This design was copied from template "${templateId}". Its files, canvas dimensions, defaults, and locked layers already exist.`,
172
- ...designSystemGenerationDirectives(designSystemId),
183
+ ...designSystemTemplateEditDirectives(designSystemId),
173
184
  `Call \`get-design-snapshot --designId="${designId}"\` exactly once before editing.`,
174
185
  "Refine the existing template with `edit-design`; do not call `generate-design`, `delete-file`, or create a replacement screen.",
175
186
  'Layers marked `data-agent-native-locked="true"` and everything inside them must remain byte-for-byte unchanged. The server rejects changes to locked backgrounds, logos, and other fixed template layers.',
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-14
4
+ ---
5
+
6
+ Templates now appear in the New Design picker with previews, linked design systems, and clear built-in labels.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-15
4
+ ---
5
+
6
+ Templates now open immediately after copying, even while the designs list refreshes.
@@ -64,7 +64,7 @@ When the user asks for a new design and the current navigation view is list, set
64
64
 
65
65
  Every web design must be responsive. Use mobile-first CSS, a viewport meta tag, and responsive layout changes for narrow widths; never ship a fixed-width desktop shell. Desktop is the default primary artboard: use a 1440×1024 canvas frame (or primaryViewport "desktop") unless the user explicitly asks for a mobile- or tablet-primary design. After generation, inspect desktop and mobile screenshots and correct overflow or broken reflow before reporting completion.
66
66
 
67
- When the user asks to start from a template, call list-design-templates and then create-design-from-template. The copied files and canvas dimensions are already the starting point. If the user also supplied a prompt, call get-design-snapshot once and refine unlocked content with edit-design; do not call generate-design or replace the template with a fresh screen. Layers marked data-agent-native-locked="true" and their descendants must remain byte-for-byte unchanged. Ask the user to unlock one explicitly if they want it changed.
67
+ When the user asks to start from a template or references a prior design/past work as the starting point, call both list-design-templates and list-designs before generating so you resolve the existing resource instead of recreating it. For a template, call create-design-from-template. The copied files and canvas dimensions are already the starting point. If the user also supplied a prompt or selected a different linked design system, call get-design-snapshot once and refine unlocked content with edit-design; do not call generate-design or replace the template with a fresh screen. Layers marked data-agent-native-locked="true" and their descendants must remain byte-for-byte unchanged. Ask the user to unlock one explicitly if they want it changed.
68
68
 
69
69
  When the user asks you to refine an existing design, call view-screen if the open design is unclear, then read the live current file with get-design-snapshot before editing. For small localized changes, call edit-design with exact search/replace edits. For broad copy-only changes such as translating all visible text, call edit-design in replace-file mode with the complete updated file content from the snapshot so the HTML structure, scripts, styles, and tweaks are preserved without dozens of fragile search blocks. Do not claim the design is updated until the mutating action succeeds.
70
70
 
@@ -84,6 +84,11 @@ needed.
84
84
  call `refresh-list` so the UI refetches. Actions that already write
85
85
  `refresh-signal` internally (e.g. `mark-thread-read`, `move-email`,
86
86
  `respond-calendar-invite`) don't need a second call.
87
+ - For broad unread cleanup in one account, call `mark-read` once with
88
+ `scope: "all-unread"`, the exact `accountEmail`, and any protected
89
+ conversation IDs in `excludeThreadIds`. Do not loop `mark-thread-read` over
90
+ many conversations. The bulk result's matched, excluded, changed, failure,
91
+ and remaining-unread counts are the proof of completion.
87
92
  - Use `view-screen` when the active thread, selected message, draft, or queue
88
93
  item is unclear.
89
94
  - Aliases (Settings → Aliases) and provider API-key connections (Gong, Pylon,
@@ -94,24 +99,24 @@ needed.
94
99
 
95
100
  ## Action Map
96
101
 
97
- | Action | Purpose |
98
- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
99
- | `search-emails` / `list-emails` | Query across Gmail or local-fallback data by view/query. |
100
- | `get-email` / `get-thread` | Full body/metadata for one message or thread. |
101
- | `find-contact` | Resolve a name/partial address to a real email. |
102
- | `get-hubspot-contact` | CRM contact + deals + tickets by email (HubSpot only). |
103
- | `create-attachment-upload` | Mint a five-minute, owner-bound raw-byte upload URL for one local attachment. |
104
- | `manage-draft` | Create/update/delete a `compose-{id}` draft (signature-aware). |
105
- | `send-email` | Real send. `needsApproval: true`; exact authenticated chat grants run once, otherwise it pauses for approval. |
106
- | `queue-email-draft` / `list-queued-drafts` / `update-queued-draft` / `open-queued-draft` / `send-queued-drafts` | Teammate/Slack draft-review workflow — see `draft-queue`. |
107
- | `mark-read` / `mark-thread-read` / `star-email` / `archive-email` / `unarchive-email` / `trash-email` / `untrash-email` / `move-email` | Per-message or per-thread state changes; most call `refresh-list` internally. |
108
- | `manage-gmail-filters` | Provider-native Gmail filters (create/replace/delete). |
109
- | `manage-automations` / `trigger-automations` | Natural-language inbox automation rules. |
110
- | `respond-calendar-invite` | Accept/decline/tentative an invite found in Mail. |
111
- | `get-mail-settings` / `update-mail-settings` / `import-gmail-signature` | Signature and writing-style settings. |
112
- | `manage-snippets` | List/create/update/delete saved reply snippets insertable from the compose slash menu. |
113
- | `get-tracking` | Open/click stats for a previously sent, tracked message. |
114
- | `provider-api-catalog` / `provider-api-docs` / `provider-api-request` | Raw Gmail, Calendar, or HubSpot API calls beyond the canned actions. |
102
+ | Action | Purpose |
103
+ | -------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
104
+ | `search-emails` / `list-emails` | Query across Gmail or local-fallback data by view/query. |
105
+ | `get-email` / `get-thread` | Full body/metadata for one message or thread. |
106
+ | `find-contact` | Resolve a name/partial address to a real email. |
107
+ | `get-hubspot-contact` | CRM contact + deals + tickets by email (HubSpot only). |
108
+ | `create-attachment-upload` | Mint a five-minute, owner-bound raw-byte upload URL for one local attachment. |
109
+ | `manage-draft` | Create/update/delete a `compose-{id}` draft (signature-aware). |
110
+ | `send-email` | Real send. `needsApproval: true`; exact authenticated chat grants run once, otherwise it pauses for approval. |
111
+ | `queue-email-draft` / `list-queued-drafts` / `update-queued-draft` / `open-queued-draft` / `send-queued-drafts` | Teammate/Slack draft-review workflow — see `draft-queue`. |
112
+ | `mark-read` / `mark-thread-read` / `star-email` / `archive-email` / `unarchive-email` / `trash-email` / `untrash-email` / `move-email` | Per-message or per-thread state changes. `mark-read` also supports one-account verified bulk unread cleanup with protected-thread exclusions; most call `refresh-list` internally. |
113
+ | `manage-gmail-filters` | Provider-native Gmail filters (create/replace/delete). |
114
+ | `manage-automations` / `trigger-automations` | Natural-language inbox automation rules. |
115
+ | `respond-calendar-invite` | Accept/decline/tentative an invite found in Mail. |
116
+ | `get-mail-settings` / `update-mail-settings` / `import-gmail-signature` | Signature and writing-style settings. |
117
+ | `manage-snippets` | List/create/update/delete saved reply snippets insertable from the compose slash menu. |
118
+ | `get-tracking` | Open/click stats for a previously sent, tracked message. |
119
+ | `provider-api-catalog` / `provider-api-docs` / `provider-api-request` | Raw Gmail, Calendar, or HubSpot API calls beyond the canned actions. |
115
120
 
116
121
  ## Application State
117
122
 
@@ -1,7 +1,13 @@
1
1
  import { defineAction } from "@agent-native/core";
2
- import { getSetting, putSetting } from "@agent-native/core/settings";
2
+ import { getRequestUserEmail } from "@agent-native/core/server";
3
3
  import { z } from "zod";
4
4
 
5
+ import {
6
+ readLocalEmails,
7
+ withLocalEmailMutationLock,
8
+ writeLocalEmails,
9
+ } from "../server/lib/local-email-store.js";
10
+
5
11
  export default defineAction({
6
12
  description:
7
13
  "Archive emails older than N days from inbox (local data only — use archive-email for Gmail-connected accounts).",
@@ -19,34 +25,32 @@ export default defineAction({
19
25
  if (isNaN(days) || days < 1)
20
26
  throw new Error("--older-than must be a positive integer (days)");
21
27
 
22
- const data = await getSetting("local-emails");
23
- if (!data || !Array.isArray((data as any).emails)) {
24
- throw new Error(
25
- "No local emails data found. This tool only works with local data.",
26
- );
27
- }
28
+ const ownerEmail = getRequestUserEmail();
29
+ if (!ownerEmail) throw new Error("no authenticated user");
28
30
 
29
- const emails: any[] = (data as any).emails;
30
- const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
31
- let archived = 0;
32
- const updated = emails.map((email) => {
33
- if (
34
- !email.isArchived &&
35
- !email.isTrashed &&
36
- !email.isDraft &&
37
- new Date(email.date).getTime() < cutoff
38
- ) {
39
- archived++;
40
- return {
41
- ...email,
42
- isArchived: true,
43
- labelIds: email.labelIds.filter((l: string) => l !== "inbox"),
44
- };
45
- }
46
- return email;
47
- });
31
+ return withLocalEmailMutationLock(ownerEmail, async () => {
32
+ const emails = await readLocalEmails(ownerEmail);
33
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
34
+ let archived = 0;
35
+ const updated = emails.map((email) => {
36
+ if (
37
+ !email.isArchived &&
38
+ !email.isTrashed &&
39
+ !email.isDraft &&
40
+ new Date(email.date).getTime() < cutoff
41
+ ) {
42
+ archived++;
43
+ return {
44
+ ...email,
45
+ isArchived: true,
46
+ labelIds: email.labelIds.filter((label) => label !== "inbox"),
47
+ };
48
+ }
49
+ return email;
50
+ });
48
51
 
49
- await putSetting("local-emails", { emails: updated });
50
- return `Archived ${archived} email(s) older than ${days} days (${emails.length} total)`;
52
+ await writeLocalEmails(ownerEmail, updated);
53
+ return `Archived ${archived} email(s) older than ${days} days (${emails.length} total)`;
54
+ });
51
55
  },
52
56
  });