@agent-native/core 0.141.2 → 0.141.4

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 (157) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +1 -1
  3. package/corpus/templates/analytics/actions/gong-calls.ts +56 -18
  4. package/corpus/templates/analytics/changelog/2026-08-05-analytics-queries-keep-every-table-tenant-scoped.md +6 -0
  5. package/corpus/templates/analytics/changelog/2026-08-05-durable-a2a-workers-avoid-startup-migration-contention.md +6 -0
  6. package/corpus/templates/analytics/changelog/2026-08-05-fixed-sign-in-and-dashboard-requests-failing-when-background.md +6 -0
  7. package/corpus/templates/analytics/changelog/2026-08-05-historical-rollups-stay-complete-while-live-events-arrive.md +6 -0
  8. package/corpus/templates/analytics/server/db/schema.ts +1 -0
  9. package/corpus/templates/analytics/server/lib/dashboard-report.ts +2 -0
  10. package/corpus/templates/analytics/server/lib/emails.ts +51 -0
  11. package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +8 -12
  12. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +9 -0
  13. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +262 -13
  14. package/corpus/templates/analytics/server/lib/gong.ts +132 -33
  15. package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -1
  16. package/corpus/templates/analytics/server/plugins/auth.ts +21 -2
  17. package/corpus/templates/analytics/server/plugins/db.ts +35 -10
  18. package/corpus/templates/analytics/server/plugins/transactional-emails.ts +5 -0
  19. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +17 -7
  20. package/corpus/templates/calendar/server/lib/booking-emails.ts +154 -70
  21. package/corpus/templates/calendar/server/lib/emails.ts +167 -0
  22. package/corpus/templates/calendar/server/lib/event-guest-notifications.ts +64 -28
  23. package/corpus/templates/calendar/server/plugins/transactional-emails.ts +5 -0
  24. package/corpus/templates/clips/actions/invite-member.ts +39 -16
  25. package/corpus/templates/clips/app/components/player/share-dialog.tsx +73 -1
  26. package/corpus/templates/clips/app/i18n/en-US.ts +3 -0
  27. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -25
  28. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  29. package/corpus/templates/clips/changelog/2026-08-05-activity-is-now-the-default-clips-recording-tab-and-ai-setup.md +6 -0
  30. package/corpus/templates/clips/changelog/2026-08-05-clips-can-copy-a-linked-email-preview-for-public-recordings.md +6 -0
  31. package/corpus/templates/clips/changelog/2026-08-05-preparing-recording-now-uses-a-clean-shadow-free-status-card.md +6 -0
  32. package/corpus/templates/clips/desktop/src/styles.css +0 -3
  33. package/corpus/templates/clips/server/lib/emails.ts +256 -0
  34. package/corpus/templates/clips/server/lib/transactional-email-templates.ts +24 -0
  35. package/corpus/templates/clips/server/plugins/transactional-email-catalog.ts +5 -0
  36. package/corpus/templates/clips/server/routes/api/thumbnail/[recordingId].get.ts +4 -1
  37. package/corpus/templates/clips/shared/email-preview.ts +61 -0
  38. package/corpus/templates/content/server/lib/comment-notifications.ts +57 -26
  39. package/corpus/templates/content/server/lib/emails.ts +70 -0
  40. package/corpus/templates/content/server/plugins/transactional-emails.ts +5 -0
  41. package/corpus/templates/design/actions/import-design-tokens.ts +11 -8
  42. package/corpus/templates/design/actions/index-design-tokens.ts +13 -6
  43. package/corpus/templates/design/changelog/2026-08-05-design-token-imports-classify-typography-dimensions-correctly.md +6 -0
  44. package/corpus/templates/dispatch/app/routes/transactional-email.$appId.$id.tsx +4 -0
  45. package/corpus/templates/dispatch/app/routes/transactional-email.tsx +4 -0
  46. package/corpus/templates/dispatch/changelog/2026-08-05-transactional-email-catalogs-and-delivery-activity-are-available-again.md +6 -0
  47. package/corpus/templates/factory/AGENTS.md +18 -17
  48. package/corpus/templates/factory/actions/get-factory-automation-health.ts +50 -0
  49. package/corpus/templates/factory/actions/get-triage-config.ts +8 -0
  50. package/corpus/templates/factory/actions/save-triage-config.ts +26 -0
  51. package/corpus/templates/factory/app/i18n/en-US.ts +27 -0
  52. package/corpus/templates/factory/app/routes/factory.tsx +376 -206
  53. package/corpus/templates/factory/changelog/2026-08-05-factory-now-keeps-production-automations-running-shows-sched.md +6 -0
  54. package/corpus/templates/factory/server/db/schema.ts +6 -0
  55. package/corpus/templates/factory/server/plugins/factory-migrations.ts +10 -0
  56. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +141 -2
  57. package/corpus/templates/forms/app/pages/FormsListPage.tsx +41 -21
  58. package/corpus/templates/forms/changelog/2026-08-05-form-links-can-now-be-cmd-clicked-to-open-in-a-new-tab.md +6 -0
  59. package/corpus/templates/forms/server/lib/emails.ts +52 -0
  60. package/corpus/templates/forms/server/lib/response-email.ts +2 -0
  61. package/corpus/templates/forms/server/plugins/transactional-emails.ts +5 -0
  62. package/corpus/templates/plan/actions/request-plan-access.ts +33 -12
  63. package/corpus/templates/plan/server/lib/comment-notifications.ts +53 -27
  64. package/corpus/templates/plan/server/lib/emails.ts +67 -0
  65. package/corpus/templates/plan/server/plugins/transactional-emails.ts +5 -0
  66. package/corpus/templates/slides/actions/import-from-url.ts +1 -0
  67. package/corpus/templates/slides/app/components/editor/NewDeckReferenceStep.tsx +492 -381
  68. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +8 -7
  69. package/corpus/templates/slides/app/components/layout/Layout.tsx +27 -23
  70. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +313 -0
  71. package/corpus/templates/slides/app/components/ui/command.tsx +1 -0
  72. package/corpus/templates/slides/app/i18n/en-US.ts +8 -2
  73. package/corpus/templates/slides/app/lib/create-deck-generation.ts +305 -0
  74. package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -12
  75. package/corpus/templates/slides/app/pages/Index.tsx +127 -137
  76. package/corpus/templates/slides/app/root.tsx +8 -0
  77. package/corpus/templates/slides/changelog/2026-08-05-deck-browsing-keeps-all-and-mine-in-the-header-with-a-tighte.md +6 -0
  78. package/corpus/templates/slides/changelog/2026-08-05-new-deck-creation-now-flows-through-a-clean-full-screen-refe.md +6 -0
  79. package/corpus/templates/slides/changelog/2026-08-05-website-imports-now-include-linked-stylesheets-for-more-accu.md +6 -0
  80. package/corpus/templates/slides/server/lib/comment-notifications.ts +44 -21
  81. package/corpus/templates/slides/server/lib/emails.ts +40 -0
  82. package/corpus/templates/slides/server/plugins/transactional-emails.ts +5 -0
  83. package/dist/brand-kit/index.d.ts +1 -0
  84. package/dist/brand-kit/index.js +1 -0
  85. package/dist/brand-kit/tokens.d.ts +91 -0
  86. package/dist/brand-kit/tokens.js +279 -0
  87. package/dist/brand-kit/types.d.ts +25 -0
  88. package/dist/client/AssistantChat.js +7 -7
  89. package/dist/client/onboarding/FirstRunOnboarding.js +26 -2
  90. package/dist/client/onboarding/first-run-registry.d.ts +25 -0
  91. package/dist/client/onboarding/first-run-registry.js +18 -0
  92. package/dist/client/onboarding/index.d.ts +2 -0
  93. package/dist/client/onboarding/index.js +1 -0
  94. package/dist/collab/awareness.d.ts +2 -2
  95. package/dist/collab/routes.d.ts +1 -1
  96. package/dist/collab/struct-routes.d.ts +1 -1
  97. package/dist/db/index.d.ts +1 -1
  98. package/dist/db/index.js +1 -1
  99. package/dist/db/migrations.d.ts +13 -3
  100. package/dist/db/migrations.js +19 -4
  101. package/dist/deploy/build.d.ts +8 -0
  102. package/dist/deploy/build.js +92 -3
  103. package/dist/deploy/workspace-deploy.js +86 -3
  104. package/dist/email-catalog/actions/list-email-activity.d.ts +5 -0
  105. package/dist/email-catalog/actions/list-email-activity.js +19 -0
  106. package/dist/email-catalog/actions/list-email-engagement.d.ts +5 -0
  107. package/dist/email-catalog/actions/list-email-engagement.js +15 -0
  108. package/dist/email-catalog/actions/list-email-log.d.ts +7 -0
  109. package/dist/email-catalog/actions/list-email-log.js +23 -0
  110. package/dist/email-catalog/actions/list-transactional-emails.d.ts +22 -0
  111. package/dist/email-catalog/actions/list-transactional-emails.js +67 -0
  112. package/dist/email-catalog/actions/render-transactional-email-preview.d.ts +9 -0
  113. package/dist/email-catalog/actions/render-transactional-email-preview.js +25 -0
  114. package/dist/email-catalog/authorize.d.ts +1 -0
  115. package/dist/email-catalog/authorize.js +9 -0
  116. package/dist/email-catalog/log.d.ts +60 -0
  117. package/dist/email-catalog/log.js +142 -0
  118. package/dist/email-catalog/provider-metrics.d.ts +34 -0
  119. package/dist/email-catalog/provider-metrics.js +182 -0
  120. package/dist/email-catalog/registry.d.ts +78 -0
  121. package/dist/email-catalog/registry.js +70 -0
  122. package/dist/email-catalog/schema.d.ts +231 -0
  123. package/dist/email-catalog/schema.js +53 -0
  124. package/dist/email-catalog/system-emails.d.ts +13 -0
  125. package/dist/email-catalog/system-emails.js +67 -0
  126. package/dist/jobs/index.d.ts +2 -0
  127. package/dist/jobs/index.js +2 -0
  128. package/dist/jobs/run-history.js +48 -1
  129. package/dist/jobs/scheduler-dispatch.d.ts +7 -0
  130. package/dist/jobs/scheduler-dispatch.js +7 -0
  131. package/dist/jobs/scheduler-health.d.ts +22 -0
  132. package/dist/jobs/scheduler-health.js +172 -0
  133. package/dist/jobs/scheduler.js +67 -2
  134. package/dist/localization/default-messages.d.ts +45 -0
  135. package/dist/localization/default-messages.js +45 -0
  136. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  137. package/dist/notifications/routes.d.ts +3 -3
  138. package/dist/observability/routes.d.ts +3 -3
  139. package/dist/org/handlers.js +9 -1
  140. package/dist/progress/routes.d.ts +1 -1
  141. package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
  142. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  143. package/dist/server/action-discovery.js +22 -0
  144. package/dist/server/agent-chat/recurring-jobs-runtime.d.ts +7 -1
  145. package/dist/server/agent-chat/recurring-jobs-runtime.js +12 -0
  146. package/dist/server/agent-chat-plugin.d.ts +2 -1
  147. package/dist/server/agent-chat-plugin.js +69 -22
  148. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  149. package/dist/server/better-auth-instance.js +17 -2
  150. package/dist/server/design-token-utils.d.ts +6 -1
  151. package/dist/server/design-token-utils.js +252 -59
  152. package/dist/server/email.d.ts +11 -0
  153. package/dist/server/email.js +54 -3
  154. package/dist/server/index.d.ts +3 -2
  155. package/dist/server/index.js +3 -2
  156. package/dist/vite/client.js +20 -14
  157. package/package.json +2 -1
