@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.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/CHANGELOG.md +6 -0
- package/corpus/templates/analytics/actions/save-dashboard-view.ts +10 -1
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +136 -4
- package/corpus/templates/analytics/app/hooks/use-dashboard-views.ts +4 -0
- package/corpus/templates/analytics/app/i18n/en-US.ts +7 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +86 -23
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +4 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +55 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/ViewsMenu.tsx +44 -7
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-filter-restore.ts +196 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +2 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +158 -47
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +2 -0
- package/corpus/templates/analytics/changelog/2026-10-05-custom-date-controls-stay-beside-the-range-selector-and-dash.md +6 -0
- package/corpus/templates/analytics/server/db/schema.ts +20 -8
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +50 -3
- package/corpus/templates/analytics/server/lib/dashboard-view-remap.ts +52 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +77 -42
- package/corpus/templates/analytics/server/lib/migrate-analytics-artifacts.ts +23 -8
- package/corpus/templates/analytics/server/plugins/db.ts +7 -0
- package/corpus/templates/clips/CHANGELOG.md +4 -0
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
- package/corpus/templates/clips/changelog/2026-10-05-the-agent-composer-is-easier-to-distinguish-from-the-shared-.md +6 -0
- package/corpus/templates/design/CHANGELOG.md +1 -0
- package/corpus/templates/design/app/components/design/multi-screen/chrome-transitions.ts +1 -1
- package/corpus/templates/design/changelog/2026-10-05-design-frame-labels-apply-truncation-as-soon-as-resizing-end.md +6 -0
- package/corpus/templates/slides/CHANGELOG.md +7 -0
- package/corpus/templates/slides/agent-native.app-skill.json +2 -2
- package/corpus/templates/slides/app/components/home/SlidesHomeLibrary.tsx +11 -11
- package/corpus/templates/slides/app/context/DeckContext.tsx +16 -12
- package/corpus/templates/slides/app/hooks/use-new-deck-generation.ts +129 -6
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +4 -1
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +25 -8
- package/corpus/templates/slides/app/pages/Index.tsx +85 -42
- package/corpus/templates/slides/changelog/2026-10-05-file-uploads-explain-the-free-builder-io-option-and-how-to-c.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-05-slides-restores-pending-generation-questions-when-reopened.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-05-the-home-page-shows-both-library-tabs-and-starter-prompts-im.md +6 -0
- package/corpus/templates/slides/changelog/2026-10-05-undo-stops-at-generated-deck.md +6 -0
- 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 &&
|
|
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
|
-
|
|
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:
|
|
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?: {
|
|
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
|
|
602
|
-
const
|
|
603
|
-
searchParams.get("generating") === "1" || Boolean(
|
|
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
|
-
|
|
616
|
-
|
|
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
|
-
!
|
|
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,
|
|
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 [
|
|
687
|
-
|
|
688
|
-
|
|
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
|
-
|
|
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())
|
|
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 (
|
|
2403
|
+
if (viewState === "empty") {
|
|
2354
2404
|
setHomeSection("templates");
|
|
2355
|
-
|
|
2405
|
+
homeLibraryTabPreferenceRef.current = "templates";
|
|
2406
|
+
persistHomeLibraryTab("templates");
|
|
2356
2407
|
return;
|
|
2357
2408
|
}
|
|
2358
|
-
if (
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
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 &&
|
|
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
|
-
|
|
2589
|
-
|
|
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
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
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
|
-
|
|
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={
|
package/package.json
CHANGED