@agent-native/core 0.137.0 → 0.137.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 (47) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/app/components/AgentCompletionSound.tsx +105 -0
  3. package/corpus/templates/analytics/app/components/layout/Layout.tsx +44 -35
  4. package/corpus/templates/analytics/app/i18n-data.ts +38 -0
  5. package/corpus/templates/analytics/app/pages/Settings.tsx +53 -1
  6. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +6 -0
  7. package/corpus/templates/analytics/changelog/2026-08-04-choose-whether-analytics-plays-a-bell-when-the-agent-finishe.md +6 -0
  8. package/corpus/templates/analytics/shared/analytics-user-prefs.ts +2 -0
  9. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +258 -0
  10. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +130 -0
  11. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +25 -337
  12. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +73 -53
  13. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +321 -0
  14. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +32 -11
  15. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +804 -0
  16. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +101 -45
  17. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +7 -4
  18. package/corpus/templates/slides/app/components/editor/bullet-editing.ts +11 -3
  19. package/corpus/templates/slides/app/components/editor/commit-active-edit.ts +21 -0
  20. package/corpus/templates/slides/app/components/editor/list-editing.ts +219 -0
  21. package/corpus/templates/slides/app/components/editor/selection-overlay-measurement.ts +0 -3
  22. package/corpus/templates/slides/app/components/editor/slide-style.ts +199 -0
  23. package/corpus/templates/slides/app/global.css +14 -3
  24. package/corpus/templates/slides/app/i18n/en-US.ts +8 -1
  25. package/corpus/templates/slides/app/lib/recent-references.ts +82 -0
  26. package/corpus/templates/slides/app/pages/DeckEditor.tsx +51 -22
  27. package/corpus/templates/slides/app/pages/Index.tsx +236 -181
  28. package/corpus/templates/slides/changelog/2026-08-01-add-slide-moved-to-the-toolbar-and-the-slide-rail-is-now-mor.md +9 -0
  29. package/corpus/templates/slides/changelog/2026-08-01-add-slide-undo-redo-and-the-text-tool-now-lead-the-slide-too.md +9 -0
  30. package/corpus/templates/slides/changelog/2026-08-01-slide-styling-now-appears-in-a-contextual-toolbar-above-the-.md +6 -0
  31. package/corpus/templates/slides/changelog/2026-08-01-the-slide-style-side-panel-is-retired-all-styling-now-lives-.md +6 -0
  32. package/corpus/templates/slides/changelog/2026-08-03-the-slide-toolbar-is-denser-swatch-only-colors-dropdowns-for.md +10 -0
  33. package/corpus/templates/slides/changelog/2026-08-04-bullet-and-numbered-list-buttons-in-the-slide-toolbar-conver.md +6 -0
  34. package/corpus/templates/slides/changelog/2026-08-04-italic-and-underline-are-now-one-click-away-in-the-slide-too.md +6 -0
  35. package/dist/client/DefaultSpinner.d.ts +0 -12
  36. package/dist/client/DefaultSpinner.js +22 -2
  37. package/dist/client/onboarding/use-onboarding.js +3 -0
  38. package/dist/collab/routes.d.ts +1 -1
  39. package/dist/collab/struct-routes.d.ts +1 -1
  40. package/dist/deploy/build.js +2 -1
  41. package/dist/notifications/routes.d.ts +2 -2
  42. package/dist/progress/routes.d.ts +1 -1
  43. package/dist/server/realtime-token.d.ts +1 -1
  44. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +25 -9
  45. package/package.json +2 -2
  46. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +25 -9
  47. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +0 -763
@@ -1,4 +1,3 @@
1
- import { askUserQuestion } from "@agent-native/core/client/agent-chat";
2
1
  import { callAction, useSession } from "@agent-native/core/client/hooks";
3
2
  import { useT } from "@agent-native/core/client/i18n";
4
3
  import { buildSignInReturnHref } from "@agent-native/core/client/ui";
@@ -21,8 +20,14 @@ import { useLocation, useNavigate, useSearchParams } from "react-router";
21
20
  import { toast } from "sonner";
22
21
 
23
22
  import DeckCard from "@/components/deck/DeckCard";