@@ -20,7 +20,10 @@ import { useLocation, useNavigate, useSearchParams } from "react-router";
20
20
  import { toast } from "sonner";
21
21
 
22
22
  import DeckCard from "@/components/deck/DeckCard";
23
- import { NewDeckReferenceStep } from "@/components/editor/NewDeckReferenceStep";
23
+ import {
24
+ NewDeckReferenceStep,
25
+ type NewDeckReferenceSelection,
26
+ } from "@/components/editor/NewDeckReferenceStep";
24
27
  import PromptPopover, {
25
28
  uploadPromptFiles,
26
29
  type UploadedFile,
@@ -46,10 +49,10 @@ import { createDeckAgentMessage } from "@/lib/agent-visible-message";
46
49
  import { savePromptToComposerDraft } from "@/lib/composer-draft";
47
50
  import { sortDecksByRecency } from "@/lib/deck-sorting";
48
51
  import {
49
- resolveNewDeckReferenceSelection,
50
- type NewDeckReferenceSelection,
51
- } from "@/lib/new-deck-reference-selection";
52
- import { rememberRecentReference } from "@/lib/recent-references";
52
+ readRecentReferences,
53
+ rememberRecentReference,
54
+ type RecentReference,
55
+ } from "@/lib/recent-references";
53
56
 
