@agent-native/core 0.149.1 → 0.149.2
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/.agents/skills/dashboard-management/SKILL.md +2 -0
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +150 -46
- package/corpus/templates/analytics/app/global.css +1 -1
- package/corpus/templates/analytics/app/i18n-data.ts +37 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -0
- package/corpus/templates/analytics/changelog/2026-08-10-dashboard-tables-can-be-copied-and-sorted-by-multiple-column.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/assets/app/global.css +1 -1
- package/corpus/templates/assets/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/brain/app/global.css +1 -1
- package/corpus/templates/brain/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-10-clips-desktop-sign-in-now-starts-with-magic-links-and-google.md +6 -0
- package/corpus/templates/clips/chrome-extension/src/overlay.css +1 -0
- package/corpus/templates/clips/desktop/src/app.tsx +274 -137
- package/corpus/templates/clips/desktop/src/styles.css +35 -0
- package/corpus/templates/content/app/global.css +1 -0
- package/corpus/templates/crm/app/global.css +1 -1
- package/corpus/templates/crm/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/forms/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/plan/app/global.css +1 -0
- package/corpus/templates/plan/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/corpus/templates/plan/server/ui-plan-html.ts +4 -1
- package/corpus/templates/slides/AGENTS.md +11 -10
- package/corpus/templates/slides/actions/add-deck.ts +2 -0
- package/corpus/templates/slides/actions/add-slide.ts +15 -1
- package/corpus/templates/slides/actions/create-deck.ts +18 -6
- package/corpus/templates/slides/actions/get-deck.ts +29 -1
- package/corpus/templates/slides/actions/patch-deck.ts +122 -9
- package/corpus/templates/slides/actions/save-deck.ts +24 -1
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +134 -618
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +69 -60
- package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +13 -1
- package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +56 -174
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +239 -116
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +3 -3
- package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +1 -1
- package/corpus/templates/slides/app/components/layout/Layout.tsx +34 -35
- package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +2 -5
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +45 -40
- package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +63 -16
- package/corpus/templates/slides/app/hooks/use-generating-slide-preview.ts +25 -3
- package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +5 -4
- package/corpus/templates/slides/app/lib/recent-references.ts +23 -1
- package/corpus/templates/slides/app/lib/slide-animation-elements.ts +41 -3
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +4 -27
- package/corpus/templates/slides/app/pages/Index.tsx +240 -89
- package/corpus/templates/slides/changelog/2026-08-10-ai-editing-avatar-badge.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-generated-decks-keep-a-clear-human-readable-title.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-new-deck-creation-now-shows-progress-opens-the-editor-direct.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-presentation-reveals-now-follow-valid-targets.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-editing-controls-are-consolidated-into-one-focused-me.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-now-verify-and-restyle-every-imported-slide-in-one-pa.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-slides-only-suggests-a-google-connection-for-relevant-reques.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-10-the-google-slides-import-option-is-now-labeled-slides.md +6 -0
- package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
- package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -3
- package/corpus/templates/slides/shared/api.ts +2 -1
- package/corpus/templates/slides/shared/deck-title.ts +135 -0
- package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +4 -1
- package/dist/agent/engine/ai-sdk-engine.js +1 -1
- package/dist/agent/production-agent.d.ts +21 -1
- package/dist/agent/production-agent.js +129 -10
- package/dist/agent/tool-approval-store.d.ts +18 -0
- package/dist/agent/tool-approval-store.js +122 -0
- package/dist/agent/types.d.ts +1 -1
- package/dist/cli/code-agent-executor.js +11 -2
- package/dist/client/AgentPanel.js +31 -6
- package/dist/client/AssistantChat.d.ts +3 -2
- package/dist/client/AssistantChat.js +7 -20
- package/dist/client/agent-chat/index.d.ts +1 -1
- package/dist/client/agent-chat-adapter.js +11 -0
- package/dist/client/agent-chat.d.ts +1 -1
- package/dist/client/chat/markdown-renderer.js +14 -2
- package/dist/client/chat/message-components.d.ts +6 -2
- package/dist/client/chat/message-components.js +25 -47
- package/dist/client/chat/tool-call-display.d.ts +3 -5
- package/dist/client/chat/tool-call-display.js +7 -212
- package/dist/client/chat-first/apps-rail.js +3 -3
- package/dist/client/chat-first/browser-pane.js +4 -1
- package/dist/client/chat-first/chat-history.d.ts +3 -1
- package/dist/client/chat-first/chat-history.js +2 -2
- package/dist/client/chat-first/surface-tabs.d.ts +1 -1
- package/dist/client/chat-first/surface-tabs.js +52 -41
- package/dist/client/chat-first/types.d.ts +3 -0
- package/dist/client/chat-first.d.ts +10 -0
- package/dist/client/chat-first.js +37 -2
- package/dist/client/components/ui/message-scroller.js +4 -1
- package/dist/client/i18n.js +3 -1
- package/dist/client/resources/McpConnectionSuggestion.js +9 -1
- package/dist/client/sse-event-processor.js +10 -0
- package/dist/client/use-chat-models.js +156 -80
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/integrations/webhook-handler.js +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/auth.js +262 -31
- package/dist/server/onboarding-html.js +56 -9
- package/dist/shared/reasoning-effort.d.ts +8 -8
- package/dist/shared/reasoning-effort.js +10 -10
- package/dist/shared/streaming-text-smoothing.js +41 -11
- package/dist/styles/agent-native.css +19 -32
- package/dist/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
- package/docs/content/template-chat.mdx +1 -1
- package/package.json +2 -2
- package/src/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
} from "@/components/editor/NewDeckReferenceStep";
|
|
34
34
|
import PromptPopover, {
|
|
35
35
|
uploadPromptFiles,
|
|
36
|
+
type PromptImportSelection,
|
|
36
37
|
type UploadedFile,
|
|
37
38
|
} from "@/components/editor/PromptDialog";
|
|
38
39
|
import {
|
|
@@ -64,6 +65,7 @@ import {
|
|
|
64
65
|
type ImportedSourceDeck,
|
|
65
66
|
} from "@/lib/import-uploaded-deck";
|
|
66
67
|
import {
|
|
68
|
+
forgetRecentReference,
|
|
67
69
|
readRecentReferences,
|
|
68
70
|
rememberRecentReference,
|
|
69
71
|
type RecentReference,
|
|
@@ -236,7 +238,7 @@ export default function Index() {
|
|
|
236
238
|
loadError,
|
|
237
239
|
reloadDecks,
|
|
238
240
|
} = useDecks();
|
|
239
|
-
const { designSystems
|
|
241
|
+
const { designSystems } = useDesignSystems();
|
|
240
242
|
const {
|
|
241
243
|
referenceDeck: workspaceReferenceDeck,
|
|
242
244
|
designSystem: workspaceDesignSystem,
|
|
@@ -294,27 +296,25 @@ export default function Index() {
|
|
|
294
296
|
() => new Map(designSystems.map((ds) => [ds.id, ds.title])),
|
|
295
297
|
[designSystems],
|
|
296
298
|
);
|
|
297
|
-
// A workspace default the caller cannot open is reported by the action as
|
|
298
|
-
// `unavailable` rather than absent; preselecting it would send every new
|
|
299
|
-
// prompt at a deck that 404s, so fall back to no reference instead.
|
|
300
|
-
const workspaceReferenceDeckId =
|
|
301
|
-
workspaceReferenceDeck && !workspaceReferenceDeck.unavailable
|
|
302
|
-
? workspaceReferenceDeck.id
|
|
303
|
-
: null;
|
|
304
299
|
const workspaceDesignSystemId =
|
|
305
300
|
workspaceDesignSystem && !workspaceDesignSystem.unavailable
|
|
306
301
|
? workspaceDesignSystem.id
|
|
307
302
|
: null;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
303
|
+
const lastUsedDesignSystemId =
|
|
304
|
+
recentReferences.find(
|
|
305
|
+
(reference) =>
|
|
306
|
+
reference.kind === "design-system" &&
|
|
307
|
+
designSystems.some((designSystem) => designSystem.id === reference.id),
|
|
308
|
+
)?.id ?? null;
|
|
309
|
+
const lastUsedReferenceDeckId =
|
|
310
|
+
recentReferences.find(
|
|
311
|
+
(reference) =>
|
|
312
|
+
reference.kind === "deck" &&
|
|
313
|
+
decks.some((deck) => deck.id === reference.id),
|
|
314
|
+
)?.id ?? null;
|
|
313
315
|
const initialDesignSystemId =
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
defaultSystem?.id ??
|
|
317
|
-
null;
|
|
316
|
+
lastUsedDesignSystemId ?? workspaceDesignSystemId;
|
|
317
|
+
const initialReferenceDeckId = lastUsedReferenceDeckId;
|
|
318
318
|
const deckFilter = searchParams.get("createdBy") === "me" ? "mine" : "all";
|
|
319
319
|
const visibleDecks = useMemo(
|
|
320
320
|
() =>
|
|
@@ -332,24 +332,16 @@ export default function Index() {
|
|
|
332
332
|
},
|
|
333
333
|
[],
|
|
334
334
|
);
|
|
335
|
+
const forgetReference = useCallback((kind: RecentReference["kind"]) => {
|
|
336
|
+
const result = forgetRecentReference(kind);
|
|
337
|
+
if (result.readable) setRecentReferences(result.items);
|
|
338
|
+
}, []);
|
|
335
339
|
|
|
336
340
|
useEffect(() => {
|
|
337
341
|
const result = readRecentReferences();
|
|
338
342
|
if (result.readable) setRecentReferences(result.items);
|
|
339
343
|
}, []);
|
|
340
344
|
|
|
341
|
-
useEffect(() => {
|
|
342
|
-
if (initialDesignSystemId) {
|
|
343
|
-
rememberReference({
|
|
344
|
-
id: initialDesignSystemId,
|
|
345
|
-
kind: "design-system",
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
if (workspaceReferenceDeckId) {
|
|
349
|
-
rememberReference({ id: workspaceReferenceDeckId, kind: "deck" });
|
|
350
|
-
}
|
|
351
|
-
}, [initialDesignSystemId, rememberReference, workspaceReferenceDeckId]);
|
|
352
|
-
|
|
353
345
|
const initialPrompt = searchParams.get("initialPrompt")?.trim() ?? "";
|
|
354
346
|
const onboardingPreview = searchParams.get("onboarding") === "preview";
|
|
355
347
|
const openInitialPrompt = useCallback(() => {
|
|
@@ -430,10 +422,10 @@ export default function Index() {
|
|
|
430
422
|
designSystemAutoRef.current = true;
|
|
431
423
|
referenceDeckAutoRef.current = true;
|
|
432
424
|
setSelectedDesignSystemId(initialDesignSystemId ?? null);
|
|
433
|
-
setSelectedReferenceDeckId(
|
|
425
|
+
setSelectedReferenceDeckId(initialReferenceDeckId ?? null);
|
|
434
426
|
setShowNewDeckPrompt(true);
|
|
435
427
|
},
|
|
436
|
-
[initialDesignSystemId,
|
|
428
|
+
[initialDesignSystemId, initialReferenceDeckId],
|
|
437
429
|
);
|
|
438
430
|
|
|
439
431
|
const setNewDeckPromptOpen = useCallback(
|
|
@@ -486,13 +478,13 @@ export default function Index() {
|
|
|
486
478
|
}
|
|
487
479
|
}, [initialDesignSystemId, designSystems.length, showNewDeckPrompt]);
|
|
488
480
|
|
|
489
|
-
// Same as above for the reference-deck picker:
|
|
481
|
+
// Same as above for the reference-deck picker: the local last-used reference
|
|
490
482
|
// can still be loading when the dialog opens, so re-apply it once it
|
|
491
483
|
// resolves unless the user already picked a reference deck.
|
|
492
484
|
useEffect(() => {
|
|
493
485
|
if (!showNewDeckPrompt || !referenceDeckAutoRef.current) return;
|
|
494
|
-
setSelectedReferenceDeckId(
|
|
495
|
-
}, [
|
|
486
|
+
setSelectedReferenceDeckId(initialReferenceDeckId ?? null);
|
|
487
|
+
}, [initialReferenceDeckId, showNewDeckPrompt]);
|
|
496
488
|
|
|
497
489
|
// Restore a prompt that was held back when the user wasn't signed in:
|
|
498
490
|
// we wrote the text to sessionStorage before redirecting to sign-in,
|
|
@@ -516,9 +508,9 @@ export default function Index() {
|
|
|
516
508
|
designSystemAutoRef.current = true;
|
|
517
509
|
referenceDeckAutoRef.current = true;
|
|
518
510
|
setSelectedDesignSystemId(initialDesignSystemId ?? null);
|
|
519
|
-
setSelectedReferenceDeckId(
|
|
511
|
+
setSelectedReferenceDeckId(initialReferenceDeckId ?? null);
|
|
520
512
|
setShowNewDeckPrompt(true);
|
|
521
|
-
}, [initialDesignSystemId,
|
|
513
|
+
}, [initialDesignSystemId, initialReferenceDeckId, session]);
|
|
522
514
|
|
|
523
515
|
// Recovering from a failed deck-generation kickoff (see
|
|
524
516
|
// recoverFromGenerationSetupFailure below) navigates back to this route
|
|
@@ -598,20 +590,44 @@ export default function Index() {
|
|
|
598
590
|
const deckId = deck.id;
|
|
599
591
|
setNewDeckPromptOpen(false);
|
|
600
592
|
|
|
601
|
-
|
|
602
|
-
|
|
593
|
+
// Leave the grid as soon as the optimistic deck exists. Persistence and
|
|
594
|
+
// source import can take several seconds for a large PDF, so the editor's
|
|
595
|
+
// generation state is the only useful surface while that work finishes.
|
|
596
|
+
navigate(`/deck/${deck.id}?generating=1`, {
|
|
597
|
+
replace: true,
|
|
598
|
+
flushSync: true,
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
const recoverFromGenerationSetupFailure = (description: string) => {
|
|
603
602
|
if (!savePromptForRetry(prompt)) {
|
|
604
603
|
setNewDeckInitialPrompt({ text: prompt, key: Date.now() });
|
|
605
604
|
}
|
|
606
605
|
setNewDeckRetryFiles(filesForGeneration);
|
|
607
606
|
deleteDeck(deckId);
|
|
608
|
-
toast.error(t("home.generationStartFailed"), {
|
|
609
|
-
|
|
607
|
+
toast.error(t("home.generationStartFailed"), { description });
|
|
608
|
+
if (
|
|
609
|
+
typeof window !== "undefined" &&
|
|
610
|
+
deckIdFromPathname(window.location.pathname) === deckId
|
|
611
|
+
) {
|
|
612
|
+
navigate("/", {
|
|
613
|
+
replace: true,
|
|
614
|
+
state: {
|
|
615
|
+
retryPrompt: prompt,
|
|
616
|
+
retryFiles: filesForGeneration,
|
|
617
|
+
} satisfies DeckGenerationRetryState,
|
|
618
|
+
flushSync: true,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
const persisted = await ensureDeckPersisted(deck.id);
|
|
624
|
+
if (!persisted.persisted) {
|
|
625
|
+
recoverFromGenerationSetupFailure(
|
|
626
|
+
describeDeckPersistenceFailure(
|
|
610
627
|
persisted,
|
|
611
628
|
t("home.generationStartFailedDescription"),
|
|
612
629
|
),
|
|
613
|
-
|
|
614
|
-
setShowNewDeckPrompt(true);
|
|
630
|
+
);
|
|
615
631
|
return;
|
|
616
632
|
}
|
|
617
633
|
|
|
@@ -622,26 +638,17 @@ export default function Index() {
|
|
|
622
638
|
deckId,
|
|
623
639
|
);
|
|
624
640
|
} catch (error) {
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
toast.error(t("home.generationStartFailed"), {
|
|
631
|
-
description:
|
|
632
|
-
error instanceof Error
|
|
633
|
-
? error.message
|
|
634
|
-
: t("home.generationStartFailedDescription"),
|
|
635
|
-
});
|
|
636
|
-
setShowNewDeckPrompt(true);
|
|
641
|
+
recoverFromGenerationSetupFailure(
|
|
642
|
+
error instanceof Error
|
|
643
|
+
? error.message
|
|
644
|
+
: t("home.generationStartFailedDescription"),
|
|
645
|
+
);
|
|
637
646
|
return;
|
|
638
647
|
}
|
|
639
648
|
|
|
640
649
|
clearPendingPromptForRetry();
|
|
641
650
|
setNewDeckInitialPrompt(null);
|
|
642
651
|
setNewDeckRetryFiles([]);
|
|
643
|
-
navigate(`/deck/${deck.id}`, { flushSync: true });
|
|
644
|
-
|
|
645
652
|
const trimmedPrompt = prompt.trim();
|
|
646
653
|
const hasImportedGoogleDocContext = trimmedPrompt.includes("<google-doc ");
|
|
647
654
|
const googleDocUrls = hasImportedGoogleDocContext
|
|
@@ -662,11 +669,11 @@ export default function Index() {
|
|
|
662
669
|
"If the action cannot read a private document, tell the user the exact sharing step from the action error instead of generating from the URL alone.",
|
|
663
670
|
].join("\n")
|
|
664
671
|
: "";
|
|
665
|
-
const referenceDeckContext =
|
|
666
|
-
await
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
672
|
+
const [referenceDeckContext, hydratedDesignSystemContext] =
|
|
673
|
+
await Promise.all([
|
|
674
|
+
loadReferenceDeckGenerationContext(referenceDeckId),
|
|
675
|
+
loadDesignSystemGenerationContext(selectedDesignSystem?.id),
|
|
676
|
+
]);
|
|
670
677
|
const designSystemContext = selectedDesignSystem
|
|
671
678
|
? [
|
|
672
679
|
"",
|
|
@@ -703,19 +710,20 @@ export default function Index() {
|
|
|
703
710
|
"Source-preserving improvement mode:",
|
|
704
711
|
`- The target deck already contains ${importedSourceDeck.slideCount} imported source slides. Treat those slides as the user's complete source, not as inspiration for a new deck.`,
|
|
705
712
|
"- Keep the exact source slide count, order, IDs, factual meaning, notes, images, charts, tables, diagrams, and freeform objects unless the user explicitly asks to change one of them.",
|
|
706
|
-
"- Read `get-deck` before editing, load the linked design system with `get-design-system`, then
|
|
713
|
+
"- Read `get-deck` once before editing to obtain every existing slide ID and source HTML, load the linked design system with `get-design-system`, then make a deck-wide restyle with one `patch-deck` call using `requireAllSourceSlides: true` and one `patch-slide` operation with `fields.content` for every source slide ID. Do not split a full-deck restyle into arbitrary batches or fall back to one-by-one `update-slide` calls; use `update-slide` only for a targeted one-slide edit. Keep every original `<img>` source and enough original factual copy for each slide; for PDF slides, use restrained design-system chrome around the page without obscuring it.",
|
|
707
714
|
"- Do not call `add-slide`, delete slides, reorder slides, or replace source images with generic cards. Do not claim success until `get-deck` verifies the same slide IDs and count after the edits.",
|
|
715
|
+
'- After the patch succeeds, verify with `get-deck` using `compact: "true"` so only slide IDs, count, and previews are returned. Do not report an initial or partial pass, and do not leave any source slides for a later run.',
|
|
708
716
|
"- If `get-deck` reports partial source fidelity or skipped images, stop and report the exact warning instead of claiming a reliable restyle.",
|
|
709
717
|
].join("\n")
|
|
710
718
|
: "";
|
|
711
719
|
const sourceModeInstructions = importedSourceDeck
|
|
712
720
|
? [
|
|
713
721
|
"The request is an in-place visual improvement of an imported source deck. Make a coherent style pass across every existing slide while preserving all source content and media.",
|
|
714
|
-
"Do not use the new-deck add-slide workflow for this source-preserving request.",
|
|
722
|
+
"Do not use the new-deck add-slide workflow for this source-preserving request. Finish every source slide in this run; if patch-deck rejects incomplete coverage, continue with the returned missing IDs instead of reporting success with a partial deck.",
|
|
715
723
|
].join("\n")
|
|
716
724
|
: [
|
|
717
725
|
"Start a `manage-progress` run so progress appears in the app header. Add the first slide as soon as it is ready, then continue one slide at a time so the editor visibly fills in.",
|
|
718
|
-
"After reading any requested or imported source material, but before adding the first slide, choose a concise, specific deck title from the user's request and source material. Call `patch-deck` with `deckId: \"" +
|
|
726
|
+
"After reading any requested or imported source material, but before adding the first slide, choose a concise, specific deck title from the user's request and source material. Never use the deck id, run id, file id, or another opaque alphanumeric token as the title. Call `patch-deck` with `deckId: \"" +
|
|
719
727
|
deckId +
|
|
720
728
|
'\"` and `operations: [{ "op": "patch-deck-fields", "fields": { "title": "<generated title>" } }]`. Include only `title` in `fields`; omit all other optional fields. Never leave a generated deck named "Untitled Deck" or another placeholder.',
|
|
721
729
|
"If the user asks for a standalone visual, diagram, hero, one-pager, poster, or a couple of visuals, create only the requested one/few polished visual slides. Do not pad the result into a full presentation.",
|
|
@@ -754,10 +762,6 @@ export default function Index() {
|
|
|
754
762
|
).catch(() => {});
|
|
755
763
|
deleteClientAppState("guided-questions").catch(() => {});
|
|
756
764
|
|
|
757
|
-
navigate(`/deck/${deck.id}?generating=1`, {
|
|
758
|
-
replace: true,
|
|
759
|
-
flushSync: true,
|
|
760
|
-
});
|
|
761
765
|
agentSubmit(createDeckAgentMessage(trimmedPrompt), context, {
|
|
762
766
|
newTab: true,
|
|
763
767
|
reuseEmptyTab: true,
|
|
@@ -774,18 +778,150 @@ export default function Index() {
|
|
|
774
778
|
[setNewDeckPromptOpen],
|
|
775
779
|
);
|
|
776
780
|
|
|
781
|
+
const handlePromptSkip = useCallback(() => {
|
|
782
|
+
setNewDeckPromptOpen(false, { clearInitialPrompt: false });
|
|
783
|
+
setPendingDeck({ prompt: "", files: [] });
|
|
784
|
+
setShowNewDeckReferenceStep(true);
|
|
785
|
+
}, [setNewDeckPromptOpen]);
|
|
786
|
+
|
|
787
|
+
const handleDirectImport = useCallback(
|
|
788
|
+
async (selection: PromptImportSelection): Promise<boolean> => {
|
|
789
|
+
if (!session) {
|
|
790
|
+
setSignInPromptHadFiles(selection.kind !== "google-slides");
|
|
791
|
+
setShowSignInDialog(true);
|
|
792
|
+
return false;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
if (selection.kind === "google-slides") {
|
|
796
|
+
const imported = (await callAction("import-google-slides-reference", {
|
|
797
|
+
presentationUrl: selection.url,
|
|
798
|
+
})) as {
|
|
799
|
+
id?: unknown;
|
|
800
|
+
imported?: unknown;
|
|
801
|
+
slideCount?: unknown;
|
|
802
|
+
};
|
|
803
|
+
if (
|
|
804
|
+
typeof imported.id !== "string" ||
|
|
805
|
+
!imported.id ||
|
|
806
|
+
imported.imported !== true ||
|
|
807
|
+
typeof imported.slideCount !== "number" ||
|
|
808
|
+
imported.slideCount < 1
|
|
809
|
+
) {
|
|
810
|
+
throw new Error(
|
|
811
|
+
"The Google Slides presentation did not create a deck.",
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
await reloadDecks();
|
|
815
|
+
navigate(`/deck/${imported.id}`, { flushSync: true });
|
|
816
|
+
return true;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
const uploaded = await uploadPromptFiles(selection.files);
|
|
820
|
+
const file = uploaded[0];
|
|
821
|
+
if (!file) throw new Error("The selected file could not be uploaded.");
|
|
822
|
+
|
|
823
|
+
if (selection.kind === "pptx") {
|
|
824
|
+
const imported = (await callAction("import-pptx", {
|
|
825
|
+
filePath: file.path,
|
|
826
|
+
designSystemId: initialDesignSystemId,
|
|
827
|
+
})) as {
|
|
828
|
+
id?: unknown;
|
|
829
|
+
imported?: unknown;
|
|
830
|
+
slideCount?: unknown;
|
|
831
|
+
};
|
|
832
|
+
if (
|
|
833
|
+
typeof imported.id !== "string" ||
|
|
834
|
+
!imported.id ||
|
|
835
|
+
imported.imported !== true ||
|
|
836
|
+
typeof imported.slideCount !== "number" ||
|
|
837
|
+
imported.slideCount < 1
|
|
838
|
+
) {
|
|
839
|
+
throw new Error("The PowerPoint presentation did not create a deck.");
|
|
840
|
+
}
|
|
841
|
+
await reloadDecks();
|
|
842
|
+
navigate(`/deck/${imported.id}`, { flushSync: true });
|
|
843
|
+
return true;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
let deck: ReturnType<typeof createDeck> | undefined;
|
|
847
|
+
flushSync(() => {
|
|
848
|
+
deck = createDeck(undefined, {
|
|
849
|
+
noDefaultSlides: true,
|
|
850
|
+
designSystemId: initialDesignSystemId,
|
|
851
|
+
});
|
|
852
|
+
});
|
|
853
|
+
if (!deck) throw new Error("The PDF deck could not be created.");
|
|
854
|
+
|
|
855
|
+
const persisted = await ensureDeckPersisted(deck.id);
|
|
856
|
+
if (!persisted.persisted) {
|
|
857
|
+
deleteDeck(deck.id);
|
|
858
|
+
throw new Error(
|
|
859
|
+
describeDeckPersistenceFailure(
|
|
860
|
+
persisted,
|
|
861
|
+
"The PDF deck could not be saved.",
|
|
862
|
+
),
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
try {
|
|
867
|
+
const imported = (await callAction("import-file", {
|
|
868
|
+
filePath: file.path,
|
|
869
|
+
format: "pdf",
|
|
870
|
+
deckId: deck.id,
|
|
871
|
+
importIntoDeck: true,
|
|
872
|
+
})) as {
|
|
873
|
+
imported?: unknown;
|
|
874
|
+
deckId?: unknown;
|
|
875
|
+
pageCount?: unknown;
|
|
876
|
+
};
|
|
877
|
+
if (
|
|
878
|
+
imported.imported !== true ||
|
|
879
|
+
imported.deckId !== deck.id ||
|
|
880
|
+
typeof imported.pageCount !== "number" ||
|
|
881
|
+
imported.pageCount < 1
|
|
882
|
+
) {
|
|
883
|
+
throw new Error("The PDF could not be imported into the new deck.");
|
|
884
|
+
}
|
|
885
|
+
await reloadDecks();
|
|
886
|
+
navigate(`/deck/${deck.id}`, { flushSync: true });
|
|
887
|
+
return true;
|
|
888
|
+
} catch (error) {
|
|
889
|
+
deleteDeck(deck.id);
|
|
890
|
+
throw error;
|
|
891
|
+
}
|
|
892
|
+
},
|
|
893
|
+
[
|
|
894
|
+
callAction,
|
|
895
|
+
createDeck,
|
|
896
|
+
deleteDeck,
|
|
897
|
+
ensureDeckPersisted,
|
|
898
|
+
initialDesignSystemId,
|
|
899
|
+
navigate,
|
|
900
|
+
reloadDecks,
|
|
901
|
+
session,
|
|
902
|
+
],
|
|
903
|
+
);
|
|
904
|
+
|
|
777
905
|
const handleReferenceSelect = useCallback(
|
|
778
906
|
async (selection: NewDeckReferenceSelection) => {
|
|
779
907
|
const pending = pendingDeck;
|
|
780
908
|
if (!pending) return;
|
|
781
|
-
if (selection.designSystemId) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
909
|
+
if (selection.designSystemId !== undefined) {
|
|
910
|
+
if (selection.designSystemId) {
|
|
911
|
+
rememberReference({
|
|
912
|
+
id: selection.designSystemId,
|
|
913
|
+
kind: "design-system",
|
|
914
|
+
});
|
|
915
|
+
} else {
|
|
916
|
+
forgetReference("design-system");
|
|
917
|
+
}
|
|
786
918
|
}
|
|
787
|
-
if (selection.referenceDeckId) {
|
|
788
|
-
|
|
919
|
+
if (selection.referenceDeckId !== undefined) {
|
|
920
|
+
if (selection.referenceDeckId) {
|
|
921
|
+
rememberReference({ id: selection.referenceDeckId, kind: "deck" });
|
|
922
|
+
} else {
|
|
923
|
+
forgetReference("deck");
|
|
924
|
+
}
|
|
789
925
|
}
|
|
790
926
|
setShowNewDeckReferenceStep(false);
|
|
791
927
|
setPendingDeck(null);
|
|
@@ -795,7 +931,12 @@ export default function Index() {
|
|
|
795
931
|
selection,
|
|
796
932
|
);
|
|
797
933
|
},
|
|
798
|
-
[
|
|
934
|
+
[
|
|
935
|
+
forgetReference,
|
|
936
|
+
handleCreateDeckWithPrompt,
|
|
937
|
+
pendingDeck,
|
|
938
|
+
rememberReference,
|
|
939
|
+
],
|
|
799
940
|
);
|
|
800
941
|
|
|
801
942
|
const handleReferenceImport = useCallback(
|
|
@@ -901,7 +1042,6 @@ export default function Index() {
|
|
|
901
1042
|
);
|
|
902
1043
|
if (importedReference) {
|
|
903
1044
|
await reloadDecks();
|
|
904
|
-
rememberReference({ id: importedReference.id, kind: "deck" });
|
|
905
1045
|
setSelectedReferenceDeckId(importedReference.id);
|
|
906
1046
|
}
|
|
907
1047
|
return importedReference;
|
|
@@ -924,7 +1064,6 @@ export default function Index() {
|
|
|
924
1064
|
ensureDeckPersisted,
|
|
925
1065
|
pendingDeck,
|
|
926
1066
|
reloadDecks,
|
|
927
|
-
rememberReference,
|
|
928
1067
|
t,
|
|
929
1068
|
],
|
|
930
1069
|
);
|
|
@@ -964,7 +1103,6 @@ export default function Index() {
|
|
|
964
1103
|
source: "google-slides",
|
|
965
1104
|
};
|
|
966
1105
|
await reloadDecks();
|
|
967
|
-
rememberReference({ id: importedReference.id, kind: "deck" });
|
|
968
1106
|
setSelectedReferenceDeckId(importedReference.id);
|
|
969
1107
|
return importedReference;
|
|
970
1108
|
} catch (error) {
|
|
@@ -979,7 +1117,7 @@ export default function Index() {
|
|
|
979
1117
|
setReferenceImporting(false);
|
|
980
1118
|
}
|
|
981
1119
|
},
|
|
982
|
-
[callAction, reloadDecks,
|
|
1120
|
+
[callAction, reloadDecks, t],
|
|
983
1121
|
);
|
|
984
1122
|
|
|
985
1123
|
const handleReferenceSkip = useCallback(() => {
|
|
@@ -990,11 +1128,22 @@ export default function Index() {
|
|
|
990
1128
|
}
|
|
991
1129
|
setShowNewDeckReferenceStep(false);
|
|
992
1130
|
setPendingDeck(null);
|
|
1131
|
+
forgetReference("design-system");
|
|
1132
|
+
forgetReference("deck");
|
|
1133
|
+
if (!pending.prompt.trim() && pending.files.length === 0) {
|
|
1134
|
+
handleCreateDeckBlank();
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
993
1137
|
void handleCreateDeckWithPrompt(pending.prompt, pending.files, {
|
|
994
1138
|
designSystemId: null,
|
|
995
1139
|
referenceDeckId: null,
|
|
996
1140
|
});
|
|
997
|
-
}, [
|
|
1141
|
+
}, [
|
|
1142
|
+
forgetReference,
|
|
1143
|
+
handleCreateDeckBlank,
|
|
1144
|
+
handleCreateDeckWithPrompt,
|
|
1145
|
+
pendingDeck,
|
|
1146
|
+
]);
|
|
998
1147
|
|
|
999
1148
|
const handleConfirmDelete = () => {
|
|
1000
1149
|
if (deckToDelete) {
|
|
@@ -1317,9 +1466,11 @@ export default function Index() {
|
|
|
1317
1466
|
onOpenChange={setNewDeckPromptOpen}
|
|
1318
1467
|
title={t("home.newDeckPromptTitle")}
|
|
1319
1468
|
placeholder={t("home.newDeckPlaceholder")}
|
|
1320
|
-
onSkip={
|
|
1469
|
+
onSkip={handlePromptSkip}
|
|
1321
1470
|
skipLabel={t("home.skipPrompt")}
|
|
1322
1471
|
onSubmit={handlePromptSubmit}
|
|
1472
|
+
onImport={handleDirectImport}
|
|
1473
|
+
importFromLabel={t("home.importFrom")}
|
|
1323
1474
|
importingLabel={t("editorToolbar.importing")}
|
|
1324
1475
|
onBeforeUpload={(prompt, files) => {
|
|
1325
1476
|
if (session) return true;
|
|
@@ -1337,11 +1488,14 @@ export default function Index() {
|
|
|
1337
1488
|
open={showNewDeckReferenceStep}
|
|
1338
1489
|
onOpenChange={(open) => {
|
|
1339
1490
|
if (!open) {
|
|
1340
|
-
const
|
|
1491
|
+
const pending = pendingDeck;
|
|
1341
1492
|
setShowNewDeckReferenceStep(false);
|
|
1342
1493
|
setPendingDeck(null);
|
|
1343
|
-
if (
|
|
1344
|
-
setNewDeckInitialPrompt({
|
|
1494
|
+
if (pending) {
|
|
1495
|
+
setNewDeckInitialPrompt({
|
|
1496
|
+
text: pending.prompt,
|
|
1497
|
+
key: Date.now(),
|
|
1498
|
+
});
|
|
1345
1499
|
setShowNewDeckPrompt(true);
|
|
1346
1500
|
}
|
|
1347
1501
|
}
|
|
@@ -1349,8 +1503,7 @@ export default function Index() {
|
|
|
1349
1503
|
designSystems={designSystems}
|
|
1350
1504
|
decks={decks}
|
|
1351
1505
|
defaultDesignSystemId={initialDesignSystemId}
|
|
1352
|
-
defaultReferenceDeckId={
|
|
1353
|
-
recentReferences={recentReferences}
|
|
1506
|
+
defaultReferenceDeckId={initialReferenceDeckId}
|
|
1354
1507
|
onSelect={(selection) => void handleReferenceSelect(selection)}
|
|
1355
1508
|
onImport={handleReferenceImport}
|
|
1356
1509
|
onImportSource={handleReferenceSourceImport}
|
|
@@ -1362,8 +1515,6 @@ export default function Index() {
|
|
|
1362
1515
|
chooseDeckLabel={t("home.referenceDeckPlaceholder")}
|
|
1363
1516
|
importingLabel={t("editorToolbar.importing")}
|
|
1364
1517
|
skipLabel={t("home.referenceDeckNone")}
|
|
1365
|
-
starredLabel={t("home.referenceDeckStarredGroup")}
|
|
1366
|
-
otherDecksLabel={t("home.referenceDeckOtherGroup")}
|
|
1367
1518
|
searchDecksLabel={t("root.searchDecks")}
|
|
1368
1519
|
promptSummary={pendingDeck?.prompt}
|
|
1369
1520
|
/>
|
|
@@ -119,7 +119,7 @@ export async function prepareSlidesChatAttachments(args: {
|
|
|
119
119
|
"",
|
|
120
120
|
"File handling rules:",
|
|
121
121
|
"- If the request refers to the current or visible deck, call `view-screen` first to confirm the active deckId, then pass that deckId to import or slide-edit actions.",
|
|
122
|
-
'- PPTX files: when the user wants the visible deck improved, call `import-pptx --filePath "<path>" --deckId <deckId>` first, then
|
|
122
|
+
'- PPTX files: when the user wants the visible deck improved, call `import-pptx --filePath "<path>" --deckId <deckId>` first, then use one patch-deck call with requireAllSourceSlides=true and one content patch per imported slide for a deck-wide restyle. Use update-slide only for a targeted one-slide edit. Do not rebuild the source deck with add-slide.',
|
|
123
123
|
'- PDF and DOCX files: call `import-file --filePath "<path>" --format auto --deckId <deckId>` and use the returned extracted text as source material before creating editable slides. For a visual PDF that the user wants preserved, beautified, or restyled from its original layout, pass `--importIntoDeck true` so each page is imported source-faithfully first; keep the full-page image and style around it, with source text persisted in slide notes for inspection.',
|
|
124
124
|
'- Figma `.fig` files: call `import-file --filePath "<path>" --format fig` to start Builder design-system indexing. Do not create a local design system directly from the upload.',
|
|
125
125
|
"- For deck-generation requests, start mutating promptly: create or update the first slide as soon as source material is extracted, then continue slide-by-slide with add-slide/update-slide.",
|
|
@@ -52,9 +52,11 @@ export default createAgentChatPlugin({
|
|
|
52
52
|
prepareRequest: prepareSlidesChatAttachments,
|
|
53
53
|
systemPrompt: `You are an AI deck assistant. You create, edit, import, export, style, share, and navigate decks through actions and shared application state. For a newly created presentation, use create-deck with slides: [] only when you are creating the deck yourself, then add-slide sequentially with full rendered HTML. The legacy generate-slides-ai action returns Markdown drafts and is not part of the persisted presentation workflow.
|
|
54
54
|
|
|
55
|
-
When the user asks to improve, beautify, restyle, or make an uploaded/existing deck on-brand, treat it as an in-place source-preserving edit unless the user explicitly asks to rewrite the story or change slide count. First call view-screen when the active deck is unclear, then get-deck. If get-deck.sourceImport exists, preserve its slide count, order, IDs, factual copy, notes, images, charts, tables, diagrams, freeform objects, and source aspect ratio.
|
|
55
|
+
When the user asks to improve, beautify, restyle, or make an uploaded/existing deck on-brand, treat it as an in-place source-preserving edit unless the user explicitly asks to rewrite the story or change slide count. First call view-screen when the active deck is unclear, then get-deck. If get-deck.sourceImport exists, preserve its slide count, order, IDs, factual copy, notes, images, charts, tables, diagrams, freeform objects, and source aspect ratio. For a deck-wide restyle, use one patch-deck call with requireAllSourceSlides=true and one patch-slide operation with fields.content for every source slide ID; the action rejects partial coverage. Do not split a full-deck restyle into arbitrary batches or use one-by-one update-slide calls - reserve update-slide for targeted one-slide edits. After the patch succeeds, verify with get-deck using compact=true so the verification does not retransmit every slide's HTML. Do not claim a partial or initial pass. Do not use add-slide, delete, reorder, or replace source imagery with generic cards for this workflow. If sourceImport.fidelity is partial or imagesSkipped is nonzero, stop and report the exact fidelity warning instead of claiming a reliable improvement.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
When the active Slides editor is already showing the deck you just changed, do not include an "Open the updated deck" or similar link in the final response. The deck is already open and the action updated it in place; say that plainly instead. Only provide an open-deck link when the user is elsewhere, the active deck is different, or the user explicitly asks for a link.
|
|
58
|
+
|
|
59
|
+
For source-faithful PDF slides, keep the original full-page image and style around it with restrained design-system chrome such as a frame, edge treatment, caption, or safe overlay that does not obscure source content; never OCR-reconstruct the page from extracted text. For PPTX slides, preserve the imported positioned HTML and every uploaded source image. The patch-deck and update-slide actions enforce these preservation rules by default; pass preserveSource=false only when the user explicitly requests a rewrite of that slide.
|
|
58
60
|
|
|
59
61
|
If the deck has designSystemId, call get-design-system before writing and follow its exact agentContext tokens, assets, and custom instructions. If the user asks for on-brand styling and no design system is linked, call get-workspace-defaults, link its usable design system with patch-deck, then call get-design-system. Do not improvise a generic Builder-like palette when configured Builder.io design-system context is available.
|
|
60
62
|
|
|
@@ -68,7 +70,7 @@ Provider-specific Slides actions are shortcuts, not limits. If a first-class act
|
|
|
68
70
|
|
|
69
71
|
Slides' Google Drive provider API uses the user's connected Google Docs OAuth account. Picker imports keep the per-file drive.file path, while pasted Google Slides links use Drive export access and may ask the user to reconnect Google. For large Drive file lists or metadata sweeps, pass stageAs and pagination options to provider-api-request, then use query-staged-dataset to count, filter, group, or project the staged rows.
|
|
70
72
|
|
|
71
|
-
When a Google Drive or Google Slides request needs authentication, tell the user to use the Connect Google button
|
|
73
|
+
When a Google Drive or Google Slides request needs authentication, tell the user to use the Connect Google button in the Google Slides import step. Do not expose internal routes, API endpoints, OAuth setup instructions, client IDs, or keys in the response, and do not ask the user to configure an API manually.`,
|
|
72
74
|
mentionProviders: async () => {
|
|
73
75
|
const { getDb } = await import("../db/index.js");
|
|
74
76
|
const { decks, deckShares } = await import("../db/schema.js");
|