@agent-native/core 0.76.10 → 0.76.11

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 (95) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +6 -0
  3. package/corpus/core/docs/content/locales/ar-SA/actions.md +7 -7
  4. package/corpus/core/docs/content/locales/ar-SA/template-plan.md +6 -4
  5. package/corpus/core/docs/content/locales/de-DE/actions.md +9 -9
  6. package/corpus/core/docs/content/locales/de-DE/template-plan.md +6 -4
  7. package/corpus/core/docs/content/locales/es-ES/actions.md +9 -9
  8. package/corpus/core/docs/content/locales/es-ES/template-plan.md +6 -4
  9. package/corpus/core/docs/content/locales/fr-FR/actions.md +9 -9
  10. package/corpus/core/docs/content/locales/fr-FR/template-plan.md +6 -4
  11. package/corpus/core/docs/content/locales/hi-IN/actions.md +9 -9
  12. package/corpus/core/docs/content/locales/hi-IN/template-plan.md +4 -4
  13. package/corpus/core/docs/content/locales/ja-JP/actions.md +9 -9
  14. package/corpus/core/docs/content/locales/ja-JP/template-plan.md +4 -4
  15. package/corpus/core/docs/content/locales/ko-KR/actions.md +9 -9
  16. package/corpus/core/docs/content/locales/ko-KR/template-plan.md +4 -4
  17. package/corpus/core/docs/content/locales/pt-BR/actions.md +9 -9
  18. package/corpus/core/docs/content/locales/pt-BR/template-plan.md +6 -4
  19. package/corpus/core/docs/content/locales/zh-CN/actions.md +9 -9
  20. package/corpus/core/docs/content/locales/zh-CN/template-plan.md +4 -4
  21. package/corpus/core/package.json +1 -1
  22. package/corpus/core/src/client/ErrorBoundary.tsx +151 -10
  23. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +51 -31
  24. package/corpus/templates/analytics/app/components/layout/DashboardHeader.tsx +26 -16
  25. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -2
  26. package/corpus/templates/analytics/app/components/layout/HeaderActions.tsx +8 -1
  27. package/corpus/templates/analytics/app/components/layout/Layout.tsx +2 -5
  28. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +172 -107
  29. package/corpus/templates/analytics/app/i18n-data.ts +1076 -5
  30. package/corpus/templates/analytics/app/pages/Settings.tsx +2 -2
  31. package/corpus/templates/analytics/changelog/2026-06-24-analytics-navigation-command-palette-and-settings-now-honor-.md +6 -0
  32. package/corpus/templates/assets/app/components/layout/Header.tsx +0 -2
  33. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +0 -2
  34. package/corpus/templates/calendar/app/pages/CalendarView.tsx +0 -2
  35. package/corpus/templates/chat/app/components/layout/Header.tsx +1 -6
  36. package/corpus/templates/clips/app/components/layout/Header.tsx +1 -6
  37. package/corpus/templates/clips/app/hooks/use-library.ts +2 -0
  38. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +3 -1
  39. package/corpus/templates/clips/changelog/2026-06-24-archive-and-spaces-navigation-is-more-resilient-when-sidebar.md +6 -0
  40. package/corpus/templates/content/app/components/layout/Header.tsx +0 -2
  41. package/corpus/templates/design/app/components/layout/Header.tsx +1 -6
  42. package/corpus/templates/forms/app/components/layout/Header.tsx +1 -6
  43. package/corpus/templates/macros/app/components/layout/Header.tsx +1 -6
  44. package/corpus/templates/mail/app/components/email/CodeBlockLangPicker.tsx +4 -2
  45. package/corpus/templates/mail/app/components/email/ComposeBubbleToolbar.tsx +18 -12
  46. package/corpus/templates/mail/app/components/email/ComposeEditor.tsx +9 -5
  47. package/corpus/templates/mail/app/components/email/ComposeModal.tsx +36 -19
  48. package/corpus/templates/mail/app/components/email/EmailList.tsx +59 -55
  49. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +7 -3
  50. package/corpus/templates/mail/app/components/email/EmailThread.tsx +25 -16
  51. package/corpus/templates/mail/app/components/email/InlineReplyComposer.tsx +25 -19
  52. package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +32 -17
  53. package/corpus/templates/mail/app/components/email/RecipientInput.tsx +26 -10
  54. package/corpus/templates/mail/app/components/email/SendLaterButton.tsx +5 -3
  55. package/corpus/templates/mail/app/components/email/SnoozeModal.tsx +4 -2
  56. package/corpus/templates/mail/app/components/email/SnoozePopover.tsx +9 -5
  57. package/corpus/templates/mail/app/components/email/extensions/ComposeImageBlock.tsx +4 -2
  58. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +168 -86
  59. package/corpus/templates/mail/app/components/layout/SearchBar.tsx +4 -2
  60. package/corpus/templates/mail/app/i18n/ar-SA.ts +212 -0
  61. package/corpus/templates/mail/app/i18n/de-DE.ts +212 -0
  62. package/corpus/templates/mail/app/i18n/en-US.ts +212 -0
  63. package/corpus/templates/mail/app/i18n/es-ES.ts +212 -0
  64. package/corpus/templates/mail/app/i18n/fr-FR.ts +212 -0
  65. package/corpus/templates/mail/app/i18n/hi-IN.ts +212 -0
  66. package/corpus/templates/mail/app/i18n/ja-JP.ts +212 -0
  67. package/corpus/templates/mail/app/i18n/ko-KR.ts +212 -0
  68. package/corpus/templates/mail/app/i18n/pt-BR.ts +212 -0
  69. package/corpus/templates/mail/app/i18n/zh-CN.ts +211 -0
  70. package/corpus/templates/mail/changelog/2026-06-24-interface-language-support-now-covers-more-mail-controls-and.md +6 -0
  71. package/corpus/templates/plan/app/components/layout/Header.tsx +1 -6
  72. package/corpus/templates/slides/app/components/layout/Header.tsx +1 -6
  73. package/corpus/templates/videos/app/components/layout/Header.tsx +1 -6
  74. package/dist/client/ErrorBoundary.d.ts.map +1 -1
  75. package/dist/client/ErrorBoundary.js +128 -7
  76. package/dist/client/ErrorBoundary.js.map +1 -1
  77. package/docs/content/locales/ar-SA/actions.md +7 -7
  78. package/docs/content/locales/ar-SA/template-plan.md +6 -4
  79. package/docs/content/locales/de-DE/actions.md +9 -9
  80. package/docs/content/locales/de-DE/template-plan.md +6 -4
  81. package/docs/content/locales/es-ES/actions.md +9 -9
  82. package/docs/content/locales/es-ES/template-plan.md +6 -4
  83. package/docs/content/locales/fr-FR/actions.md +9 -9
  84. package/docs/content/locales/fr-FR/template-plan.md +6 -4
  85. package/docs/content/locales/hi-IN/actions.md +9 -9
  86. package/docs/content/locales/hi-IN/template-plan.md +4 -4
  87. package/docs/content/locales/ja-JP/actions.md +9 -9
  88. package/docs/content/locales/ja-JP/template-plan.md +4 -4
  89. package/docs/content/locales/ko-KR/actions.md +9 -9
  90. package/docs/content/locales/ko-KR/template-plan.md +4 -4
  91. package/docs/content/locales/pt-BR/actions.md +9 -9
  92. package/docs/content/locales/pt-BR/template-plan.md +6 -4
  93. package/docs/content/locales/zh-CN/actions.md +9 -9
  94. package/docs/content/locales/zh-CN/template-plan.md +4 -4
  95. package/package.json +1 -1
