@agent-native/core 0.149.0 → 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 (134) 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 +139 -10
  35. package/corpus/templates/slides/actions/save-deck.ts +24 -1
  36. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +46 -0
  37. package/corpus/templates/slides/app/components/editor/BlockBubbleMenu.tsx +194 -3
  38. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +10 -1
  39. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +134 -618
  40. package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +74 -63
  41. package/corpus/templates/slides/app/components/editor/GeneratingSlidePreview.tsx +1 -1
  42. package/corpus/templates/slides/app/components/editor/GoogleDriveConnectionCta.tsx +13 -1
  43. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +56 -174
  44. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +239 -116
  45. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +29 -7
  46. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +45 -0
  47. package/corpus/templates/slides/app/components/layout/AgentWorkIndicator.tsx +1 -1
  48. package/corpus/templates/slides/app/components/layout/Layout.tsx +34 -35
  49. package/corpus/templates/slides/app/components/layout/layout-route-policy.ts +2 -5
  50. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +45 -40
  51. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +63 -16
  52. package/corpus/templates/slides/app/hooks/use-generating-slide-preview.ts +25 -3
  53. package/corpus/templates/slides/app/i18n/en-US.ts +1 -0
  54. package/corpus/templates/slides/app/lib/create-deck-generation.ts +5 -4
  55. package/corpus/templates/slides/app/lib/export-pdf-client.ts +84 -25
  56. package/corpus/templates/slides/app/lib/import-uploaded-deck.ts +11 -0
  57. package/corpus/templates/slides/app/lib/recent-references.ts +23 -1
  58. package/corpus/templates/slides/app/lib/slide-animation-elements.ts +41 -3
  59. package/corpus/templates/slides/app/pages/DeckEditor.tsx +16 -27
  60. package/corpus/templates/slides/app/pages/Index.tsx +256 -98
  61. package/corpus/templates/slides/changelog/2026-08-08-google-slides-export-now-tells-you-why-a-deck-fell-back-to-a.md +6 -0
  62. package/corpus/templates/slides/changelog/2026-08-09-delete-key-now-removes-a-selected-image.md +6 -0
  63. package/corpus/templates/slides/changelog/2026-08-09-editing-a-slide-in-an-imported-deck-no-longer-fails-to-save.md +6 -0
  64. package/corpus/templates/slides/changelog/2026-08-09-fixed-pdf-and-custom-size-powerpoint-imports-rendering-with-.md +6 -0
  65. package/corpus/templates/slides/changelog/2026-08-09-images-from-sites-without-cors-no-longer-export-blank.md +6 -0
  66. package/corpus/templates/slides/changelog/2026-08-09-imported-pdf-headings-no-longer-lose-the-space-between-color.md +6 -0
  67. package/corpus/templates/slides/changelog/2026-08-09-imported-slide-text-no-longer-renders-too-large-for-its-box.md +6 -0
  68. package/corpus/templates/slides/changelog/2026-08-09-large-decks-no-longer-render-slowly-or-glitch-in-the-editor.md +6 -0
  69. package/corpus/templates/slides/changelog/2026-08-09-revise-highlighted-slide-text-with-ai.md +6 -0
  70. package/corpus/templates/slides/changelog/2026-08-09-the-deck-editor-no-longer-stays-dimmed-after-the-window-narro.md +6 -0
  71. package/corpus/templates/slides/changelog/2026-08-10-ai-editing-avatar-badge.md +6 -0
  72. package/corpus/templates/slides/changelog/2026-08-10-generated-decks-keep-a-clear-human-readable-title.md +6 -0
  73. package/corpus/templates/slides/changelog/2026-08-10-new-deck-creation-now-shows-progress-opens-the-editor-direct.md +6 -0
  74. package/corpus/templates/slides/changelog/2026-08-10-presentation-reveals-now-follow-valid-targets.md +6 -0
  75. package/corpus/templates/slides/changelog/2026-08-10-slides-editing-controls-are-consolidated-into-one-focused-me.md +6 -0
  76. package/corpus/templates/slides/changelog/2026-08-10-slides-now-verify-and-restyle-every-imported-slide-in-one-pa.md +6 -0
  77. package/corpus/templates/slides/changelog/2026-08-10-slides-only-suggests-a-google-connection-for-relevant-reques.md +6 -0
  78. package/corpus/templates/slides/changelog/2026-08-10-the-google-slides-import-option-is-now-labeled-slides.md +6 -0
  79. package/corpus/templates/slides/server/handlers/import/html-converter.ts +115 -22
  80. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +11 -2
  81. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  82. package/corpus/templates/slides/server/lib/fetch-remote-image.ts +192 -0
  83. package/corpus/templates/slides/server/lib/image-proxy-url.ts +192 -0
  84. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -3
  85. package/corpus/templates/slides/server/routes/api/image-proxy.get.ts +65 -0
  86. package/corpus/templates/slides/shared/api.ts +2 -1
  87. package/corpus/templates/slides/shared/deck-title.ts +135 -0
  88. package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +4 -1
  89. package/dist/agent/engine/ai-sdk-engine.js +1 -1
  90. package/dist/agent/production-agent.d.ts +21 -1
  91. package/dist/agent/production-agent.js +129 -10
  92. package/dist/agent/tool-approval-store.d.ts +18 -0
  93. package/dist/agent/tool-approval-store.js +122 -0
  94. package/dist/agent/types.d.ts +1 -1
  95. package/dist/cli/code-agent-executor.js +11 -2
  96. package/dist/client/AgentPanel.js +31 -6
  97. package/dist/client/AssistantChat.d.ts +3 -2
  98. package/dist/client/AssistantChat.js +7 -20
  99. package/dist/client/agent-chat/index.d.ts +1 -1
  100. package/dist/client/agent-chat-adapter.js +11 -0
  101. package/dist/client/agent-chat.d.ts +1 -1
  102. package/dist/client/chat/markdown-renderer.js +14 -2
  103. package/dist/client/chat/message-components.d.ts +6 -2
  104. package/dist/client/chat/message-components.js +25 -47
  105. package/dist/client/chat/tool-call-display.d.ts +3 -5
  106. package/dist/client/chat/tool-call-display.js +7 -212
  107. package/dist/client/chat-first/apps-rail.js +3 -3
  108. package/dist/client/chat-first/browser-pane.js +4 -1
  109. package/dist/client/chat-first/chat-history.d.ts +3 -1
  110. package/dist/client/chat-first/chat-history.js +2 -2
  111. package/dist/client/chat-first/surface-tabs.d.ts +1 -1
  112. package/dist/client/chat-first/surface-tabs.js +52 -41
  113. package/dist/client/chat-first/types.d.ts +3 -0
  114. package/dist/client/chat-first.d.ts +10 -0
  115. package/dist/client/chat-first.js +37 -2
  116. package/dist/client/components/ui/message-scroller.js +4 -1
  117. package/dist/client/i18n.js +3 -1
  118. package/dist/client/progress/RunsTray.js +14 -1
  119. package/dist/client/resources/McpConnectionSuggestion.js +9 -1
  120. package/dist/client/sse-event-processor.js +10 -0
  121. package/dist/client/use-chat-models.js +156 -80
  122. package/dist/integrations/webhook-handler.js +1 -1
  123. package/dist/provider-api/actions/custom-provider-registration.d.ts +1 -1
  124. package/dist/secrets/routes.d.ts +6 -6
  125. package/dist/server/auth.js +262 -31
  126. package/dist/server/onboarding-html.js +56 -9
  127. package/dist/shared/reasoning-effort.d.ts +8 -8
  128. package/dist/shared/reasoning-effort.js +10 -10
  129. package/dist/shared/streaming-text-smoothing.js +41 -11
  130. package/dist/styles/agent-native.css +19 -32
  131. package/dist/templates/chat/changelog/2026-08-10-full-page-chat-now-uses-the-available-width-up-to-1000px-for.md +6 -0
  132. package/docs/content/template-chat.mdx +1 -1
  133. package/package.json +2 -2
  134. 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 {
@@ -59,10 +60,12 @@ import { createDeckAgentMessage } from "@/lib/agent-visible-message";
59
60
  import { savePromptToComposerDraft } from "@/lib/composer-draft";
60
61
  import { sortDecksByRecency } from "@/lib/deck-sorting";
61
62
  import {
63
+ IMPORT_ACTION_TIMEOUT_MS,
62
64
  importUploadedDeckIntoDeck,
63
65
  type ImportedSourceDeck,
64
66
  } from "@/lib/import-uploaded-deck";
65
67
  import {
68
+ forgetRecentReference,
66
69
  readRecentReferences,
67
70
  rememberRecentReference,
68
71
  type RecentReference,
@@ -235,7 +238,7 @@ export default function Index() {
235
238
  loadError,
236
239
  reloadDecks,
237
240
  } = useDecks();
238
- const { designSystems, defaultSystem } = useDesignSystems();
241
+ const { designSystems } = useDesignSystems();
239
242
  const {
240
243
  referenceDeck: workspaceReferenceDeck,
241
244
  designSystem: workspaceDesignSystem,
@@ -293,27 +296,25 @@ export default function Index() {
293
296
  () => new Map(designSystems.map((ds) => [ds.id, ds.title])),
294
297
  [designSystems],
295
298
  );
296
- // A workspace default the caller cannot open is reported by the action as
297
- // `unavailable` rather than absent; preselecting it would send every new
298
- // prompt at a deck that 404s, so fall back to no reference instead.
299
- const workspaceReferenceDeckId =
300
- workspaceReferenceDeck && !workspaceReferenceDeck.unavailable
301
- ? workspaceReferenceDeck.id
302
- : null;
303
299
  const workspaceDesignSystemId =
304
300
  workspaceDesignSystem && !workspaceDesignSystem.unavailable
305
301
  ? workspaceDesignSystem.id
306
302
  : null;
307
- // Same precedence the server uses in `create-deck`: an explicit personal
308
- // default, then the workspace default, then whatever exists. `defaultSystem`
309
- // already collapses the first and last of those, so match it deliberately.
310
- const personalDefaultDesignSystemId =
311
- 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;
312
315
  const initialDesignSystemId =
313
- personalDefaultDesignSystemId ??
314
- workspaceDesignSystemId ??
315
- defaultSystem?.id ??
316
- null;
316
+ lastUsedDesignSystemId ?? workspaceDesignSystemId;
317
+ const initialReferenceDeckId = lastUsedReferenceDeckId;
317
318
  const deckFilter = searchParams.get("createdBy") === "me" ? "mine" : "all";
318
319
  const visibleDecks = useMemo(
319
320
  () =>
@@ -331,24 +332,16 @@ export default function Index() {
331
332
  },
332
333
  [],
333
334
  );
335
+ const forgetReference = useCallback((kind: RecentReference["kind"]) => {
336
+ const result = forgetRecentReference(kind);
337
+ if (result.readable) setRecentReferences(result.items);
338
+ }, []);
334
339
 
335
340
  useEffect(() => {
336
341
  const result = readRecentReferences();
337
342
  if (result.readable) setRecentReferences(result.items);
338
343
  }, []);
339
344
 
340
- useEffect(() => {
341
- if (initialDesignSystemId) {
342
- rememberReference({
343
- id: initialDesignSystemId,
344
- kind: "design-system",
345
- });
346
- }
347
- if (workspaceReferenceDeckId) {
348
- rememberReference({ id: workspaceReferenceDeckId, kind: "deck" });
349
- }
350
- }, [initialDesignSystemId, rememberReference, workspaceReferenceDeckId]);
351
-
352
345
  const initialPrompt = searchParams.get("initialPrompt")?.trim() ?? "";
353
346
  const onboardingPreview = searchParams.get("onboarding") === "preview";
354
347
  const openInitialPrompt = useCallback(() => {
@@ -429,10 +422,10 @@ export default function Index() {
429
422
  designSystemAutoRef.current = true;
430
423
  referenceDeckAutoRef.current = true;
431
424
  setSelectedDesignSystemId(initialDesignSystemId ?? null);
432
- setSelectedReferenceDeckId(workspaceReferenceDeckId ?? null);
425
+ setSelectedReferenceDeckId(initialReferenceDeckId ?? null);
433
426
  setShowNewDeckPrompt(true);
434
427
  },
435
- [initialDesignSystemId, workspaceReferenceDeckId],
428
+ [initialDesignSystemId, initialReferenceDeckId],
436
429
  );
437
430
 
438
431
  const setNewDeckPromptOpen = useCallback(
@@ -485,13 +478,13 @@ export default function Index() {
485
478
  }
486
479
  }, [initialDesignSystemId, designSystems.length, showNewDeckPrompt]);
487
480
 
488
- // Same as above for the reference-deck picker: `workspaceReferenceDeckId`
481
+ // Same as above for the reference-deck picker: the local last-used reference
489
482
  // can still be loading when the dialog opens, so re-apply it once it
490
483
  // resolves unless the user already picked a reference deck.
491
484
  useEffect(() => {
492
485
  if (!showNewDeckPrompt || !referenceDeckAutoRef.current) return;
493
- setSelectedReferenceDeckId(workspaceReferenceDeckId ?? null);
494
- }, [workspaceReferenceDeckId, showNewDeckPrompt]);
486
+ setSelectedReferenceDeckId(initialReferenceDeckId ?? null);
487
+ }, [initialReferenceDeckId, showNewDeckPrompt]);
495
488
 
496
489
  // Restore a prompt that was held back when the user wasn't signed in:
497
490
  // we wrote the text to sessionStorage before redirecting to sign-in,
@@ -515,9 +508,9 @@ export default function Index() {
515
508
  designSystemAutoRef.current = true;
516
509
  referenceDeckAutoRef.current = true;
517
510
  setSelectedDesignSystemId(initialDesignSystemId ?? null);
518
- setSelectedReferenceDeckId(workspaceReferenceDeckId ?? null);
511
+ setSelectedReferenceDeckId(initialReferenceDeckId ?? null);
519
512
  setShowNewDeckPrompt(true);
520
- }, [initialDesignSystemId, workspaceReferenceDeckId, session]);
513
+ }, [initialDesignSystemId, initialReferenceDeckId, session]);
521
514
 
