@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.
Files changed (115) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +2 -0
  3. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +150 -46
  4. package/corpus/templates/analytics/app/global.css +1 -1
  5. package/corpus/templates/analytics/app/i18n-data.ts +37 -0
  6. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +1 -1
  7. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +32 -0
  8. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -0
  9. package/corpus/templates/analytics/changelog/2026-08-10-dashboard-tables-can-be-copied-and-sorted-by-multiple-column.md +6 -0
  10. package/corpus/templates/analytics/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  11. package/corpus/templates/assets/app/global.css +1 -1
  12. package/corpus/templates/assets/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  13. package/corpus/templates/brain/app/global.css +1 -1
  14. package/corpus/templates/brain/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  15. package/corpus/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  16. package/corpus/templates/clips/changelog/2026-08-10-clips-desktop-sign-in-now-starts-with-magic-links-and-google.md +6 -0
  17. package/corpus/templates/clips/chrome-extension/src/overlay.css +1 -0
  18. package/corpus/templates/clips/desktop/src/app.tsx +274 -137
  19. package/corpus/templates/clips/desktop/src/styles.css +35 -0
  20. package/corpus/templates/content/app/global.css +1 -0
  21. package/corpus/templates/crm/app/global.css +1 -1
  22. package/corpus/templates/crm/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  23. package/corpus/templates/dispatch/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  24. package/corpus/templates/factory/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  25. package/corpus/templates/forms/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  26. package/corpus/templates/plan/app/global.css +1 -0
  27. package/corpus/templates/plan/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  28. package/corpus/templates/plan/server/ui-plan-html.ts +4 -1
  29. package/corpus/templates/slides/AGENTS.md +11 -10
  30. package/corpus/templates/slides/actions/add-deck.ts +2 -0
  31. package/corpus/templates/slides/actions/add-slide.ts +15 -1
  32. package/corpus/templates/slides/actions/create-deck.ts +18 -6
  33. package/corpus/templates/slides/actions/get-deck.ts +29 -1
  34. package/corpus/templates/slides/actions/patch-deck.ts +122 -9
  35. package/corpus/templates/slides/actions/save-deck.ts +24 -1
  36. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +134 -618
  37. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +69 -60
  38. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -1
  39. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +13 -1
  40. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +56 -174
  41. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +239 -116
  42. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +3 -3
  43. package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +1 -1
  44. package/corpus/templates/slides/app/components/layout/Layout.tsx +34 -35
  45. package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +2 -5
  46. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +45 -40
  47. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +63 -16
  48. package/corpus/templates/slides/app/hooks/use-generating-slide-preview.ts +25 -3
  49. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  50. package/corpus/templates/slides/app/lib/create-deck-generation.ts +5 -4
  51. package/corpus/templates/slides/app/lib/recent-references.ts +23 -1
  52. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +41 -3
  53. package/corpus/templates/slides/app/pages/DeckEditor.tsx +4 -27
  54. package/corpus/templates/slides/app/pages/Index.tsx +240 -89
  55. package/corpus/templates/slides/changelog/2026-08-10-ai-editing-avatar-badge.md +6 -0
  56. package/corpus/templates/slides/changelog/2026-08-10-generated-decks-keep-a-clear-human-readable-title.md +6 -0
  57. package/corpus/templates/slides/changelog/2026-08-10-new-deck-creation-now-shows-progress-opens-the-editor-direct.md +6 -0
  58. package/corpus/templates/slides/changelog/2026-08-10-presentation-reveals-now-follow-valid-targets.md +6 -0
  59. package/corpus/templates/slides/changelog/2026-08-10-slides-editing-controls-are-consolidated-into-one-focused-me.md +6 -0
  60. package/corpus/templates/slides/changelog/2026-08-10-slides-now-verify-and-restyle-every-imported-slide-in-one-pa.md +6 -0
  61. package/corpus/templates/slides/changelog/2026-08-10-slides-only-suggests-a-google-connection-for-relevant-reques.md +6 -0
  62. package/corpus/templates/slides/changelog/2026-08-10-the-google-slides-import-option-is-now-labeled-slides.md +6 -0
  63. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  64. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -3
  65. package/corpus/templates/slides/shared/api.ts +2 -1
  66. package/corpus/templates/slides/shared/deck-title.ts +135 -0
  67. package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +4 -1
  68. package/dist/agent/engine/ai-sdk-engine.js +1 -1
  69. package/dist/agent/production-agent.d.ts +21 -1
  70. package/dist/agent/production-agent.js +129 -10
  71. package/dist/agent/tool-approval-store.d.ts +18 -0
  72. package/dist/agent/tool-approval-store.js +122 -0
  73. package/dist/agent/types.d.ts +1 -1
  74. package/dist/cli/code-agent-executor.js +11 -2
  75. package/dist/client/AgentPanel.js +31 -6
  76. package/dist/client/AssistantChat.d.ts +3 -2
  77. package/dist/client/AssistantChat.js +7 -20
  78. package/dist/client/agent-chat/index.d.ts +1 -1
  79. package/dist/client/agent-chat-adapter.js +11 -0
  80. package/dist/client/agent-chat.d.ts +1 -1
  81. package/dist/client/chat/markdown-renderer.js +14 -2
  82. package/dist/client/chat/message-components.d.ts +6 -2
  83. package/dist/client/chat/message-components.js +25 -47
  84. package/dist/client/chat/tool-call-display.d.ts +3 -5
  85. package/dist/client/chat/tool-call-display.js +7 -212
  86. package/dist/client/chat-first/apps-rail.js +3 -3
  87. package/dist/client/chat-first/browser-pane.js +4 -1
  88. package/dist/client/chat-first/chat-history.d.ts +3 -1
  89. package/dist/client/chat-first/chat-history.js +2 -2
  90. package/dist/client/chat-first/surface-tabs.d.ts +1 -1
  91. package/dist/client/chat-first/surface-tabs.js +52 -41
  92. package/dist/client/chat-first/types.d.ts +3 -0
  93. package/dist/client/chat-first.d.ts +10 -0
  94. package/dist/client/chat-first.js +37 -2
  95. package/dist/client/components/ui/message-scroller.js +4 -1
  96. package/dist/client/i18n.js +3 -1
  97. package/dist/client/resources/McpConnectionSuggestion.js +9 -1
  98. package/dist/client/sse-event-processor.js +10 -0
  99. package/dist/client/use-chat-models.js +156 -80
  100. package/dist/collab/struct-routes.d.ts +1 -1
  101. package/dist/integrations/webhook-handler.js +1 -1
  102. package/dist/observability/routes.d.ts +1 -1
  103. package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
  104. package/dist/resources/handlers.d.ts +1 -1
  105. package/dist/secrets/routes.d.ts +9 -9
  106. package/dist/server/auth.js +262 -31
  107. package/dist/server/onboarding-html.js +56 -9
  108. package/dist/shared/reasoning-effort.d.ts +8 -8
  109. package/dist/shared/reasoning-effort.js +10 -10
  110. package/dist/shared/streaming-text-smoothing.js +41 -11
  111. package/dist/styles/agent-native.css +19 -32
  112. package/dist/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  113. package/docs/content/template-chat.mdx +1 -1
  114. package/package.json +2 -2
  115. 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, defaultSystem } = useDesignSystems();
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
- // Same precedence the server uses in `create-deck`: an explicit personal
309
- // default, then the workspace default, then whatever exists. `defaultSystem`
310
- // already collapses the first and last of those, so match it deliberately.
311
- const personalDefaultDesignSystemId =
312
- designSystems.find((ds) => ds.isDefault)?.id ?? null;
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
- personalDefaultDesignSystemId ??
315
- workspaceDesignSystemId ??
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(workspaceReferenceDeckId ?? null);
425
+ setSelectedReferenceDeckId(initialReferenceDeckId ?? null);
434
426
  setShowNewDeckPrompt(true);
