@agent-native/core 0.141.2 → 0.141.4

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 (157) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +1 -1
  3. package/corpus/templates/analytics/actions/gong-calls.ts +56 -18
  4. package/corpus/templates/analytics/changelog/2026-08-05-analytics-queries-keep-every-table-tenant-scoped.md +6 -0
  5. package/corpus/templates/analytics/changelog/2026-08-05-durable-a2a-workers-avoid-startup-migration-contention.md +6 -0
  6. package/corpus/templates/analytics/changelog/2026-08-05-fixed-sign-in-and-dashboard-requests-failing-when-background.md +6 -0
  7. package/corpus/templates/analytics/changelog/2026-08-05-historical-rollups-stay-complete-while-live-events-arrive.md +6 -0
  8. package/corpus/templates/analytics/server/db/schema.ts +1 -0
  9. package/corpus/templates/analytics/server/lib/dashboard-report.ts +2 -0
  10. package/corpus/templates/analytics/server/lib/emails.ts +51 -0
  11. package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +8 -12
  12. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +9 -0
  13. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +262 -13
  14. package/corpus/templates/analytics/server/lib/gong.ts +132 -33
  15. package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -1
  16. package/corpus/templates/analytics/server/plugins/auth.ts +21 -2
  17. package/corpus/templates/analytics/server/plugins/db.ts +35 -10
  18. package/corpus/templates/analytics/server/plugins/transactional-emails.ts +5 -0
  19. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +17 -7
  20. package/corpus/templates/calendar/server/lib/booking-emails.ts +154 -70
  21. package/corpus/templates/calendar/server/lib/emails.ts +167 -0
  22. package/corpus/templates/calendar/server/lib/event-guest-notifications.ts +64 -28
  23. package/corpus/templates/calendar/server/plugins/transactional-emails.ts +5 -0
  24. package/corpus/templates/clips/actions/invite-member.ts +39 -16
  25. package/corpus/templates/clips/app/components/player/share-dialog.tsx +73 -1
  26. package/corpus/templates/clips/app/i18n/en-US.ts +3 -0
  27. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -25
  28. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  29. package/corpus/templates/clips/changelog/2026-08-05-activity-is-now-the-default-clips-recording-tab-and-ai-setup.md +6 -0
  30. package/corpus/templates/clips/changelog/2026-08-05-clips-can-copy-a-linked-email-preview-for-public-recordings.md +6 -0
  31. package/corpus/templates/clips/changelog/2026-08-05-preparing-recording-now-uses-a-clean-shadow-free-status-card.md +6 -0
  32. package/corpus/templates/clips/desktop/src/styles.css +0 -3
  33. package/corpus/templates/clips/server/lib/emails.ts +256 -0
  34. package/corpus/templates/clips/server/lib/transactional-email-templates.ts +24 -0
  35. package/corpus/templates/clips/server/plugins/transactional-email-catalog.ts +5 -0
  36. package/corpus/templates/clips/server/routes/api/thumbnail/[recordingId].get.ts +4 -1
  37. package/corpus/templates/clips/shared/email-preview.ts +61 -0
  38. package/corpus/templates/content/server/lib/comment-notifications.ts +57 -26
  39. package/corpus/templates/content/server/lib/emails.ts +70 -0
  40. package/corpus/templates/content/server/plugins/transactional-emails.ts +5 -0
  41. package/corpus/templates/design/actions/import-design-tokens.ts +11 -8
  42. package/corpus/templates/design/actions/index-design-tokens.ts +13 -6
  43. package/corpus/templates/design/changelog/2026-08-05-design-token-imports-classify-typography-dimensions-correctly.md +6 -0
  44. package/corpus/templates/dispatch/app/routes/transactional-email.$appId.$id.tsx +4 -0
  45. package/corpus/templates/dispatch/app/routes/transactional-email.tsx +4 -0
  46. package/corpus/templates/dispatch/changelog/2026-08-05-transactional-email-catalogs-and-delivery-activity-are-available-again.md +6 -0
  47. package/corpus/templates/factory/AGENTS.md +18 -17
  48. package/corpus/templates/factory/actions/get-factory-automation-health.ts +50 -0
  49. package/corpus/templates/factory/actions/get-triage-config.ts +8 -0
  50. package/corpus/templates/factory/actions/save-triage-config.ts +26 -0
  51. package/corpus/templates/factory/app/i18n/en-US.ts +27 -0
  52. package/corpus/templates/factory/app/routes/factory.tsx +376 -206
  53. package/corpus/templates/factory/changelog/2026-08-05-factory-now-keeps-production-automations-running-shows-sched.md +6 -0
  54. package/corpus/templates/factory/server/db/schema.ts +6 -0
  55. package/corpus/templates/factory/server/plugins/factory-migrations.ts +10 -0
  56. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +141 -2
  57. package/corpus/templates/forms/app/pages/FormsListPage.tsx +41 -21
  58. package/corpus/templates/forms/changelog/2026-08-05-form-links-can-now-be-cmd-clicked-to-open-in-a-new-tab.md +6 -0
  59. package/corpus/templates/forms/server/lib/emails.ts +52 -0
  60. package/corpus/templates/forms/server/lib/response-email.ts +2 -0
  61. package/corpus/templates/forms/server/plugins/transactional-emails.ts +5 -0
  62. package/corpus/templates/plan/actions/request-plan-access.ts +33 -12
  63. package/corpus/templates/plan/server/lib/comment-notifications.ts +53 -27
  64. package/corpus/templates/plan/server/lib/emails.ts +67 -0
  65. package/corpus/templates/plan/server/plugins/transactional-emails.ts +5 -0
  66. package/corpus/templates/slides/actions/import-from-url.ts +1 -0
  67. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +492 -381
  68. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +8 -7
  69. package/corpus/templates/slides/app/components/layout/Layout.tsx +27 -23
  70. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +313 -0
  71. package/corpus/templates/slides/app/components/ui/command.tsx +1 -0
  72. package/corpus/templates/slides/app/i18n/en-US.ts +8 -2
  73. package/corpus/templates/slides/app/lib/create-deck-generation.ts +305 -0
  74. package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -12
  75. package/corpus/templates/slides/app/pages/Index.tsx +127 -137
  76. package/corpus/templates/slides/app/root.tsx +8 -0
  77. package/corpus/templates/slides/changelog/2026-08-05-deck-browsing-keeps-all-and-mine-in-the-header-with-a-tighte.md +6 -0
  78. package/corpus/templates/slides/changelog/2026-08-05-new-deck-creation-now-flows-through-a-clean-full-screen-refe.md +6 -0
  79. package/corpus/templates/slides/changelog/2026-08-05-website-imports-now-include-linked-stylesheets-for-more-accu.md +6 -0
  80. package/corpus/templates/slides/server/lib/comment-notifications.ts +44 -21
  81. package/corpus/templates/slides/server/lib/emails.ts +40 -0
  82. package/corpus/templates/slides/server/plugins/transactional-emails.ts +5 -0
  83. package/dist/brand-kit/index.d.ts +1 -0
  84. package/dist/brand-kit/index.js +1 -0
  85. package/dist/brand-kit/tokens.d.ts +91 -0
  86. package/dist/brand-kit/tokens.js +279 -0
  87. package/dist/brand-kit/types.d.ts +25 -0
  88. package/dist/client/AssistantChat.js +7 -7
  89. package/dist/client/onboarding/FirstRunOnboarding.js +26 -2
  90. package/dist/client/onboarding/first-run-registry.d.ts +25 -0
  91. package/dist/client/onboarding/first-run-registry.js +18 -0
  92. package/dist/client/onboarding/index.d.ts +2 -0
  93. package/dist/client/onboarding/index.js +1 -0
  94. package/dist/collab/awareness.d.ts +2 -2
  95. package/dist/collab/routes.d.ts +1 -1
  96. package/dist/collab/struct-routes.d.ts +1 -1
  97. package/dist/db/index.d.ts +1 -1
  98. package/dist/db/index.js +1 -1
  99. package/dist/db/migrations.d.ts +13 -3
  100. package/dist/db/migrations.js +19 -4
  101. package/dist/deploy/build.d.ts +8 -0
  102. package/dist/deploy/build.js +92 -3
  103. package/dist/deploy/workspace-deploy.js +86 -3
  104. package/dist/email-catalog/actions/list-email-activity.d.ts +5 -0
  105. package/dist/email-catalog/actions/list-email-activity.js +19 -0
  106. package/dist/email-catalog/actions/list-email-engagement.d.ts +5 -0
  107. package/dist/email-catalog/actions/list-email-engagement.js +15 -0
  108. package/dist/email-catalog/actions/list-email-log.d.ts +7 -0
  109. package/dist/email-catalog/actions/list-email-log.js +23 -0
  110. package/dist/email-catalog/actions/list-transactional-emails.d.ts +22 -0
  111. package/dist/email-catalog/actions/list-transactional-emails.js +67 -0
  112. package/dist/email-catalog/actions/render-transactional-email-preview.d.ts +9 -0
  113. package/dist/email-catalog/actions/render-transactional-email-preview.js +25 -0
  114. package/dist/email-catalog/authorize.d.ts +1 -0
  115. package/dist/email-catalog/authorize.js +9 -0
  116. package/dist/email-catalog/log.d.ts +60 -0
  117. package/dist/email-catalog/log.js +142 -0
  118. package/dist/email-catalog/provider-metrics.d.ts +34 -0
  119. package/dist/email-catalog/provider-metrics.js +182 -0
  120. package/dist/email-catalog/registry.d.ts +78 -0
  121. package/dist/email-catalog/registry.js +70 -0
  122. package/dist/email-catalog/schema.d.ts +231 -0
  123. package/dist/email-catalog/schema.js +53 -0
  124. package/dist/email-catalog/system-emails.d.ts +13 -0
  125. package/dist/email-catalog/system-emails.js +67 -0
  126. package/dist/jobs/index.d.ts +2 -0
  127. package/dist/jobs/index.js +2 -0
  128. package/dist/jobs/run-history.js +48 -1
  129. package/dist/jobs/scheduler-dispatch.d.ts +7 -0
  130. package/dist/jobs/scheduler-dispatch.js +7 -0
  131. package/dist/jobs/scheduler-health.d.ts +22 -0
  132. package/dist/jobs/scheduler-health.js +172 -0
  133. package/dist/jobs/scheduler.js +67 -2
  134. package/dist/localization/default-messages.d.ts +45 -0
  135. package/dist/localization/default-messages.js +45 -0
  136. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  137. package/dist/notifications/routes.d.ts +3 -3
  138. package/dist/observability/routes.d.ts +3 -3
  139. package/dist/org/handlers.js +9 -1
  140. package/dist/progress/routes.d.ts +1 -1
  141. package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
  142. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  143. package/dist/server/action-discovery.js +22 -0
  144. package/dist/server/agent-chat/recurring-jobs-runtime.d.ts +7 -1
  145. package/dist/server/agent-chat/recurring-jobs-runtime.js +12 -0
  146. package/dist/server/agent-chat-plugin.d.ts +2 -1
  147. package/dist/server/agent-chat-plugin.js +69 -22
  148. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  149. package/dist/server/better-auth-instance.js +17 -2
  150. package/dist/server/design-token-utils.d.ts +6 -1
  151. package/dist/server/design-token-utils.js +252 -59
  152. package/dist/server/email.d.ts +11 -0
  153. package/dist/server/email.js +54 -3
  154. package/dist/server/index.d.ts +3 -2
  155. package/dist/server/index.js +3 -2
  156. package/dist/vite/client.js +20 -14
  157. package/package.json +2 -1
