@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
@@ -8,17 +8,30 @@ import {
8
8
  EmbeddedApp,
9
9
  type EmbeddedAppRef,
10
10
  } from "@agent-native/core/embedding/react";
11
+ import {
12
+ Command,
13
+ CommandEmpty,
14
+ CommandGroup,
15
+ CommandInput,
16
+ CommandItem,
17
+ CommandList,
18
+ } from "@agent-native/toolkit/ui/command";
11
19
  import {
12
20
  IconApps,
21
+ IconCheck,
22
+ IconChevronDown,
13
23
  IconPalette,
14
24
  IconPhoto,
15
25
  IconPlus,
26
+ IconTemplate,
16
27
  IconUpload,
17
28
  IconX,
18
29
  } from "@tabler/icons-react";
19
30
  import { useState, useEffect, useCallback, useRef } from "react";
20
31
  import { toast } from "sonner";
21
32
 
33
+ import { TemplatePreview } from "@/components/templates/TemplatePreview";
34
+ import { Badge } from "@/components/ui/badge";
22
35
  import { Button } from "@/components/ui/button";
23
36
  import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
24
37
  import {
@@ -32,9 +45,14 @@ import {
32
45
  SelectContent,
33
46
  SelectItem,
34
47
  SelectTrigger,
35
- SelectValue,
36
48
  } from "@/components/ui/select";
37
49
  import { Skeleton } from "@/components/ui/skeleton";
50
+ import {
51
+ Tooltip,
52
+ TooltipContent,
53
+ TooltipTrigger,
54
+ } from "@/components/ui/tooltip";
55
+ import { cn } from "@/lib/utils";
38
56
 
39
57
  export interface UploadedFile {
40
58
  path: string;
@@ -296,7 +314,9 @@ interface PromptPopoverProps {
296
314
  onOpenChange: (open: boolean) => void;
297
315
  title: string;
298
316
  placeholder?: string;
299
- onSkip?: () => void | Promise<void>;
317
+ /** Return false when the caller navigates and the popover must not issue a
318
+ * competing close-state navigation after the handoff completes. */
319
+ onSkip?: () => void | boolean | Promise<void | boolean>;
300
320
  skipLabel?: string;
301
321
  onSubmit: (
302
322
  prompt: string,
@@ -311,6 +331,10 @@ interface PromptPopoverProps {
311
331
  selectedDesignSystemId?: string | null;
312
332
  onDesignSystemChange?: (id: string | null) => void;
313
333
  onCreateDesignSystem?: () => void;
334
+ templateOptions?: PromptTemplateOption[];
335
+ templatesLoading?: boolean;
336
+ selectedTemplateId?: string | null;
337
+ onTemplateChange?: (id: string | null) => void;
314
338
  /**
315
339
  * "Design" (inline prototype, default) vs "Full app" (Builder Fusion cloud
316
340
  * container). Omit both this and `onCreationModeChange` to hide the mode
@@ -338,12 +362,24 @@ export interface PromptDesignSystemOption {
338
362
  isDefault?: boolean;
339
363
  }
340
364
 
365
+ export interface PromptTemplateOption {
366
+ id: string;
367
+ title: string;
368
+ description?: string | null;
369
+ category?: string;
370
+ width?: number | null;
371
+ height?: number | null;
372
+ previewHtml?: string | null;
373
+ designSystemId?: string | null;
374
+ isBuiltIn: boolean;
375
+ }
376
+
341
377
  function isNestedPromptPopoverTarget(target: EventTarget | null) {
342
378
  return (
343
379
  target instanceof Element &&
344
380
  Boolean(
345
381
  target.closest(
346
- "[data-agent-native-composer-popover],[data-assets-picker-dialog],[data-agent-native-prompt-select]",
382
+ "[data-agent-native-composer-popover],[data-assets-picker-dialog],[data-agent-native-prompt-select],[data-agent-native-template-popover]",
347
383
  ),
348
384
  )
349
385
  );
@@ -371,7 +407,8 @@ function hasOpenNestedPromptPopoverSurface() {
371
407
  document.querySelector(
372
408
  '[data-agent-native-composer-popover][data-state="open"],' +
373
409
  "[data-assets-picker-dialog]," +
374
- '[data-agent-native-prompt-select][data-state="open"]',
410
+ '[data-agent-native-prompt-select][data-state="open"],' +
411
+ '[data-agent-native-template-popover][data-state="open"]',
375
412
  ),
376
413
  );
377
414
  }
@@ -392,6 +429,10 @@ export default function PromptPopover({
392
429
  selectedDesignSystemId,
393
430
  onDesignSystemChange,
394
431
  onCreateDesignSystem,
432
+ templateOptions = [],
433
+ templatesLoading = false,
434
+ selectedTemplateId,
435
+ onTemplateChange,
395
436
  creationMode,
396
437
  onCreationModeChange,
397
438
  draftScope,
@@ -403,6 +444,7 @@ export default function PromptPopover({
403
444
  const [pickedAssets, setPickedAssets] = useState<UploadedFile[]>([]);
404
445
  const [selectedUploadFiles, setSelectedUploadFiles] = useState<File[]>([]);
405
446
  const [assetsPickerOpen, setAssetsPickerOpen] = useState(false);
447
+ const [templatePickerOpen, setTemplatePickerOpen] = useState(false);
406
448
  // Restores a typed prompt into the composer after a failed submit. The
407
449
  // composer optimistically clears its text as soon as onSubmit is invoked
408
450
  // (see TiptapComposer.submitComposer), so without this an upload failure or
@@ -459,6 +501,7 @@ export default function PromptPopover({
459
501
  useEffect(() => {
460
502
  if (open) return;
461
503
  setAssetsPickerOpen(false);
504
+ setTemplatePickerOpen(false);
462
505
  setPickedAssets([]);
463
506
  setSelectedUploadFiles([]);
464
507
  // Only sticks for the session immediately following a failed submit; a
@@ -659,6 +702,9 @@ export default function PromptPopover({
659
702
  },
660
703
  },
661
704
  }));
705
+ const selectedDesignSystem =
706
+ designSystems.find((system) => system.id === selectedDesignSystemId) ??
707
+ null;
662
708
 
663
709
  return (
664
710
  <Popover open={open} onOpenChange={handlePopoverOpenChange}>
@@ -718,6 +764,7 @@ export default function PromptPopover({
718
764
 
719
765
  <div className="px-2 pb-2">
720
766
  <PromptComposer
767
+ key={placeholder ?? t("home.describeBuild")}
721
768
  autoFocus
722
769
  attachmentsEnabled
723
770
  disabled={loading || uploading}
@@ -738,58 +785,94 @@ export default function PromptPopover({
738
785
  />
739
786
  </div>
740
787
 
741
- {(onDesignSystemChange || onCreateDesignSystem) && (
742
- <div className="flex flex-wrap items-center gap-2 border-t border-border px-3.5 py-2">
743
- <div className="flex min-w-0 flex-1 items-center gap-2">
744
- <IconPalette className="h-4 w-4 shrink-0 text-muted-foreground" />
745
- {designSystems.length > 0 ? (
746
- <Select
747
- value={selectedDesignSystemId ?? "none"}
748
- onValueChange={(value) =>
749
- onDesignSystemChange?.(value === "none" ? null : value)
750
- }
751
- onOpenChange={(nextOpen) => {
752
- if (!nextOpen) markNestedSelectJustClosed();
753
- }}
754
- disabled={designSystemsLoading}
755
- >
756
- <SelectTrigger className="h-8 min-w-0 flex-1 text-xs">
757
- <SelectValue placeholder={t("promptDialog.designSystem")} />
758
- </SelectTrigger>
759
- <SelectContent data-agent-native-prompt-select>
760
- <SelectItem value="none" className="text-xs">
761
- {t("promptDialog.noDesignSystem")}
762
- </SelectItem>
763
- {designSystems.map((system) => (
764
- <SelectItem
765
- key={system.id}
766
- value={system.id}
767
- className="text-xs"
788
+ {(onTemplateChange || onDesignSystemChange || onCreateDesignSystem) && (
789
+ <div className="grid grid-cols-[minmax(0,1fr)_2.25rem] gap-2 border-t border-border px-3.5 py-2.5">
790
+ {onTemplateChange ? (
791
+ <>
792
+ <TemplatePickerControl
793
+ open={templatePickerOpen}
794
+ onOpenChange={setTemplatePickerOpen}
795
+ options={templateOptions}
796
+ loading={templatesLoading}
797
+ selectedId={selectedTemplateId ?? null}
798
+ onChange={onTemplateChange}
799
+ />
800
+ <span aria-hidden="true" className="size-9" />
801
+ </>
802
+ ) : null}
803
+ {onDesignSystemChange || onCreateDesignSystem ? (
804
+ <>
805
+ {designSystemsLoading ? (
806
+ <Skeleton className="h-9 w-full rounded-md" />
807
+ ) : designSystems.length > 0 ? (
808
+ <Select
809
+ value={selectedDesignSystemId ?? "none"}
810
+ onValueChange={(value) =>
811
+ onDesignSystemChange?.(value === "none" ? null : value)
812
+ }
813
+ onOpenChange={(nextOpen) => {
814
+ if (!nextOpen) markNestedSelectJustClosed();
815
+ }}
816
+ >
817
+ <SelectTrigger className="h-9 min-w-0 justify-start gap-2 px-2.5 text-xs [&>svg:last-child]:ms-auto">
818
+ <IconPalette className="size-4 shrink-0 text-muted-foreground" />
819
+ <span
820
+ className="min-w-0 flex-1 truncate text-start"
821
+ title={
822
+ selectedDesignSystem?.title ??
823
+ t("promptDialog.noDesignSystem")
824
+ }
768
825
  >
769
- {system.title}
770
- {system.isDefault ? " (default)" : ""}
826
+ {selectedDesignSystem?.title ??
827
+ t("promptDialog.noDesignSystem")}
828
+ </span>
829
+ </SelectTrigger>
830
+ <SelectContent data-agent-native-prompt-select>
831
+ <SelectItem value="none" className="text-xs">
832
+ {t("promptDialog.noDesignSystem")}
771
833
  </SelectItem>
772
- ))}
773
- </SelectContent>
774
- </Select>
775
- ) : (
776
- <span className="min-w-0 flex-1 truncate text-xs text-muted-foreground">
777
- {t("promptDialog.noDesignSystem")}
778
- </span>
779
- )}
780
- </div>
781
- {onCreateDesignSystem && (
782
- <Button
783
- type="button"
784
- variant="outline"
785
- size="sm"
786
- className="h-8 shrink-0"
787
- onClick={onCreateDesignSystem}
788
- >
789
- <IconPlus className="h-3.5 w-3.5" />
790
- {t("promptDialog.newDesignSystem")}
791
- </Button>
792
- )}
834
+ {designSystems.map((system) => (
835
+ <SelectItem
836
+ key={system.id}
837
+ value={system.id}
838
+ className="text-xs"
839
+ >
840
+ {system.title}
841
+ </SelectItem>
842
+ ))}
843
+ </SelectContent>
844
+ </Select>
845
+ ) : (
846
+ <div className="flex h-9 min-w-0 items-center gap-2 rounded-md border border-input px-2.5 text-xs text-muted-foreground">
847
+ <IconPalette className="size-4 shrink-0" />
848
+ <span className="truncate">
849
+ {t("promptDialog.noDesignSystem")}
850
+ </span>
851
+ </div>
852
+ )}
853
+ {onCreateDesignSystem ? (
854
+ <Tooltip>
855
+ <TooltipTrigger asChild>
856
+ <Button
857
+ type="button"
858
+ variant="outline"
859
+ size="icon"
860
+ className="size-9 shrink-0"
861
+ onClick={onCreateDesignSystem}
862
+ aria-label={t("promptDialog.createDesignSystem")}
863
+ >
864
+ <IconPlus className="size-4" />
865
+ </Button>
866
+ </TooltipTrigger>
867
+ <TooltipContent>
868
+ {t("promptDialog.createDesignSystem")}
869
+ </TooltipContent>
870
+ </Tooltip>
871
+ ) : (
872
+ <span aria-hidden="true" className="size-9" />
873
+ )}
874
+ </>
875
+ ) : null}
793
876
  </div>
794
877
  )}
795
878
 
@@ -845,8 +928,8 @@ export default function PromptPopover({
845
928
  setSkipInFlight(true);
846
929
  void (async () => {
847
930
  try {
848
- await onSkip();
849
- onOpenChange(false);
931
+ const shouldClose = await onSkip();
932
+ if (shouldClose !== false) onOpenChange(false);
850
933
  } catch {
851
934
  // The caller owns error presentation. Keep the prompt open
852
935
  // and usable so the user can retry or submit a prompt.
@@ -874,6 +957,148 @@ export default function PromptPopover({
874
957
  );
875
958
  }
876
959
 
960
+ function TemplatePickerControl({
961
+ open,
962
+ onOpenChange,
963
+ options,
964
+ loading,
965
+ selectedId,
966
+ onChange,
967
+ }: {
968
+ open: boolean;
969
+ onOpenChange: (open: boolean) => void;
970
+ options: PromptTemplateOption[];
971
+ loading: boolean;
972
+ selectedId: string | null;
973
+ onChange: (id: string | null) => void;
974
+ }) {
975
+ const t = useT();
976
+ const selected = options.find((option) => option.id === selectedId) ?? null;
977
+ const userTemplates = options.filter((option) => !option.isBuiltIn);
978
+ const builtInTemplates = options.filter((option) => option.isBuiltIn);
979
+
980
+ const choose = (id: string | null) => {
981
+ onChange(id);
982
+ onOpenChange(false);
983
+ };
984
+
985
+ const item = (template: PromptTemplateOption) => (
986
+ <CommandItem
987
+ key={template.id}
988
+ value={`${template.title} ${template.description ?? ""} ${template.category ?? ""}`}
989
+ onSelect={() => choose(template.id)}
990
+ data-template-option={template.id}
991
+ className="min-h-12 py-2"
992
+ >
993
+ <TemplatePreview
994
+ html={template.previewHtml}
995
+ title={template.title}
996
+ width={template.width}
997
+ height={template.height}
998
+ className="h-8 w-12 shrink-0 rounded-md border bg-muted/40"
999
+ />
1000
+ <span className="min-w-0 flex-1 truncate">{template.title}</span>
1001
+ {template.isBuiltIn ? (
1002
+ <Badge
1003
+ variant="secondary"
1004
+ className="h-5 shrink-0 px-1.5 text-[10px] font-medium"
1005
+ >
1006
+ {t("promptDialog.builtIn")}
1007
+ </Badge>
1008
+ ) : null}
1009
+ {selectedId === template.id ? (
1010
+ <IconCheck className="size-4 shrink-0" />
1011
+ ) : null}
1012
+ </CommandItem>
1013
+ );
1014
+
1015
+ return (
1016
+ <Popover open={open} onOpenChange={onOpenChange}>
1017
+ <PopoverTrigger asChild>
1018
+ <Button
1019
+ type="button"
1020
+ variant="outline"
1021
+ size="sm"
1022
+ className="h-9 w-full min-w-0 justify-start gap-2 px-2.5 text-xs"
1023
+ aria-label={t("promptDialog.chooseTemplate")}
1024
+ disabled={loading}
1025
+ data-template-picker-trigger
1026
+ >
1027
+ {selected ? (
1028
+ <TemplatePreview
1029
+ html={selected.previewHtml}
1030
+ title={selected.title}
1031
+ width={selected.width}
1032
+ height={selected.height}
1033
+ className="h-4 w-7 shrink-0 rounded-[3px] border bg-muted/40"
1034
+ />
1035
+ ) : (
1036
+ <IconTemplate className="size-4 shrink-0 text-muted-foreground" />
1037
+ )}
1038
+ <span
1039
+ className={cn(
1040
+ "min-w-0 truncate",
1041
+ selected ? "text-foreground" : "text-muted-foreground",
1042
+ )}
1043
+ >
1044
+ {t("promptDialog.template")} ·{" "}
1045
+ {selected ? selected.title : t("promptDialog.blank")}
1046
+ </span>
1047
+ {selected?.isBuiltIn ? (
1048
+ <Badge
1049
+ variant="secondary"
1050
+ className="h-5 shrink-0 px-1.5 text-[10px] font-medium"
1051
+ >
1052
+ {t("promptDialog.builtIn")}
1053
+ </Badge>
1054
+ ) : null}
1055
+ <IconChevronDown className="ms-auto size-4 shrink-0 text-muted-foreground" />
1056
+ </Button>
1057
+ </PopoverTrigger>
1058
+ <PopoverContent
1059
+ align="start"
1060
+ side="top"
1061
+ sideOffset={8}
1062
+ data-agent-native-template-popover
1063
+ className="w-[min(360px,calc(100vw-32px))] p-0"
1064
+ >
1065
+ <Command>
1066
+ <CommandInput placeholder={t("promptDialog.searchTemplates")} />
1067
+ <CommandList className="max-h-[min(420px,60vh)]">
1068
+ <CommandEmpty>{t("promptDialog.noTemplatesFound")}</CommandEmpty>
1069
+ <CommandGroup>
1070
+ <CommandItem
1071
+ value={t("promptDialog.blank")}
1072
+ onSelect={() => choose(null)}
1073
+ data-template-option="blank"
1074
+ className="min-h-11"
1075
+ >
1076
+ <span className="flex h-8 w-12 shrink-0 items-center justify-center rounded-md border bg-muted/40 text-muted-foreground">
1077
+ <IconTemplate className="size-4" />
1078
+ </span>
1079
+ <span className="min-w-0 flex-1 truncate">
1080
+ {t("promptDialog.blank")}
1081
+ </span>
1082
+ {!selectedId ? <IconCheck className="size-4 shrink-0" /> : null}
1083
+ </CommandItem>
1084
+ </CommandGroup>
1085
+ {userTemplates.length > 0 ? (
1086
+ <CommandGroup heading={t("promptDialog.yourTemplates")}>
1087
+ {userTemplates.map(item)}
1088
+ </CommandGroup>
1089
+ ) : null}
1090
+ {builtInTemplates.length > 0 ? (
1091
+ <CommandGroup heading={t("promptDialog.builtInTemplates")}>
1092
+ {builtInTemplates.map(item)}
1093
+ </CommandGroup>
1094
+ ) : null}
1095
+ </CommandList>
1096
+ </Command>
1097
+ </PopoverContent>
1098
+ </Popover>
1099
+ );
1100
+ }
1101
+
877
1102
  function PromptAttachmentMenu({
878
1103
  disabled,
879
1104
  onUploadFiles,
@@ -1,16 +1,20 @@
1
1
  import { IconTemplate } from "@tabler/icons-react";
2
2
  import { useEffect, useRef, useState } from "react";
3
3
 
4
+ import { cn } from "@/lib/utils";
5
+
4
6
  export function TemplatePreview({
5
7
  html,
6
8
  title,
7
9
  width,
8
10
  height,
11
+ className,
9
12
  }: {
10
13
  html?: string | null;
11
14
  title: string;
12
15
  width?: number | null;
13
16
  height?: number | null;
17
+ className?: string;
14
18
  }) {
15
19
  const containerRef = useRef<HTMLDivElement>(null);
16
20
  const [scale, setScale] = useState(0.25);
@@ -40,7 +44,12 @@ export function TemplatePreview({
40
44
 
41
45
  if (!html) {
42
46
  return (
43
- <div className="flex aspect-video items-center justify-center bg-muted/50">
47
+ <div
48
+ className={cn(
49
+ "flex aspect-video items-center justify-center bg-muted/50",
50
+ className,
51
+ )}
52
+ >
44
53
  <IconTemplate className="size-8 text-muted-foreground/35" />
45
54
  </div>
46
55
  );
@@ -49,7 +58,10 @@ export function TemplatePreview({
49
58
  return (
50
59
  <div
51
60
  ref={containerRef}
52
- className="relative aspect-video overflow-hidden bg-white"
61
+ className={cn(
62
+ "relative aspect-video overflow-hidden bg-white",
63
+ className,
64
+ )}
53
65
  >
54
66
  <iframe
55
67
  title={`${title} preview`}
@@ -267,8 +267,6 @@ export function useNavigationState(enabled = true) {
267
267
  if (designSystemId) state.designSystemId = designSystemId;
268
268
  } else if (pathname.startsWith("/templates")) {
269
269
  state.view = "templates";
270
- const templateId = searchParams.get("templateId");
271
- if (templateId) state.templateId = templateId;
272
270
  } else if (pathname.startsWith("/present/")) {
273
271
  state.view = "present";
274
272
  state.designId = params.id;
@@ -276,6 +274,9 @@ export function useNavigationState(enabled = true) {
276
274
  state.view = "settings";
277
275
  }
278
276
 
277
+ const templateId = searchParams.get("templateId");
278
+ if (templateId) state.templateId = templateId;
279
+
279
280
  return state;
280
281
  },
281
282
  getCommandPath: (cmd) => {
@@ -982,6 +982,7 @@ const messages = {
982
982
  decideDescription: "用判斷力繼續推進。",
983
983
  },
984
984
  promptDialog: {
985
+ ...designTemplateFeatureOverrides["zh-TW"].promptDialog,
985
986
  assetsTitle: "Assets",
986
987
  assetsImagePicker: "Assets圖片選取器",
987
988
  loadingAssetsPicker: "載入 Assets 選取器",
@@ -1121,9 +1122,8 @@ const messages = {
1121
1122
  },
1122
1123
  templatesPage: {
1123
1124
  ...designTemplateFeatureOverrides["zh-TW"].templatesPage,
1124
- title: "行銷範本",
1125
- description:
1126
- "用於發布、廣告、簡報、活動和 PDF 講義的尺寸可編輯的入門設計。",
1125
+ title: "範本",
1126
+ description: "從正確的尺寸與預設值開始,再用提示調整未鎖定的內容。",
1127
1127
  brand: "品牌: {{title}}",
1128
1128
  useTemplate: "使用範本",
1129
1129
  socialSquare: "社交廣場",
@@ -1123,6 +1123,18 @@ const enUS = {
1123
1123
  designSystem: "Design system",
1124
1124
  noDesignSystem: "No design system",
1125
1125
  newDesignSystem: "New",
1126
+ createDesignSystem: "Create design system",
1127
+ template: "Template",
1128
+ blank: "Blank",
1129
+ builtIn: "Built-in",
1130
+ chooseTemplate: "Choose a template",
1131
+ searchTemplates: "Search templates...",
1132
+ noTemplatesFound: "No templates found.",
1133
+ yourTemplates: "Your templates",
1134
+ builtInTemplates: "Built-in templates",
1135
+ templatePromptPlaceholder: "Describe how to adapt {{title}}...",
1136
+ reskinTemplatePrompt:
1137
+ "Apply the {{system}} design system to {{title}} while preserving its structure and locked layers.",
1126
1138
  addAttachment: "Add",
1127
1139
  removeAttachment: "Remove {{name}}",
1128
1140
  uploadFile: "Upload file",
@@ -1256,11 +1268,18 @@ const enUS = {
1256
1268
  description:
1257
1269
  "Start with the right dimensions and defaults, then prompt the unlocked content into place.",
1258
1270
  searchPlaceholder: "Search templates...",
1259
- starterTemplates: "Starter templates",
1260
- savedTemplates: "Saved templates",
1271
+ starterTemplates: "Built-in templates",
1272
+ savedTemplates: "Your templates",
1261
1273
  savedTemplatesDescription:
1262
1274
  "Save any Design project as a reusable template from its editor menu.",
1263
1275
  savedEmpty: "Open a design and choose Save as template to add it here.",
1276
+ yourTemplates: "Your templates",
1277
+ yourTemplatesDescription:
1278
+ "Save any Design project as a reusable template from its editor menu.",
1279
+ yourTemplatesEmpty:
1280
+ "Open a design and choose Save as template to add it here.",
1281
+ builtInTemplates: "Built-in templates",
1282
+ builtIn: "Built-in",
1264
1283
  brand: "Brand: {{title}}",
1265
1284
  useTemplate: "Use template",
1266
1285
  useAsIs: "Use template as-is",