@agent-native/core-corpus 0.201.0 → 0.201.1

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 (40) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +6 -0
  3. package/corpus/templates/analytics/actions/save-dashboard-view.ts +10 -1
  4. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +136 -4
  5. package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +4 -0
  6. package/corpus/templates/analytics/app/i18n/en-US.ts +7 -0
  7. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +86 -23
  8. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +4 -3
  9. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +55 -1
  10. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +44 -7
  11. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-filter-restore.ts +196 -0
  12. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +2 -0
  13. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +158 -47
  14. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -0
  15. package/corpus/templates/analytics/changelog/2026-10-05-custom-date-controls-stay-beside-the-range-selector-and-dash.md +6 -0
  16. package/corpus/templates/analytics/server/db/schema.ts +20 -8
  17. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +50 -3
  18. package/corpus/templates/analytics/server/lib/dashboard-view-remap.ts +52 -0
  19. package/corpus/templates/analytics/server/lib/dashboards-store.ts +77 -42
  20. package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +23 -8
  21. package/corpus/templates/analytics/server/plugins/db.ts +7 -0
  22. package/corpus/templates/clips/CHANGELOG.md +4 -0
  23. package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
  24. package/corpus/templates/clips/changelog/2026-10-05-the-agent-composer-is-easier-to-distinguish-from-the-shared-.md +6 -0
  25. package/corpus/templates/design/CHANGELOG.md +1 -0
  26. package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +1 -1
  27. package/corpus/templates/design/changelog/2026-10-05-design-frame-labels-apply-truncation-as-soon-as-resizing-end.md +6 -0
  28. package/corpus/templates/slides/CHANGELOG.md +7 -0
  29. package/corpus/templates/slides/agent-native.app-skill.json +2 -2
  30. package/corpus/templates/slides/app/components/home/SlidesHomeLibrary.tsx +11 -11
  31. package/corpus/templates/slides/app/context/DeckContext.tsx +16 -12
  32. package/corpus/templates/slides/app/hooks/use-new-deck-generation.ts +129 -6
  33. package/corpus/templates/slides/app/lib/create-deck-generation.ts +4 -1
  34. package/corpus/templates/slides/app/pages/DeckEditor.tsx +25 -8
  35. package/corpus/templates/slides/app/pages/Index.tsx +85 -42
  36. package/corpus/templates/slides/changelog/2026-10-05-file-uploads-explain-the-free-builder-io-option-and-how-to-c.md +6 -0
  37. package/corpus/templates/slides/changelog/2026-10-05-slides-restores-pending-generation-questions-when-reopened.md +6 -0
  38. package/corpus/templates/slides/changelog/2026-10-05-the-home-page-shows-both-library-tabs-and-starter-prompts-im.md +6 -0
  39. package/corpus/templates/slides/changelog/2026-10-05-undo-stops-at-generated-deck.md +6 -0
  40. package/package.json +1 -1
@@ -18,6 +18,8 @@ import { CHAT_STOP_DEBOUNCE_MS } from "./use-agent-generating";
18
18
 
19
19
  export const NEW_DECK_GENERATION_SUBMIT_TARGET_EVENT =
20
20
  "agentNative.chatSubmitTarget";
21
+ const NEW_DECK_GENERATION_RUN_CLEARED_EVENT =
22
+ "slides:new-deck-generation:cleared";
21
23
 