54
57
  const NEW_DECK_DRAFT_SCOPE = "slides-new-deck";
55
58
  const PENDING_PROMPT_KEY = "slides:pending-deck-prompt";
@@ -237,6 +240,9 @@ export default function Index() {
237
240
  } | null>(null);
238
241
  const [showNewDeckReferenceStep, setShowNewDeckReferenceStep] =
239
242
  useState(false);
243
+ const [recentReferences, setRecentReferences] = useState<RecentReference[]>(
244
+ [],
245
+ );
240
246
  const [referenceImporting, setReferenceImporting] = useState(false);
241
247
  const initialPromptConsumedRef = useRef(false);
242
248
  const [signInPromptHadFiles, setSignInPromptHadFiles] = useState(false);
@@ -296,11 +302,18 @@ export default function Index() {
296
302
  [deckFilter, decks],
297
303
  );
298
304
  const rememberReference = useCallback(
299
- (reference: Parameters<typeof rememberRecentReference>[0]) =>
300
- rememberRecentReference(reference),
305
+ (reference: Parameters<typeof rememberRecentReference>[0]) => {
306
+ const result = rememberRecentReference(reference);
307
+ if (result.readable) setRecentReferences(result.items);
308
+ },
301
309
  [],
302
310
  );
303
311
 