@@ -218,13 +218,14 @@ export default function PromptPopover({
218
218
  )}
219
219
  <div
220
220
  ref={panelRef}
221
- className="fixed z-[200] w-[min(420px,calc(100vw-24px))] rounded-xl border border-border bg-popover shadow-2xl shadow-black/60"
221
+ className="fixed z-[200] w-[min(500px,calc(100vw-24px))] rounded-xl border border-border/80 bg-popover shadow-xl shadow-black/15"
222
+ role="dialog"
223
+ aria-modal="true"
224
+ aria-label={title}
222
225
  style={{ top: 0, left: 0, visibility: "visible" }}
223
226
  >
224
- <div className="flex items-center justify-between gap-3 px-3.5 pt-3 pb-2">
225
- <span className="text-sm font-medium text-foreground/90">
226
- {title}
227
- </span>
227
+ <div className="flex items-center justify-between gap-3 px-4 pb-2.5 pt-3.5">
228
+ <span className="text-sm font-medium text-foreground">{title}</span>
228
229
  {onSkip && (
229
230
  <button
230
231
  type="button"
@@ -232,14 +233,14 @@ export default function PromptPopover({
232
233
  onSkip();
233
234
  onOpenChange(false);
234
235
  }}
235
- className="shrink-0 cursor-pointer text-xs text-primary hover:text-primary/80"
236
+ className="shrink-0 text-xs text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
236
237
  >
237
238
  {skipLabel}
238
239
  </button>
239
240
  )}
240
241
  </div>
241
242
 
242
- <div className="px-2 pb-2">
243
+ <div className="px-2.5 pb-2.5">
243
244
  <PromptComposer
244
245
  autoFocus
245
246
  attachmentsEnabled
@@ -49,7 +49,9 @@ export function Layout({ children }: LayoutProps) {
49
49
  useState<EditorSidebarOverride | null>(null);
50
50
  const { collapsed: sidebarCollapsed, setCollapsed: setSidebarCollapsed } =
51
51
  useSidebarCollapsed();
52
- const { getDeck } = useDecks();
52
+ const { decks, getDeck, loading: decksLoading } = useDecks();
53
+ const isEmptyDecksState =
54
+ location.pathname === "/" && !decksLoading && decks.length === 0;
53
55
 
54
56
  // Scope new chats to the deck the user is currently editing. The route
55
57
  // is `/deck/:id`; everywhere else (list, presentation) leaves
@@ -124,28 +126,30 @@ export function Layout({ children }: LayoutProps) {
124
126
  onClick={() => setSidebarOpen(false)}
125
127
  />
126
128
  )}
127
- <div
128
- className={cn(
129
- "agent-layout-left-drawer fixed inset-y-0 start-0 z-50 transition-transform duration-200 ease-out md:static md:z-auto md:transition-none",
130
- sidebarOpen
131
- ? "translate-x-0"
132
- : "-translate-x-full rtl:translate-x-full md:translate-x-0 md:rtl:translate-x-0",
133
- )}
134
- >
135
- <Sidebar
136
- collapsed={effectiveSidebarCollapsed && !sidebarOpen}
137
- // In the mobile drawer the sidebar is forced expanded, so the
138
- // desktop collapse toggle would be a silent no-op (worse: it'd
139
- // mutate the desktop preference). Hide it while the drawer is
140
- // open.
141
- onToggleCollapsed={
142
- sidebarOpen ? undefined : toggleSidebarCollapsed
143
- }
144
- />
145
- </div>
129
+ {!isEmptyDecksState && (
130
+ <div
131
+ className={cn(
132
+ "agent-layout-left-drawer fixed inset-y-0 start-0 z-50 transition-transform duration-200 ease-out md:static md:z-auto md:transition-none",
133
+ sidebarOpen
134
+ ? "translate-x-0"
135
+ : "-translate-x-full rtl:translate-x-full md:translate-x-0 md:rtl:translate-x-0",
136
+ )}
137
+ >
138
+ <Sidebar
139
+ collapsed={effectiveSidebarCollapsed && !sidebarOpen}
140
+ // In the mobile drawer the sidebar is forced expanded, so the
141
+ // desktop collapse toggle would be a silent no-op (worse: it'd
142
+ // mutate the desktop preference). Hide it while the drawer is
143
+ // open.
144
+ onToggleCollapsed={
145
+ sidebarOpen ? undefined : toggleSidebarCollapsed
146
+ }
147
+ />
148
+ </div>
149
+ )}
146
150
  <div className="agent-layout-main-surface flex h-full min-w-0 flex-1 flex-col overflow-hidden">