522
515
  // Recovering from a failed deck-generation kickoff (see
523
516
  // recoverFromGenerationSetupFailure below) navigates back to this route
@@ -597,20 +590,44 @@ export default function Index() {
597
590
  const deckId = deck.id;
598
591
  setNewDeckPromptOpen(false);
599
592
 
600
- const persisted = await ensureDeckPersisted(deck.id);
601
- 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) => {
602
602
  if (!savePromptForRetry(prompt)) {
603
603
  setNewDeckInitialPrompt({ text: prompt, key: Date.now() });
604
604
  }
605
605
  setNewDeckRetryFiles(filesForGeneration);
606
606
  deleteDeck(deckId);
607
- toast.error(t("home.generationStartFailed"), {
608
- 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(
609
627
  persisted,
610
628
  t("home.generationStartFailedDescription"),
611
629
  ),
612
- });
613
- setShowNewDeckPrompt(true);
630
+ );
614
631
  return;
615
632
  }
616
633
 
@@ -621,26 +638,17 @@ export default function Index() {
621
638
  deckId,
622
639
  );
623
640
  } catch (error) {
624
- if (!savePromptForRetry(prompt)) {
625
- setNewDeckInitialPrompt({ text: prompt, key: Date.now() });
626
- }
627
- setNewDeckRetryFiles(filesForGeneration);
628
- deleteDeck(deckId);
629
- toast.error(t("home.generationStartFailed"), {
630
- description:
631
- error instanceof Error
632
- ? error.message
633
- : t("home.generationStartFailedDescription"),
634
- });
635
- setShowNewDeckPrompt(true);
641
+ recoverFromGenerationSetupFailure(
642
+ error instanceof Error
643
+ ? error.message
644
+ : t("home.generationStartFailedDescription"),
645
+ );
636
646
  return;
637
647
  }