312
+ useEffect(() => {
313
+ const result = readRecentReferences();
314
+ if (result.readable) setRecentReferences(result.items);
315
+ }, []);
316
+
304
317
  useEffect(() => {
305
318
  if (initialDesignSystemId) {
306
319
  rememberReference({
@@ -314,15 +327,13 @@ export default function Index() {
314
327
  }, [initialDesignSystemId, rememberReference, workspaceReferenceDeckId]);
315
328
 
316
329
  const initialPrompt = searchParams.get("initialPrompt")?.trim() ?? "";
317
- const openInitialPromptReferenceStep = useCallback(() => {
330
+ const onboardingPreview = searchParams.get("onboarding") === "preview";
331
+ const openInitialPrompt = useCallback(() => {
318
332
  if (!initialPrompt || initialPromptConsumedRef.current) return;
319
333
  initialPromptConsumedRef.current = true;
320
- setPendingDeck({ prompt: initialPrompt, files: [] });
321
- designSystemAutoRef.current = true;
322
- referenceDeckAutoRef.current = true;
323
- setSelectedDesignSystemId(initialDesignSystemId ?? null);
324
- setSelectedReferenceDeckId(workspaceReferenceDeckId ?? null);
325
- setShowNewDeckReferenceStep(true);
334
+ anchorElRef.current = null;
335
+ setNewDeckInitialPrompt({ text: initialPrompt, key: Date.now() });
336
+ setShowNewDeckPrompt(true);
326
337
  setSearchParams(
327
338
  (previous) => {
328
339
  const next = new URLSearchParams(previous);
@@ -336,22 +347,39 @@ export default function Index() {
336
347
  useEffect(() => {
337
348
  if (!initialPrompt || initialPromptConsumedRef.current) return;
338
349
  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();
350
+ onboardingPreview ||
351
+ (typeof document !== "undefined" &&
352
+ document.cookie
353
+ .split(";")
354
+ .some((part) => part.trim().startsWith("agent-native-first-run=")));
355
+ const handleFirstRunCompleted = () => openInitialPrompt();
344
356
  window.addEventListener(
345
357
  "agent-native:first-run-completed",
346
358
  handleFirstRunCompleted,
347
359
  );
348
- if (!firstRunActive) openInitialPromptReferenceStep();
349
- return () =>
360
+ let fallbackTimer: number | undefined;
361
+ if (!firstRunActive) {
362
+ openInitialPrompt();
363
+ } else {
364
+ // A landing link can open in a tab where the first-run marker exists but
365
+ // the onboarding surface is not mounted. Do not leave the prompt waiting
366
+ // forever in that case, while still giving the real onboarding overlay
367
+ // time to load and dispatch its completion event.
368
+ fallbackTimer = window.setTimeout(() => {
369
+ const firstRunSurface = document.querySelector(
370
+ "[data-onboarding-screen], [data-onboarding-loading]",
371
+ );
372
+ if (!firstRunSurface) openInitialPrompt();
373
+ }, 2500);
374
+ }
375
+ return () => {
350
376
  window.removeEventListener(
351
377
  "agent-native:first-run-completed",
352
378
  handleFirstRunCompleted,
353
379
  );
354
- }, [initialPrompt, openInitialPromptReferenceStep]);
380
+ if (fallbackTimer !== undefined) window.clearTimeout(fallbackTimer);
381
+ };
382
+ }, [initialPrompt, onboardingPreview, openInitialPrompt]);
355
383
 
356
384
  const setDeckFilter = useCallback(
357
385
  (value: string) => {
@@ -504,7 +532,7 @@ export default function Index() {
504
532
  const handleCreateDeckWithPrompt = async (
505
533
  prompt: string,
506
534
  files: UploadedFile[],
507
- referenceSelection: Partial<NewDeckReferenceSelection> = {},
535
+ referenceSelection: NewDeckReferenceSelection = {},
508
536
  ) => {
509
537
  // Pre-flight auth check. The add-deck action returns 403 silently
510
538
  // when unauthenticated, leaving the user stuck on a deck page that
@@ -520,22 +548,14 @@ export default function Index() {
520
548
  newDeckRetryFiles,
521
549
  files,
522
550
  );
523
- const resolvedSelection = resolveNewDeckReferenceSelection({
524
- designSystemAuto: designSystemAutoRef.current,
525
- selectedDesignSystemId,
526
- defaultDesignSystemId: initialDesignSystemId,
527
- referenceDeckAuto: referenceDeckAutoRef.current,
528
- selectedReferenceDeckId,
529
- defaultReferenceDeckId: workspaceReferenceDeckId,
530
- });
531
551
  const designSystemId =
532
552
  referenceSelection.designSystemId !== undefined
533
553
  ? referenceSelection.designSystemId
534
- : resolvedSelection.designSystemId;
554
+ : selectedDesignSystemId;
535
555
  const referenceDeckId =
536
556
  referenceSelection.referenceDeckId !== undefined
537
557
  ? referenceSelection.referenceDeckId
538
- : resolvedSelection.referenceDeckId;
558
+ : selectedReferenceDeckId;
539
559
  const selectedDesignSystem = designSystemId
540
560
  ? designSystems.find((ds) => ds.id === designSystemId)
541
561
  : undefined;
@@ -608,6 +628,19 @@ export default function Index() {
608
628
  "Design system selection:",
609
629
  "- None selected. Do not apply a design system unless the user asks for one.",
610
630
  ].join("\n");
631
+ const referenceSource = referenceSelection.referenceSource;
632
+ const referenceSourceContext = referenceSource
633
+ ? [
634
+ "",
635
+ "Additional reference source selected in the reference step:",
636
+ `- ${referenceSource.kind}: ${referenceSource.value}`,
637
+ referenceSource.kind === "google-docs"
638
+ ? "Call `import-google-doc` before generating and use the returned text as source material."
639
+ : referenceSource.kind === "website"
640
+ ? "Call `import-from-url` before generating and use the returned page context as a reference."
641
+ : "Use the Figma source as the design reference. If Builder or Figma access is required, report the exact connection step instead of guessing.",
642
+ ].join("\n")
643
+ : "";
611
644
 
612
645
  const context = [
613
646
  `The user just created a new empty deck (id: "${deckId}") and wants to create a presentation or standalone visual.`,
@@ -616,6 +649,7 @@ export default function Index() {
616
649
  fileContext,
617
650
  referenceDeckContext,
618
651
  designSystemContext,
652
+ referenceSourceContext,
619
653
  "",
620
654
  "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.",
621
655
  "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.",
@@ -652,64 +686,30 @@ export default function Index() {
652
686
  [setNewDeckPromptOpen],
653
687
  );
654
688
 
655
- const handleReferenceDesignSystemSelect = useCallback(
656
- (designSystemId: string | null) => {
657
- designSystemAutoRef.current = false;
658
- setSelectedDesignSystemId(designSystemId);
659
- if (designSystemId) {
660
- rememberReference({ id: designSystemId, kind: "design-system" });
689
+ const handleReferenceSelect = useCallback(
690
+ async (selection: NewDeckReferenceSelection) => {
691
+ const pending = pendingDeck;
692
+ if (!pending) return;
693
+ if (selection.designSystemId) {
694
+ rememberReference({
695
+ id: selection.designSystemId,
696
+ kind: "design-system",
697
+ });
661
698
  }
662
- },
663
- [rememberReference],
664
- );
665
-
666
- const handleReferenceDeckSelect = useCallback(
667
- (referenceDeckId: string | null) => {
668
- referenceDeckAutoRef.current = false;
669
- setSelectedReferenceDeckId(referenceDeckId);
670
- if (referenceDeckId) {
671
- rememberReference({ id: referenceDeckId, kind: "deck" });
699
+ if (selection.referenceDeckId) {
700
+ rememberReference({ id: selection.referenceDeckId, kind: "deck" });
672
701
  }
702
+ setShowNewDeckReferenceStep(false);
703
+ setPendingDeck(null);
704
+ await handleCreateDeckWithPrompt(
705
+ pending.prompt,
706
+ pending.files,
707
+ selection,
708
+ );
673
709
  },
674
- [rememberReference],
675
- );
676
-
677
- const handleGoogleSlidesImported = useCallback(
678
- async ({ id, title }: { id: string; title: string }) => {
679
- await reloadDecks();
680
- rememberReference({ id, kind: "deck" });
681
- referenceDeckAutoRef.current = false;
682
- setSelectedReferenceDeckId(id);
683
- toast.success(t("editorToolbar.importComplete"), {
684
- description: title,
685
- });
686
- },
687
- [reloadDecks, rememberReference, t],
710
+ [handleCreateDeckWithPrompt, pendingDeck, rememberReference],
688
711
  );
689
712
 
690
- const handleReferenceContinue = useCallback(() => {
691
- const pending = pendingDeck;
692
- if (!pending) return;
693
- setShowNewDeckReferenceStep(false);
694
- setPendingDeck(null);
695
- const selection = resolveNewDeckReferenceSelection({
696
- designSystemAuto: designSystemAutoRef.current,
697
- selectedDesignSystemId,
698
- defaultDesignSystemId: initialDesignSystemId,
699
- referenceDeckAuto: referenceDeckAutoRef.current,
700
- selectedReferenceDeckId,
701
- defaultReferenceDeckId: workspaceReferenceDeckId,
702
- });
703
- void handleCreateDeckWithPrompt(pending.prompt, pending.files, selection);
704
- }, [
705
- handleCreateDeckWithPrompt,
706
- initialDesignSystemId,
707
- pendingDeck,
708
- selectedDesignSystemId,
709
- selectedReferenceDeckId,
710
- workspaceReferenceDeckId,
711
- ]);
712
-
713
713
  const handleReferenceImport = useCallback(
714
714
  async (files: File[]) => {
715
715
  const pending = pendingDeck;
@@ -723,6 +723,10 @@ export default function Index() {
723
723
  const pdfReference = uploaded.find((file) =>
724
724
  file.originalName.toLowerCase().endsWith(".pdf"),
725
725
  );
726
+ let referenceSelection: NewDeckReferenceSelection = {
727
+ designSystemId: null,
728
+ referenceDeckId: null,
729
+ };
726
730
  if (pptxReference) {
727
731
  const imported = (await callAction("import-pptx", {
728
732
  filePath: pptxReference.path,
@@ -732,8 +736,11 @@ export default function Index() {
732
736
  }
733
737
  await reloadDecks();
734
738
  rememberReference({ id: imported.id, kind: "deck" });
735
- referenceDeckAutoRef.current = false;
736
739
  setSelectedReferenceDeckId(imported.id);
740
+ referenceSelection = {
741
+ designSystemId: null,
742
+ referenceDeckId: imported.id,
743
+ };
737
744
  }
738
745
  if (!pptxReference && pdfReference) {
739
746
  const referenceDeck = createDeck(undefined, {
@@ -759,8 +766,11 @@ export default function Index() {
759
766
  }
760
767
  await reloadDecks();
761
768
  rememberReference({ id: referenceDeck.id, kind: "deck" });
762
- referenceDeckAutoRef.current = false;
763
769
  setSelectedReferenceDeckId(referenceDeck.id);
770
+ referenceSelection = {
771
+ designSystemId: null,
772
+ referenceDeckId: referenceDeck.id,
773
+ };
764
774
  } catch (error) {
765
775
  deleteDeck(referenceDeck.id);
766
776
  throw error;
@@ -770,19 +780,13 @@ export default function Index() {
770
780
  const generationFiles = uploaded.filter(
771
781
  (file) => file !== importedReference,
772
782
  );
773
- if (generationFiles.length > 0) {
774
- setPendingDeck((current) =>
775
- current
776
- ? {
777
- ...current,
778
- files: mergeUploadedFilesForRetry(
779
- current.files,
780
- generationFiles,
781
- ),
782
- }
783
- : current,
784
- );
785
- }
783
+ setShowNewDeckReferenceStep(false);
784
+ setPendingDeck(null);
785
+ await handleCreateDeckWithPrompt(
786
+ pending.prompt,
787
+ [...pending.files, ...generationFiles],
788
+ referenceSelection,
789
+ );
786
790
  } catch (error) {
787
791
  toast.error(t("editorToolbar.uploadFailed"), {
788
792
  description:
@@ -799,6 +803,7 @@ export default function Index() {
799
803
  createDeck,
800
804
  deleteDeck,
801
805
  ensureDeckPersisted,
806
+ handleCreateDeckWithPrompt,
802
807
  pendingDeck,
803
808
  reloadDecks,
804
809
  rememberReference,
@@ -950,8 +955,8 @@ export default function Index() {
950
955
  <main className="min-w-0 flex-1 overflow-y-auto px-4 py-6 sm:px-6 sm:py-10">
951
956
  {loading ? (
952
957
  <>
953
- <div className="flex items-center justify-end mb-4">
954
- <div className="h-3 w-16 rounded bg-muted animate-pulse" />
958
+ <div className="mb-4 flex items-center justify-end">
959
+ <div className="h-3 w-16 animate-pulse rounded bg-muted" />
955
960
  </div>
956
961
  <div className="deck-grid-container">
957
962
  <div className="deck-grid grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
@@ -1150,45 +1155,36 @@ export default function Index() {
1150
1155
  <NewDeckReferenceStep
1151
1156
  open={showNewDeckReferenceStep}
1152
1157
  onOpenChange={(open) => {
1153
- if (!open) handleReferenceSkip();
1158
+ if (!open) {
1159
+ const prompt = pendingDeck?.prompt;
1160
+ setShowNewDeckReferenceStep(false);
1161
+ setPendingDeck(null);
1162
+ if (prompt) {
1163
+ setNewDeckInitialPrompt({ text: prompt, key: Date.now() });
1164
+ setShowNewDeckPrompt(true);
1165
+ }
1166
+ }
1154
1167
  }}
1155
1168
  designSystems={designSystems}
1156
1169
  decks={decks}
1157
1170
  defaultDesignSystemId={initialDesignSystemId}
1158
1171
  defaultReferenceDeckId={workspaceReferenceDeckId}
1159
- selectedDesignSystemId={selectedDesignSystemId}
1160
- selectedReferenceDeckId={selectedReferenceDeckId}
1161
- onSelectDesignSystem={handleReferenceDesignSystemSelect}
1162
- onSelectReferenceDeck={handleReferenceDeckSelect}
1172
+ recentReferences={recentReferences}
1173
+ onSelect={(selection) => void handleReferenceSelect(selection)}
1163
1174
  onImport={handleReferenceImport}
1164
- onGoogleSlidesImported={handleGoogleSlidesImported}
1165
- onContinue={handleReferenceContinue}
1166
1175
  onSkip={handleReferenceSkip}
1167
1176
  importing={referenceImporting}
1168
- title={t("home.referenceReviewTitle")}
1169
- description={t("home.referenceReviewDescription")}
1177
+ title={t("home.newDeckPromptTitle")}
1170
1178
  designSystemLabel={t("home.designSystem")}
1171
1179
  referenceDeckLabel={t("home.referenceDeck")}
1172
- noneLabel={t("home.none")}
1173
- importFileLabel={t("editorToolbar.importFile")}
1180
+ chooseDeckLabel={t("home.referenceDeckPlaceholder")}
1174
1181
  importingLabel={t("editorToolbar.importing")}
1175
- continueLabel={t("designSystemSetup.continueToGeneration")}
1176
- skipLabel={t("home.skipReferences")}
1182
+ skipLabel={t("home.referenceDeckNone")}
1177
1183
  defaultSuffix={t("home.defaultSuffix")}
1178
1184
  starredLabel={t("home.referenceDeckStarredGroup")}
1179
1185
  otherDecksLabel={t("home.referenceDeckOtherGroup")}
1180
1186
  searchDecksLabel={t("root.searchDecks")}
1181
- chooseAnotherDeckLabel={t("home.chooseAnotherDeck")}
1182
- noMatchingDecksLabel={t("home.noMatchingDecks")}
1183
- googleSlidesTitle={t("home.googleSlidesReferenceTitle")}
1184
- googleSlidesConnectLabel={t("home.googleSlidesReferenceConnect")}
1185
- googleSlidesChooseLabel={t("home.googleSlidesReferenceChoose")}
1186
- googleSlidesPickingLabel={t("home.googleSlidesReferencePicking")}
1187
- googleSlidesConnectedLabel={t("home.googleSlidesReferenceConnected")}
1188
- googleSlidesUnavailableLabel={t(
1189
- "home.googleSlidesReferenceUnavailable",
1190
- )}
1191
- promptNote={pendingDeck?.prompt}
1187
+ promptSummary={pendingDeck?.prompt}
1192
1188
  />
1193
1189
 
1194
1190
  {/* Sign-in required to create a deck. Shown when an unauthenticated
@@ -1227,23 +1223,17 @@ function EmptyState({
1227
1223
  }) {
1228
1224
  const t = useT();
1229
1225
  return (
1230
- <div className="flex flex-col items-center justify-center min-h-[60vh] text-center">
1231
- <div className="w-16 h-16 rounded-2xl bg-gradient-to-br from-[#609FF8]/20 to-[#4080E0]/20 border border-[#609FF8]/20 flex items-center justify-center mb-6">
1232
- <IconStack2 className="w-7 h-7 text-[#609FF8]" />
1233
- </div>
1234
- <h2 className="text-xl font-semibold text-foreground mb-2">
1226
+ <div className="flex min-h-[60vh] flex-col items-center justify-center gap-5 text-center">
1227
+ <h2 className="text-xl font-semibold text-foreground">
1235
1228
  {t("home.emptyTitle")}
1236
1229
  </h2>
1237
- <p className="text-sm text-muted-foreground max-w-sm mb-8 leading-relaxed">
1238
- {t("home.emptyDescription")}
1239
- </p>
1240
1230
  <Button
1241
1231
  onClick={(e: React.MouseEvent<HTMLButtonElement>) =>
1242
1232
  onCreateDeck(e as React.MouseEvent<HTMLElement>)
1243
1233
  }
1244
1234
  >
1245
- <IconPlus className="w-4 h-4" />
1246
- {t("home.newDeck")}
1235
+ <IconPlus className="size-4" />
1236
+ {t("home.createFirstDeck")}
1247
1237
  </Button>
1248
1238
  </div>
1249
1239
  );
@@ -16,6 +16,7 @@ import {
16
16
  CommandMenu,
17
17
  useCommandMenuShortcut,
18
18
  } from "@agent-native/core/client/navigation";
19
+ import { registerFirstRunOnboardingExtension } from "@agent-native/core/client/onboarding";
19
20
  import { getThemeInitScript } from "@agent-native/core/client/ui";
20
21
  import { IconHierarchy2, IconSun, IconMoon } from "@tabler/icons-react";
21
22
  import { useQueryClient } from "@tanstack/react-query";
@@ -33,6 +34,7 @@ import {
33
34
  import type { LinksFunction } from "react-router";
34
35
 
35
36
  import { Layout as AppLayout } from "@/components/layout/Layout";
37
+ import { FirstDeckOnboardingFlow } from "@/components/onboarding/FirstDeckOnboardingFlow";
36
38
  import { AppToolkitProvider } from "@/components/ui/toolkit-provider";
37
39
  import { DeckProvider } from "@/context/DeckContext";
38
40
  import { useNavigationState } from "@/hooks/use-navigation-state";
@@ -42,6 +44,12 @@ import changelog from "../CHANGELOG.md?raw";
42
44
  import { i18nCatalog } from "./i18n";
43
45
 
44
46
  import stylesheet from "./global.css?url";
47
+
48
+ registerFirstRunOnboardingExtension({
49
+ id: "slides-first-deck",
50
+ component: FirstDeckOnboardingFlow,
51
+ });
52
+
45
53
  configureTracking({
46
54
  getDefaultProps: (_name, properties) => ({
47
55
  ...properties,
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Deck browsing keeps All and Mine in the header with a tighter grid layout.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ New deck creation now flows through a clean full-screen reference step
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Website imports now include linked stylesheets for more accurate brand colors and fonts
@@ -21,6 +21,7 @@ import { and, eq } from "drizzle-orm";
21
21
  import { getDeckUrl } from "../../actions/_app-url.js";
22
22
  import { SLIDES_USER_PREFS_KEY } from "../../shared/slides-user-prefs.js";
23
23
  import { getDb, schema } from "../db/index.js";
24
+ import { SLIDES_DECK_COMMENT_EMAIL_ID } from "./emails.js";
24
25
 
25
26
  /**
26
27
  * `deck-missing` stays distinct from `no-recipients`: one means the deck could
@@ -99,6 +100,41 @@ async function threadParticipants(
99
100
  return rows.map((row) => row.authorEmail);
100
101
  }
101
102
 
103
+ export function renderDeckCommentEmail({
104
+ actor,
105
+ title,
106
+ url,
107
+ content,
108
+ isReply,
109
+ }: {
110
+ actor: string;
111
+ title: string;
112
+ url: string;
113
+ content: string;
114
+ isReply: boolean;
115
+ }) {
116
+ return {
117
+ subject: isReply
118
+ ? `${actor} replied to a comment on "${title}"`
119
+ : `${actor} commented on "${title}"`,
120
+ ...renderEmail({
121
+ preheader: isReply
122
+ ? `${actor} replied to a comment on ${title}.`
123
+ : `${actor} commented on ${title}.`,
124
+ heading: isReply ? "New reply on your deck" : "New comment",
125
+ paragraphs: [
126
+ isReply
127
+ ? `${emailStrong(actor)} replied in a comment thread on ${emailStrong(title)}.`
128
+ : `${emailStrong(actor)} commented on ${emailStrong(title)}.`,
129
+ `"${excerpt(content)}"`,
130
+ ],
131
+ cta: { label: "Open deck", url },
132
+ footer:
133
+ "You received this because you own or participated in this thread. Turn these off in Slides settings.",
134
+ }),
135
+ };
136
+ }
137
+
102
138
  export async function notifyDeckComment(input: {
103
139
  deckId: string;
104
140
  slideId: string;
@@ -153,29 +189,16 @@ async function deliverDeckCommentEmails(input: {
153
189
  preferenceKey: SLIDES_USER_PREFS_KEY,
154
190
  logLabel: LOG_LABEL,
155
191
  send: async (to) => {
156
- const { html, text } = renderEmail({
157
- preheader: input.isReply
158
- ? `${actor} replied to a comment on ${deck.title}.`
159
- : `${actor} commented on ${deck.title}.`,
160
- heading: input.isReply ? "New reply on your deck" : "New comment",
161
- paragraphs: [
162
- input.isReply
163
- ? `${emailStrong(actor)} replied in a comment thread on ${emailStrong(deck.title)}.`
164
- : `${emailStrong(actor)} commented on ${emailStrong(deck.title)}.`,
165
- `"${excerpt(input.content)}"`,
166
- ],
167
- cta: { label: "Open deck", url },
168
- footer:
169
- "You received this because you own or participated in this thread. Turn these off in Slides settings.",
170
- });
171
-
172
192
  await sendEmail({
193
+ ...renderDeckCommentEmail({
194
+ actor,
195
+ title: deck.title,
196
+ url,
197
+ content: input.content,
198
+ isReply: input.isReply,
199
+ }),
173
200
  to,
174
- subject: input.isReply
175
- ? `${actor} replied to a comment on "${deck.title}"`
176
- : `${actor} commented on "${deck.title}"`,
177
- html,
178
- text,
201
+ templateId: SLIDES_DECK_COMMENT_EMAIL_ID,
179
202
  });
180
203
  },
181
204
  });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Catalog entries for the transactional emails Slides sends.
3
+ *
4
+ * Registered from `server/plugins/transactional-emails.ts` so Dispatch can list
5
+ * and preview them without the app having sent anything yet.
6
+ */
7
+
8
+ import { defineTransactionalEmail } from "@agent-native/core/email-catalog";
9
+
10
+ import { renderDeckCommentEmail } from "./comment-notifications.js";
11
+
12
+ export const SLIDES_DECK_COMMENT_EMAIL_ID = "slides.deck-comment";
13
+
14
+ let registered = false;
15
+
16
+ export function registerSlidesEmails(): void {
17
+ if (registered) return;
18
+ registered = true;
19
+
20
+ defineTransactionalEmail({
21
+ id: SLIDES_DECK_COMMENT_EMAIL_ID,
22
+ name: "Deck comment",
23
+ trigger:
24
+ "Someone posts a comment or a thread reply on a slide. Subject and copy differ slightly for a reply; both send under this id.",
25
+ recipientLabel: "Deck owner and authors",
26
+ recipient:
27
+ "The deck owner, plus every prior author in the thread when the new comment is a reply. The list is re-checked against the deck's live ACL and filtered by each user's `emailNotifications` preference; the comment's own author never receives it.",
28
+ senderLabel: "Default sender",
29
+ sender:
30
+ "The configured default sender. This call site sets no `from`, `fromName`, `replyTo`, or `appSender`.",
31
+ preview: () =>
32
+ renderDeckCommentEmail({
33
+ actor: "Sam Rivera",
34
+ title: "Series A narrative",
35
+ url: "https://example.com/decks/deck_sample?slide=3",
36
+ content: "Slide 3 needs the updated revenue chart before Thursday.",
37
+ isReply: false,
38
+ }),
39
+ });
40
+ }
@@ -0,0 +1,5 @@
1
+ import { registerSlidesEmails } from "../lib/emails.js";
2
+
3
+ export default function registerTransactionalEmails(): void {
4
+ registerSlidesEmails();
5
+ }
@@ -16,5 +16,6 @@
16
16
  */
17
17
  export * from "./types.js";
18
18
  export * from "./brand-signals.js";
19
+ export * from "./tokens.js";
19
20
  export { extractDesignTokensFromUrl, validateUrl, parseOwnerRepo, fetchGitHubJson, fetchGitHubJsonResult, fetchGitHubRaw, parseTailwindConfig, parseCss, detectStylingFramework, createCodeAnalysisState, analyzeCodeFile, analyzeCssFile, analyzeTailwindConfig, analyzeJsonTheme, analyzePackageJson, analyzeThemeSourceFile, addFont, extractCssVars, extractCodeColors, extractCodeFonts, extractDocumentColors, extractDocumentFonts, classifyFile, suggestionsForType, unique, MAX_FILES, MAX_FILE_SIZE, FETCH_TIMEOUT, ROOT_PATTERNS, SECONDARY_PATHS, CODE_MAX_FILES, CODE_MAX_TOTAL_BYTES, } from "../server/design-token-utils.js";
20
21
  export type { ContentType, ParsedCss, ParsedTailwindConfig, CodeAnalysisState, UrlExtractionResult, GitHubFetchOptions, GitHubJsonResult, } from "../server/design-token-utils.js";
@@ -16,6 +16,7 @@
16
16
  */
17
17
  export * from "./types.js";
18
18
  export * from "./brand-signals.js";
19
+ export * from "./tokens.js";
19
20
  // Import-source parsing + token extraction (Tailwind/CSS/GitHub/URL/document/
20
21
  // code analysis). These already power the import-* actions across templates.
21
22
  export {