@@ -36,7 +36,7 @@ import {
36
36
  } from "@/hooks/use-emails";
37
37
  import { useAliases } from "@/hooks/use-aliases";
38
38
  import { expandAliasTokens } from "@/lib/alias-utils";
39
- import { useAgentChatGenerating } from "@agent-native/core/client";
39
+ import { useAgentChatGenerating, useT } from "@agent-native/core/client";
40
40
  import { toast } from "sonner";
41
41
  import type { ComposeState, EmailMessage } from "@shared/types";
42
42
  import {
@@ -89,6 +89,7 @@ export const InlineReplyComposer = forwardRef<
89
89
  },
90
90
  ref,
91
91
  ) {
92
+ const t = useT();
92
93
  const [showQuoted, setShowQuoted] = useState(false);
93
94
  const [generateOpen, setGenerateOpen] = useState(false);
94
95
  const [generatePrompt, setGeneratePrompt] = useState("");
@@ -452,7 +453,7 @@ export const InlineReplyComposer = forwardRef<
452
453
  <>
453
454
  <div className="flex items-center justify-between px-4 pt-3 pb-1">
454
455
  <span className="text-[13px] font-semibold text-green-400">
455
- Forward
456
+ {t("mail.compose.forward")}
456
457
  </span>
457
458
  <Tooltip>
458
459
  <TooltipTrigger asChild>
@@ -463,7 +464,7 @@ export const InlineReplyComposer = forwardRef<
463
464
  <IconExternalLink className="h-3.5 w-3.5" />
464
465
  </button>
465
466
  </TooltipTrigger>
466
- <TooltipContent>Pop out to compose window</TooltipContent>
467
+ <TooltipContent>{t("mail.compose.popOut")}</TooltipContent>
467
468
  </Tooltip>
468
469
  </div>
469
470
  {recipientFields}
@@ -473,10 +474,10 @@ export const InlineReplyComposer = forwardRef<
473
474
  <div className="flex items-center justify-between px-4 pt-3 pb-1">
474
475
  <div className="flex items-center gap-1.5 min-w-0">
475
476
  <span className="text-[13px] font-semibold text-green-400">
476
- Reply
477
+ {t("mail.compose.reply")}
477
478
  </span>
478
479
  <span className="text-[13px] text-muted-foreground/70 truncate">
479
- to {recipientDisplay}
480
+ {t("mail.compose.replyTo", { recipient: recipientDisplay })}
480
481
  </span>
481
482
  </div>
482
483
  <Tooltip>
@@ -488,7 +489,7 @@ export const InlineReplyComposer = forwardRef<
488
489
  <IconExternalLink className="h-3.5 w-3.5" />
489
490
  </button>
490
491
  </TooltipTrigger>
491
- <TooltipContent>Pop out to compose window</TooltipContent>
492
+ <TooltipContent>{t("mail.compose.popOut")}</TooltipContent>
492
493
  </Tooltip>
493
494
  </div>
494
495
  {recipientFields}
@@ -540,7 +541,11 @@ export const InlineReplyComposer = forwardRef<
540
541
  <>
541
542
  <button
542
543
  type="button"
543
- aria-label={showQuoted ? "Hide quoted text" : "Show quoted text"}
544
+ aria-label={
545
+ showQuoted
546
+ ? t("mail.thread.hideQuotedText")
547
+ : t("mail.thread.showQuotedText")
548
+ }
544
549
  onClick={() => setShowQuoted(!showQuoted)}
545
550
  className="mt-1 inline-flex h-6 w-6 items-center justify-center rounded text-muted-foreground/50 transition-colors hover:bg-accent hover:text-muted-foreground"
546
551
  >
@@ -577,7 +582,7 @@ export const InlineReplyComposer = forwardRef<
577
582
  <IconBold className="h-3.5 w-3.5" />
578
583
  </Button>
579
584
  </TooltipTrigger>
580
- <TooltipContent>Bold</TooltipContent>
585
+ <TooltipContent>{t("mail.compose.bold")}</TooltipContent>
581
586
  </Tooltip>
582
587
  <Tooltip>
583
588
  <TooltipTrigger asChild>
@@ -590,7 +595,7 @@ export const InlineReplyComposer = forwardRef<
590
595
  <IconItalic className="h-3.5 w-3.5" />
591
596
  </Button>
592
597
  </TooltipTrigger>
593
- <TooltipContent>Italic</TooltipContent>
598
+ <TooltipContent>{t("mail.compose.italic")}</TooltipContent>
594
599
  </Tooltip>
595
600
  <Tooltip>
596
601
  <TooltipTrigger asChild>
@@ -603,7 +608,7 @@ export const InlineReplyComposer = forwardRef<
603
608
  <IconLink className="h-3.5 w-3.5" />
604
609
  </Button>
605
610
  </TooltipTrigger>
606
- <TooltipContent>Insert link</TooltipContent>
611
+ <TooltipContent>{t("mail.compose.insertLink")}</TooltipContent>
607
612
  </Tooltip>
608
613
  <Tooltip>
609
614
  <TooltipTrigger asChild>
@@ -616,7 +621,7 @@ export const InlineReplyComposer = forwardRef<
616
621
  <IconPaperclip className="h-3.5 w-3.5" />
617
622
  </Button>
618
623
  </TooltipTrigger>
619
- <TooltipContent>Attach file</TooltipContent>
624
+ <TooltipContent>{t("mail.compose.attachFile")}</TooltipContent>
620
625
  </Tooltip>
621
626
 
622
627
  <div className="mx-1 h-4 w-px bg-border" />
@@ -624,7 +629,7 @@ export const InlineReplyComposer = forwardRef<
624
629
  {isGenerating ? (
625
630
  <div className="flex items-center gap-1.5 px-2 text-xs text-muted-foreground">
626
631
  <IconLoader2 className="h-3.5 w-3.5 animate-spin" />
627
- <span>Generating…</span>
632
+ <span>{t("mail.compose.generating")}</span>
628
633
  </div>
629
634
  ) : (
630
635
  <Popover open={generateOpen} onOpenChange={setGenerateOpen}>
@@ -634,13 +639,13 @@ export const InlineReplyComposer = forwardRef<
634
639
  size="sm"
635
640
  className="h-7 gap-1.5 px-2 text-xs"
636
641
  >
637
- Generate
642
+ {t("mail.compose.generate")}
638
643
  </Button>
639
644
  </PopoverTrigger>
640
645
  <PopoverContent side="top" align="start" className="w-80 p-3">
641
646
  <div className="flex flex-col gap-2">
642
647
  <label className="text-xs font-medium text-muted-foreground">
643
- What should the agent write?
648
+ {t("mail.compose.agentPromptLabel")}
644
649
  </label>
645
650
  <textarea
646
651
  value={generatePrompt}
@@ -655,13 +660,14 @@ export const InlineReplyComposer = forwardRef<
655
660
  setGenerateOpen(false);
656
661
  }
657
662
  }}