22
24
  type NewDeckGenerationLifecycle = {
23
25
  deckId: string;
@@ -135,6 +137,11 @@ type NewDeckGenerationRun = {
135
137
  tabId: string | null;
136
138
  };
137
139
 
140
+ type NewDeckGenerationRunReference = {
141
+ submitMessageId: string;
142
+ tabId: string;
143
+ };
144
+
138
145
  const runTabIds = new Map<string, string>();
139
146
 
140
147
  export function clearNewDeckGenerationRun(
@@ -144,6 +151,15 @@ export function clearNewDeckGenerationRun(
144
151
  const key = getRunTabStorageKey(deckId, submitMessageId);
145
152
  runTabIds.delete(key);
146
153
  window.sessionStorage.removeItem(key);
154
+ const activeRun = getActiveRun(deckId);
155
+ if (activeRun?.submitMessageId === submitMessageId) {
156
+ window.sessionStorage.removeItem(getActiveRunStorageKey(deckId));
157
+ }
158
+ window.dispatchEvent(
159
+ new CustomEvent(NEW_DECK_GENERATION_RUN_CLEARED_EVENT, {
160
+ detail: { deckId, submitMessageId },
161
+ }),
162
+ );
147
163
  }
148
164
 
149
165
  export function useNewDeckGenerationRun(
@@ -152,6 +168,7 @@ export function useNewDeckGenerationRun(
152
168
  submitMessageId: string | null,
153
169
  ): {
154
170
  generating: boolean;
171
+ submitMessageId: string | null;
155
172
  tabId: string | null;
156
173
  questionContinuationPending: boolean;
157
174
  expectQuestionContinuation: (submitMessageId: string) => void;
@@ -165,8 +182,15 @@ export function useNewDeckGenerationRun(
165
182
  );
166
183
  const startsNewRoute = isNewDeckRoute && !run.isNewDeckRoute;
167
184
  const newSubmit = submitMessageId && submitMessageId !== run.submitMessageId;
185
+ const storedActiveRun = getActiveRun(deckId);
186
+ const restoresActiveRun =
187
+ run.submitMessageId !== null &&
188
+ storedActiveRun?.submitMessageId === run.submitMessageId;
168
189
  const clearsFinishedSubmit =
169
- !isNewDeckRoute && !submitMessageId && run.submitMessageId !== null;
190
+ !isNewDeckRoute &&
191
+ !submitMessageId &&
192
+ run.submitMessageId !== null &&
193
+ !restoresActiveRun;
170
194
  let currentRun = run;
171
195
  if (
172
196
  run.deckId !== deckId ||
@@ -189,11 +213,46 @@ export function useNewDeckGenerationRun(
189
213
  (previous.deckId !== currentRun.deckId ||
190
214
  previous.submitMessageId !== currentRun.submitMessageId)
191
215
  ) {
192
- clearNewDeckGenerationRun(previous.deckId, previous.submitMessageId);
216
+ const hasRecoverableRun =
217
+ previous.deckId !== currentRun.deckId &&
218
+ hasStoredNewDeckGenerationRun(
219
+ previous.deckId,
220
+ previous.submitMessageId,
221
+ );
222
+ if (!hasRecoverableRun) {
223
+ clearNewDeckGenerationRun(previous.deckId, previous.submitMessageId);
224
+ }
193
225
  }
194
226
  previousRunRef.current = currentRun;
195
227
  }, [currentRun.deckId, currentRun.submitMessageId]);
196
228
 
229
+ useEffect(() => {
230
+ const handleRunCleared = (event: Event) => {
231
+ const detail = (event as CustomEvent).detail;
232
+ if (
233
+ detail?.deckId !== currentRunRef.current.deckId ||
234
+ detail?.submitMessageId !== currentRunRef.current.submitMessageId
235
+ ) {
236
+ return;
237
+ }
238
+ setRun({
239
+ deckId: detail.deckId,
240
+ submitMessageId: null,
241
+ isNewDeckRoute: false,
242
+ tabId: null,
243
+ });
244
+ };
245
+ window.addEventListener(
246
+ NEW_DECK_GENERATION_RUN_CLEARED_EVENT,
247
+ handleRunCleared,
248
+ );
249
+ return () =>
250
+ window.removeEventListener(
251
+ NEW_DECK_GENERATION_RUN_CLEARED_EVENT,
252
+ handleRunCleared,
253
+ );
254
+ }, []);
255
+
197
256
  const currentRunRef = useRef(currentRun);
198
257
  currentRunRef.current = currentRun;
199
258
  const routeCleanupTokenRef = useRef<symbol | null>(null);
@@ -206,7 +265,11 @@ export function useNewDeckGenerationRun(
206
265
  if (
207
266
  routeCleanupTokenRef.current === token &&
208
267
  runAtExit.submitMessageId &&
209
- !runAtExit.tabId
268
+ !runAtExit.tabId &&
269
+ !hasStoredNewDeckGenerationRun(
270
+ runAtExit.deckId,
271
+ runAtExit.submitMessageId,
272
+ )
210
273
  ) {
211
274
  clearNewDeckGenerationRun(
212
275
  runAtExit.deckId,
@@ -412,6 +475,7 @@ export function useNewDeckGenerationRun(
412
475
 
413
476
  return {
414
477
  generating: activeRun.runKey === runKey && activeRun.generating,
478
+ submitMessageId: currentRun.submitMessageId,
415
479
  tabId: currentRun.tabId,
416
480
  questionContinuationPending: currentContinuation.submitMessageId !== null,
417
481
  expectQuestionContinuation,
@@ -424,11 +488,19 @@ function createRun(
424
488
  isNewDeckRoute: boolean,
425
489
  submitMessageId: string | null,
426
490
  ): NewDeckGenerationRun {
491
+ const activeRun = submitMessageId ? null : getActiveRun(deckId);
492
+ const currentSubmitMessageId =
493
+ submitMessageId ?? activeRun?.submitMessageId ?? null;
427
494
  return {
428
495
  deckId,
429
- submitMessageId,
430
- isNewDeckRoute,
431
- tabId: submitMessageId ? getRunTabId(deckId, submitMessageId) : null,
496
+ submitMessageId: currentSubmitMessageId,
497
+ isNewDeckRoute: isNewDeckRoute || currentSubmitMessageId !== null,
498
+ tabId: currentSubmitMessageId
499
+ ? (getRunTabId(deckId, currentSubmitMessageId) ??
500
+ (activeRun?.submitMessageId === currentSubmitMessageId
501
+ ? activeRun.tabId
502
+ : null))
503
+ : null,
432
504
  };
433
505
  }
434
506
 
@@ -436,6 +508,50 @@ function getRunTabStorageKey(deckId: string, submitMessageId: string): string {
436
508
  return `slides:new-deck-generation:${deckId}:${submitMessageId}`;
437
509
  }
438
510
 
511
+ function getActiveRunStorageKey(deckId: string): string {
512
+ return `slides:new-deck-generation-active:${deckId}`;
513
+ }
514
+
515
+ function getActiveRun(deckId: string): NewDeckGenerationRunReference | null {
516
+ if (typeof window === "undefined") return null;
517
+ const serialized = window.sessionStorage.getItem(
518
+ getActiveRunStorageKey(deckId),
519
+ );
520
+ if (!serialized) return null;
521
+ let value: unknown;
522
+ try {
523
+ value = JSON.parse(serialized);
524
+ } catch (error) {
525
+ throw new Error(
526
+ `Cannot restore Slides generation ownership for deck ${deckId}: invalid session data.`,
527
+ { cause: error },
528
+ );
529
+ }
530
+ if (
531
+ typeof value !== "object" ||
532
+ value === null ||
533
+ !("submitMessageId" in value) ||
534
+ typeof value.submitMessageId !== "string" ||
535
+ !("tabId" in value) ||
536
+ typeof value.tabId !== "string"
537
+ ) {
538
+ throw new Error(
539
+ `Cannot restore Slides generation ownership for deck ${deckId}: invalid session data.`,
540
+ );
541
+ }
542
+ return {
543
+ submitMessageId: value.submitMessageId,
544
+ tabId: value.tabId,
545
+ };
546
+ }
547
+
548
+ function hasStoredNewDeckGenerationRun(
549
+ deckId: string,
550
+ submitMessageId: string,
551
+ ): boolean {
552
+ return getActiveRun(deckId)?.submitMessageId === submitMessageId;
553
+ }
554
+
439
555
  function getRunTabId(deckId: string, submitMessageId: string): string | null {
440
556
  if (typeof window === "undefined") return null;
441
557
  const key = getRunTabStorageKey(deckId, submitMessageId);
@@ -454,6 +570,13 @@ export function rememberNewDeckGenerationRunTab(
454
570
  const key = getRunTabStorageKey(deckId, submitMessageId);
455
571
  runTabIds.set(key, tabId);
456
572
  window.sessionStorage.setItem(key, tabId);
573
+ window.sessionStorage.setItem(
574
+ getActiveRunStorageKey(deckId),
575
+ JSON.stringify({
576
+ submitMessageId,
577
+ tabId,
578
+ } satisfies NewDeckGenerationRunReference),
579
+ );
457
580
  }
458
581
 
459
582
  function createLifecycle(
@@ -227,7 +227,10 @@ type Navigate = (
227
227
 
228
228
  type CreateDeck = (
229
229
  title?: string,
230
- options?: { noDefaultSlides?: boolean; designSystemId?: string | null },
230
+ options?: {
231
+ noDefaultSlides?: boolean;
232
+ designSystemId?: string | null;
233
+ },
231
234
  ) => Deck;
232
235
 
233
236
  type SubmitAgent = (
@@ -598,23 +598,28 @@ export default function DeckEditor() {
598
598
  const { generating } = useAgentGenerating();
599
599
  const { generating: addSlideAgentGenerating, submit: addSlideAgentSubmit } =
600
600
  useAgentGenerating();
601
- const generationSubmitId = searchParams.get("generationSubmitId");
602
- const isNewDeckGenerationRoute =
603
- searchParams.get("generating") === "1" || Boolean(generationSubmitId);
601
+ const routeGenerationSubmitId = searchParams.get("generationSubmitId");
602
+ const isNewDeckGenerationRouteFromUrl =
603
+ searchParams.get("generating") === "1" || Boolean(routeGenerationSubmitId);
604
604
  const retryEmptyGenerationInFlightRef = useRef(false);
605
605
  const emptyGenerationRecoveryRef = useRef<string | null>(null);
606
606
  const [retryEmptyGenerationPending, setRetryEmptyGenerationPending] =
607
607
  useState(false);
608
608
  const {
609
609
  generating: newDeckGenerationGenerating,
610
+ submitMessageId: restoredGenerationSubmitId,
610
611
  tabId: newDeckGenerationTabId,
611
612
  questionContinuationPending,
612
613
  submitQuestionContinuation: submitTrackedQuestionContinuation,
613
614
  } = useNewDeckGenerationRun(
614
615
  id ?? "",
615
- isNewDeckGenerationRoute,
616
- generationSubmitId,
616
+ isNewDeckGenerationRouteFromUrl,
617
+ routeGenerationSubmitId,
617
618
  );
619
+ const generationSubmitId =
620
+ routeGenerationSubmitId ?? restoredGenerationSubmitId;
621
+ const isNewDeckGenerationRoute =
622
+ isNewDeckGenerationRouteFromUrl || Boolean(restoredGenerationSubmitId);
618
623
  const submitQuestionContinuation = useCallback(
619
624
  ({ message, context }: { message: string; context: string }) => {
620
625
  if (!generationSubmitId) {
@@ -1831,9 +1836,21 @@ export default function DeckEditor() {
1831
1836
  `The user skipped the pre-generation questions for deck ${id}. Proceed with reasonable defaults. Every slide is rendered into a fixed native canvas (${fitDims.width}x${fitDims.height} CSS pixels; standard padding leaves ${Math.max(0, fitDims.width - 220)}x${Math.max(0, fitDims.height - 160)}px for main content); keep each slide within that fit budget and split dense source material across more slides instead of packing it tightly. Never use zoom, transform: scale(), clipping, or scroll overflow to hide content overflow, and keep body text at least 16px. Start a manage-progress run, add the first slide as soon as it is ready, then continue sequentially using add-slide with --deckId=${id}. Wait for each add-slide result before calling it again.`,
1832
1837
  onSubmitMessage: submitQuestionContinuation,
1833
1838
  onSkipMessage: submitQuestionContinuation,
1839
+ threadId: newDeckGenerationTabId ?? undefined,
1834
1840
  });
1835
1841
 
1836
1842
  const showQuestionFlow = Boolean(questionFlowQuestions?.length);
1843
+ const pendingQuestionKey =
1844
+ questionFlowQuestions?.map((question) => question.id).join(":") ?? "";
1845
+ useEffect(() => {
1846
+ if (!pendingQuestionKey || !newDeckGenerationTabId) return;
1847
+ window.dispatchEvent(
1848
+ new CustomEvent("agent-chat:open-thread", {
1849
+ detail: { threadId: newDeckGenerationTabId },
1850
+ }),
1851
+ );
1852
+ window.dispatchEvent(new Event("agent-panel:open"));
1853
+ }, [id, newDeckGenerationTabId, pendingQuestionKey]);
1837
1854
  const waitingOnNewDeckQuestions =
1838
1855
  showQuestionFlow || questionContinuationPending;
1839
1856
  const { isNewDeckCreation, phase: newDeckGenerationPhase } =
@@ -2152,10 +2169,9 @@ export default function DeckEditor() {
2152
2169
  ]);
2153
2170
 
2154
2171
  useEffect(() => {
2155
- const submitMessageId = searchParams.get("generationSubmitId");
2156
2172
  if (
2157
2173
  !id ||
2158
- !submitMessageId ||
2174
+ !generationSubmitId ||
2159
2175
  !shouldClearNewDeckGenerationRun({
2160
2176
  generating: newDeckGenerationGenerating || newDeckGenerationSignal,
2161
2177
  waitingOnQuestions: waitingOnNewDeckQuestions,
@@ -2167,7 +2183,7 @@ export default function DeckEditor() {
2167
2183
  let cancelled = false;
2168
2184
  void refetchPendingQuestion().then((stillWaiting) => {
2169
2185
  if (cancelled || stillWaiting) return;
2170
- clearNewDeckGenerationRun(id, submitMessageId);
2186
+ clearNewDeckGenerationRun(id, generationSubmitId);
2171
2187
  setSearchParams(
2172
2188
  (prev) => {
2173
2189
  const next = new URLSearchParams(prev);
@@ -2181,6 +2197,7 @@ export default function DeckEditor() {
2181
2197
  cancelled = true;
2182
2198
  };
2183
2199
  }, [
2200
+ generationSubmitId,
2184
2201
  id,
2185
2202
  newDeckGenerationGenerating,
2186
2203
  newDeckGenerationSignal,
@@ -495,6 +495,34 @@ async function loadReferenceDeckGenerationContext(
495
495
  ].join("\n");
496
496
  }
497
497
 
498
+ const HOME_LIBRARY_TAB_STORAGE_KEY = "slides-home-library-tab";
499
+
500
+ function readHomeLibraryTabPreference():
501
+ | { status: "available"; value: SlidesHomeLibraryTab | null }
502
+ | { status: "unavailable" } {
503
+ if (typeof window === "undefined") return { status: "unavailable" };
504
+ try {
505
+ const value = window.localStorage.getItem(HOME_LIBRARY_TAB_STORAGE_KEY);
506
+ return {
507
+ status: "available",
508
+ value: value === "templates" || value === "recent" ? value : null,
509
+ };
510
+ } catch {
511
+ return { status: "unavailable" };
512
+ }
513
+ }
514
+
515
+ function writeHomeLibraryTabPreference(
516
+ value: SlidesHomeLibraryTab,
517
+ ): { status: "available" } | { status: "unavailable" } {
518
+ try {
519
+ window.localStorage.setItem(HOME_LIBRARY_TAB_STORAGE_KEY, value);
520
+ return { status: "available" };
521
+ } catch {
522
+ return { status: "unavailable" };
523
+ }
524
+ }
525
+
498
526
  export default function Index({ active = true }: { active?: boolean }) {
499
527
  const t = useT();
500
528
  const openMobileSidebar = useOpenMobileSidebar();
@@ -516,6 +544,11 @@ export default function Index({ active = true }: { active?: boolean }) {
516
544
  reloadDecks,
517
545
  catchUpStaleDeckList,
518
546
  } = useDecks();
547
+ const viewState = deckListViewState({
548
+ loading,
549
+ loadError,
550
+ deckCount: decks.length,
551
+ });
519
552
  const systemsFlag = useDesignSystemWorkflowsState();
520
553
  const systemsEnabled = systemsFlag.enabled;
521
554
  const {
@@ -683,20 +716,42 @@ export default function Index({ active = true }: { active?: boolean }) {
683
716
  string | null
684
717
  >(null);
685
718
  const [deckSearch, setDeckSearch] = useState("");
686
- const [homeSection, setHomeSection] =
687
- useState<SlidesHomeLibraryTab>("templates");
688
- const homeLibraryTabWasSelectedRef = useRef(false);
719
+ const [storedHomeLibraryTab] = useState(readHomeLibraryTabPreference);
720
+ const homeLibraryTabPreferenceRef = useRef(
721
+ storedHomeLibraryTab.status === "available"
722
+ ? storedHomeLibraryTab.value
723
+ : null,
724
+ );
725
+ const homeLibraryTabStorageAvailableRef = useRef(
726
+ storedHomeLibraryTab.status === "available",
727
+ );
728
+ const [homeSection, setHomeSection] = useState<SlidesHomeLibraryTab>(
729
+ homeLibraryTabPreferenceRef.current ??
730
+ (viewState === "decks" ? "recent" : "templates"),
731
+ );
732
+ const persistHomeLibraryTab = useCallback((value: SlidesHomeLibraryTab) => {
733
+ if (!homeLibraryTabStorageAvailableRef.current) return;
734
+ homeLibraryTabStorageAvailableRef.current =
735
+ writeHomeLibraryTabPreference(value).status === "available";
736
+ }, []);
737
+ const selectHomeLibraryTab = useCallback(
738
+ (value: SlidesHomeLibraryTab) => {
739
+ homeLibraryTabPreferenceRef.current = value;
740
+ setHomeSection(value);
741
+ persistHomeLibraryTab(value);
742
+ },
743
+ [persistHomeLibraryTab],
744
+ );
689
745
  const deckFilterWasSelectedRef = useRef(false);
690
746
  const revealRecentSearch = useCallback(() => {
691
747
  if (decks.length === 0) return false;
692
- homeLibraryTabWasSelectedRef.current = true;
693
- setHomeSection("recent");
748
+ selectHomeLibraryTab("recent");
694
749
  return true;
695
- }, [decks.length]);
750
+ }, [decks.length, selectHomeLibraryTab]);
696
751
  useHomeSearchShortcut(isHome, revealRecentSearch);
697
752
  useEffect(() => {
698
- if (deckSearch.trim()) setHomeSection("recent");
699
- }, [deckSearch]);
753
+ if (deckSearch.trim()) selectHomeLibraryTab("recent");
754
+ }, [deckSearch, selectHomeLibraryTab]);
700
755
  const [storedDeckFilter, setStoredDeckFilter] = useState<DeckFilter>("mine");
701
756
  const referenceDeckAutoRef = useRef(true);
702
757
  const [showSignInDialog, setShowSignInDialog] = useState(false);
@@ -1106,6 +1161,7 @@ export default function Index({ active = true }: { active?: boolean }) {
1106
1161
  noDefaultSlides: true,
1107
1162
  designSystemId: selectedDesignSystem?.id ?? null,
1108
1163
  deferPersistence: true,
1164
+ undoableCreation: false,
1109
1165
  });
1110
1166
  });
1111
1167
  if (!deck) {
@@ -2342,24 +2398,23 @@ export default function Index({ active = true }: { active?: boolean }) {
2342
2398
 
2343
2399
  const homeTitle = t("home.decksTitle");
2344
2400
  const deckImport = usePromptImport({ onImport: handleDirectImport });
2345
- const viewState = deckListViewState({
2346
- loading,
2347
- loadError,
2348
- deckCount: decks.length,
2349
- });
2350
- const hasDecks = viewState === "decks";
2351
2401
  useEffect(() => {
2352
2402
  if (viewState === "loading") return;
2353
- if (!hasDecks) {
2403
+ if (viewState === "empty") {
2354
2404
  setHomeSection("templates");
2355
- homeLibraryTabWasSelectedRef.current = false;
2405
+ homeLibraryTabPreferenceRef.current = "templates";
2406
+ persistHomeLibraryTab("templates");
2356
2407
  return;
2357
2408
  }
2358
- if (homeLibraryTabWasSelectedRef.current) return;
2359
-
2360
- setHomeSection("recent");
2361
- homeLibraryTabWasSelectedRef.current = true;
2362
- }, [hasDecks, viewState]);
2409
+ if (viewState === "error") {
2410
+ setHomeSection("templates");
2411
+ return;
2412
+ }
2413
+ const preferredTab = homeLibraryTabPreferenceRef.current ?? "recent";
2414
+ homeLibraryTabPreferenceRef.current = preferredTab;
2415
+ setHomeSection(preferredTab);
2416
+ persistHomeLibraryTab(preferredTab);
2417
+ }, [persistHomeLibraryTab, viewState]);
2363
2418
  const homeHeaderActions = useMemo(
2364
2419
  () => (
2365
2420
  <HomeHeaderActions>
@@ -2542,7 +2597,7 @@ export default function Index({ active = true }: { active?: boolean }) {
2542
2597
  </div>
2543
2598
  }
2544
2599
  quickActions={
2545
- isHome && quickActionsEnabled ? (
2600
+ isHome && showNewDeckPrompt ? (
2546
2601
  <AgentSuggestionBar
2547
2602
  suggestions={homeSuggestions.map((suggestion, index) => ({
2548
2603
  ...suggestion,
@@ -2585,16 +2640,8 @@ export default function Index({ active = true }: { active?: boolean }) {
2585
2640
  ) : null}
2586
2641
  <ClientOnly>
2587
2642
  <SlidesHomeLibrary
2588
- recentVisible={hasDecks}
2589
- value={
2590
- hasDecks && !homeLibraryTabWasSelectedRef.current
2591
- ? "recent"
2592
- : homeSection
2593
- }
2594
- onValueChange={(value) => {
2595
- homeLibraryTabWasSelectedRef.current = true;
2596
- setHomeSection(value);
2597
- }}
2643
+ value={homeSection}
2644
+ onValueChange={selectHomeLibraryTab}
2598
2645
  labels={{
2599
2646
  templates: t("templatesPage.title"),
2600
2647
  recent: t("home.recent"),
@@ -2608,18 +2655,14 @@ export default function Index({ active = true }: { active?: boolean }) {
2608
2655
  </Button>
2609
2656
  }
2610
2657
  search={
2611
- hasDecks ? (
2612
- <DeckSearchInput
2613
- value={deckSearch}
2614
- onChange={setDeckSearch}
2615
- className="w-full sm:w-64 sm:shrink-0"
2616
- />
2617
- ) : null
2658
+ <DeckSearchInput
2659
+ value={deckSearch}
2660
+ onChange={setDeckSearch}
2661
+ className="w-full sm:w-64 sm:shrink-0"
2662
+ />
2618
2663
  }
2619
2664
  recentActions={
2620
- hasDecks ? (
2621
- <DeckFilterMenu value={deckFilter} onChange={setDeckFilter} />
2622
- ) : null
2665
+ <DeckFilterMenu value={deckFilter} onChange={setDeckFilter} />
2623
2666
  }
2624
2667
  templates={<DeckTemplateLibrary enabled={isHome} />}
2625
2668
  recent={
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-05
4
+ ---
5
+
6
+ File uploads explain the free Builder.io option and how to connect S3-compatible storage.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-05
4
+ ---
5
+
6
+ Slides keeps a pending generation question and its chat available when you leave and reopen a deck.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-05
4
+ ---
5
+
6
+ The home page shows both library tabs and starter prompts immediately while content loads.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-05
4
+ ---
5
+
6
+ Prompt-generated Slides decks remain when Cmd+Z reaches deck creation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core-corpus",
3
- "version": "0.201.0",
3
+ "version": "0.201.1",
4
4
  "description": "Opt-in source corpus for Agent-Native Core source-search.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {