@agent-native/core 0.148.0 → 0.149.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/changelog/2026-08-10-wide-tables-in-analytics-chat-now-scroll-horizontally-within.md +6 -0
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +13 -53
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -70
- package/corpus/templates/calendar/app/lib/event-layout.ts +122 -0
- package/corpus/templates/calendar/app/routes/_app.availability.tsx +1 -1
- package/corpus/templates/calendar/changelog/2026-08-10-opening-availability-settings-from-the-agent-lands-on-the-av.md +6 -0
- package/corpus/templates/calendar/vitest.config.ts +6 -1
- package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +3 -1
- package/corpus/templates/clips/app/components/player/video-player.tsx +44 -4
- package/corpus/templates/clips/changelog/2026-08-10-fixed-transcription-failures-for-recordings-without-captured.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-10-mobile-clips-can-now-enter-fullscreen-on-iphone-safari.md +6 -0
- package/corpus/templates/content/actions/_property-utils.ts +11 -1
- package/corpus/templates/content/actions/view-screen.ts +62 -12
- package/corpus/templates/content/server/plugins/agent-chat.ts +2 -0
- package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +22 -0
- package/corpus/templates/design/AGENTS.md +3 -0
- package/corpus/templates/design/actions/create-design-from-template.ts +19 -0
- package/corpus/templates/design/actions/get-design-snapshot.ts +18 -0
- package/corpus/templates/design/actions/get-design-template.ts +178 -0
- package/corpus/templates/design/actions/list-designs.ts +3 -1
- package/corpus/templates/design/actions/view-screen.ts +31 -0
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +225 -214
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +3 -0
- package/corpus/templates/design/app/i18n-data.ts +78 -0
- package/corpus/templates/design/app/lib/design-authors.ts +64 -0
- package/corpus/templates/design/app/pages/Index.tsx +114 -4
- package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +1 -0
- package/corpus/templates/design/changelog/2026-08-07-alt-dragging-an-element-to-copy-it-now-keeps-the-copy-visibl.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-design-cards-now-show-who-created-each-design-with-a-filter-.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-drawing-a-shape-on-the-overview-canvas-now-keeps-the-shape-s.md +6 -0
- package/corpus/templates/design/changelog/2026-08-07-hold-option-alt-while-dragging-a-frame-on-the-overview-canva.md +6 -0
- package/corpus/templates/design/server/lib/design-template-data.ts +143 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/forms/AGENTS.md +2 -2
- package/corpus/templates/forms/actions/create-form.ts +1 -1
- package/corpus/templates/forms/actions/update-form.ts +5 -5
- package/corpus/templates/forms/changelog/2026-08-10-forms-now-keep-agent-applied-field-changes-visible-after-an-.md +6 -0
- package/corpus/templates/forms/changelog/2026-08-10-published-form-links-always-use-the-working-public-form-url.md +6 -0
- package/corpus/templates/forms/server/plugins/agent-chat.ts +2 -2
- package/corpus/templates/slides/AGENTS.md +4 -3
- package/corpus/templates/slides/actions/import-google-slides-reference.ts +56 -10
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +12 -74
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +5 -1
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +15 -4
- package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +116 -55
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -0
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +143 -18
- package/corpus/templates/slides/app/i18n/en-US.ts +6 -0
- package/corpus/templates/slides/app/lib/generation-state.ts +7 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +32 -14
- package/corpus/templates/slides/app/pages/Index.tsx +110 -128
- package/corpus/templates/slides/changelog/2026-08-10-imported-reference-decks-now-show-clear-success-and-stay-sel.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-pasted-google-slides-links-import-after-reconnecting-google-.md +6 -0
- package/corpus/templates/slides/server/handlers/google-docs-auth.ts +4 -0
- package/corpus/templates/slides/server/lib/google-docs-oauth.ts +13 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +1 -1
- package/dist/agent/engine/registry.d.ts +8 -2
- package/dist/agent/engine/registry.js +63 -29
- package/dist/agent/production-agent.js +7 -0
- package/dist/agent/run-manager.d.ts +5 -4
- package/dist/agent/run-manager.js +6 -5
- package/dist/cli/claude-code-participant.d.ts +2 -1
- package/dist/cli/claude-code-participant.js +19 -0
- package/dist/cli/code-agent-executor.js +223 -0
- package/dist/cli/create-workspace.js +3 -3
- package/dist/cli/create.d.ts +3 -2
- package/dist/cli/create.js +20 -24
- package/dist/client/AgentPanel.js +8 -4
- package/dist/client/AssistantChat.js +34 -1
- package/dist/client/agent-chat-adapter.d.ts +1 -0
- package/dist/client/agent-chat-adapter.js +10 -2
- package/dist/client/blocks/library/diagram.js +1 -1
- package/dist/client/blocks/library/sanitize-html.js +66 -2
- package/dist/client/blocks/library/wireframe-kit.js +69 -0
- package/dist/client/chat/markdown-renderer.d.ts +1 -0
- package/dist/client/chat/markdown-renderer.js +4 -0
- package/dist/client/chat/message-components.d.ts +1 -0
- package/dist/client/chat/message-components.js +9 -1
- package/dist/client/chat/tool-call-display.d.ts +8 -0
- package/dist/client/chat/tool-call-display.js +30 -21
- package/dist/client/chat-first/app-pane.js +1 -1
- package/dist/client/chat-first/apps-rail.d.ts +1 -1
- package/dist/client/chat-first/apps-rail.js +12 -12
- package/dist/client/chat-first/chat-history.d.ts +1 -1
- package/dist/client/chat-first/chat-history.js +2 -0
- package/dist/client/chat-first/copy.js +1 -0
- package/dist/client/chat-first/primary-nav.js +1 -1
- package/dist/client/chat-first/types.d.ts +1 -0
- package/dist/client/resources/McpConnectionSuggestion.js +2 -2
- package/dist/client/settings/SettingsTabsPage.d.ts +3 -1
- package/dist/client/settings/SettingsTabsPage.js +2 -2
- package/dist/client/sse-event-processor.d.ts +8 -0
- package/dist/client/sse-event-processor.js +13 -6
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +5 -7
- package/dist/deploy/build.js +19 -12
- package/dist/localization/default-messages.js +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/provider-api.d.ts +2 -2
- package/dist/scripts/call-agent.js +3 -3
- package/dist/scripts/describe-workspace-apps.js +1 -1
- package/dist/secrets/routes.d.ts +6 -6
- package/dist/server/agent-capabilities.js +0 -0
- package/dist/server/agent-chat/context-tools.js +1 -1
- package/dist/server/agent-chat/prompt-resources.js +1 -1
- package/dist/server/agent-chat-plugin.js +1 -1
- package/dist/server/auth.js +190 -42
- package/dist/server/better-auth-migrations.js +53 -0
- package/dist/server/http-response-telemetry.js +7 -1
- package/dist/server/onboarding-html.js +119 -33
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/password-policy.d.ts +2 -1
- package/dist/shared/password-policy.js +2 -1
- package/dist/styles/agent-conversation.css +20 -3
- package/dist/styles/agent-native.css +14 -2
- package/dist/styles/blocks.css +132 -0
- package/dist/templates/default/app/i18n/ar-SA.ts +1 -1
- package/dist/templates/default/app/i18n/de-DE.ts +1 -1
- package/dist/templates/default/app/i18n/en-US.ts +1 -1
- package/dist/templates/default/app/i18n/es-ES.ts +1 -1
- package/dist/templates/default/app/i18n/fr-FR.ts +1 -1
- package/dist/templates/default/app/i18n/hi-IN.ts +1 -1
- package/dist/templates/default/app/i18n/ja-JP.ts +1 -1
- package/dist/templates/default/app/i18n/ko-KR.ts +1 -1
- package/dist/templates/default/app/i18n/pt-BR.ts +1 -1
- package/dist/templates/default/app/i18n/zh-CN.ts +1 -1
- package/dist/templates/default/app/i18n/zh-TW.ts +1 -1
- package/dist/templates/default/pnpm-workspace.yaml +1 -0
- package/dist/templates/workspace-root/pnpm-workspace.yaml +2 -0
- package/docs/content/actions.mdx +4 -1
- package/docs/content/agent-surfaces.mdx +8 -6
- package/docs/content/automations.mdx +1 -1
- package/docs/content/cloneable-saas.mdx +6 -6
- package/docs/content/database.mdx +6 -1
- package/docs/content/deployment.mdx +10 -8
- package/docs/content/docs-components.mdx +22 -14
- package/docs/content/drop-in-agent.mdx +1 -1
- package/docs/content/environment-variables.mdx +4 -3
- package/docs/content/getting-started-actions.mdx +5 -4
- package/docs/content/getting-started-pages.mdx +7 -6
- package/docs/content/getting-started.mdx +4 -3
- package/docs/content/notifications.mdx +6 -6
- package/docs/content/progress.mdx +5 -5
- package/docs/content/routing.mdx +3 -1
- package/docs/content/server-database.mdx +13 -4
- package/docs/content/server-overview.mdx +9 -9
- package/docs/content/server-plugins.mdx +10 -4
- package/docs/content/server-routes.mdx +7 -2
- package/docs/content/tracking.mdx +4 -4
- package/docs/content/what-is-agent-native.mdx +4 -4
- package/package.json +2 -2
- package/src/templates/default/app/i18n/ar-SA.ts +1 -1
- package/src/templates/default/app/i18n/de-DE.ts +1 -1
- package/src/templates/default/app/i18n/en-US.ts +1 -1
- package/src/templates/default/app/i18n/es-ES.ts +1 -1
- package/src/templates/default/app/i18n/fr-FR.ts +1 -1
- package/src/templates/default/app/i18n/hi-IN.ts +1 -1
- package/src/templates/default/app/i18n/ja-JP.ts +1 -1
- package/src/templates/default/app/i18n/ko-KR.ts +1 -1
- package/src/templates/default/app/i18n/pt-BR.ts +1 -1
- package/src/templates/default/app/i18n/zh-CN.ts +1 -1
- package/src/templates/default/app/i18n/zh-TW.ts +1 -1
- package/src/templates/default/pnpm-workspace.yaml +1 -0
- package/src/templates/workspace-root/pnpm-workspace.yaml +2 -0
|
@@ -48,6 +48,17 @@ export interface NewDeckReferenceSelection {
|
|
|
48
48
|
value: string;
|
|
49
49
|
} | null;
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
export type NewDeckReferenceSource = NonNullable<
|
|
53
|
+
NewDeckReferenceSelection["referenceSource"]
|
|
54
|
+
>;
|
|
55
|
+
|
|
56
|
+
export interface ImportedReference {
|
|
57
|
+
id: string;
|
|
58
|
+
title: string;
|
|
59
|
+
source: "pptx" | "pdf" | "google-slides";
|
|
60
|
+
}
|
|
61
|
+
|
|
51
62
|
interface DesignSystemOption {
|
|
52
63
|
id: string;
|
|
53
64
|
title: string;
|
|
@@ -61,9 +72,12 @@ interface NewDeckReferenceStepProps {
|
|
|
61
72
|
defaultDesignSystemId: string | null;
|
|
62
73
|
defaultReferenceDeckId: string | null;
|
|
63
74
|
recentReferences: RecentReference[];
|
|
64
|
-
onSelect: (selection: NewDeckReferenceSelection) => void
|
|
65
|
-
onImport: (files: File[]) => Promise<
|
|
66
|
-
|
|
75
|
+
onSelect: (selection: NewDeckReferenceSelection) => void | Promise<void>;
|
|
76
|
+
onImport: (files: File[]) => Promise<ImportedReference | null>;
|
|
77
|
+
onImportSource: (
|
|
78
|
+
source: NewDeckReferenceSource,
|
|
79
|
+
) => Promise<ImportedReference | null>;
|
|
80
|
+
onSkip: () => void | Promise<void>;
|
|
67
81
|
onOpenChange: (open: boolean) => void;
|
|
68
82
|
importing?: boolean;
|
|
69
83
|
title: string;
|
|
@@ -72,7 +86,6 @@ interface NewDeckReferenceStepProps {
|
|
|
72
86
|
chooseDeckLabel: string;
|
|
73
87
|
importingLabel: string;
|
|
74
88
|
skipLabel: string;
|
|
75
|
-
defaultSuffix: string;
|
|
76
89
|
starredLabel: string;
|
|
77
90
|
otherDecksLabel: string;
|
|
78
91
|
searchDecksLabel: string;
|
|
@@ -88,6 +101,7 @@ export function NewDeckReferenceStep({
|
|
|
88
101
|
recentReferences,
|
|
89
102
|
onSelect,
|
|
90
103
|
onImport,
|
|
104
|
+
onImportSource,
|
|
91
105
|
onSkip,
|
|
92
106
|
onOpenChange,
|
|
93
107
|
importing = false,
|
|
@@ -97,7 +111,6 @@ export function NewDeckReferenceStep({
|
|
|
97
111
|
chooseDeckLabel,
|
|
98
112
|
importingLabel,
|
|
99
113
|
skipLabel,
|
|
100
|
-
defaultSuffix,
|
|
101
114
|
starredLabel,
|
|
102
115
|
otherDecksLabel,
|
|
103
116
|
searchDecksLabel,
|
|
@@ -110,6 +123,8 @@ export function NewDeckReferenceStep({
|
|
|
110
123
|
const [selectedReferenceDeckId, setSelectedReferenceDeckId] = useState<
|
|
111
124
|
string | null
|
|
112
125
|
>(defaultReferenceDeckId);
|
|
126
|
+
const [importedReference, setImportedReference] =
|
|
127
|
+
useState<ImportedReference | null>(null);
|
|
113
128
|
const [selectedSource, setSelectedSource] =
|
|
114
129
|
useState<NewDeckReferenceSelection["referenceSource"]>(null);
|
|
115
130
|
const [referenceDeckSearchOpen, setReferenceDeckSearchOpen] = useState(false);
|
|
@@ -141,9 +156,6 @@ export function NewDeckReferenceStep({
|
|
|
141
156
|
);
|
|
142
157
|
const starredDecks = decks.filter((deck) => deck.starred);
|
|
143
158
|
const otherDecks = decks.filter((deck) => !deck.starred);
|
|
144
|
-
const selectedDesignSystem = selectedDesignSystemId
|
|
145
|
-
? designSystemById.get(selectedDesignSystemId)
|
|
146
|
-
: undefined;
|
|
147
159
|
const selectedReferenceDeck = selectedReferenceDeckId
|
|
148
160
|
? deckById.get(selectedReferenceDeckId)
|
|
149
161
|
: undefined;
|
|
@@ -152,6 +164,7 @@ export function NewDeckReferenceStep({
|
|
|
152
164
|
if (!open) return;
|
|
153
165
|
setSelectedDesignSystemId(defaultDesignSystemId);
|
|
154
166
|
setSelectedReferenceDeckId(defaultReferenceDeckId);
|
|
167
|
+
setImportedReference(null);
|
|
155
168
|
setSelectedSource(null);
|
|
156
169
|
setReferenceDeckSearchOpen(false);
|
|
157
170
|
}, [open, defaultDesignSystemId, defaultReferenceDeckId]);
|
|
@@ -160,17 +173,33 @@ export function NewDeckReferenceStep({
|
|
|
160
173
|
const files = Array.from(event.target.files ?? []);
|
|
161
174
|
event.target.value = "";
|
|
162
175
|
if (files.length === 0) return;
|
|
163
|
-
await onImport(files);
|
|
176
|
+
const imported = await onImport(files);
|
|
177
|
+
if (imported) applyImportedReference(imported);
|
|
164
178
|
};
|
|
165
179
|
|
|
166
|
-
const
|
|
180
|
+
const applyImportedReference = (imported: ImportedReference) => {
|
|
181
|
+
setSelectedDesignSystemId(null);
|
|
182
|
+
setSelectedReferenceDeckId(imported.id);
|
|
183
|
+
setSelectedSource(null);
|
|
184
|
+
setImportedReference(imported);
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const handleContinue = async () => {
|
|
188
|
+
const trimmedSource =
|
|
189
|
+
selectedSource && selectedSource.value.trim()
|
|
190
|
+
? { ...selectedSource, value: selectedSource.value.trim() }
|
|
191
|
+
: null;
|
|
192
|
+
|
|
193
|
+
if (trimmedSource?.kind === "google-docs") {
|
|
194
|
+
const imported = await onImportSource(trimmedSource);
|
|
195
|
+
if (imported) applyImportedReference(imported);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
167
199
|
onSelect({
|
|
168
200
|
designSystemId: selectedDesignSystemId,
|
|
169
201
|
referenceDeckId: selectedReferenceDeckId,
|
|
170
|
-
referenceSource:
|
|
171
|
-
selectedSource && selectedSource.value.trim()
|
|
172
|
-
? { ...selectedSource, value: selectedSource.value.trim() }
|
|
173
|
-
: null,
|
|
202
|
+
referenceSource: trimmedSource,
|
|
174
203
|
});
|
|
175
204
|
};
|
|
176
205
|
|
|
@@ -182,23 +211,20 @@ export function NewDeckReferenceStep({
|
|
|
182
211
|
setSelectedDesignSystemId(null);
|
|
183
212
|
} else {
|
|
184
213
|
setSelectedReferenceDeckId(null);
|
|
214
|
+
setImportedReference(null);
|
|
185
215
|
}
|
|
186
216
|
};
|
|
187
217
|
const selectedSourceLabel =
|
|
188
218
|
selectedSource?.kind === "google-docs"
|
|
189
|
-
? "
|
|
219
|
+
? t("home.googleSlidesReferenceTitle")
|
|
190
220
|
: selectedSource?.kind === "website"
|
|
191
221
|
? "Website"
|
|
192
222
|
: "Figma";
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
) : (
|
|
199
|
-
<IconBrandFigma className="size-4" />
|
|
200
|
-
);
|
|
201
|
-
|
|
223
|
+
const selectedReferenceDeckTitle =
|
|
224
|
+
selectedReferenceDeck?.title ??
|
|
225
|
+
(selectedReferenceDeckId === importedReference?.id
|
|
226
|
+
? importedReference.title
|
|
227
|
+
: undefined);
|
|
202
228
|
return open ? (
|
|
203
229
|
<div
|
|
204
230
|
className="fixed inset-0 z-[200] flex min-h-screen flex-col bg-background text-foreground"
|
|
@@ -228,29 +254,6 @@ export function NewDeckReferenceStep({
|
|
|
228
254
|
</p>
|
|
229
255
|
)}
|
|
230
256
|
|
|
231
|
-
{(selectedDesignSystem || selectedSource) && (
|
|
232
|
-
<div className="mt-8 space-y-2">
|
|
233
|
-
{selectedDesignSystem && (
|
|
234
|
-
<ReferenceRow
|
|
235
|
-
icon={<IconPalette className="size-4" />}
|
|
236
|
-
label={selectedDesignSystem.title}
|
|
237
|
-
meta={`${designSystemLabel}${defaultDesignSystemId === selectedDesignSystem.id ? defaultSuffix : ""}`}
|
|
238
|
-
selected
|
|
239
|
-
onClick={() => setSelectedDesignSystemId(null)}
|
|
240
|
-
/>
|
|
241
|
-
)}
|
|
242
|
-
{selectedSource && (
|
|
243
|
-
<ReferenceRow
|
|
244
|
-
icon={selectedSourceIcon}
|
|
245
|
-
label={selectedSourceLabel}
|
|
246
|
-
meta={selectedSource.value || "Add a link below"}
|
|
247
|
-
selected
|
|
248
|
-
onClick={() => setSelectedSource(null)}
|
|
249
|
-
/>
|
|
250
|
-
)}
|
|
251
|
-
</div>
|
|
252
|
-
)}
|
|
253
|
-
|
|
254
257
|
<div className="mt-10 space-y-6">
|
|
255
258
|
<div className="grid gap-2">
|
|
256
259
|
<div className="flex items-center justify-between gap-3">
|
|
@@ -309,7 +312,7 @@ export function NewDeckReferenceStep({
|
|
|
309
312
|
className="flex h-10 w-full items-center justify-between gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
310
313
|
>
|
|
311
314
|
<span className="truncate">
|
|
312
|
-
{
|
|
315
|
+
{selectedReferenceDeckTitle ?? chooseDeckLabel}
|
|
313
316
|
</span>
|
|
314
317
|
<IconChevronDown className="size-4 shrink-0 opacity-50" />
|
|
315
318
|
</button>
|
|
@@ -336,6 +339,7 @@ export function NewDeckReferenceStep({
|
|
|
336
339
|
value={`${deck.title} ${deck.id}`}
|
|
337
340
|
onSelect={() => {
|
|
338
341
|
setSelectedReferenceDeckId(deck.id);
|
|
342
|
+
setImportedReference(null);
|
|
339
343
|
setSelectedSource(null);
|
|
340
344
|
setReferenceDeckSearchOpen(false);
|
|
341
345
|
}}
|
|
@@ -361,6 +365,7 @@ export function NewDeckReferenceStep({
|
|
|
361
365
|
value={`${deck.title} ${deck.id}`}
|
|
362
366
|
onSelect={() => {
|
|
363
367
|
setSelectedReferenceDeckId(deck.id);
|
|
368
|
+
setImportedReference(null);
|
|
364
369
|
setSelectedSource(null);
|
|
365
370
|
setReferenceDeckSearchOpen(false);
|
|
366
371
|
}}
|
|
@@ -430,6 +435,8 @@ export function NewDeckReferenceStep({
|
|
|
430
435
|
accept=".pptx"
|
|
431
436
|
icon={<IconPresentation className="size-4" />}
|
|
432
437
|
label="PPT"
|
|
438
|
+
imported={importedReference?.source === "pptx"}
|
|
439
|
+
importedLabel={t("home.imported")}
|
|
433
440
|
importing={importing}
|
|
434
441
|
importingLabel={importingLabel}
|
|
435
442
|
onChange={handleImport}
|
|
@@ -438,6 +445,8 @@ export function NewDeckReferenceStep({
|
|
|
438
445
|
accept=".pdf"
|
|
439
446
|
icon={<IconFileTypePdf className="size-4" />}
|
|
440
447
|
label="PDF"
|
|
448
|
+
imported={importedReference?.source === "pdf"}
|
|
449
|
+
importedLabel={t("home.imported")}
|
|
441
450
|
importing={importing}
|
|
442
451
|
importingLabel={importingLabel}
|
|
443
452
|
onChange={handleImport}
|
|
@@ -446,25 +455,34 @@ export function NewDeckReferenceStep({
|
|
|
446
455
|
accept=".docx"
|
|
447
456
|
icon={<IconFileText className="size-4" />}
|
|
448
457
|
label="DOCX"
|
|
458
|
+
imported={false}
|
|
459
|
+
importedLabel={t("home.imported")}
|
|
449
460
|
importing={importing}
|
|
450
461
|
importingLabel={importingLabel}
|
|
451
462
|
onChange={handleImport}
|
|
452
463
|
/>
|
|
453
464
|
<ImportOption
|
|
454
465
|
icon={<IconBrandGoogle className="size-4" />}
|
|
455
|
-
label="
|
|
456
|
-
|
|
466
|
+
label={t("home.googleSlidesReferenceTitle")}
|
|
467
|
+
confirmed={importedReference?.source === "google-slides"}
|
|
468
|
+
confirmedLabel={t("home.imported")}
|
|
469
|
+
selected={
|
|
470
|
+
selectedSource?.kind === "google-docs" ||
|
|
471
|
+
importedReference?.source === "google-slides"
|
|
472
|
+
}
|
|
457
473
|
onClick={() => chooseSource("google-docs")}
|
|
458
474
|
/>
|
|
459
475
|
<ImportOption
|
|
460
476
|
icon={<IconWorld className="size-4" />}
|
|
461
477
|
label="Website"
|
|
478
|
+
confirmedLabel={t("home.imported")}
|
|
462
479
|
selected={selectedSource?.kind === "website"}
|
|
463
480
|
onClick={() => chooseSource("website")}
|
|
464
481
|
/>
|
|
465
482
|
<ImportOption
|
|
466
483
|
icon={<IconBrandFigma className="size-4" />}
|
|
467
484
|
label="Figma"
|
|
485
|
+
confirmedLabel={t("home.imported")}
|
|
468
486
|
selected={selectedSource?.kind === "figma"}
|
|
469
487
|
onClick={() => chooseSource("figma")}
|
|
470
488
|
/>
|
|
@@ -484,23 +502,54 @@ export function NewDeckReferenceStep({
|
|
|
484
502
|
}
|
|
485
503
|
/>
|
|
486
504
|
)}
|
|
505
|
+
{importedReference && (
|
|
506
|
+
<div
|
|
507
|
+
className="mt-3 flex items-start gap-3 rounded-lg border border-primary/30 bg-primary/5 px-3 py-2.5 text-sm"
|
|
508
|
+
role="status"
|
|
509
|
+
aria-live="polite"
|
|
510
|
+
>
|
|
511
|
+
<span className="mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground">
|
|
512
|
+
<IconCheck className="size-3.5" />
|
|
513
|
+
</span>
|
|
514
|
+
<div className="min-w-0">
|
|
515
|
+
<p className="font-medium text-foreground">
|
|
516
|
+
{t("home.referenceImportSuccess")}
|
|
517
|
+
</p>
|
|
518
|
+
<p className="truncate text-xs text-muted-foreground">
|
|
519
|
+
{t("home.referenceImportSelected", {
|
|
520
|
+
title: importedReference.title,
|
|
521
|
+
})}
|
|
522
|
+
</p>
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
)}
|
|
487
526
|
</div>
|
|
488
527
|
</div>
|
|
489
528
|
</div>
|
|
490
529
|
</main>
|
|
491
530
|
|
|
492
531
|
<footer className="flex shrink-0 items-center justify-between border-t border-border px-5 py-4 sm:px-8">
|
|
493
|
-
<Button
|
|
532
|
+
<Button
|
|
533
|
+
type="button"
|
|
534
|
+
variant="ghost"
|
|
535
|
+
onClick={onSkip}
|
|
536
|
+
disabled={importing}
|
|
537
|
+
>
|
|
494
538
|
{skipLabel}
|
|
495
539
|
</Button>
|
|
496
540
|
<Button
|
|
497
541
|
type="button"
|
|
498
|
-
onClick={handleContinue}
|
|
542
|
+
onClick={() => void handleContinue()}
|
|
543
|
+
aria-busy={importing}
|
|
499
544
|
disabled={
|
|
500
545
|
importing || Boolean(selectedSource && !selectedSource.value.trim())
|
|
501
546
|
}
|
|
502
547
|
>
|
|
503
|
-
|
|
548
|
+
{importing
|
|
549
|
+
? importingLabel
|
|
550
|
+
: importedReference
|
|
551
|
+
? t("home.continueToGenerate")
|
|
552
|
+
: t("home.continue")}
|
|
504
553
|
<IconCheck className="ms-1.5 size-4" />
|
|
505
554
|
</Button>
|
|
506
555
|
</footer>
|
|
@@ -512,6 +561,8 @@ function FileImportOption({
|
|
|
512
561
|
accept,
|
|
513
562
|
icon,
|
|
514
563
|
label,
|
|
564
|
+
imported = false,
|
|
565
|
+
importedLabel,
|
|
515
566
|
importing,
|
|
516
567
|
importingLabel,
|
|
517
568
|
onChange,
|
|
@@ -519,6 +570,8 @@ function FileImportOption({
|
|
|
519
570
|
accept: string;
|
|
520
571
|
icon: ReactNode;
|
|
521
572
|
label: string;
|
|
573
|
+
imported?: boolean;
|
|
574
|
+
importedLabel: string;
|
|
522
575
|
importing: boolean;
|
|
523
576
|
importingLabel: string;
|
|
524
577
|
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
@@ -529,8 +582,9 @@ function FileImportOption({
|
|
|
529
582
|
"flex cursor-pointer items-center justify-center gap-2 rounded-md border border-border px-3 py-2 text-sm font-medium transition-colors hover:bg-accent",
|
|
530
583
|
importing && "pointer-events-none opacity-60",
|
|
531
584
|
)}
|
|
585
|
+
aria-label={imported ? `${label} - ${importedLabel}` : label}
|
|
532
586
|
>
|
|
533
|
-
{icon}
|
|
587
|
+
{imported ? <IconCheck className="size-4 text-primary" /> : icon}
|
|
534
588
|
<span>{importing ? importingLabel : label}</span>
|
|
535
589
|
<input
|
|
536
590
|
type="file"
|
|
@@ -547,11 +601,15 @@ function FileImportOption({
|
|
|
547
601
|
function ImportOption({
|
|
548
602
|
icon,
|
|
549
603
|
label,
|
|
604
|
+
confirmed = false,
|
|
605
|
+
confirmedLabel,
|
|
550
606
|
selected = false,
|
|
551
607
|
onClick,
|
|
552
608
|
}: {
|
|
553
609
|
icon: ReactNode;
|
|
554
610
|
label: string;
|
|
611
|
+
confirmed?: boolean;
|
|
612
|
+
confirmedLabel?: string;
|
|
555
613
|
selected?: boolean;
|
|
556
614
|
onClick: () => void;
|
|
557
615
|
}) {
|
|
@@ -565,9 +623,12 @@ function ImportOption({
|
|
|
565
623
|
? "border-primary/50 bg-primary/5 text-primary"
|
|
566
624
|
: "border-border hover:bg-accent",
|
|
567
625
|
)}
|
|
626
|
+
aria-label={
|
|
627
|
+
confirmed && confirmedLabel ? `${label} - ${confirmedLabel}` : label
|
|
628
|
+
}
|
|
568
629
|
aria-pressed={selected}
|
|
569
630
|
>
|
|
570
|
-
{icon}
|
|
631
|
+
{confirmed ? <IconCheck className="size-4 text-primary" /> : icon}
|
|
571
632
|
<span>{label}</span>
|
|
572
633
|
</button>
|
|
573
634
|
);
|
|
@@ -9,7 +9,9 @@ import { toast } from "sonner";
|
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
NewDeckReferenceStep,
|
|
12
|
+
type ImportedReference,
|
|
12
13
|
type NewDeckReferenceSelection,
|
|
14
|
+
type NewDeckReferenceSource,
|
|
13
15
|
} from "@/components/editor/NewDeckReferenceStep";
|
|
14
16
|
import {
|
|
15
17
|
uploadPromptFiles,
|
|
@@ -204,37 +206,159 @@ export function FirstDeckOnboardingFlow({
|
|
|
204
206
|
);
|
|
205
207
|
|
|
206
208
|
const handleReferenceImport = useCallback(
|
|
207
|
-
async (files: File[]) => {
|
|
209
|
+
async (files: File[]): Promise<ImportedReference | null> => {
|
|
208
210
|
setReferenceImporting(true);
|
|
209
211
|
try {
|
|
210
212
|
const uploaded = await uploadPromptFiles(files);
|
|
211
213
|
const pptxReference = uploaded.find((file) =>
|
|
212
214
|
file.originalName.toLowerCase().endsWith(".pptx"),
|
|
213
215
|
);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
const pdfReference = uploaded.find((file) =>
|
|
217
|
+
file.originalName.toLowerCase().endsWith(".pdf"),
|
|
218
|
+
);
|
|
219
|
+
let importedReference: ImportedReference | null = null;
|
|
218
220
|
let generationFiles = uploaded;
|
|
219
221
|
if (pptxReference) {
|
|
220
222
|
const imported = (await callAction("import-pptx", {
|
|
221
223
|
filePath: pptxReference.path,
|
|
222
|
-
})) as {
|
|
223
|
-
|
|
224
|
+
})) as {
|
|
225
|
+
id?: unknown;
|
|
226
|
+
imported?: unknown;
|
|
227
|
+
slideCount?: unknown;
|
|
228
|
+
title?: unknown;
|
|
229
|
+
};
|
|
230
|
+
if (
|
|
231
|
+
typeof imported.id !== "string" ||
|
|
232
|
+
!imported.id ||
|
|
233
|
+
imported.imported !== true ||
|
|
234
|
+
typeof imported.slideCount !== "number" ||
|
|
235
|
+
imported.slideCount < 1
|
|
236
|
+
) {
|
|
224
237
|
throw new Error("The imported presentation did not create a deck.");
|
|
225
238
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
239
|
+
importedReference = {
|
|
240
|
+
id: imported.id,
|
|
241
|
+
title:
|
|
242
|
+
typeof imported.title === "string" && imported.title
|
|
243
|
+
? imported.title
|
|
244
|
+
: t("home.importedReferenceDeck"),
|
|
245
|
+
source: "pptx",
|
|
231
246
|
};
|
|
232
247
|
generationFiles = uploaded.filter((file) => file !== pptxReference);
|
|
248
|
+
} else if (pdfReference) {
|
|
249
|
+
const referenceDeck = createDeck(undefined, {
|
|
250
|
+
noDefaultSlides: true,
|
|
251
|
+
});
|
|
252
|
+
const persisted = await ensureDeckPersisted(referenceDeck.id);
|
|
253
|
+
if (!persisted.persisted) {
|
|
254
|
+
deleteDeck(referenceDeck.id);
|
|
255
|
+
throw new Error(
|
|
256
|
+
describeDeckPersistenceFailure(
|
|
257
|
+
persisted,
|
|
258
|
+
"The PDF reference deck could not be saved.",
|
|
259
|
+
),
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
const imported = (await callAction("import-file", {
|
|
264
|
+
filePath: pdfReference.path,
|
|
265
|
+
format: "pdf",
|
|
266
|
+
deckId: referenceDeck.id,
|
|
267
|
+
importIntoDeck: true,
|
|
268
|
+
})) as {
|
|
269
|
+
imported?: unknown;
|
|
270
|
+
deckId?: unknown;
|
|
271
|
+
pageCount?: unknown;
|
|
272
|
+
title?: unknown;
|
|
273
|
+
};
|
|
274
|
+
if (
|
|
275
|
+
imported.imported !== true ||
|
|
276
|
+
imported.deckId !== referenceDeck.id ||
|
|
277
|
+
typeof imported.pageCount !== "number" ||
|
|
278
|
+
imported.pageCount < 1
|
|
279
|
+
) {
|
|
280
|
+
throw new Error("The PDF reference deck could not be imported.");
|
|
281
|
+
}
|
|
282
|
+
importedReference = {
|
|
283
|
+
id: referenceDeck.id,
|
|
284
|
+
title:
|
|
285
|
+
typeof imported.title === "string" && imported.title
|
|
286
|
+
? imported.title
|
|
287
|
+
: t("home.importedReferenceDeck"),
|
|
288
|
+
source: "pdf",
|
|
289
|
+
};
|
|
290
|
+
generationFiles = uploaded.filter((file) => file !== pdfReference);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
deleteDeck(referenceDeck.id);
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
233
295
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
296
|
+
setPromptFiles((current) => [...current, ...generationFiles]);
|
|
297
|
+
if (importedReference) {
|
|
298
|
+
await reloadDecks();
|
|
299
|
+
rememberReference({ id: importedReference.id, kind: "deck" });
|
|
300
|
+
}
|
|
301
|
+
return importedReference;
|
|
302
|
+
} catch (error) {
|
|
303
|
+
toast.error(t("editorToolbar.uploadFailed"), {
|
|
304
|
+
description:
|
|
305
|
+
error instanceof Error
|
|
306
|
+
? error.message
|
|
307
|
+
: t("editorToolbar.importFailedDescription"),
|
|
308
|
+
});
|
|
309
|
+
return null;
|
|
310
|
+
} finally {
|
|
311
|
+
setReferenceImporting(false);
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
[
|
|
315
|
+
callAction,
|
|
316
|
+
createDeck,
|
|
317
|
+
deleteDeck,
|
|
318
|
+
ensureDeckPersisted,
|
|
319
|
+
reloadDecks,
|
|
320
|
+
rememberReference,
|
|
321
|
+
t,
|
|
322
|
+
],
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
const handleReferenceSourceImport = useCallback(
|
|
326
|
+
async (
|
|
327
|
+
source: NewDeckReferenceSource,
|
|
328
|
+
): Promise<ImportedReference | null> => {
|
|
329
|
+
if (source.kind !== "google-docs") return null;
|
|
330
|
+
setReferenceImporting(true);
|
|
331
|
+
try {
|
|
332
|
+
const imported = (await callAction("import-google-slides-reference", {
|
|
333
|
+
presentationUrl: source.value,
|
|
334
|
+
})) as {
|
|
335
|
+
id?: unknown;
|
|
336
|
+
imported?: unknown;
|
|
337
|
+
slideCount?: unknown;
|
|
338
|
+
title?: unknown;
|
|
339
|
+
};
|
|
340
|
+
if (
|
|
341
|
+
typeof imported.id !== "string" ||
|
|
342
|
+
!imported.id ||
|
|
343
|
+
imported.imported !== true ||
|
|
344
|
+
typeof imported.slideCount !== "number" ||
|
|
345
|
+
imported.slideCount < 1
|
|
346
|
+
) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
"The Google Slides presentation did not create a deck.",
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
const importedReference: ImportedReference = {
|
|
352
|
+
id: imported.id,
|
|
353
|
+
title:
|
|
354
|
+
typeof imported.title === "string" && imported.title
|
|
355
|
+
? imported.title
|
|
356
|
+
: t("home.importedReferenceDeck"),
|
|
357
|
+
source: "google-slides",
|
|
358
|
+
};
|
|
359
|
+
await reloadDecks();
|
|
360
|
+
rememberReference({ id: importedReference.id, kind: "deck" });
|
|
361
|
+
return importedReference;
|
|
238
362
|
} catch (error) {
|
|
239
363
|
toast.error(t("editorToolbar.uploadFailed"), {
|
|
240
364
|
description:
|
|
@@ -242,11 +366,12 @@ export function FirstDeckOnboardingFlow({
|
|
|
242
366
|
? error.message
|
|
243
367
|
: t("editorToolbar.importFailedDescription"),
|
|
244
368
|
});
|
|
369
|
+
return null;
|
|
245
370
|
} finally {
|
|
246
371
|
setReferenceImporting(false);
|
|
247
372
|
}
|
|
248
373
|
},
|
|
249
|
-
[
|
|
374
|
+
[callAction, reloadDecks, rememberReference, t],
|
|
250
375
|
);
|
|
251
376
|
|
|
252
377
|
const handleReferenceSkip = useCallback(() => {
|
|
@@ -267,6 +392,7 @@ export function FirstDeckOnboardingFlow({
|
|
|
267
392
|
recentReferences={recentReferences}
|
|
268
393
|
onSelect={handleReferenceSelect}
|
|
269
394
|
onImport={handleReferenceImport}
|
|
395
|
+
onImportSource={handleReferenceSourceImport}
|
|
270
396
|
onSkip={handleReferenceSkip}
|
|
271
397
|
onOpenChange={(open) => {
|
|
272
398
|
if (!open) setStep("prompt");
|
|
@@ -278,7 +404,6 @@ export function FirstDeckOnboardingFlow({
|
|
|
278
404
|
chooseDeckLabel={t("home.referenceDeckPlaceholder")}
|
|
279
405
|
importingLabel={t("raw.uploading")}
|
|
280
406
|
skipLabel={t("home.referenceDeckNone")}
|
|
281
|
-
defaultSuffix={t("home.defaultSuffix")}
|
|
282
407
|
starredLabel={t("home.referenceDeckStarredGroup")}
|
|
283
408
|
otherDecksLabel={t("home.referenceDeckOtherGroup")}
|
|
284
409
|
searchDecksLabel={t("root.searchDecks")}
|
|
@@ -672,6 +672,12 @@ const messages = {
|
|
|
672
672
|
chooseReferences: "Choose references",
|
|
673
673
|
addDesignSystem: "+ Design system",
|
|
674
674
|
importFrom: "Import from",
|
|
675
|
+
imported: "Imported",
|
|
676
|
+
importedReferenceDeck: "Imported reference deck",
|
|
677
|
+
referenceImportSuccess: "Imported successfully",
|
|
678
|
+
referenceImportSelected: '"{{title}}" is now the reference deck.',
|
|
679
|
+
continue: "Continue",
|
|
680
|
+
continueToGenerate: "Continue to generate",
|
|
675
681
|
designSystem: "Design system",
|
|
676
682
|
referenceDeck: "Reference deck",
|
|
677
683
|
referenceDeckPlaceholder: "Match the style of an existing deck",
|
|
@@ -2,12 +2,18 @@ export function shouldShowNewDeckGeneratingOverlay({
|
|
|
2
2
|
generating,
|
|
3
3
|
isNewDeckCreation,
|
|
4
4
|
slideCount,
|
|
5
|
+
generationStarted,
|
|
5
6
|
}: {
|
|
6
7
|
generating: boolean;
|
|
7
8
|
isNewDeckCreation: boolean;
|
|
8
9
|
slideCount?: number | null;
|
|
10
|
+
generationStarted: boolean;
|
|
9
11
|
}): boolean {
|
|
10
|
-
return
|
|
12
|
+
return (
|
|
13
|
+
isNewDeckCreation &&
|
|
14
|
+
(slideCount ?? 0) === 0 &&
|
|
15
|
+
(generating || !generationStarted)
|
|
16
|
+
);
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
export function shouldShowNewDeckGeneratingProgress({
|