658
- placeholder="e.g. Write a polite follow-up..."
663
+ placeholder={t("mail.compose.agentPromptPlaceholder")}
659
664
  className="min-h-[60px] w-full resize-none rounded-md border bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground focus:ring-1 focus:ring-ring"
660
665
  autoFocus
661
666
  />
662
667
  <div className="flex items-center justify-between">
663
668
  <span className="text-[10px] text-muted-foreground">
664
- <kbd className="kbd-hint">↵</kbd> to submit
669
+ <kbd className="kbd-hint">↵</kbd>{" "}
670
+ {t("mail.compose.toSubmit")}
665
671
  </span>
666
672
  <Button
667
673
  size="sm"
@@ -669,7 +675,7 @@ export const InlineReplyComposer = forwardRef<
669
675
  disabled={!generatePrompt.trim()}
670
676
  className="h-7 gap-1.5 px-3 text-xs"
671
677
  >
672
- Generate
678
+ {t("mail.compose.generate")}
673
679
  </Button>
674
680
  </div>
675
681
  </div>
@@ -690,7 +696,7 @@ export const InlineReplyComposer = forwardRef<
690
696
  <IconTrash className="h-3.5 w-3.5" />
691
697
  </Button>
692
698
  </TooltipTrigger>
693
- <TooltipContent>Discard draft</TooltipContent>
699
+ <TooltipContent>{t("mail.compose.discardDraft")}</TooltipContent>
694
700
  </Tooltip>