435
427
  },
436
- [initialDesignSystemId, workspaceReferenceDeckId],
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: `workspaceReferenceDeckId`
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(workspaceReferenceDeckId ?? null);
495
- }, [workspaceReferenceDeckId, showNewDeckPrompt]);
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(workspaceReferenceDeckId ?? null);
511
+ setSelectedReferenceDeckId(initialReferenceDeckId ?? null);
520
512
  setShowNewDeckPrompt(true);
521
- }, [initialDesignSystemId, workspaceReferenceDeckId, session]);
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
- const persisted = await ensureDeckPersisted(deck.id);
602
- if (!persisted.persisted) {
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
- description: describeDeckPersistenceFailure(
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
- if (!savePromptForRetry(prompt)) {
626
- setNewDeckInitialPrompt({ text: prompt, key: Date.now() });
627
- }
628
- setNewDeckRetryFiles(filesForGeneration);
629
- deleteDeck(deckId);
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 loadReferenceDeckGenerationContext(referenceDeckId);
667
- const hydratedDesignSystemContext = await loadDesignSystemGenerationContext(
668
- selectedDesignSystem?.id,
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 use one `update-slide` call per existing slide ID for bounded visual improvements. 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.",
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
- rememberReference({
783
- id: selection.designSystemId,
784
- kind: "design-system",
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
- rememberReference({ id: selection.referenceDeckId, kind: "deck" });
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
- [handleCreateDeckWithPrompt, pendingDeck, rememberReference],
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, rememberReference, t],
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
- }, [handleCreateDeckWithPrompt, pendingDeck]);
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={handleCreateDeckBlank}
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 prompt = pendingDeck?.prompt;
1491
+ const pending = pendingDeck;
1341
1492
  setShowNewDeckReferenceStep(false);
1342
1493
  setPendingDeck(null);
1343
- if (prompt) {
1344
- setNewDeckInitialPrompt({ text: prompt, key: Date.now() });
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={workspaceReferenceDeckId}
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
  />
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ AI editing status now uses a cleaner combined avatar badge.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ Generated decks keep a clear, human-readable title
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ New deck creation now shows progress, opens the editor directly, and remembers your reference choices
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ Presentation reveals now follow validated ordered targets, queue rapid navigation, and settle cleanly after transitions.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ Slides editing controls are consolidated into one focused menu
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ Slides now verify and restyle every imported slide in one pass
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ Slides only suggests a Google connection for relevant requests, and the suggestion can be dismissed.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: changed
3
+ date: 2026-08-10
4
+ ---
5
+
6
+ The Google Slides import option is now labeled Slides.
@@ -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 edit those imported slide IDs in place with update-slide. Do not rebuild the source deck with add-slide.',
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. Use update-slide on existing slide IDs. Do not use add-slide, delete, reorder, or replace source imagery with generic cards for this workflow. Verify the same slide IDs and count with get-deck after editing. If sourceImport.fidelity is partial or imagesSkipped is nonzero, stop and report the exact fidelity warning instead of claiming a reliable improvement.
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
- 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 update-slide action enforces these preservation rules by default; pass preserveSource=false only when the user explicitly requests a rewrite of that slide.
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 shown in Slides. 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.`,
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");