@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
@@ -27,12 +27,15 @@ import {
27
27
  } from "@tabler/icons-react";
28
28
  import { useQueryClient } from "@tanstack/react-query";
29
29
  import { nanoid } from "nanoid";
30
- import { useState, useRef, useCallback, useEffect } from "react";
31
- import { useNavigate, Link } from "react-router";
30
+ import { useState, useRef, useCallback, useEffect, useMemo } from "react";
31
+ import { useNavigate, Link, useSearchParams } from "react-router";
32
32
  import { toast } from "sonner";
33
33
 
34
34
  import PromptPopover from "@/components/editor/PromptDialog";
35
- import type { UploadedFile } from "@/components/editor/PromptDialog";
35
+ import type {
36
+ PromptTemplateOption,
37
+ UploadedFile,
38
+ } from "@/components/editor/PromptDialog";
36
39
  import { QueryErrorState } from "@/components/QueryErrorState";
37
40
  import {
38
41
  AlertDialog,
@@ -83,6 +86,7 @@ interface Design {
83
86
  export default function Index() {
84
87
  const t = useT();
85
88
  const navigate = useNavigate();
89
+ const [searchParams, setSearchParams] = useSearchParams();
86
90
  const queryClient = useQueryClient();
87
91
  const [search, setSearch] = useState("");
88
92
  const [deleteId, setDeleteId] = useState<string | null>(null);
@@ -95,6 +99,7 @@ export default function Index() {
95
99
  const [newDesignSystemId, setNewDesignSystemId] = useState<
96
100
  string | null | undefined
97
101
  >(undefined);
102
+ const [newTemplateId, setNewTemplateId] = useState<string | null>(null);
98
103
  // "Design" (default, inline prototype) vs "Full app" (Builder Fusion
99
104
  // cloud container). Only reachable behind FULL_APP_BUILDING_ENABLED — the
100
105
  // popover renders no mode control at all when the flag is off, so this
@@ -108,6 +113,7 @@ export default function Index() {
108
113
  const anchorElRef = useRef<HTMLElement | null>(null);
109
114
  const anchorRef = useRef<HTMLElement | null>(null);
110
115
  const skipToEditorPendingRef = useRef(false);
116
+ const newDesignSystemWasChosenRef = useRef(false);
111
117
  // Keep anchorRef.current in sync so PromptPopover can read it
112
118
  anchorRef.current = anchorElRef.current;
113
119
 
@@ -117,12 +123,16 @@ export default function Index() {
117
123
  isError,
118
124
  isFetching,
119
125
  refetch,
120
- } = useActionQuery<{
121
- count: number;
122
- designs: Design[];
123
- }>("list-designs", { includePreview: "true" });
126
+ } = useActionQuery("list-designs", { includePreview: "true" });
127
+ const { data: templatesData, isLoading: templatesLoading } = useActionQuery(
128
+ "list-design-templates",
129
+ { includePreview: "true" },
130
+ );
124
131
 
125
132
  const createMutation = useActionMutation("create-design");
133
+ const createFromTemplateMutation = useActionMutation(
134
+ "create-design-from-template",
135
+ );
126
136
  // Fires the fusion-backed cloud container build; only ever called when
127
137
  // FULL_APP_BUILDING_ENABLED is true and the user picked "Full app".
128
138
  const createFusionAppMutation = useActionMutation("create-fusion-app");
@@ -139,7 +149,24 @@ export default function Index() {
139
149
  isLoading: designSystemsLoading,
140
150
  } = useDesignSystems();
141
151
 
142
- const designs = designsData?.designs ?? [];
152
+ const designs = (designsData?.designs ?? []) as Design[];
153
+ const templateOptions = useMemo<PromptTemplateOption[]>(
154
+ () =>
155
+ (templatesData?.templates ?? []).map((template) => ({
156
+ id: template.id,
157
+ title: template.title,
158
+ description: template.description,
159
+ category: template.category,
160
+ width: template.width,
161
+ height: template.height,
162
+ previewHtml: template.previewHtml,
163
+ designSystemId: template.designSystemId,
164
+ isBuiltIn: template.isBuiltIn,
165
+ })),
166
+ [templatesData?.templates],
167
+ );
168
+ const selectedTemplate =
169
+ templateOptions.find((template) => template.id === newTemplateId) ?? null;
143
170
 
144
171
  const filtered = search
145
172
  ? designs.filter((d) =>
@@ -157,24 +184,42 @@ export default function Index() {
157
184
  [defaultSystem?.id, designSystems],
158
185
  );
159
186
 
187
+ const syncSelectedTemplate = useCallback(
188
+ (templateId: string | null) => {
189
+ setNewTemplateId(templateId);
190
+ const next = new URLSearchParams(searchParams);
191
+ if (templateId) next.set("templateId", templateId);
192
+ else next.delete("templateId");
193
+ setSearchParams(next, { replace: true });
194
+ },
195
+ [searchParams, setSearchParams],
196
+ );
197
+
160
198
  const openNewDesign = useCallback(
161
199
  (e: React.MouseEvent<HTMLElement>) => {
162
200
  anchorElRef.current = e.currentTarget;
201
+ newDesignSystemWasChosenRef.current = false;
202
+ syncSelectedTemplate(null);
163
203
  setNewDesignSystemId(
164
204
  designSystemsLoading ? undefined : resolveDefaultDesignSystemId(),
165
205
  );
166
206
  setShowNewPrompt(true);
167
207
  },
168
- [designSystemsLoading, resolveDefaultDesignSystemId],
208
+ [designSystemsLoading, resolveDefaultDesignSystemId, syncSelectedTemplate],
169
209
  );
170
210
 
171
- const handleNewPromptOpenChange = useCallback((open: boolean) => {
172
- setShowNewPrompt(open);
173
- if (!open) {
174
- setNewDesignSystemId(undefined);
175
- setNewDesignMode("design");
176
- }
177
- }, []);
211
+ const handleNewPromptOpenChange = useCallback(
212
+ (open: boolean) => {
213
+ setShowNewPrompt(open);
214
+ if (!open) {
215
+ newDesignSystemWasChosenRef.current = false;
216
+ syncSelectedTemplate(null);
217
+ setNewDesignSystemId(undefined);
218
+ setNewDesignMode("design");
219
+ }
220
+ },
221
+ [syncSelectedTemplate],
222
+ );
178
223
 
179
224
  useEffect(() => {
180
225
  if (
@@ -191,6 +236,40 @@ export default function Index() {
191
236
  showNewPrompt,
192
237
  ]);
193
238
 
239
+ const handleTemplateChange = useCallback(
240
+ (templateId: string | null) => {
241
+ syncSelectedTemplate(templateId);
242
+ const template = templateOptions.find(
243
+ (candidate) => candidate.id === templateId,
244
+ );
245
+ if (newDesignSystemWasChosenRef.current) return;
246
+ const linkedSystemId =
247
+ template?.designSystemId &&
248
+ designSystems.some((system) => system.id === template.designSystemId)
249
+ ? template.designSystemId
250
+ : null;
251
+ setNewDesignSystemId(
252
+ linkedSystemId ??
253
+ (designSystemsLoading ? undefined : resolveDefaultDesignSystemId()),
254
+ );
255
+ },
256
+ [
257
+ designSystems,
258
+ designSystemsLoading,
259
+ resolveDefaultDesignSystemId,
260
+ syncSelectedTemplate,
261
+ templateOptions,
262
+ ],
263
+ );
264
+
265
+ const handleNewDesignSystemChange = useCallback(
266
+ (designSystemId: string | null) => {
267
+ newDesignSystemWasChosenRef.current = true;
268
+ setNewDesignSystemId(designSystemId);
269
+ },
270
+ [],
271
+ );
272
+
194
273
  const toggleDesignSelection = useCallback((id: string) => {
195
274
  setSelectedDesignIds((current) => {
196
275
  const next = new Set(current);
@@ -318,21 +397,86 @@ export default function Index() {
318
397
  );
319
398
 
320
399
  const handleSubmitPrompt = useCallback(
321
- (
400
+ async (
322
401
  prompt: string,
323
402
  files: UploadedFile[],
324
403
  options: PromptComposerSubmitOptions,
325
404
  pendingOptions?: { skipQuestions?: boolean },
326
405
  ) => {
327
- // Derive a short title from the prompt — first line, ~40 chars max,
328
- // word-boundary truncated. The full prompt still drives generation;
329
- // the title is just a label, so longer is worse.
330
- const derivedTitle = derivePromptTitle(prompt);
406
+ const trimmedPrompt = prompt.trim();
331
407
  const designSystemId =
332
408
  newDesignSystemId === undefined
333
409
  ? resolveDefaultDesignSystemId()
334
410
  : newDesignSystemId;
335
411
 
412
+ if (selectedTemplate && newDesignMode === "design") {
413
+ setNewDesignHandoffPending(true);
414
+ const title = trimmedPrompt
415
+ ? derivePromptTitle(trimmedPrompt)
416
+ : selectedTemplate.title;
417
+ try {
418
+ const result = await createFromTemplateMutation.mutateAsync({
419
+ templateId: selectedTemplate.id,
420
+ title,
421
+ designSystemId,
422
+ ...(trimmedPrompt ? { prompt: trimmedPrompt } : {}),
423
+ });
424
+ if (!result.id) {
425
+ throw new Error("Template copy did not return a design ID");
426
+ }
427
+ const effectiveDesignSystemId = result.designSystemId ?? null;
428
+ if (result.adaptationPending) {
429
+ const effectiveSystemTitle =
430
+ designSystems.find(
431
+ (system) => system.id === effectiveDesignSystemId,
432
+ )?.title ?? t("promptDialog.designSystem");
433
+ writePendingGeneration(result.id, {
434
+ prompt:
435
+ trimmedPrompt ||
436
+ t("promptDialog.reskinTemplatePrompt", {
437
+ title: selectedTemplate.title,
438
+ system: effectiveSystemTitle,
439
+ }),
440
+ files,
441
+ title: result.title ?? title,
442
+ source: selectedTemplate.title,
443
+ templateId: selectedTemplate.id,
444
+ templateBaselineFiles: result.templateBaselineFiles,
445
+ designSystemId: effectiveDesignSystemId,
446
+ skipQuestions: true,
447
+ ...options,
448
+ });
449
+ }
450
+ if (trimmedPrompt) {
451
+ handleGenerateDesignTitle(
452
+ result.id,
453
+ trimmedPrompt,
454
+ result.title ?? title,
455
+ );
456
+ }
457
+ void queryClient
458
+ .invalidateQueries({
459
+ queryKey: ["action", "list-designs"],
460
+ })
461
+ .catch(() => {});
462
+ navigate(`/design/${result.id}`);
463
+ return;
464
+ } catch (error) {
465
+ setNewDesignHandoffPending(false);
466
+ toast.error(
467
+ error instanceof Error
468
+ ? error.message
469
+ : t("templatesPage.createFailed"),
470
+ );
471
+ throw error;
472
+ }
473
+ }
474
+
475
+ // Derive a short title from the prompt — first line, ~40 chars max,
476
+ // word-boundary truncated. The full prompt still drives generation;
477
+ // the title is just a label, so longer is worse.
478
+ const derivedTitle = derivePromptTitle(prompt);
479
+
336
480
  const { id, title, ready } = createDesign(derivedTitle, designSystemId);
337
481
  handleGenerateDesignTitle(id, prompt, title);
338
482
 
@@ -394,16 +538,25 @@ export default function Index() {
394
538
  },
395
539
  [
396
540
  createDesign,
541
+ createFromTemplateMutation,
397
542
  createFusionAppMutation,
543
+ designSystems,
398
544
  handleGenerateDesignTitle,
399
545
  navigate,
400
546
  newDesignMode,
401
547
  newDesignSystemId,
548
+ queryClient,
402
549
  resolveDefaultDesignSystemId,
550
+ selectedTemplate,
551
+ t,
403
552
  ],
404
553
  );
405
554
 
406
555
  const handleSkipToEditor = useCallback(async () => {
556
+ if (selectedTemplate && newDesignMode === "design") {
557
+ await handleSubmitPrompt("", [], {});
558
+ return false;
559
+ }
407
560
  if (skipToEditorPendingRef.current) return;
408
561
  skipToEditorPendingRef.current = true;
409
562
  setNewDesignHandoffPending(true);
@@ -423,6 +576,7 @@ export default function Index() {
423
576
  // row to persist so the first get-design read cannot briefly return 404.
424
577
  await ready;
425
578
  navigate(`/design/${id}`);
579
+ return false;
426
580
  } catch (error) {
427
581
  skipToEditorPendingRef.current = false;
428
582
  setNewDesignHandoffPending(false);
@@ -431,9 +585,12 @@ export default function Index() {
431
585
  }
432
586
  }, [
433
587
  createDesign,
588
+ handleSubmitPrompt,
434
589
  navigate,
590
+ newDesignMode,
435
591
  newDesignSystemId,
436
592
  resolveDefaultDesignSystemId,
593
+ selectedTemplate,
437
594
  t,
438
595
  ]);
439
596
 
@@ -801,15 +958,29 @@ export default function Index() {
801
958
  open={showNewPrompt}
802
959
  onOpenChange={handleNewPromptOpenChange}
803
960
  title={t("home.newDesignLower")}
804
- placeholder={t("home.describeBuild")}
961
+ placeholder={
962
+ selectedTemplate
963
+ ? t("promptDialog.templatePromptPlaceholder", {
964
+ title: selectedTemplate.title,
965
+ })
966
+ : t("home.describeBuild")
967
+ }
805
968
  onSkip={handleSkipToEditor}
806
- skipLabel={t("home.skipToEditor")}
969
+ skipLabel={
970
+ selectedTemplate
971
+ ? t("templatesPage.useTemplate")
972
+ : t("home.skipToEditor")
973
+ }
807
974
  onSubmit={handleSubmitPrompt}
808
975
  anchorRef={anchorRef}
976
+ templateOptions={templateOptions}
977
+ templatesLoading={templatesLoading}
978
+ selectedTemplateId={newTemplateId}
979
+ onTemplateChange={handleTemplateChange}
809
980
  designSystems={designSystems}
810
981
  designSystemsLoading={designSystemsLoading}
811
982
  selectedDesignSystemId={newDesignSystemId ?? null}
812
- onDesignSystemChange={setNewDesignSystemId}
983
+ onDesignSystemChange={handleNewDesignSystemChange}
813
984
  loading={newDesignHandoffPending}
814
985
  onCreateDesignSystem={() => {
815
986
  handleNewPromptOpenChange(false);