695
701
  <Button
696
702
  size="sm"
@@ -699,7 +705,7 @@ export const InlineReplyComposer = forwardRef<
699
705
  className="gap-1.5"
700
706
  >
701
707
  <IconSend className="h-3.5 w-3.5" />
702
- Send
708
+ {t("mail.compose.send")}
703
709
  </Button>
704
710
  </div>
705
711
  </div>
@@ -19,7 +19,7 @@ import {
19
19
  DropdownMenuContent,
20
20
  DropdownMenuItem,
21
21
  } from "@/components/ui/dropdown-menu";
22
- import { useSendToAgentChat } from "@agent-native/core/client";
22
+ import { useSendToAgentChat, useT } from "@agent-native/core/client";
23
23
  import { ExtensionSlot } from "@agent-native/core/client/extensions";
24
24
  import {
25
25
  useIntegration,
@@ -330,6 +330,7 @@ function IntegrationSetup() {
330
330
  }
331
331
 
332
332
  function AddIntegrationButton() {
333
+ const t = useT();
333
334
  const [open, setOpen] = useState(false);
334
335
  const [value, setValue] = useState("");
335
336
  const textareaRef = useRef<HTMLTextAreaElement>(null);
@@ -365,7 +366,7 @@ function AddIntegrationButton() {
365
366
  <div className="h-7 w-7 rounded-md border border-dashed border-border/40 flex items-center justify-center shrink-0">
366
367
  <IconPlus className="h-3 w-3" />
367
368
  </div>
368
- <span>Add integration</span>
369
+ <span>{t("mail.integrations.addIntegration")}</span>
369
370
  </button>
370
371
  </PopoverTrigger>
371
372
  <PopoverContent
@@ -377,7 +378,7 @@ function AddIntegrationButton() {
377
378
  >
378
379
  <div className="px-3.5 pt-3 pb-1.5">
379
380
  <span className="text-[12px] font-medium text-foreground/80">
380
- New integration
381
+ {t("mail.integrations.newIntegration")}
381
382
  </span>
382
383
  </div>
383
384
 
@@ -397,7 +398,7 @@ function AddIntegrationButton() {
397
398
  handleSubmit();
398
399
  }
399
400
  }}
400
- placeholder="e.g. Salesforce CRM — show deal stage and recent activity for the contact"
401
+ placeholder={t("mail.integrations.newIntegrationPlaceholder")}
401
402
  className="w-full bg-transparent text-[12px] text-foreground/90 placeholder:text-muted-foreground/30 outline-none resize-none"
402
403
  rows={3}
403
404
  style={{ maxHeight: "200px" }}
@@ -407,7 +408,7 @@ function AddIntegrationButton() {
407
408
  <div className="px-3.5 py-2 flex items-center justify-end gap-2 border-t border-border/30">
408
409
  <span className="text-[11px] text-muted-foreground/60">
409
410
  {/Mac|iPhone|iPad/.test(navigator.userAgent) ? "⌘" : "Ctrl"}
410
- +Enter to submit
411
+ +Enter {t("mail.compose.toSubmit")}
411
412
  </span>
412
413
  <Tooltip>
413
414
  <TooltipTrigger asChild>
@@ -420,12 +421,16 @@ function AddIntegrationButton() {
420
421
  ? "bg-primary hover:bg-primary/90 text-primary-foreground"
421
422
  : "bg-muted/50 text-muted-foreground/30 cursor-not-allowed",
422
423
  )}
423
- aria-label="Submit"
424
+ aria-label={t("mail.integrations.submit")}
424
425
  >
425
426
  <IconArrowUp className="w-4 h-4" />
426
427
  </button>
427
428
  </TooltipTrigger>
428
- <TooltipContent>{`Submit (${/Mac|iPhone|iPad/.test(navigator.userAgent) ? "⌘" : "Ctrl"}+Enter)`}</TooltipContent>
429
+ <TooltipContent>
430
+ {t("mail.integrations.submitShortcut", {
431
+ shortcut: `${/Mac|iPhone|iPad/.test(navigator.userAgent) ? "⌘" : "Ctrl"}+Enter`,
432
+ })}
433
+ </TooltipContent>
429
434
  </Tooltip>
430
435
  </div>
431
436
  </PopoverContent>
@@ -443,6 +448,7 @@ function IntegrationRow({
443
448
  connected: boolean;
444
449
  onConfigure: () => void;
445
450
  }) {
451
+ const t = useT();
446
452
  const { disconnect } = useIntegration(def.id);
447
453
 
448
454
  return (
@@ -470,20 +476,20 @@ function IntegrationRow({
470
476
  </button>
471
477
  </DropdownMenuTrigger>
472
478
  </TooltipTrigger>
473
- <TooltipContent>Settings</TooltipContent>
479
+ <TooltipContent>{t("mail.toolbar.settings")}</TooltipContent>
474
480
  </Tooltip>
475
481
  <DropdownMenuContent align="end" className="w-36">
476
482
  <DropdownMenuItem
477
483
  onClick={() => onConfigure()}
478
484
  className="text-[12px] text-foreground/70"
479
485
  >
480
- Update key
486
+ {t("mail.integrations.updateKey")}
481
487
  </DropdownMenuItem>
482
488
  <DropdownMenuItem
483
489
  onClick={() => disconnect.mutate()}
484
490
  className="text-[12px] text-red-400/80"
485
491
  >
486
- Disconnect
492
+ {t("mail.integrations.disconnect")}
487
493
  </DropdownMenuItem>
488
494
  </DropdownMenuContent>
489
495
  </DropdownMenu>
@@ -493,7 +499,7 @@ function IntegrationRow({
493
499
  onClick={onConfigure}
494
500
  className="shrink-0 text-[11px] text-primary hover:text-primary/90 font-medium transition-colors"
495
501
  >
496
- Connect
502
+ {t("mail.integrations.connect")}
497
503
  </button>
498
504
  )}
499
505
  </div>
@@ -507,6 +513,7 @@ function IntegrationKeyEntry({
507
513
  def: IntegrationDef;
508
514
  onBack: () => void;
509
515
  }) {
516
+ const t = useT();
510
517
  const [apiKey, setApiKey] = useState("");
511
518
  const { connect } = useIntegration(def.id);
512
519
  const errorMessage =
@@ -550,7 +557,9 @@ function IntegrationKeyEntry({
550
557
  disabled={!apiKey.trim() || connect.isPending}
551
558
  className="shrink-0 rounded-md bg-primary px-2.5 py-1 text-[11px] font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50 transition-colors"
552
559
  >
553
- {connect.isPending ? "Checking..." : "Save"}
560
+ {connect.isPending
561
+ ? t("mail.integrations.checking")
562
+ : t("mail.integrations.save")}
554
563
  </button>
555
564
  </div>
556
565
 
@@ -561,7 +570,7 @@ function IntegrationKeyEntry({
561
570
  {/* Instructions always visible */}
562
571
  <div className="rounded-md bg-accent/30 px-2.5 py-2">
563
572
  <p className="text-[11px] text-muted-foreground/70 mb-1.5">
564
- To get your API key:
573
+ {t("mail.integrations.getApiKey")}
565
574
  </p>
566
575
  <ol className="text-[11px] text-muted-foreground/50 space-y-0.5 list-decimal ps-3 mb-1.5">
567
576
  {def.helpSteps.map((step, i) => (
@@ -574,7 +583,10 @@ function IntegrationKeyEntry({
574
583
  rel="noopener noreferrer"
575
584
  className="text-[11px] text-primary/80 hover:text-primary hover:underline transition-colors"
576
585
  >
577
- Open {def.name} Settings &rarr;
586
+ {t("mail.integrations.openProviderSettings", {
587
+ provider: def.name,
588
+ })}{" "}
589
+ &rarr;
578
590
  </a>
579
591
  </div>
580
592
  </div>
@@ -817,7 +829,7 @@ function ApolloSection({ email }: { email: string }) {
817
829
  rel="noopener noreferrer"
818
830
  className="text-[11px] text-muted-foreground/60 hover:text-foreground transition-colors"
819
831
  >
820
- LinkedIn
832
+ LinkedIn{/* i18n-ignore */}
821
833
  </a>
822
834
  )}
823
835
  {safeExternalHref(person.twitter_url) && (
@@ -837,7 +849,7 @@ function ApolloSection({ email }: { email: string }) {
837
849
  rel="noopener noreferrer"
838
850
  className="text-[11px] text-muted-foreground/60 hover:text-foreground transition-colors"
839
851
  >
840
- GitHub
852
+ GitHub{/* i18n-ignore */}
841
853
  </a>
842
854
  )}
843
855
  {safeExternalHref(person.organization?.website_url) && (
@@ -932,7 +944,10 @@ function HubSpotSection({ email }: { email: string }) {
932
944
  <>
933
945
  <div className="h-px bg-border/30 mx-4" />
934
946
  <div className="px-4 py-3">
935
- <SectionHeader logo={INTEGRATIONS[1].logo} label="HubSpot" />
947
+ <SectionHeader
948
+ logo={INTEGRATIONS[1].logo}
949
+ label={"HubSpot" /* i18n-ignore */}
950
+ />
936
951
 
937
952
  {name && (
938
953
  <p className="text-[12px] text-foreground/80 font-medium">{name}</p>
@@ -37,6 +37,7 @@ import {
37
37
  TooltipContent,
38
38
  TooltipTrigger,
39
39
  } from "@/components/ui/tooltip";
40
+ import { useT } from "@agent-native/core/client";
40
41
 
41
42
  /** Which header field a RecipientInput represents — used for cross-field drag. */
42
43
  export type RecipientField = "to" | "cc" | "bcc";
@@ -144,6 +145,7 @@ function AliasPopover({
144
145
  onClose,
145
146
  onExpand,
146
147
  }: AliasPopoverProps) {
148
+ const t = useT();
147
149
  const navigate = useNavigate();
148
150
  const panelRef = useRef<HTMLDivElement>(null);
149
151
  const [pos, setPos] = useState({ top: 0, left: 0 });
@@ -194,7 +196,9 @@ function AliasPopover({
194
196
  {alias.name}
195
197
  </span>
196
198
  <span className="rounded-full bg-indigo-500/15 px-2 py-0.5 text-[11px] font-medium text-indigo-300">
197
- {alias.emails.length} recipients
199
+ {t("mail.recipients.recipientCount", {
200
+ count: alias.emails.length,
201
+ })}
198
202
  </span>
199
203
  <Tooltip>
200
204
  <TooltipTrigger asChild>
@@ -206,7 +210,7 @@ function AliasPopover({
206
210
  <IconPencil className="size-3.5" />
207
211
  </button>
208
212
  </TooltipTrigger>
209
- <TooltipContent>Edit alias</TooltipContent>
213
+ <TooltipContent>{t("mail.recipients.editAlias")}</TooltipContent>
210
214
  </Tooltip>
211
215
  <Tooltip>
212
216
  <TooltipTrigger asChild>
@@ -218,7 +222,9 @@ function AliasPopover({
218
222
  <IconArrowsDiagonal className="size-3.5" />
219
223
  </button>
220
224
  </TooltipTrigger>
221
- <TooltipContent>Expand to individual emails</TooltipContent>
225
+ <TooltipContent>
226
+ {t("mail.recipients.expandToIndividualEmails")}
227
+ </TooltipContent>
222
228
  </Tooltip>
223
229
  </div>
224
230
  {/* Email list */}
@@ -250,6 +256,7 @@ interface SaveAliasModalProps {
250
256
  }
251
257
 
252
258
  function SaveAliasModal({ emails, onClose }: SaveAliasModalProps) {
259
+ const t = useT();
253
260
  const [name, setName] = useState("");
254
261
  const createAlias = useCreateAlias();
255
262
 
@@ -263,9 +270,13 @@ function SaveAliasModal({ emails, onClose }: SaveAliasModalProps) {
263
270
  <Dialog open onOpenChange={(open) => !open && onClose()}>
264
271
  <DialogContent className="max-w-sm">
265
272
  <DialogHeader>
266
- <DialogTitle className="text-[14px]">Save as alias</DialogTitle>
273
+ <DialogTitle className="text-[14px]">
274
+ {t("mail.recipients.saveAsAlias")}
275
+ </DialogTitle>
267
276
  <DialogDescription className="text-[12px]">
268
- Create a reusable group of {emails.length} recipients
277
+ {t("mail.recipients.createReusableGroup", {
278
+ count: emails.length,
279
+ })}
269
280
  </DialogDescription>
270
281
  </DialogHeader>
271
282
  <Input
@@ -277,7 +288,7 @@ function SaveAliasModal({ emails, onClose }: SaveAliasModalProps) {
277
288
  if (e.key === "Enter") handleSave();
278
289
  e.stopPropagation();
279
290
  }}
280
- placeholder="Alias name"
291
+ placeholder={t("mail.recipients.aliasName")}
281
292
  />
282
293
  <DialogFooter>
283
294
  <button
@@ -285,7 +296,7 @@ function SaveAliasModal({ emails, onClose }: SaveAliasModalProps) {
285
296
  onClick={onClose}
286
297
  className="rounded-lg px-3 py-1.5 text-[13px] text-muted-foreground hover:bg-accent hover:text-foreground"
287
298
  >
288
- Cancel
299
+ {t("mail.compose.cancel")}
289
300
  </button>
290
301
  <button
291
302
  type="button"
@@ -293,7 +304,9 @@ function SaveAliasModal({ emails, onClose }: SaveAliasModalProps) {
293
304
  disabled={!name.trim() || createAlias.isPending}
294
305
  className="rounded-lg bg-indigo-600 px-3 py-1.5 text-[13px] font-medium text-white hover:opacity-90 disabled:opacity-40"
295
306
  >
296
- {createAlias.isPending ? "Saving…" : "Save"}
307
+ {createAlias.isPending
308
+ ? t("mail.recipients.saving")
309
+ : t("mail.integrations.save")}
297
310
  </button>
298
311
  </DialogFooter>
299
312
  </DialogContent>
@@ -311,6 +324,7 @@ export function RecipientInput({
311
324
  field,
312
325
  onMoveRecipient,
313
326
  }: RecipientInputProps) {
327
+ const t = useT();
314
328
  const [inputValue, setInputValue] = useState("");
315
329
  const [showSuggestions, setShowSuggestions] = useState(false);
316
330
  const [selectedIndex, setSelectedIndex] = useState(0);
@@ -611,7 +625,9 @@ export function RecipientInput({
611
625
  {alias.name}
612
626
  </span>
613
627
  <span className="shrink-0 text-[11px] text-indigo-400/70">
614
- {alias.emails.length} people
628
+ {t("mail.recipients.peopleCount", {
629
+ count: alias.emails.length,
630
+ })}
615
631
  </span>
616
632
  </button>
617
633
  ))}
@@ -765,7 +781,7 @@ export function RecipientInput({
765
781
  className="flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] text-muted-foreground/35 transition-colors hover:text-muted-foreground/60"
766
782
  >
767
783
  <IconPlus className="size-3" />
768
- Save as alias
784
+ {t("mail.recipients.saveAsAlias")}
769
785
  </button>
770
786
  )}
771
787
  </div>
@@ -6,6 +6,7 @@ import {
6
6
  } from "@/components/ui/popover";
7
7
  import { Button } from "@/components/ui/button";
8
8
  import { IconSend, IconChevronDown, IconCalendar } from "@tabler/icons-react";
9
+ import { useT } from "@agent-native/core/client";
9
10
 
10
11
  interface SendLaterButtonProps {
11
12
  onSend: () => void;
@@ -54,6 +55,7 @@ export function SendLaterButton({
54
55
  disabled,
55
56
  isSending,
56
57
  }: SendLaterButtonProps) {
58
+ const t = useT();
57
59
  const [open, setOpen] = useState(false);
58
60
  const presets = getPresets();
59
61
  const dateInputRef = useRef<HTMLInputElement>(null);
@@ -73,7 +75,7 @@ export function SendLaterButton({
73
75
  onClick={onSend}
74
76
  >
75
77
  <IconSend className="h-3.5 w-3.5 mr-1.5" />
76
- {isSending ? "Sending..." : "Send"}
78
+ {isSending ? t("mail.compose.sending") : t("mail.compose.send")}
77
79
  </Button>
78
80
  <Popover open={open} onOpenChange={setOpen}>
79
81
  <PopoverTrigger asChild>
@@ -89,7 +91,7 @@ export function SendLaterButton({
89
91
  <PopoverContent className="w-64 p-3" align="end" side="top">
90
92
  <div className="space-y-2">
91
93
  <div className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
92
- Schedule send
94
+ {t("mail.sendLater.scheduleSend")}
93
95
  </div>
94
96
  <div className="space-y-1">
95
97
  {presets.map((preset) => (
@@ -112,7 +114,7 @@ export function SendLaterButton({
112
114
  className="w-full flex items-center gap-2 px-2.5 py-2 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors text-sm text-muted-foreground"
113
115
  >
114
116
  <IconCalendar className="h-4 w-4" />
115
- Pick date &amp; time...
117
+ {t("mail.sendLater.pickDateTime")}
116
118
  </button>
117
119
  <input
118
120
  ref={dateInputRef}
@@ -5,6 +5,7 @@ import { useParseDate, useSnoozeEmail } from "@/hooks/use-scheduled-jobs";
5
5
  import { toast } from "sonner";
6
6
  import { Dialog, DialogContent } from "@/components/ui/dialog";
7
7
  import { Input } from "@/components/ui/input";
8
+ import { useT } from "@agent-native/core/client";
8
9
 
9
10
  interface SnoozeModalProps {
10
11
  open: boolean;
@@ -103,6 +104,7 @@ export function SnoozeModal({
103
104
  onClose,
104
105
  onSnoozed,
105
106
  }: SnoozeModalProps) {
107
+ const t = useT();
106
108
  const [nlInput, setNlInput] = useState("");
107
109
  const [selectedIndex, setSelectedIndex] = useState(0);
108
110
  const [parsedDate, setParsedDate] = useState<Date | null>(null);
@@ -274,7 +276,7 @@ export function SnoozeModal({
274
276
  type="text"
275
277
  value={nlInput}
276
278
  onChange={(e) => setNlInput(e.target.value)}
277
- placeholder="Try: 8 am, 3 days, aug 7"
279
+ placeholder={t("mail.snooze.modalPlaceholder")}
278
280
  className="h-11 border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 focus-visible:ring-offset-0"
279
281
  />
280
282
  {nlTyping && (nlParsed || parseDate.isPending) && (
@@ -287,7 +289,7 @@ export function SnoozeModal({
287
289
  {/* Options list */}
288
290
  <div className="max-h-[300px] overflow-y-auto overflow-x-hidden p-1">
289
291
  <div className="px-2 py-1.5 text-xs font-medium text-muted-foreground">
290
- Snooze until
292
+ {t("mail.snooze.snoozeUntil")}
291
293
  </div>
292
294
  {options.map((opt, i) => {
293
295
  const active = i === selectedIndex;
@@ -9,6 +9,7 @@ import { cn } from "@/lib/utils";
9
9
  import { useParseDate, useSnoozeEmail } from "@/hooks/use-scheduled-jobs";
10
10
  import { IconAlarm } from "@tabler/icons-react";
11
11
  import { toast } from "sonner";
12
+ import { useT } from "@agent-native/core/client";
12
13
 
13
14
  interface SnoozePopoverProps {
14
15
  emailId: string;
@@ -68,6 +69,7 @@ export function SnoozePopover({
68
69
  onArchive,
69
70
  children,
70
71
  }: SnoozePopoverProps) {
72
+ const t = useT();
71
73
  const [open, setOpen] = useState(false);
72
74
  const [nlInput, setNlInput] = useState("");
73
75
  const [selectedDate, setSelectedDate] = useState<Date | null>(null);
@@ -140,7 +142,7 @@ export function SnoozePopover({
140
142
  <PopoverContent className="w-72 p-3" align="end">
141
143
  <div className="space-y-3">
142
144
  <div className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
143
- Snooze until
145
+ {t("mail.snooze.snoozeUntil")}
144
146
  </div>
145
147
 
146
148
  {/* Preset chips */}
@@ -171,17 +173,17 @@ export function SnoozePopover({
171
173
  type="text"
172
174
  value={nlInput}
173
175
  onChange={(e) => setNlInput(e.target.value)}
174
- placeholder="or try: friday 5pm, in 2 weeks..."
176
+ placeholder={t("mail.snooze.tryPlaceholder")}
175
177
  className="w-full text-xs px-2.5 py-1.5 rounded-md border border-input bg-background placeholder:text-muted-foreground/60 focus:outline-none focus:ring-1 focus:ring-ring/30"
176
178
  />
177
179
  {nlInput && displayDate && (
178
180
  <div className="text-[11px] px-1 text-muted-foreground">
179
- Snooze until {displayDate}
181
+ {t("mail.snooze.snoozeUntilDate", { date: displayDate })}
180
182
  </div>
181
183
  )}
182
184
  {nlInput && !displayDate && parseDate.isPending && (
183
185
  <div className="text-[11px] px-1 text-muted-foreground">
184
- Parsing...
186
+ {t("mail.snooze.parsing")}
185
187
  </div>
186
188
  )}
187
189
  </div>
@@ -194,7 +196,9 @@ export function SnoozePopover({
194
196
  onClick={handleSnooze}
195
197
  >
196
198
  <IconAlarm className="h-3.5 w-3.5 mr-1.5" />
197
- {snoozeEmail.isPending ? "Snoozing..." : "Snooze"}
199
+ {snoozeEmail.isPending
200
+ ? t("mail.snooze.snoozing")
201
+ : t("mail.snooze.snooze")}
198
202
  {displayDate && !snoozeEmail.isPending && (
199
203
  <span className="ml-1 opacity-60 truncate max-w-[120px]">
200
204
  · {displayDate}
@@ -2,6 +2,7 @@ import { NodeViewWrapper, type NodeViewProps } from "@tiptap/react";
2
2
  import { useState, useCallback } from "react";
3
3
  import { IconPhoto, IconLoader2, IconX } from "@tabler/icons-react";
4
4
  import { openFilePicker, uploadFile } from "@/lib/upload";
5
+ import { useT } from "@agent-native/core/client";
5
6
 
6
7
  export function ComposeImageBlock({
7
8
  node,
@@ -9,6 +10,7 @@ export function ComposeImageBlock({
9
10
  deleteNode,
10
11
  selected,
11
12
  }: NodeViewProps) {
13
+ const t = useT();
12
14
  const [isUploading, setIsUploading] = useState(false);
13
15
  const [isHovered, setIsHovered] = useState(false);
14
16
  const src = node.attrs.src as string;
@@ -40,12 +42,12 @@ export function ComposeImageBlock({
40
42
  {isUploading ? (
41
43
  <>
42
44
  <IconLoader2 size={20} className="animate-spin" />
43
- <span>Uploading...</span>
45
+ <span>{t("mail.compose.uploading")}</span>
44
46
  </>
45
47
  ) : (
46
48
  <>
47
49
  <IconPhoto size={20} />
48
- <span>Add an image</span>
50
+ <span>{t("mail.compose.addImage")}</span>
49
51
  </>
50
52
  )}
51
53
  </div>