147
151
  {/* Mobile-only nav strip with hamburger — only when there's no page toolbar */}
148
- {!ownToolbar && (
152
+ {!isEmptyDecksState && !ownToolbar && (
149
153
  <div className="flex h-12 items-center border-b border-border px-4 md:hidden shrink-0">
150
154
  <button
151
155
  onClick={() => setSidebarOpen(true)}
@@ -156,8 +160,8 @@ export function Layout({ children }: LayoutProps) {
156
160
  </button>
157
161
  </div>
158
162
  )}
159
- {!ownToolbar && <Header />}
160
- <InvitationBanner />
163
+ {!isEmptyDecksState && !ownToolbar && <Header />}
164
+ {!isEmptyDecksState && <InvitationBanner />}
161
165
  <main
162
166
  className={cn(
163
167
  "agent-native-app-main min-h-0 flex-1",
@@ -0,0 +1,313 @@
1
+ import { PromptComposer } from "@agent-native/core/client/composer";
2
+ import { callAction, useSession } from "@agent-native/core/client/hooks";
3
+ import { useT } from "@agent-native/core/client/i18n";
4
+ import type { FirstRunOnboardingExtensionProps } from "@agent-native/core/client/onboarding";
5
+ import { IconArrowLeft } from "@tabler/icons-react";
6
+ import { useCallback, useEffect, useState } from "react";
7
+ import { useNavigate, useSearchParams } from "react-router";
8
+ import { toast } from "sonner";
9
+
10
+ import {
11
+ NewDeckReferenceStep,
12
+ type NewDeckReferenceSelection,
13
+ } from "@/components/editor/NewDeckReferenceStep";
14
+ import {
15
+ uploadPromptFiles,
16
+ type UploadedFile,
17
+ } from "@/components/editor/PromptDialog";
18
+ import { useDecks } from "@/context/DeckContext";
19
+ import { useAgentGenerating } from "@/hooks/use-agent-generating";
20
+ import { useDesignSystems } from "@/hooks/use-design-systems";
21
+ import { useWorkspaceDefaults } from "@/hooks/use-workspace-defaults";
22
+ import { startDeckGeneration } from "@/lib/create-deck-generation";
23
+ import {
24
+ readRecentReferences,
25
+ rememberRecentReference,
26
+ type RecentReference,
27
+ } from "@/lib/recent-references";
28
+
29
+ type FirstDeckStep = "prompt" | "references";
30
+
31
+ export function FirstDeckOnboardingFlow({
32
+ onComplete,
33
+ onSkip,
34
+ }: FirstRunOnboardingExtensionProps) {
35
+ const t = useT();
36
+ const navigate = useNavigate();
37
+ const [searchParams, setSearchParams] = useSearchParams();
38
+ const { session } = useSession();
39
+ const { decks, createDeck, ensureDeckPersisted, deleteDeck, reloadDecks } =
40
+ useDecks();
41
+ const { designSystems, defaultSystem } = useDesignSystems();
42
+ const {
43
+ referenceDeck: workspaceReferenceDeck,
44
+ designSystem: workspaceDesignSystem,
45
+ } = useWorkspaceDefaults();
46
+ const { submit: agentSubmit } = useAgentGenerating();
47
+ const [step, setStep] = useState<FirstDeckStep>("prompt");
48
+ const [prompt, setPrompt] = useState("");
49
+ const [promptFiles, setPromptFiles] = useState<UploadedFile[]>([]);
50
+ const [promptInitialText, setPromptInitialText] = useState<string>();
51
+ const [promptInitialTextKey, setPromptInitialTextKey] = useState<number>();
52
+ const [uploading, setUploading] = useState(false);
53
+ const [referenceImporting, setReferenceImporting] = useState(false);
54
+ const [recentReferences, setRecentReferences] = useState<RecentReference[]>(
55
+ [],
56
+ );
57
+
58
+ const initialPrompt = searchParams.get("initialPrompt")?.trim() ?? "";
59
+ const workspaceReferenceDeckId =
60
+ workspaceReferenceDeck && !workspaceReferenceDeck.unavailable
61
+ ? workspaceReferenceDeck.id
62
+ : null;
63
+ const workspaceDesignSystemId =
64
+ workspaceDesignSystem && !workspaceDesignSystem.unavailable
65
+ ? workspaceDesignSystem.id
66
+ : null;
67
+ const initialDesignSystemId =
68
+ designSystems.find((designSystem) => designSystem.isDefault)?.id ??
69
+ workspaceDesignSystemId ??
70
+ defaultSystem?.id ??
71
+ null;
72
+
73
+ useEffect(() => {
74
+ const result = readRecentReferences();
75
+ if (result.readable) setRecentReferences(result.items);
76
+ }, []);
77
+
78
+ useEffect(() => {
79
+ if (!initialPrompt) return;
80
+ setPromptInitialText(initialPrompt);
81
+ setPromptInitialTextKey(Date.now());
82
+ setSearchParams(
83
+ (previous) => {
84
+ const next = new URLSearchParams(previous);
85
+ next.delete("initialPrompt");
86
+ return next;
87
+ },
88
+ { replace: true },
89
+ );
90
+ }, [initialPrompt, setSearchParams]);
91
+
92
+ const rememberReference = useCallback(
93
+ (reference: Omit<RecentReference, "lastUsedAt">) => {
94
+ const result = rememberRecentReference(reference);
95
+ if (result.readable) setRecentReferences(result.items);
96
+ },
97
+ [],
98
+ );
99
+
100
+ const handlePromptSubmit = useCallback(
101
+ async (text: string, files: File[]) => {
102
+ setUploading(true);
103
+ try {
104
+ const uploaded = await uploadPromptFiles(files);
105
+ setPrompt(text.trim());
106
+ setPromptFiles(uploaded);
107
+ setStep("references");
108
+ } catch (error) {
109
+ toast.error(t("raw.uploadFailed"), {
110
+ description:
111
+ error instanceof Error
112
+ ? error.message
113
+ : t("raw.uploadAttachedFailed"),
114
+ });
115
+ } finally {
116
+ setUploading(false);
117
+ }
118
+ },
119
+ [t],
120
+ );
121
+
122
+ const startGeneration = useCallback(
123
+ async (
124
+ files: UploadedFile[],
125
+ selection: NewDeckReferenceSelection = {},
126
+ ) => {
127
+ const result = await startDeckGeneration({
128
+ session,
129
+ prompt,
130
+ files,
131
+ referenceSelection: selection,
132
+ selectedDesignSystemId: initialDesignSystemId,
133
+ selectedReferenceDeckId: workspaceReferenceDeckId,
134
+ designSystems,
135
+ createDeck,
136
+ ensureDeckPersisted,
137
+ deleteDeck,
138
+ navigate,
139
+ agentSubmit,
140
+ onPromptClosed: () => undefined,
141
+ onUnauthenticated: () => {
142
+ toast.error(t("home.signInTitle"));
143
+ },
144
+ onPersistenceFailure: (failedPrompt) => {
145
+ setPromptInitialText(failedPrompt);
146
+ setPromptInitialTextKey(Date.now());
147
+ setStep("prompt");
148
+ toast.error(t("home.generationStartFailed"), {
149
+ description: t("home.generationStartFailedDescription"),
150
+ });
151
+ },
152
+ });
153
+ if (result === "started") onComplete();
154
+ },
155
+ [
156
+ agentSubmit,
157
+ createDeck,
158
+ deleteDeck,
159
+ designSystems,
160
+ ensureDeckPersisted,
161
+ initialDesignSystemId,
162
+ navigate,
163
+ onComplete,
164
+ prompt,
165
+ session,
166
+ t,
167
+ workspaceReferenceDeckId,
168
+ ],
169
+ );
170
+
171
+ const handleReferenceSelect = useCallback(
172
+ async (selection: NewDeckReferenceSelection) => {
173
+ if (selection.designSystemId) {
174
+ rememberReference({
175
+ id: selection.designSystemId,
176
+ kind: "design-system",
177
+ });
178
+ }
179
+ if (selection.referenceDeckId) {
180
+ rememberReference({ id: selection.referenceDeckId, kind: "deck" });
181
+ }
182
+ await startGeneration(promptFiles, selection);
183
+ },
184
+ [promptFiles, rememberReference, startGeneration],
185
+ );
186
+
187
+ const handleReferenceImport = useCallback(
188
+ async (files: File[]) => {
189
+ setReferenceImporting(true);
190
+ try {
191
+ const uploaded = await uploadPromptFiles(files);
192
+ const pptxReference = uploaded.find((file) =>
193
+ file.originalName.toLowerCase().endsWith(".pptx"),
194
+ );
195
+ let referenceSelection: NewDeckReferenceSelection = {
196
+ designSystemId: null,
197
+ referenceDeckId: null,
198
+ };
199
+ let generationFiles = uploaded;
200
+ if (pptxReference) {
201
+ const imported = (await callAction("import-pptx", {
202
+ filePath: pptxReference.path,
203
+ })) as { id?: unknown };
204
+ if (typeof imported.id !== "string" || !imported.id) {
205
+ throw new Error("The imported presentation did not create a deck.");
206
+ }
207
+ await reloadDecks();
208
+ rememberReference({ id: imported.id, kind: "deck" });
209
+ referenceSelection = {
210
+ designSystemId: null,
211
+ referenceDeckId: imported.id,
212
+ };
213
+ generationFiles = uploaded.filter((file) => file !== pptxReference);
214
+ }
215
+ await startGeneration(
216
+ [...promptFiles, ...generationFiles],
217
+ referenceSelection,
218
+ );
219
+ } catch (error) {
220
+ toast.error(t("editorToolbar.uploadFailed"), {
221
+ description:
222
+ error instanceof Error
223
+ ? error.message
224
+ : t("editorToolbar.importFailedDescription"),
225
+ });
226
+ } finally {
227
+ setReferenceImporting(false);
228
+ }
229
+ },
230
+ [promptFiles, reloadDecks, rememberReference, startGeneration, t],
231
+ );
232
+
233
+ const handleReferenceSkip = useCallback(() => {
234
+ void startGeneration(promptFiles, {
235
+ designSystemId: null,
236
+ referenceDeckId: null,
237
+ });
238
+ }, [promptFiles, startGeneration]);
239
+
240
+ if (step === "references") {
241
+ return (
242
+ <NewDeckReferenceStep
243
+ open
244
+ decks={decks}
245
+ designSystems={designSystems}
246
+ defaultDesignSystemId={initialDesignSystemId}
247
+ defaultReferenceDeckId={workspaceReferenceDeckId}
248
+ recentReferences={recentReferences}
249
+ onSelect={handleReferenceSelect}
250
+ onImport={handleReferenceImport}
251
+ onSkip={handleReferenceSkip}
252
+ onOpenChange={(open) => {
253
+ if (!open) setStep("prompt");
254
+ }}
255
+ importing={referenceImporting}
256
+ title={t("home.newDeck")}
257
+ designSystemLabel={t("home.designSystem")}
258
+ referenceDeckLabel={t("home.referenceDeck")}
259
+ chooseDeckLabel={t("home.referenceDeckPlaceholder")}
260
+ importingLabel={t("raw.uploading")}
261
+ skipLabel={t("home.referenceDeckNone")}
262
+ defaultSuffix={t("home.defaultSuffix")}
263
+ starredLabel={t("home.referenceDeckStarredGroup")}
264
+ otherDecksLabel={t("home.referenceDeckOtherGroup")}
265
+ searchDecksLabel={t("root.searchDecks")}
266
+ promptSummary={prompt}
267
+ />
268
+ );
269
+ }
270
+
271
+ return (
272
+ <div
273
+ className="fixed inset-0 z-[200] flex min-h-screen flex-col bg-background text-foreground"
274
+ role="dialog"
275
+ aria-modal="true"
276
+ aria-label={t("home.firstDeckPromptTitle")}
277
+ >
278
+ <header className="flex h-14 shrink-0 items-center justify-between border-b border-border px-5 sm:px-8">
279
+ <div className="flex items-center gap-2 text-sm text-muted-foreground">
280
+ <IconArrowLeft className="size-4" />
281
+ <span>{t("home.newDeck")}</span>
282
+ </div>
283
+ <button
284
+ type="button"
285
+ onClick={onSkip}
286
+ className="text-xs text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
287
+ >
288
+ {t("home.firstDeckSkip")}
289
+ </button>
290
+ </header>
291
+ <main className="flex min-h-0 flex-1 items-center justify-center overflow-y-auto px-5 py-10 sm:px-8">
292
+ <div className="w-full max-w-xl">
293
+ <h1 className="text-center text-2xl font-semibold tracking-[-0.04em] sm:text-3xl">
294
+ {t("home.firstDeckPromptTitle")}
295
+ </h1>
296
+ <PromptComposer
297
+ className="mt-8"
298
+ autoFocus
299
+ attachmentsEnabled
300
+ disabled={uploading}
301
+ placeholder={t("home.newDeckPlaceholder")}
302
+ onSubmit={handlePromptSubmit}
303
+ draftScope="slides-first-deck"
304
+ initialText={promptInitialText}
305
+ initialTextKey={promptInitialTextKey}
306
+ />
307
+ </div>
308
+ </main>
309
+ </div>
310
+ );
311
+ }
312
+
313
+ export default FirstDeckOnboardingFlow;
@@ -0,0 +1 @@
1
+ export * from "@agent-native/toolkit/ui/command";
@@ -664,10 +664,15 @@ const messages = {
664
664
  newDeckPromptTitle: "New deck",
665
665
  newDeckPlaceholder: "Describe your deck, visual, or diagram...",
666
666
  skipPrompt: "Skip prompt",
667
+ firstDeckPromptTitle: "What do you want to make?",
668
+ firstDeckSkip: "Skip",
669
+ chooseReferences: "Choose references",
670
+ addDesignSystem: "+ Design system",
671
+ importFrom: "Import from",
667
672
  designSystem: "Design system",
668
673
  referenceDeck: "Reference deck",
669
674
  referenceDeckPlaceholder: "Match the style of an existing deck",
670
- referenceDeckNone: "Don't use one",
675
+ referenceDeckNone: "Skip",
671
676
  referenceDeckStarredGroup: "Starred",
672
677
  referenceDeckOtherGroup: "Other decks",
673
678
  referenceReviewTitle: "Review references",
@@ -708,7 +713,8 @@ const messages = {
708
713
  signInDescriptionWithFiles:
709
714
  "You need to sign in before generating a deck. We've saved your prompt; reattach any files once you're back.",
710
715
  signIn: "Sign in",
711
- emptyTitle: "Create your first deck or visual",
716
+ emptyTitle: "No decks yet",
717
+ createFirstDeck: "Create your first deck",
712
718
  emptyDescription:
713
719
  "Build beautiful presentations, standalone visuals, diagrams, and image-rich stories with AI-powered generation.",
714
720
  },