24
- import PromptPopover from "@/components/editor/PromptDialog";
25
- import type { UploadedFile } from "@/components/editor/PromptDialog";
23
+ import {
24
+ NewDeckReferenceStep,
25
+ type NewDeckReferenceSelection,
26
+ } from "@/components/editor/NewDeckReferenceStep";
27
+ import PromptPopover, {
28
+ uploadPromptFiles,
29
+ type UploadedFile,
30
+ } from "@/components/editor/PromptDialog";
26
31
  import {
27
32
  AlertDialog,
28
33
  AlertDialogAction,
@@ -34,15 +39,6 @@ import {
34
39
  AlertDialogTitle,
35
40
  } from "@/components/ui/alert-dialog";
36
41
  import { Button } from "@/components/ui/button";
37
- import {
38
- Select,
39
- SelectContent,
40
- SelectGroup,
41
- SelectItem,
42
- SelectLabel,
43
- SelectTrigger,
44
- SelectValue,
45
- } from "@/components/ui/select";
46
42
  import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
47
43
  import type { Deck } from "@/context/DeckContext";
48
44
  import { useDecks } from "@/context/DeckContext";
@@ -51,7 +47,11 @@ import { useDesignSystems } from "@/hooks/use-design-systems";
51
47
  import { useWorkspaceDefaults } from "@/hooks/use-workspace-defaults";
52
48
  import { createDeckAgentMessage } from "@/lib/agent-visible-message";
53
49
  import { savePromptToComposerDraft } from "@/lib/composer-draft";
54
- import { cn } from "@/lib/utils";
50
+ import {
51
+ readRecentReferences,
52
+ rememberRecentReference,
53
+ type RecentReference,
54
+ } from "@/lib/recent-references";
55
55
 
56
56
  const NEW_DECK_DRAFT_SCOPE = "slides-new-deck";
57
57
  const PENDING_PROMPT_KEY = "slides:pending-deck-prompt";
@@ -233,6 +233,17 @@ export default function Index() {
233
233
  const [newDeckRetryFiles, setNewDeckRetryFiles] = useState<UploadedFile[]>(
234
234
  [],
235
235
  );
236
+ const [pendingDeck, setPendingDeck] = useState<{
237
+ prompt: string;
238
+ files: UploadedFile[];
239
+ } | null>(null);
240
+ const [showNewDeckReferenceStep, setShowNewDeckReferenceStep] =
241
+ useState(false);
242
+ const [recentReferences, setRecentReferences] = useState<RecentReference[]>(
243
+ [],
244
+ );
245
+ const [referenceImporting, setReferenceImporting] = useState(false);
246
+ const initialPromptConsumedRef = useRef(false);
236
247
  const [signInPromptHadFiles, setSignInPromptHadFiles] = useState(false);
237
248
  const [selectedDesignSystemId, setSelectedDesignSystemId] = useState("");
238
249
  const [selectedReferenceDeckId, setSelectedReferenceDeckId] = useState("");
@@ -254,14 +265,6 @@ export default function Index() {
254
265
  () => new Map(designSystems.map((ds) => [ds.id, ds.title])),
255
266
  [designSystems],
256
267
  );
257
- const starredDecks = useMemo(
258
- () => decks.filter((deck) => deck.starred),
259
- [decks],
260
- );
261
- const unstarredDecks = useMemo(
262
- () => decks.filter((deck) => !deck.starred),
263
- [decks],
264
- );
265
268
  // A workspace default the caller cannot open is reported by the action as
266
269
  // `unavailable` rather than absent; preselecting it would send every new
267
270
  // prompt at a deck that 404s, so fall back to no reference instead.
@@ -289,6 +292,67 @@ export default function Index() {
289
292
  deckFilter === "mine" ? decks.filter((deck) => deck.createdByMe) : decks,
290
293
  [deckFilter, decks],
291
294
  );
295
+ const rememberReference = useCallback(
296
+ (reference: Omit<RecentReference, "lastUsedAt">) => {
297
+ const result = rememberRecentReference(reference);
298
+ if (result.readable) setRecentReferences(result.items);
299
+ },
300
+ [],
301
+ );
302
+
303
+ useEffect(() => {
304
+ const result = readRecentReferences();
305
+ if (result.readable) setRecentReferences(result.items);
306
+ }, []);
307
+
308
+ useEffect(() => {
309
+ if (initialDesignSystemId) {
310
+ rememberReference({
311
+ id: initialDesignSystemId,
312
+ kind: "design-system",
313
+ });
314
+ }
315
+ if (workspaceReferenceDeckId) {
316
+ rememberReference({ id: workspaceReferenceDeckId, kind: "deck" });
317
+ }
318
+ }, [initialDesignSystemId, rememberReference, workspaceReferenceDeckId]);
319
+
320
+ const initialPrompt = searchParams.get("initialPrompt")?.trim() ?? "";
321
+ const openInitialPromptReferenceStep = useCallback(() => {
322
+ if (!initialPrompt || initialPromptConsumedRef.current) return;
323
+ initialPromptConsumedRef.current = true;
324
+ setPendingDeck({ prompt: initialPrompt, files: [] });
325
+ setShowNewDeckReferenceStep(true);
326
+ setSearchParams(
327
+ (previous) => {
328
+ const next = new URLSearchParams(previous);
329
+ next.delete("initialPrompt");
330
+ return next;
331
+ },
332
+ { replace: true },
333
+ );
334
+ }, [initialPrompt, setSearchParams]);
335
+
336
+ useEffect(() => {
337
+ if (!initialPrompt || initialPromptConsumedRef.current) return;
338
+ const firstRunActive =
339
+ typeof document !== "undefined" &&
340
+ document.cookie
341
+ .split(";")
342
+ .some((part) => part.trim().startsWith("agent-native-first-run="));
343
+ const handleFirstRunCompleted = () => openInitialPromptReferenceStep();
344
+ window.addEventListener(
345
+ "agent-native:first-run-completed",
346
+ handleFirstRunCompleted,
347
+ );
348
+ if (!firstRunActive) openInitialPromptReferenceStep();
349
+ return () =>
350
+ window.removeEventListener(
351
+ "agent-native:first-run-completed",
352
+ handleFirstRunCompleted,
353
+ );
354
+ }, [initialPrompt, openInitialPromptReferenceStep]);
355
+
292
356
  const setDeckFilter = useCallback(
293
357
  (value: string) => {
294
358
  const nextFilter = value === "mine" ? "mine" : "all";
@@ -443,6 +507,7 @@ export default function Index() {
443
507
  const handleCreateDeckWithPrompt = async (
444
508
  prompt: string,
445
509
  files: UploadedFile[],
510
+ referenceSelection: NewDeckReferenceSelection = {},
446
511
  ) => {
447
512
  // Pre-flight auth check. The add-deck action returns 403 silently
448
513
  // when unauthenticated, leaving the user stuck on a deck page that
@@ -458,10 +523,21 @@ export default function Index() {
458
523
  newDeckRetryFiles,
459
524
  files,
460
525
  );
461
- const selectedDesignSystem =
462
- selectedDesignSystemId && selectedDesignSystemId !== "none"
463
- ? designSystems.find((ds) => ds.id === selectedDesignSystemId)
464
- : undefined;
526
+ const designSystemId =
527
+ referenceSelection.designSystemId !== undefined
528
+ ? referenceSelection.designSystemId
529
+ : selectedDesignSystemId && selectedDesignSystemId !== "none"
530
+ ? selectedDesignSystemId
531
+ : null;
532
+ const referenceDeckId =
533
+ referenceSelection.referenceDeckId !== undefined
534
+ ? referenceSelection.referenceDeckId
535
+ : selectedReferenceDeckId && selectedReferenceDeckId !== "none"
536
+ ? selectedReferenceDeckId
537
+ : null;
538
+ const selectedDesignSystem = designSystemId
539
+ ? designSystems.find((ds) => ds.id === designSystemId)
540
+ : undefined;
465
541
  let deck: ReturnType<typeof createDeck> | undefined;
466
542
  flushSync(() => {
467
543
  deck = createDeck(undefined, {
@@ -492,56 +568,6 @@ export default function Index() {
492
568
  setNewDeckRetryFiles([]);
493
569
  navigate(`/deck/${deck.id}`, { flushSync: true });
494
570
 
495
- // The deck now exists server-side and the user is looking at it, so a
496
- // failure past this point (e.g. askUserQuestion rejecting on an
497
- // application-state write error) must undo the navigation and restore
498
- // the prompt instead of stranding the user on a permanently empty deck.
499
- const recoverFromGenerationSetupFailure = () => {
500
- deleteDeck(deckId);
501
- toast.error(t("home.generationStartFailed"), {
502
- description: t("home.generationStartFailedDescription"),
503
- });
504
- // This Index instance is about to unmount (we already navigated away
505
- // to the deck page above), so state setters here would land on a dead
506
- // component. Hand the retry payload to the next Index mount via router
507
- // state instead; the effect above restores it.
508
- const retryState: DeckGenerationRetryState = {
509
- retryPrompt: prompt,
510
- retryFiles: filesForGeneration,
511
- };
512
- navigate("/", { state: retryState });
513
- };
514
-
515
- let deckLength: Awaited<ReturnType<typeof askUserQuestion>> = null;
516
- try {
517
- // One quick, skippable decision so the agent doesn't guess the deck size.
518
- deckLength = await askUserQuestion({
519
- question: t("home.deckLengthQuestion"),
520
- header: t("home.deckLengthHeader"),
521
- options: [
522
- { label: t("home.deckLengthShort"), value: "3–5 slides" },
523
- {
524
- label: t("home.deckLengthMedium"),
525
- value: "6–10 slides",
526
- recommended: true,
527
- },
528
- { label: t("home.deckLengthLong"), value: "11+ slides" },
529
- {
530
- label: t("home.deckLengthSingleVisual"),
531
- value: "a single standalone visual slide",
532
- },
533
- ],
534
- allowFreeText: false,
535
- });
536
- } catch {
537
- recoverFromGenerationSetupFailure();
538
- return;
539
- }
540
- const deckLengthContext =
541
- typeof deckLength === "string" && deckLength
542
- ? `Target length: aim for ${deckLength} unless the user's request clearly specifies a different count.`
543
- : "";
544
-
545
571
  const trimmedPrompt = prompt.trim();
546
572
  const hasImportedGoogleDocContext = trimmedPrompt.includes("<google-doc ");
547
573
  const googleDocUrls = hasImportedGoogleDocContext
@@ -561,11 +587,8 @@ export default function Index() {
561
587
  "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.",
562
588
  ].join("\n")
563
589
  : "";
564
- const referenceDeckContext = await loadReferenceDeckGenerationContext(
565
- selectedReferenceDeckId && selectedReferenceDeckId !== "none"
566
- ? selectedReferenceDeckId
567
- : null,
568
- );
590
+ const referenceDeckContext =
591
+ await loadReferenceDeckGenerationContext(referenceDeckId);
569
592
  const hydratedDesignSystemContext = await loadDesignSystemGenerationContext(
570
593
  selectedDesignSystem?.id,
571
594
  );
@@ -593,7 +616,7 @@ export default function Index() {
593
616
  referenceDeckContext,
594
617
  designSystemContext,
595
618
  "",
596
- deckLengthContext,
619
+ "Before generating, if the request or selected references leave a meaningful choice unresolved, use the `ask-question` tool to ask one concise, prompt-specific question in the inline guided-question flow. Generate the question wording and 2 to 4 options from the user's request and selected references, like Claude's design-question flow; do not use a fixed generic questionnaire. Ask only a choice that materially affects the deck, such as audience, tone, structure, or length. If the prompt already makes the choice clear, do not ask it again. Wait for the user's answer or skip before adding slides.",
597
620
  "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.",
598
621
  "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: \"" +
599
622
  deckId +
@@ -619,6 +642,108 @@ export default function Index() {
619
642
  });
620
643
  };
621
644
 
645
+ const handlePromptSubmit = useCallback(
646
+ (prompt: string, files: UploadedFile[]) => {
647
+ setNewDeckPromptOpen(false, { clearInitialPrompt: false });
648
+ setPendingDeck({ prompt, files });
649
+ setShowNewDeckReferenceStep(true);
650
+ },
651
+ [setNewDeckPromptOpen],
652
+ );
653
+
654
+ const handleReferenceSelect = useCallback(
655
+ async (selection: NewDeckReferenceSelection) => {
656
+ const pending = pendingDeck;
657
+ if (!pending) return;
658
+ if (selection.designSystemId) {
659
+ rememberReference({
660
+ id: selection.designSystemId,
661
+ kind: "design-system",
662
+ });
663
+ }
664
+ if (selection.referenceDeckId) {
665
+ rememberReference({ id: selection.referenceDeckId, kind: "deck" });
666
+ }
667
+ setShowNewDeckReferenceStep(false);
668
+ setPendingDeck(null);
669
+ await handleCreateDeckWithPrompt(
670
+ pending.prompt,
671
+ pending.files,
672
+ selection,
673
+ );
674
+ },
675
+ [handleCreateDeckWithPrompt, pendingDeck, rememberReference],
676
+ );
677
+
678
+ const handleReferenceImport = useCallback(
679
+ async (files: File[]) => {
680
+ const pending = pendingDeck;
681
+ if (!pending) return;
682
+ setReferenceImporting(true);
683
+ try {
684
+ const uploaded = await uploadPromptFiles(files);
685
+ const pptxReference = uploaded.find((file) =>
686
+ file.originalName.toLowerCase().endsWith(".pptx"),
687
+ );
688
+ let referenceSelection: NewDeckReferenceSelection = {};
689
+ let generationFiles = uploaded;
690
+ if (pptxReference) {
691
+ const imported = (await callAction("import-pptx", {
692
+ filePath: pptxReference.path,
693
+ })) as { id?: unknown };
694
+ if (typeof imported.id !== "string" || !imported.id) {
695
+ throw new Error("The imported presentation did not create a deck.");
696
+ }
697
+ await reloadDecks();
698
+ rememberReference({ id: imported.id, kind: "deck" });
699
+ referenceSelection = {
700
+ designSystemId: null,
701
+ referenceDeckId: imported.id,
702
+ };
703
+ generationFiles = uploaded.filter((file) => file !== pptxReference);
704
+ }
705
+ setShowNewDeckReferenceStep(false);
706
+ setPendingDeck(null);
707
+ await handleCreateDeckWithPrompt(
708
+ pending.prompt,
709
+ [...pending.files, ...generationFiles],
710
+ referenceSelection,
711
+ );
712
+ } catch (error) {
713
+ toast.error(t("editorToolbar.uploadFailed"), {
714
+ description:
715
+ error instanceof Error
716
+ ? error.message
717
+ : t("editorToolbar.importFailedDescription"),
718
+ });
719
+ } finally {
720
+ setReferenceImporting(false);
721
+ }
722
+ },
723
+ [
724
+ callAction,
725
+ handleCreateDeckWithPrompt,
726
+ pendingDeck,
727
+ reloadDecks,
728
+ rememberReference,
729
+ t,
730
+ ],
731
+ );
732
+
733
+ const handleReferenceSkip = useCallback(() => {
734
+ const pending = pendingDeck;
735
+ if (!pending) {
736
+ setShowNewDeckReferenceStep(false);
737
+ return;
738
+ }
739
+ setShowNewDeckReferenceStep(false);
740
+ setPendingDeck(null);
741
+ void handleCreateDeckWithPrompt(pending.prompt, pending.files, {
742
+ designSystemId: null,
743
+ referenceDeckId: null,
744
+ });
745
+ }, [handleCreateDeckWithPrompt, pendingDeck]);
746
+
622
747
  const handleConfirmDelete = () => {
623
748
  if (deckToDelete) {
624
749
  deleteDeck(deckToDelete);
@@ -933,7 +1058,7 @@ export default function Index() {
933
1058
  placeholder={t("home.newDeckPlaceholder")}
934
1059
  onSkip={handleCreateDeckBlank}
935
1060
  skipLabel={t("home.skipPrompt")}
936
- onSubmit={handleCreateDeckWithPrompt}
1061
+ onSubmit={handlePromptSubmit}
937
1062
  onBeforeUpload={(prompt, files) => {
938
1063
  if (session) return true;
939
1064
  preservePromptForSignIn(prompt, { hadFiles: files.length > 0 });
@@ -944,105 +1069,35 @@ export default function Index() {
944
1069
  draftScope={NEW_DECK_DRAFT_SCOPE}
945
1070
  initialText={newDeckInitialPrompt?.text}
946
1071
  initialTextKey={newDeckInitialPrompt?.key}
947
- >
948
- {(designSystems.length > 0 || decks.length > 0) && (
949
- <div
950
- className={cn(
951
- "grid gap-3 border-t border-border px-3.5 py-2",
952
- designSystems.length > 0 && decks.length > 0
953
- ? "grid-cols-2"
954
- : "grid-cols-1",
955
- )}
956
- >
957
- {designSystems.length > 0 && (
958
- <div className="min-w-0">
959
- <label className="mb-1.5 block text-[11px] font-medium text-muted-foreground">
960
- {t("home.designSystem")}
961
- </label>
962
- <Select
963
- value={selectedDesignSystemId || "none"}
964
- onValueChange={(value) => {
965
- designSystemAutoRef.current = false;
966
- setSelectedDesignSystemId(value);
967
- }}
968
- >
969
- <SelectTrigger className="h-8 w-full bg-accent/40 text-xs">
970
- <SelectValue placeholder={t("raw.chooseDesignSystem")} />
971
- </SelectTrigger>
972
- <SelectContent>
973
- <SelectItem value="none">{t("home.none")}</SelectItem>
974
- {designSystems.map((ds) => (
975
- <SelectItem key={ds.id} value={ds.id}>
976
- {ds.title}
977
- {ds.isDefault || ds.id === workspaceDesignSystemId
978
- ? t("home.defaultSuffix")
979
- : ""}
980
- </SelectItem>
981
- ))}
982
- </SelectContent>
983
- </Select>
984
- </div>
985
- )}
986
- {decks.length > 0 && (
987
- <div className="min-w-0">
988
- <label className="mb-1.5 block text-[11px] font-medium text-muted-foreground">
989
- {t("home.referenceDeck")}
990
- </label>
991
- <Select
992
- value={selectedReferenceDeckId || "none"}
993
- onValueChange={(value) => {
994
- referenceDeckAutoRef.current = false;
995
- setSelectedReferenceDeckId(value);
996
- }}
997
- >
998
- <SelectTrigger className="h-8 w-full bg-accent/40 text-xs">
999
- <SelectValue
1000
- placeholder={t("home.referenceDeckPlaceholder")}
1001
- />
1002
- </SelectTrigger>
1003
- <SelectContent>
1004
- <SelectItem value="none">
1005
- {t("home.referenceDeckNone")}
1006
- </SelectItem>
1007
- {starredDecks.length > 0 && (
1008
- <SelectGroup>
1009
- <SelectLabel>
1010
- {t("home.referenceDeckStarredGroup")}
1011
- </SelectLabel>
1012
- {starredDecks.map((deck) => (
1013
- <SelectItem key={deck.id} value={deck.id}>
1014
- {deck.title}
1015
- {deck.id === workspaceReferenceDeckId
1016
- ? t("home.defaultSuffix")
1017
- : ""}
1018
- </SelectItem>
1019
- ))}
1020
- </SelectGroup>
1021
- )}
1022
- {unstarredDecks.length > 0 && (
1023
- <SelectGroup>
1024
- {starredDecks.length > 0 && (
1025
- <SelectLabel>
1026
- {t("home.referenceDeckOtherGroup")}
1027
- </SelectLabel>
1028
- )}
1029
- {unstarredDecks.map((deck) => (
1030
- <SelectItem key={deck.id} value={deck.id}>
1031
- {deck.title}
1032
- {deck.id === workspaceReferenceDeckId
1033
- ? t("home.defaultSuffix")
1034
- : ""}
1035
- </SelectItem>
1036
- ))}
1037
- </SelectGroup>
1038
- )}
1039
- </SelectContent>
1040
- </Select>
1041
- </div>
1042
- )}
1043
- </div>
1044
- )}
1045
- </PromptPopover>
1072
+ />
1073
+
1074
+ <NewDeckReferenceStep
1075
+ open={showNewDeckReferenceStep}
1076
+ onOpenChange={(open) => {
1077
+ if (!open) handleReferenceSkip();
1078
+ }}
1079
+ designSystems={designSystems}
1080
+ decks={decks}
1081
+ defaultDesignSystemId={initialDesignSystemId}
1082
+ defaultReferenceDeckId={workspaceReferenceDeckId}
1083
+ recentReferences={recentReferences}
1084
+ onSelect={(selection) => void handleReferenceSelect(selection)}
1085
+ onImport={handleReferenceImport}
1086
+ onSkip={handleReferenceSkip}
1087
+ importing={referenceImporting}
1088
+ title={t("home.newDeckPromptTitle")}
1089
+ description={t("home.referenceDeckPlaceholder")}
1090
+ designSystemLabel={t("home.designSystem")}
1091
+ referenceDeckLabel={t("home.referenceDeck")}
1092
+ chooseDeckLabel={t("home.referenceDeckPlaceholder")}
1093
+ importFileLabel={t("editorToolbar.importFile")}
1094
+ importingLabel={t("editorToolbar.importing")}
1095
+ skipLabel={t("home.referenceDeckNone")}
1096
+ defaultSuffix={t("home.defaultSuffix")}
1097
+ starredLabel={t("home.referenceDeckStarredGroup")}
1098
+ otherDecksLabel={t("home.referenceDeckOtherGroup")}
1099
+ promptNote={pendingDeck?.prompt}
1100
+ />
1046
1101
 
1047
1102
  {/* Sign-in required to create a deck. Shown when an unauthenticated
1048
1103
  user submits a prompt — the typed prompt is preserved in
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: changed
3
+ date: 2026-08-01
4
+ ---
5
+
6
+ Adding a slide now starts from a `+` at the head of the editor toolbar, next to
7
+ the deck title, instead of a header above the slide thumbnails. The rail itself
8
+ drops that header, tucks the drag handle onto the thumbnail on hover, and is
9
+ narrower — so more slides fit on screen and the canvas gets the width back.
@@ -0,0 +1,9 @@
1
+ ---
2
+ type: changed
3
+ date: 2026-08-01
4
+ ---
5
+
6
+ The contextual slide toolbar now opens with the actions that never change: add
7
+ slide, undo, redo, and the text-box tool. Undo and redo previously worked only
8
+ through Cmd+Z with no visible control; clicking them commits whatever text you
9
+ are editing first, so nothing you just typed is lost.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-01
4
+ ---
5
+
6
+ Slide styling now appears in a contextual toolbar above the canvas, so the slide keeps its full width while you edit.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-01
4
+ ---
5
+
6
+ The slide style side panel is retired; all styling now lives in the contextual toolbar above the canvas.
@@ -0,0 +1,10 @@
1
+ ---
2
+ type: changed
3
+ date: 2026-08-03
4
+ ---
5
+
6
+ Colour controls in the slide toolbar are now just the swatch, with no hex code
7
+ beside it. Font weight and text alignment collapsed from four buttons each into
8
+ a single control with a dropdown, and alignment uses standard icons instead of
9
+ words. Undo and redo moved out of the toolbar into the overflow menu, since
10
+ Cmd+Z already covers the common case.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-08-04
4
+ ---
5
+
6
+ Bullet and numbered list buttons in the slide toolbar convert a text box to a real list
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-08-04
4
+ ---
5
+
6
+ Italic and underline are now one click away in the slide toolbar, without entering text edit mode
@@ -1,14 +1,2 @@
1
- /**
2
- * Full-screen loading spinner rendered during SSR and initial hydration.
3
- * Uses inline SVG + styles because Tailwind may not be loaded yet on the server.
4
- * Respects the user's OS color scheme so dark-mode users don't get a white flash.
5
- *
6
- * The stall hint is revealed by a pure-CSS `animation-delay`, never a timer:
7
- * the states that strand a user here (hydration never runs, the route module
8
- * 404s, a cold dev-server compile) are exactly the states where no JS of ours
9
- * executes, so a `setTimeout` fallback would never fire. A featureless spinner
10
- * is indistinguishable from a blank screen, and reads as "the app is broken"
11
- * rather than "look at the terminal" — that mis-read is what this text buys.
12
- */
13
1
  export declare function DefaultSpinner(): import("react").JSX.Element;
14
2
  //# sourceMappingURL=DefaultSpinner.d.ts.map
@@ -1,17 +1,33 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
2
3
  /**
3
4
  * Full-screen loading spinner rendered during SSR and initial hydration.
4
5
  * Uses inline SVG + styles because Tailwind may not be loaded yet on the server.
5
6
  * Respects the user's OS color scheme so dark-mode users don't get a white flash.
6
7
  *
7
- * The stall hint is revealed by a pure-CSS `animation-delay`, never a timer:
8
+ * In development builds, the stall hint is revealed by a pure-CSS
9
+ * `animation-delay`, never a timer:
8
10
  * the states that strand a user here (hydration never runs, the route module
9
11
  * 404s, a cold dev-server compile) are exactly the states where no JS of ours
10
12
  * executes, so a `setTimeout` fallback would never fire. A featureless spinner
11
13
  * is indistinguishable from a blank screen, and reads as "the app is broken"
12
14
  * rather than "look at the terminal" — that mis-read is what this text buys.
13
15
  */
16
+ function isDevelopmentBuild() {
17
+ if (typeof process !== "undefined" &&
18
+ process.env?.NODE_ENV === "production") {
19
+ return false;
20
+ }
21
+ const viteEnv = import.meta.env;
22
+ if (viteEnv?.PROD === true)
23
+ return false;
24
+ return viteEnv?.DEV === true;
25
+ }
14
26
  export function DefaultSpinner() {
27
+ const [showStallHint, setShowStallHint] = useState(false);
28
+ useEffect(() => {
29
+ setShowStallHint(isDevelopmentBuild());
30
+ }, []);
15
31
  return (_jsxs("div", { style: {
16
32
  display: "flex",
17
33
  flexDirection: "column",
@@ -20,8 +36,10 @@ export function DefaultSpinner() {
20
36
  gap: 16,
21
37
  height: "100vh",
22
38
  width: "100%",
23
- }, children: [_jsx("svg", { role: "status", "aria-label": "Loading", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "an-spin 1s linear infinite", opacity: 0.7 }, children: _jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }), _jsx("p", { className: "an-stall-hint", children: "Still loading. A first run compiles dependencies and can take a minute \u2014 if it does not finish, check the terminal running the dev server for errors." }), _jsx("style", { children: `
39
+ }, children: [_jsx("svg", { role: "status", "aria-label": "Loading", width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "an-spin 1s linear infinite", opacity: 0.7 }, children: _jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }), showStallHint && (_jsx("p", { className: "an-stall-hint", children: "Still loading. A first run compiles dependencies and can take a minute \u2014 if it does not finish, check the terminal running the dev server for errors." })), _jsx("style", { children: `
24
40
  @keyframes an-spin { to { transform: rotate(360deg) } }
41
+ ${showStallHint
42
+ ? `
25
43
  @keyframes an-stall-in { to { opacity: 0.6 } }
26
44
  .an-stall-hint {
27
45
  opacity: 0;
@@ -37,6 +55,8 @@ export function DefaultSpinner() {
37
55
  @media (prefers-reduced-motion: reduce) {
38
56
  .an-stall-hint { animation-duration: 0s }
39
57
  }
58
+ `
59
+ : ""}
40
60
  html {
41
61
  background: hsl(var(--background, 0 0% 100%));
42
62
  color: hsl(var(--foreground, 240 10% 3.9%));
@@ -109,6 +109,9 @@ export function useOnboarding(options = {}) {
109
109
  return;
110
110
  }
111
111
  setFirstRun(false);
112
+ if (typeof window !== "undefined") {
113
+ window.dispatchEvent(new CustomEvent("agent-native:first-run-completed"));
114
+ }
112
115
  await fetchAll();
113
116
  }, [fetchAll]);
114
117
  const totalCount = steps.length;
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- ok?: undefined;
30
29
  error: string;
30
+ ok?: undefined;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;