638
648
 
639
649
  clearPendingPromptForRetry();
640
650
  setNewDeckInitialPrompt(null);
641
651
  setNewDeckRetryFiles([]);
642
- navigate(`/deck/${deck.id}`, { flushSync: true });
643
-
644
652
  const trimmedPrompt = prompt.trim();
645
653
  const hasImportedGoogleDocContext = trimmedPrompt.includes("<google-doc ");
646
654
  const googleDocUrls = hasImportedGoogleDocContext
@@ -661,11 +669,11 @@ export default function Index() {
661
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.",
662
670
  ].join("\n")
663
671
  : "";
664
- const referenceDeckContext =
665
- await loadReferenceDeckGenerationContext(referenceDeckId);
666
- const hydratedDesignSystemContext = await loadDesignSystemGenerationContext(
667
- selectedDesignSystem?.id,
668
- );
672
+ const [referenceDeckContext, hydratedDesignSystemContext] =
673
+ await Promise.all([
674
+ loadReferenceDeckGenerationContext(referenceDeckId),
675
+ loadDesignSystemGenerationContext(selectedDesignSystem?.id),
676
+ ]);
669
677
  const designSystemContext = selectedDesignSystem
670
678
  ? [
671
679
  "",
@@ -702,19 +710,20 @@ export default function Index() {
702
710
  "Source-preserving improvement mode:",
703
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.`,
704
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.",
705
- "- 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.",
706
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.',
707
716
  "- If `get-deck` reports partial source fidelity or skipped images, stop and report the exact warning instead of claiming a reliable restyle.",
708
717
  ].join("\n")
709
718
  : "";
710
719
  const sourceModeInstructions = importedSourceDeck
711
720
  ? [
712
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.",
713
- "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.",
714
723
  ].join("\n")
715
724
  : [
716
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.",
717
- "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: \"" +
718
727
  deckId +
719
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.',
720
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.",
@@ -753,10 +762,6 @@ export default function Index() {
753
762
  ).catch(() => {});
754
763
  deleteClientAppState("guided-questions").catch(() => {});
755
764
 
756
- navigate(`/deck/${deck.id}?generating=1`, {
757
- replace: true,
758
- flushSync: true,
759
- });
760
765
  agentSubmit(createDeckAgentMessage(trimmedPrompt), context, {
761
766
  newTab: true,
762
767
  reuseEmptyTab: true,
@@ -773,18 +778,150 @@ export default function Index() {
773
778
  [setNewDeckPromptOpen],
774
779
  );
775
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
+
776
905
  const handleReferenceSelect = useCallback(
777
906
  async (selection: NewDeckReferenceSelection) => {
778
907
  const pending = pendingDeck;
779
908
  if (!pending) return;
780
- if (selection.designSystemId) {
781
- rememberReference({
782
- id: selection.designSystemId,
783
- kind: "design-system",
784
- });
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
+ }
785
918
  }
786
- if (selection.referenceDeckId) {
787
- 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
+ }
788
925
  }
789
926
  setShowNewDeckReferenceStep(false);
790
927
  setPendingDeck(null);
@@ -794,7 +931,12 @@ export default function Index() {
794
931
  selection,
795
932
  );
796
933
  },
797
- [handleCreateDeckWithPrompt, pendingDeck, rememberReference],
934
+ [
935
+ forgetReference,
936
+ handleCreateDeckWithPrompt,
937
+ pendingDeck,
938
+ rememberReference,
939
+ ],
798
940
  );
799
941
 
800
942
  const handleReferenceImport = useCallback(
@@ -813,9 +955,11 @@ export default function Index() {
813
955
  let importedReference: ImportedReference | null = null;
814
956
  let generationFiles = uploaded;
815
957
  if (pptxReference) {
816
- const imported = (await callAction("import-pptx", {
817
- filePath: pptxReference.path,
818
- })) as {
958
+ const imported = (await callAction(
959
+ "import-pptx",
960
+ { filePath: pptxReference.path },
961
+ { timeoutMs: IMPORT_ACTION_TIMEOUT_MS },
962
+ )) as {
819
963
  id?: unknown;
820
964
  imported?: unknown;
821
965
  slideCount?: unknown;
@@ -854,12 +998,16 @@ export default function Index() {
854
998
  );
855
999
  }
856
1000
  try {
857
- const imported = (await callAction("import-file", {
858
- filePath: pdfReference.path,
859
- format: "pdf",
860
- deckId: referenceDeck.id,
861
- importIntoDeck: true,
862
- })) as {
1001
+ const imported = (await callAction(
1002
+ "import-file",
1003
+ {
1004
+ filePath: pdfReference.path,
1005
+ format: "pdf",
1006
+ deckId: referenceDeck.id,
1007
+ importIntoDeck: true,
1008
+ },
1009
+ { timeoutMs: IMPORT_ACTION_TIMEOUT_MS },
1010
+ )) as {
863
1011
  imported?: unknown;
864
1012
  deckId?: unknown;
865
1013
  pageCount?: unknown;
@@ -894,7 +1042,6 @@ export default function Index() {
894
1042
  );
895
1043
  if (importedReference) {
896
1044
  await reloadDecks();
897
- rememberReference({ id: importedReference.id, kind: "deck" });
898
1045
  setSelectedReferenceDeckId(importedReference.id);
899
1046
  }
900
1047
  return importedReference;
@@ -917,7 +1064,6 @@ export default function Index() {
917
1064
  ensureDeckPersisted,
918
1065
  pendingDeck,
919
1066
  reloadDecks,
920
- rememberReference,
921
1067
  t,
922
1068
  ],
923
1069
  );
@@ -957,7 +1103,6 @@ export default function Index() {
957
1103
  source: "google-slides",
958
1104
  };
959
1105
  await reloadDecks();
960
- rememberReference({ id: importedReference.id, kind: "deck" });
961
1106
  setSelectedReferenceDeckId(importedReference.id);
962
1107
  return importedReference;
963
1108
  } catch (error) {
@@ -972,7 +1117,7 @@ export default function Index() {
972
1117
  setReferenceImporting(false);
973
1118
  }
974
1119
  },
975
- [callAction, reloadDecks, rememberReference, t],
1120
+ [callAction, reloadDecks, t],
976
1121
  );
977
1122
 
978
1123
  const handleReferenceSkip = useCallback(() => {
@@ -983,11 +1128,22 @@ export default function Index() {
983
1128
  }
984
1129
  setShowNewDeckReferenceStep(false);
985
1130
  setPendingDeck(null);
1131
+ forgetReference("design-system");
1132
+ forgetReference("deck");
1133
+ if (!pending.prompt.trim() && pending.files.length === 0) {
1134
+ handleCreateDeckBlank();
1135
+ return;
1136
+ }
986
1137
  void handleCreateDeckWithPrompt(pending.prompt, pending.files, {
987
1138
  designSystemId: null,
988
1139
  referenceDeckId: null,
989
1140
  });
990
- }, [handleCreateDeckWithPrompt, pendingDeck]);
1141
+ }, [
1142
+ forgetReference,
1143
+ handleCreateDeckBlank,
1144
+ handleCreateDeckWithPrompt,
1145
+ pendingDeck,
1146
+ ]);
991
1147
 
992
1148
  const handleConfirmDelete = () => {
993
1149
  if (deckToDelete) {
@@ -1310,9 +1466,11 @@ export default function Index() {
1310
1466
  onOpenChange={setNewDeckPromptOpen}
1311
1467
  title={t("home.newDeckPromptTitle")}
1312
1468
  placeholder={t("home.newDeckPlaceholder")}
1313
- onSkip={handleCreateDeckBlank}
1469
+ onSkip={handlePromptSkip}
1314
1470
  skipLabel={t("home.skipPrompt")}
1315
1471
  onSubmit={handlePromptSubmit}
1472
+ onImport={handleDirectImport}
1473
+ importFromLabel={t("home.importFrom")}
1316
1474
  importingLabel={t("editorToolbar.importing")}
1317
1475
  onBeforeUpload={(prompt, files) => {
1318
1476
  if (session) return true;
@@ -1330,11 +1488,14 @@ export default function Index() {
1330
1488
  open={showNewDeckReferenceStep}
1331
1489
  onOpenChange={(open) => {
1332
1490
  if (!open) {
1333
- const prompt = pendingDeck?.prompt;
1491
+ const pending = pendingDeck;
1334
1492
  setShowNewDeckReferenceStep(false);
1335
1493
  setPendingDeck(null);
1336
- if (prompt) {
1337
- setNewDeckInitialPrompt({ text: prompt, key: Date.now() });
1494
+ if (pending) {
1495
+ setNewDeckInitialPrompt({
1496
+ text: pending.prompt,
1497
+ key: Date.now(),
1498
+ });
1338
1499
  setShowNewDeckPrompt(true);
1339
1500
  }
1340
1501
  }
@@ -1342,8 +1503,7 @@ export default function Index() {
1342
1503
  designSystems={designSystems}
1343
1504
  decks={decks}
1344
1505
  defaultDesignSystemId={initialDesignSystemId}
1345
- defaultReferenceDeckId={workspaceReferenceDeckId}
1346
- recentReferences={recentReferences}
1506
+ defaultReferenceDeckId={initialReferenceDeckId}
1347
1507
  onSelect={(selection) => void handleReferenceSelect(selection)}
1348
1508
  onImport={handleReferenceImport}
1349
1509
  onImportSource={handleReferenceSourceImport}
@@ -1355,8 +1515,6 @@ export default function Index() {
1355
1515
  chooseDeckLabel={t("home.referenceDeckPlaceholder")}
1356
1516
  importingLabel={t("editorToolbar.importing")}
1357
1517
  skipLabel={t("home.referenceDeckNone")}
1358
- starredLabel={t("home.referenceDeckStarredGroup")}
1359
- otherDecksLabel={t("home.referenceDeckOtherGroup")}
1360
1518
  searchDecksLabel={t("root.searchDecks")}
1361
1519
  promptSummary={pendingDeck?.prompt}
1362
1520
  />
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-08
4
+ ---
5
+
6
+ Google Slides export now tells you why a deck fell back to a .pptx download instead of reporting success.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Fixed the Delete key doing nothing after selecting an image that sits inside a slide's layout, such as a picture in a card grid. Only images that had been moved freely on the canvas could be deleted before.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Fixed direct edits to a PDF/PPTX-imported deck's slide text sometimes failing to save with a generic "Internal server error" — the source-preservation guard meant to stop an agent from silently dropping the original artwork or copy was also blocking ordinary human edits, which have no way to opt out of it.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Fixed PDF and custom-size PowerPoint imports rendering with distorted, mispositioned images and text on non-16:9 pages.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Fixed images vanishing from exported PDFs and PowerPoint files when they came from a site that blocks direct browser access, such as a blog or a stock photo host. Those images are now fetched through the app and appear in the export.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Fixed imported PDF text losing the space between words when a line changes color or weight mid-sentence, which ran headings like "7 Air purifying house plants" together into "7 Airpurifying".
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Fixed imported PDF/PowerPoint slide text rendering larger than its original box (often overlapping neighboring text) whenever the source page's physical size didn't match the deck canvas's assumed size — font sizes now scale by the same factor as element positions instead of a fixed point-to-pixel conversion.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Fixed decks with many slides loading slowly and rendering incorrectly in the editor. Every slide thumbnail measured its full layout on mount, so a long deck forced hundreds of page reflows at once; off-screen thumbnails now wait until they scroll into view, and the browser skips painting them entirely until then. The hover buttons on each thumbnail also no longer blur what is behind them, which was making the rail flicker and leaving dark patches over the editor.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Highlight text on a slide and the formatting bar now offers "Revise with AI". Describe the change you want — shorter, punchier, on-brand — and the agent rewrites just that text without disturbing the rest of the slide.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-09
4
+ ---
5
+
6
+ Fixed the deck editor staying covered by the mobile slide-rail dimming overlay after the window was narrowed, which washed the whole editor dark with no way to dismiss it.
@@ -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.