From 22418c17cfa5ae9ad24926b7cbea509e56f5e21e Mon Sep 17 00:00:00 2001 From: Shuki Vaknin Date: Wed, 8 Jul 2026 00:46:15 +0300 Subject: [PATCH] feat(i18n): Hebrew locale + full RTL support --- app/(main)/[locale]/calendar/page.tsx | 10 +- app/(main)/[locale]/contacts/page.tsx | 8 +- app/(main)/[locale]/error.tsx | 4 +- app/(main)/[locale]/files/page.tsx | 4 +- app/(main)/[locale]/login/page.tsx | 10 +- app/(main)/[locale]/page.tsx | 26 +- app/(main)/[locale]/settings/page.tsx | 18 +- .../admin/_tabs/_jmap-servers-section.tsx | 2 +- app/(main)/admin/_tabs/logs.tsx | 8 +- app/(main)/admin/_tabs/marketplace.tsx | 4 +- .../admin/_tabs/plugin-config-panel.tsx | 6 +- app/(main)/admin/_tabs/settings.tsx | 4 +- app/(main)/admin/change-password/page.tsx | 2 +- app/(main)/admin/layout.tsx | 24 +- app/(main)/admin/marketplace/[slug]/page.tsx | 6 +- app/(main)/global-error.tsx | 2 +- app/(main)/layout.tsx | 3 +- app/(main)/setup/page.tsx | 10 +- app/globals.css | 27 +- components/calendar/calendar-agenda-view.tsx | 4 +- components/calendar/calendar-day-view.tsx | 8 +- components/calendar/calendar-month-view.tsx | 4 +- .../calendar/calendar-sidebar-panel.tsx | 6 +- components/calendar/calendar-toolbar.tsx | 18 +- components/calendar/calendar-week-view.tsx | 16 +- components/calendar/create-calendar-modal.tsx | 2 +- components/calendar/event-card.tsx | 8 +- components/calendar/event-detail-popover.tsx | 28 +- components/calendar/event-modal.tsx | 36 +- components/calendar/ical-import-modal.tsx | 2 +- .../calendar/ical-subscription-modal.tsx | 2 +- components/calendar/task-modal.tsx | 2 +- components/calendar/task-toolbar.tsx | 4 +- components/contacts/contact-activity.tsx | 4 +- components/contacts/contact-detail.tsx | 8 +- components/contacts/contact-form.tsx | 20 +- components/contacts/contact-group-detail.tsx | 4 +- components/contacts/contact-group-form.tsx | 4 +- components/contacts/contact-group-list.tsx | 4 +- components/contacts/contact-import-dialog.tsx | 2 +- components/contacts/contact-list.tsx | 14 +- components/contacts/contacts-sidebar.tsx | 40 +- .../email/calendar-invitation-banner.tsx | 4 +- components/email/email-composer.tsx | 22 +- components/email/email-context-menu.tsx | 4 +- components/email/email-hover-actions.tsx | 15 +- components/email/email-list-item.tsx | 2 +- components/email/email-list.tsx | 4 +- components/email/email-viewer.tsx | 130 +- components/email/read-receipt-banner.tsx | 2 +- components/email/recipient-popover.tsx | 2 +- components/email/rich-text-editor.tsx | 37 +- components/email/text-direction.ts | 58 + components/email/thread-conversation-view.tsx | 12 +- components/email/thread-email-item.tsx | 6 +- components/email/thread-list-item.tsx | 6 +- components/email/unsubscribe-banner.tsx | 4 +- components/error/error-fallbacks.tsx | 12 +- components/files/file-browser.tsx | 74 +- components/files/file-preview-modal.tsx | 6 +- components/files/file-upload-area.tsx | 4 +- components/files/folder-tree-sidebar.tsx | 16 +- components/files/pdf-mobile-viewer.tsx | 2 +- components/filters/sieve-editor-modal.tsx | 4 +- components/identity/identity-form.tsx | 2 +- .../identity/identity-manager-modal.tsx | 2 +- components/identity/sub-address-helper.tsx | 2 +- components/keyboard-shortcuts-modal.tsx | 2 +- components/layout/account-switcher.tsx | 4 +- components/layout/icon-picker.tsx | 2 +- components/layout/sidebar-apps-modal.tsx | 4 +- components/layout/sidebar.tsx | 21 +- components/pro/pro-tab-bar.tsx | 4 +- .../protocol/protocol-account-picker.tsx | 2 +- components/providers/intl-provider.tsx | 47 +- components/search/advanced-search-panel.tsx | 2 +- components/search/search-chips.tsx | 2 +- components/settings/about-data-settings.tsx | 4 +- .../settings/account-security-settings.tsx | 22 +- components/settings/account-settings.tsx | 6 +- components/settings/appearance-settings.tsx | 2 +- components/settings/composing-settings.tsx | 10 +- components/settings/contacts-settings.tsx | 4 +- components/settings/debug-settings.tsx | 2 +- components/settings/files-settings.tsx | 10 +- components/settings/filter-settings.tsx | 6 +- components/settings/language-settings.tsx | 2 +- components/settings/layout-settings.tsx | 12 +- components/settings/settings-section.tsx | 4 +- .../settings/share-collection-dialog.tsx | 8 +- components/settings/sidebar-apps-settings.tsx | 2 +- components/settings/spam-siege-game.tsx | 2 +- components/settings/template-settings.tsx | 4 +- components/settings/themes-settings.tsx | 2 +- components/settings/vacation-settings.tsx | 2 +- components/templates/template-form.tsx | 8 +- .../templates/template-manager-modal.tsx | 2 +- components/templates/template-picker.tsx | 4 +- components/trusted-senders-modal.tsx | 2 +- components/ui/context-menu.tsx | 4 +- components/ui/flag-icons.tsx | 18 +- components/ui/language-switcher.tsx | 19 +- components/ui/toast.tsx | 2 +- components/ui/welcome-banner.tsx | 4 +- i18n/detect-locale.ts | 38 + i18n/direction.ts | 6 + i18n/merge-messages.ts | 21 + i18n/request.ts | 32 +- i18n/routing.ts | 2 +- lib/utils.ts | 16 +- locales/en/common.json | 6 +- locales/he/common.json | 3221 +++++++++++++++++ stores/settings-store.ts | 3 + 113 files changed, 3958 insertions(+), 494 deletions(-) create mode 100644 components/email/text-direction.ts create mode 100644 i18n/detect-locale.ts create mode 100644 i18n/direction.ts create mode 100644 i18n/merge-messages.ts create mode 100644 locales/he/common.json diff --git a/app/(main)/[locale]/calendar/page.tsx b/app/(main)/[locale]/calendar/page.tsx index 07a2e98d..0c9ffefd 100644 --- a/app/(main)/[locale]/calendar/page.tsx +++ b/app/(main)/[locale]/calendar/page.tsx @@ -1149,13 +1149,13 @@ export default function CalendarPage() {
diff --git a/app/(main)/[locale]/error.tsx b/app/(main)/[locale]/error.tsx index 50706cff..7ddd9107 100644 --- a/app/(main)/[locale]/error.tsx +++ b/app/(main)/[locale]/error.tsx @@ -38,11 +38,11 @@ export default function LocaleError({

diff --git a/app/(main)/[locale]/files/page.tsx b/app/(main)/[locale]/files/page.tsx index 4c3a5fad..88f24225 100644 --- a/app/(main)/[locale]/files/page.tsx +++ b/app/(main)/[locale]/files/page.tsx @@ -477,7 +477,7 @@ export default function FilesPage() { onClick={() => router.push("/")} className="justify-start" > - + {t("title")} @@ -611,7 +611,7 @@ export default function FilesPage() { : '0%' }} /> -

+

{migrationProgress.current} / {migrationProgress.total}

diff --git a/app/(main)/[locale]/login/page.tsx b/app/(main)/[locale]/login/page.tsx index 5160a11f..77f3cd78 100644 --- a/app/(main)/[locale]/login/page.tsx +++ b/app/(main)/[locale]/login/page.tsx @@ -725,7 +725,7 @@ export default function LoginPage() { )} > - {option.label} + {option.label} {isActive && } ); @@ -874,7 +874,7 @@ export default function LoginPage() { )} > - {option.label} + {option.label} {isActive && } ); @@ -1133,7 +1133,7 @@ export default function LoginPage() { type={showPassword ? "text" : "password"} value={formData.password} onChange={(e) => setFormData({ ...formData, password: e.target.value })} - className="h-11 px-3.5 pr-11 bg-muted/40 border-border/60 rounded-xl focus:bg-background focus:border-primary/50 transition-all duration-200" + className="h-11 px-3.5 pe-11 bg-muted/40 border-border/60 rounded-xl focus:bg-background focus:border-primary/50 transition-all duration-200" placeholder={t("password_placeholder")} required autoComplete="current-password" @@ -1253,9 +1253,9 @@ export default function LoginPage() { disabled={oauthLoading || isLoading} > {oauthLoading ? ( - + ) : ( - + )} {t("sign_in_sso")} diff --git a/app/(main)/[locale]/page.tsx b/app/(main)/[locale]/page.tsx index 04d8b8b9..5d98c72e 100644 --- a/app/(main)/[locale]/page.tsx +++ b/app/(main)/[locale]/page.tsx @@ -75,7 +75,7 @@ import { appLifecycleHooks, uiHooks, routerHooks, toastHooks, emailHooks } from import { emailToReadView } from "@/lib/plugin-projection"; import { buildQuoteHeader } from "@/lib/quote-header"; import { buildReplySubject, buildForwardSubject } from "@/lib/subject-prefix"; -import { useLocaleStore } from "@/stores/locale-store"; +import { getEffectiveLocale } from '@/i18n/detect-locale'; import type { QuoteHeader } from "@/lib/plugin-types"; const SCHEDULED_MAILBOX_ID = '__scheduled__'; @@ -1280,7 +1280,7 @@ export default function Home() { }, newTo, newCc, - locale: useLocaleStore.getState().locale, + locale: getEffectiveLocale(), timeFormat: useSettingsStore.getState().timeFormat, unknownLabel: tCommon('unknown'), labels: { @@ -2761,17 +2761,17 @@ export default function Home() { "relative flex flex-col bg-background", isHorizontalMailLayout ? (shouldHideHorizontalViewerPane ? "md:w-full md:min-h-0" : "md:w-full md:h-auto") - : "h-full border-r border-border", + : "h-full border-e border-border", // Mobile: full width, hidden when viewing email - "max-md:flex-1 max-md:border-r-0 max-md:border-b-0", + "max-md:flex-1 max-md:border-e-0 max-md:border-b-0", isMobile && activeView !== "list" && "max-md:hidden", // Tablet/Desktop: fixed width with collapse animation - !isHorizontalMailLayout && (shouldHideViewerPane ? "md:flex-1 md:border-r-0" : "md:flex-shrink-0"), + !isHorizontalMailLayout && (shouldHideViewerPane ? "md:flex-1 md:border-e-0" : "md:flex-shrink-0"), isHorizontalMailLayout && (shouldHideHorizontalViewerPane ? "md:flex-1" : "md:flex-shrink-0"), isHorizontalMailLayout && !shouldHideHorizontalViewerPane && "md:shadow-[0_8px_12px_-6px_rgba(0,0,0,0.18)] dark:md:shadow-[0_8px_14px_-6px_rgba(0,0,0,0.55)]", !isHorizontalMailLayout && "md:shadow-sm", !isResizing && "transition-all duration-200 ease-out", - shouldCollapseListPane && "md:w-0 md:opacity-0 md:overflow-hidden md:border-r-0" + shouldCollapseListPane && "md:w-0 md:opacity-0 md:overflow-hidden md:border-e-0" )} style={ isMobile @@ -2824,13 +2824,13 @@ export default function Home() { )}
{ e.preventDefault(); if (searchQuery.trim()) handleSearch(searchQuery); }} className="relative flex-1"> - + setSearchQuery(e.target.value)} - className={cn("pl-9 h-9", searchQuery && "pr-8")} + className={cn("ps-9 h-9", searchQuery && "pe-8")} data-search-input data-tour="search-input" disabled={isUnifiedView || isScheduledView} @@ -2840,7 +2840,7 @@ export default function Home() { @@ -3122,7 +3122,7 @@ export default function Home() { }} className={cn( "absolute z-40 rounded-full shadow-lg", - isMobile ? "bottom-4 right-4 h-14 w-14" : "bottom-4 right-4 h-12 w-12" + isMobile ? "bottom-4 end-4 h-14 w-14" : "bottom-4 end-4 h-12 w-12" )} aria-label={t('sidebar.compose')} title={t('sidebar.compose_hint')} @@ -3250,13 +3250,13 @@ export default function Home() { setShowComposer(true); if (isMobile) setActiveView('viewer'); }} - className="flex items-center gap-3 px-4 py-2.5 bg-primary/10 border-b border-primary/20 hover:bg-primary/15 transition-colors cursor-pointer w-full text-left" + className="flex items-center gap-3 px-4 py-2.5 bg-primary/10 border-b border-primary/20 hover:bg-primary/15 transition-colors cursor-pointer w-full text-start" >
{t('email_composer.continue_draft')} {pendingDraft.subject && ( - {pendingDraft.subject} + {pendingDraft.subject} )}
{t('scoped.back')} - + {t('scoped.managing', { name: managedAccount.name })} @@ -820,7 +820,7 @@ export default function SettingsPage() { value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder={t('search_placeholder')} - className="pl-9 pr-9 h-10" + className="ps-9 pe-9 h-10" aria-label={t('search_placeholder')} /> {searchQuery && ( @@ -868,7 +868,7 @@ export default function SettingsPage() { @@ -932,7 +932,7 @@ export default function SettingsPage() { <>
router.push('/')} className="w-full justify-start" > - + {t('back_to_mail')}
@@ -960,7 +960,7 @@ export default function SettingsPage() { value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder={t('search_placeholder')} - className="pl-8 pr-8 h-9 text-sm" + className="ps-8 pe-8 h-9 text-sm" aria-label={t('search_placeholder')} /> {searchQuery && ( @@ -997,7 +997,7 @@ export default function SettingsPage() { diff --git a/app/(main)/admin/_tabs/_jmap-servers-section.tsx b/app/(main)/admin/_tabs/_jmap-servers-section.tsx index 638e9fee..ef4e00ac 100644 --- a/app/(main)/admin/_tabs/_jmap-servers-section.tsx +++ b/app/(main)/admin/_tabs/_jmap-servers-section.tsx @@ -220,7 +220,7 @@ export function JmapServersSection({ value, source, onChange, onRevert }: Props) Per-server OAuth (optional, overrides global) {d.oauthExpanded && ( -
+
- Time - Action - Details - IP + Time + Action + Details + IP diff --git a/app/(main)/admin/_tabs/marketplace.tsx b/app/(main)/admin/_tabs/marketplace.tsx index 64fda763..6bf82c38 100644 --- a/app/(main)/admin/_tabs/marketplace.tsx +++ b/app/(main)/admin/_tabs/marketplace.tsx @@ -171,7 +171,7 @@ export function MarketplaceTab() { placeholder="Search extensions..." value={searchInput} onChange={(e) => setSearchInput(e.target.value)} - className="w-full h-9 pl-9 pr-3 rounded-md border border-input bg-background text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring/20 focus:border-ring" + className="w-full h-9 ps-9 pe-3 rounded-md border border-input bg-background text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring/20 focus:border-ring" />
@@ -210,7 +210,7 @@ export function MarketplaceTab() { {loading && !error && (
- Searching extensions... + Searching extensions...
)} diff --git a/app/(main)/admin/_tabs/plugin-config-panel.tsx b/app/(main)/admin/_tabs/plugin-config-panel.tsx index 70e83cb7..2cc5fe7f 100644 --- a/app/(main)/admin/_tabs/plugin-config-panel.tsx +++ b/app/(main)/admin/_tabs/plugin-config-panel.tsx @@ -155,7 +155,7 @@ export function PluginConfigPanel({ pluginId, onBack }: Props) { if (loading) { return (
- + Loading...
); @@ -217,7 +217,7 @@ export function PluginConfigPanel({ pluginId, onBack }: Props) {
{field.description && (

{field.description}

@@ -250,7 +250,7 @@ export function PluginConfigPanel({ pluginId, onBack }: Props) { value={formValues[key] ?? ''} onChange={(e) => setFormValues(prev => ({ ...prev, [key]: e.target.value }))} placeholder={config[key] ? '•••••••• (unchanged)' : (field.placeholder || '')} - className="w-full h-9 px-3 pr-10 rounded-md border border-input bg-background text-sm focus:outline-none focus:ring-2 focus:ring-ring font-mono" + className="w-full h-9 px-3 pe-10 rounded-md border border-input bg-background text-sm focus:outline-none focus:ring-2 focus:ring-ring font-mono" />
{logoUrl ? ( - + ) : ( - + )} Admin Panel
diff --git a/app/(main)/admin/marketplace/[slug]/page.tsx b/app/(main)/admin/marketplace/[slug]/page.tsx index e8095b05..d601e66c 100644 --- a/app/(main)/admin/marketplace/[slug]/page.tsx +++ b/app/(main)/admin/marketplace/[slug]/page.tsx @@ -186,7 +186,7 @@ export default function MarketplacePreviewPage() { if (loading) { return (
- + Loading...
); @@ -512,7 +512,7 @@ export default function MarketplacePreviewPage() {
diff --git a/app/(main)/layout.tsx b/app/(main)/layout.tsx index 65d4bba1..04a86963 100644 --- a/app/(main)/layout.tsx +++ b/app/(main)/layout.tsx @@ -1,4 +1,5 @@ import type { Metadata, Viewport } from "next"; +import { getLocaleDirection } from "@/i18n/direction"; import { Geist, Geist_Mono } from "next/font/google"; import { headers } from "next/headers"; import { getLocale, getTranslations } from "next-intl/server"; @@ -76,7 +77,7 @@ export default async function RootLayout({ const parentOrigin = process.env.NEXT_PUBLIC_PARENT_ORIGIN || ""; return ( - + diff --git a/app/(main)/setup/page.tsx b/app/(main)/setup/page.tsx index 6f3fd608..299715c5 100644 --- a/app/(main)/setup/page.tsx +++ b/app/(main)/setup/page.tsx @@ -780,7 +780,7 @@ function ServerStep({ config, setConfig, onNext }: Pick
-
{showUrlField && ( -
+
{uploadError}

+

{uploadError}

)}
); @@ -1601,7 +1601,7 @@ function SummaryRow({ label, value, mono }: { label: string; value: string; mono return (
{label} - + {value || -}
diff --git a/app/globals.css b/app/globals.css index ce708b75..545a415d 100644 --- a/app/globals.css +++ b/app/globals.css @@ -660,13 +660,13 @@ body { .tiptap ul { list-style-type: disc; - padding-left: 1.5rem; + padding-inline-start: 1.5rem; margin: 0.25rem 0; } .tiptap ol { list-style-type: decimal; - padding-left: 1.5rem; + padding-inline-start: 1.5rem; margin: 0.25rem 0; } @@ -675,8 +675,8 @@ body { } .tiptap blockquote { - border-left: 3px solid var(--color-border); - padding-left: 1rem; + border-inline-start: 3px solid var(--color-border); + padding-inline-start: 1rem; margin: 0.5rem 0; color: var(--color-muted-foreground); } @@ -719,7 +719,7 @@ body { .tiptap p.is-editor-empty:first-child::before { content: attr(data-placeholder); - float: left; + float: inline-start; color: var(--color-muted-foreground); pointer-events: none; height: 0; @@ -808,3 +808,20 @@ body { border-radius: 8px; animation: settings-search-pulse 1.6s ease-in-out forwards; } + +/* RTL: mirror directional icons (chevrons/arrows) so prev/next, back/forward, + and panel-collapse affordances point the correct way in right-to-left layouts. + lucide-react emits a `lucide-` class per icon, so we target the + directional ones only — vertical chevrons (up/down) are intentionally left. */ +[dir="rtl"] .lucide-chevron-left, +[dir="rtl"] .lucide-chevron-right, +[dir="rtl"] .lucide-chevrons-left, +[dir="rtl"] .lucide-chevrons-right, +[dir="rtl"] .lucide-arrow-left, +[dir="rtl"] .lucide-arrow-right, +[dir="rtl"] .lucide-arrow-big-left, +[dir="rtl"] .lucide-arrow-big-right, +[dir="rtl"] .lucide-panel-left, +[dir="rtl"] .lucide-panel-right { + transform: scaleX(-1); +} diff --git a/components/calendar/calendar-agenda-view.tsx b/components/calendar/calendar-agenda-view.tsx index a31510e8..d757305c 100644 --- a/components/calendar/calendar-agenda-view.tsx +++ b/components/calendar/calendar-agenda-view.tsx @@ -131,7 +131,7 @@ export function CalendarAgendaView({ )}> {formatDateHeader(group.date)} - + {intlFormatter.dateTime(group.date, { month: "short", day: "numeric", year: "numeric" })}
@@ -163,7 +163,7 @@ export function CalendarAgendaView({ onMouseLeave={() => onHoverLeave?.()} onContextMenu={onContextMenuEvent ? (e) => onContextMenuEvent(e, ev) : undefined} className={cn( - "w-full flex items-start px-4 hover:bg-muted/50 transition-colors text-left", + "w-full flex items-start px-4 hover:bg-muted/50 transition-colors text-start", isCancelled && "opacity-60" )} style={{ gap: 'var(--density-item-gap)', paddingBlock: 'var(--density-item-py)' }} diff --git a/components/calendar/calendar-day-view.tsx b/components/calendar/calendar-day-view.tsx index b06528fc..943480b3 100644 --- a/components/calendar/calendar-day-view.tsx +++ b/components/calendar/calendar-day-view.tsx @@ -218,7 +218,7 @@ export function CalendarDayView({ {HOURS.map((h) => (
{h > 0 && ( @@ -231,7 +231,7 @@ export function CalendarDayView({
handleGridPointerDown(e, dayKey, selectedDate)} @@ -312,7 +312,7 @@ export function CalendarDayView({ style={{ top: (nowMinutes / 60) * HOUR_HEIGHT }} >
-
+
@@ -346,7 +346,7 @@ export function CalendarDayView({ style={{ top: (dropTarget.minutes / 60) * HOUR_HEIGHT }} >
-
+
diff --git a/components/calendar/calendar-month-view.tsx b/components/calendar/calendar-month-view.tsx index c6b26e49..0b230512 100644 --- a/components/calendar/calendar-month-view.tsx +++ b/components/calendar/calendar-month-view.tsx @@ -145,7 +145,7 @@ export function CalendarMonthView({
{dayHeaders.map((d) => (
{isMobile ? t(`days.${d}`).slice(0, 2) : t(`days.${d}`)} @@ -181,7 +181,7 @@ export function CalendarMonthView({ onDragLeave={handleCellDragLeave} onDrop={(e) => handleCellDrop(e, day)} className={cn( - "border-r border-border last:border-r-0 p-1 cursor-pointer transition-colors touch-manipulation", + "border-e border-border last:border-e-0 p-1 cursor-pointer transition-colors touch-manipulation", !inMonth && "bg-muted/30", "hover:bg-muted/50", selected && isMobile && "bg-primary/10", diff --git a/components/calendar/calendar-sidebar-panel.tsx b/components/calendar/calendar-sidebar-panel.tsx index 2fc8dc11..38dc2dcc 100644 --- a/components/calendar/calendar-sidebar-panel.tsx +++ b/components/calendar/calendar-sidebar-panel.tsx @@ -397,7 +397,7 @@ export function CalendarSidebarPanel({ {t('tasks.label')} {pendingTaskCount > 0 && ( - {pendingTaskCount} + {pendingTaskCount} )} {overdueTaskCount > 0 && ( {overdueTaskCount} {t('tasks.filter_overdue').toLowerCase()} @@ -437,7 +437,7 @@ export function CalendarSidebarPanel({ onCreateCalendar(); } }} - className="ml-auto p-0.5 rounded text-muted-foreground/70 opacity-0 group-hover:opacity-100 hover:text-foreground hover:bg-muted transition-colors cursor-pointer" + className="ms-auto p-0.5 rounded text-muted-foreground/70 opacity-0 group-hover:opacity-100 hover:text-foreground hover:bg-muted transition-colors cursor-pointer" title={tMgmt('add_calendar')} > @@ -445,7 +445,7 @@ export function CalendarSidebarPanel({ )} {expanded && ( -
+
{owned.length > 0 && (
diff --git a/components/calendar/calendar-toolbar.tsx b/components/calendar/calendar-toolbar.tsx index c642bd79..ec9480dd 100644 --- a/components/calendar/calendar-toolbar.tsx +++ b/components/calendar/calendar-toolbar.tsx @@ -124,7 +124,7 @@ export function CalendarToolbar({ variant="ghost" size="icon" onClick={onMenuClick} - className="h-8 w-8 -ml-1 mr-1" + className="h-8 w-8 -ms-1 me-1" aria-label={t("nav_open_menu")} > @@ -138,7 +138,7 @@ export function CalendarToolbar({ {onMenuClick && ( -
@@ -284,7 +284,7 @@ export function CalendarToolbar({ {/* ── DESKTOP TOOLBAR ── */} {!isMobile && (
- - + {getDateLabel()}
@@ -328,9 +328,9 @@ export function CalendarToolbar({ {(onImport || onSubscribe) && !isMobile && (
{showImportDropdown && (
@@ -359,7 +359,7 @@ export function CalendarToolbar({ {!isMobile && ( )} diff --git a/components/calendar/calendar-week-view.tsx b/components/calendar/calendar-week-view.tsx index 16e822aa..4d4a4052 100644 --- a/components/calendar/calendar-week-view.tsx +++ b/components/calendar/calendar-week-view.tsx @@ -211,7 +211,7 @@ export function CalendarWeekView({
{hasAllDay && (
{t("events.all_day")} @@ -306,7 +306,7 @@ export function CalendarWeekView({
-
+
{weekDays.map((day) => { const todayCol = isToday(day); const selected = isSameDay(day, selectedDate); @@ -318,7 +318,7 @@ export function CalendarWeekView({ role="columnheader" aria-label={fullLabel} className={cn( - "text-center py-2 text-sm border-r border-border last:border-r-0 transition-colors touch-manipulation", + "text-center py-2 text-sm border-e border-border last:border-e-0 transition-colors touch-manipulation", "hover:bg-muted/50", todayCol && "font-bold", )} @@ -345,7 +345,7 @@ export function CalendarWeekView({ {HOURS.map((h) => (
{h > 0 && ( @@ -357,7 +357,7 @@ export function CalendarWeekView({ ))}
-
+
{weekDays.map((day) => { const key = format(day, "yyyy-MM-dd"); const dayEvents = timedEvents.get(key) || []; @@ -367,7 +367,7 @@ export function CalendarWeekView({ return (
handleGridPointerDown(e, key, day)} @@ -448,7 +448,7 @@ export function CalendarWeekView({ style={{ top: (nowMinutes / 60) * HOUR_HEIGHT }} >
-
+
@@ -482,7 +482,7 @@ export function CalendarWeekView({ style={{ top: (dropTarget.minutes / 60) * HOUR_HEIGHT }} >
-
+
diff --git a/components/calendar/create-calendar-modal.tsx b/components/calendar/create-calendar-modal.tsx index e5b675e7..656dc8d9 100644 --- a/components/calendar/create-calendar-modal.tsx +++ b/components/calendar/create-calendar-modal.tsx @@ -137,7 +137,7 @@ export function CreateCalendarModal({ client, onClose }: CreateCalendarModalProp
{calendar && ( -

+

{calendar.name} {event.status === "tentative" && ( - + {t("detail.tentative")} )} {event.status === "cancelled" && ( - + {t("detail.cancelled")} )} @@ -346,13 +346,13 @@ export function EventDetailPopover({ <>

{formatEventDate(startDate)} - + {formatTime(startDate)}
{formatEventDate(endDate)} - + {formatTime(endDate)}
@@ -367,11 +367,11 @@ export function EventDetailPopover({ {formatEventDate(startDate)} {event.showWithoutTime ? ( - {t("events.all_day")} + {t("events.all_day")} ) : (
{formatTime(startDate)} – {formatTime(endDate)} - ({formatDurationDisplay(durationMinutes)}) + ({formatDurationDisplay(durationMinutes)})
)} @@ -437,7 +437,7 @@ export function EventDetailPopover({ {p.name || p.email} {p.isOrganizer && ( - + ({t("participants.organizer").toLowerCase()}) )} @@ -515,7 +515,7 @@ export function EventDetailPopover({ disabled={!noteText.trim() || isSavingNote} className="h-7 text-xs" > - + {t("detail.save_note")}
@@ -549,7 +549,7 @@ export function EventDetailPopover({ : "text-success border-success/30 hover:bg-success/10" } > - {userCurrentStatus === "accepted" && } + {userCurrentStatus === "accepted" && } {t("participants.accepted")}
@@ -609,7 +609,7 @@ export function EventDetailPopover({ ) : ( <>
diff --git a/components/calendar/event-modal.tsx b/components/calendar/event-modal.tsx index d6a16c02..cd65425d 100644 --- a/components/calendar/event-modal.tsx +++ b/components/calendar/event-modal.tsx @@ -666,11 +666,11 @@ export function EventModal({
{formatEventDate(startD)} - {format(startD, timeDisplayFmt)} + {format(startD, timeDisplayFmt)}
{formatEventDate(endD)} - {format(endD, timeDisplayFmt)} + {format(endD, timeDisplayFmt)}
); @@ -679,7 +679,7 @@ export function EventModal({
{formatEventDate(startD)} {!event.showWithoutTime && ( - + {format(startD, timeDisplayFmt)} – {format(endD, timeDisplayFmt)} )} @@ -701,7 +701,7 @@ export function EventModal({ {t("participants.title")}
-
+
{participants.map(p => (
{p.name || p.email} @@ -726,7 +726,7 @@ export function EventModal({ ? "bg-success hover:bg-success/80 text-success-foreground" : "text-success border-success/30 hover:bg-success/10"} > - {userCurrentStatus === "accepted" && } + {userCurrentStatus === "accepted" && } {t("participants.accepted")}
@@ -784,7 +784,7 @@ export function EventModal({

{event.title || t("events.no_title")}

{eventCalendar && ( -

{eventCalendar.name}

+

{eventCalendar.name}

)}
) : ( ) )} {onDuplicate && !showDeleteConfirm && ( )}
{!showDeleteConfirm && ( )} @@ -1320,7 +1320,7 @@ export function EventModal({ onClick={() => setShowDeleteConfirm(true)} className="text-red-600 dark:text-red-400" > - + {t("events.delete")} ) @@ -1332,7 +1332,7 @@ export function EventModal({ onClick={handleDuplicate} aria-label={t("events.duplicate")} > - + {t("events.duplicate")} )} diff --git a/components/calendar/ical-import-modal.tsx b/components/calendar/ical-import-modal.tsx index 36215829..b08f0913 100644 --- a/components/calendar/ical-import-modal.tsx +++ b/components/calendar/ical-import-modal.tsx @@ -444,7 +444,7 @@ export function ICalImportModal({ calendars, client, onClose, initialUrl }: ICal onClick={handleImport} disabled={selectedIndices.size === 0} > - + {t("import_button")} ({selectedIndices.size}) )} diff --git a/components/calendar/ical-subscription-modal.tsx b/components/calendar/ical-subscription-modal.tsx index 7583ad12..05001fa2 100644 --- a/components/calendar/ical-subscription-modal.tsx +++ b/components/calendar/ical-subscription-modal.tsx @@ -208,7 +208,7 @@ export function ICalSubscriptionModal({ client, onClose, editSubscription, initi )} diff --git a/components/calendar/task-toolbar.tsx b/components/calendar/task-toolbar.tsx index 5111f69a..cdda536e 100644 --- a/components/calendar/task-toolbar.tsx +++ b/components/calendar/task-toolbar.tsx @@ -44,7 +44,7 @@ export function TaskToolbar({ ))}
-
diff --git a/components/contacts/contact-activity.tsx b/components/contacts/contact-activity.tsx index 86b50b98..d5d23e16 100644 --- a/components/contacts/contact-activity.tsx +++ b/components/contacts/contact-activity.tsx @@ -229,7 +229,7 @@ export function ContactActivity({ contact }: ContactActivityProps) { key={email.id} type="button" onClick={() => handleOpenEmail(email)} - className="w-full text-left flex items-start gap-3 px-2 py-2 rounded-md hover:bg-muted/60 transition-colors touch-manipulation" + className="w-full text-start flex items-start gap-3 px-2 py-2 rounded-md hover:bg-muted/60 transition-colors touch-manipulation" >
@@ -277,7 +277,7 @@ export function ContactActivity({ contact }: ContactActivityProps) { key={event.id} type="button" onClick={() => handleOpenEvent(event)} - className="w-full text-left flex items-baseline gap-3 px-2 py-2 rounded-md hover:bg-muted/60 transition-colors touch-manipulation" + className="w-full text-start flex items-baseline gap-3 px-2 py-2 rounded-md hover:bg-muted/60 transition-colors touch-manipulation" > {formatEventTime(event)} diff --git a/components/contacts/contact-detail.tsx b/components/contacts/contact-detail.tsx index 24292071..9cf0d4d5 100644 --- a/components/contacts/contact-detail.tsx +++ b/components/contacts/contact-detail.tsx @@ -201,7 +201,7 @@ export function ContactDetail({ contact, onEdit, onDelete, onAddToGroup, onDupli onClick={onCompose} className="touch-manipulation" > - + {t("detail.compose_email")} )} @@ -210,12 +210,12 @@ export function ContactDetail({ contact, onEdit, onDelete, onAddToGroup, onDupli href={`tel:${phone}`} className="inline-flex items-center justify-center rounded-md font-medium h-9 px-3 text-sm border border-input bg-background hover:bg-accent hover:text-accent-foreground transition-colors touch-manipulation" > - + {t("context_menu.call")} )} @@ -587,7 +587,7 @@ function MoreActionsMenu({ items, label }: { items: MoreItem[]; label: string }) setOpen(false); }} className={cn( - "w-full flex items-center gap-2 px-3 py-1.5 text-sm text-left hover:bg-muted focus:bg-muted focus:outline-none transition-colors", + "w-full flex items-center gap-2 px-3 py-1.5 text-sm text-start hover:bg-muted focus:bg-muted focus:outline-none transition-colors", item.destructive && "text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-950 focus:bg-red-50 dark:focus:bg-red-950", )} > diff --git a/components/contacts/contact-form.tsx b/components/contacts/contact-form.tsx index 697e9cc0..a2e898eb 100644 --- a/components/contacts/contact-form.tsx +++ b/components/contacts/contact-form.tsx @@ -72,7 +72,7 @@ function FormSection({ icon: Icon, title, children, collapsible, defaultOpen = t
{emailErrors[i] && ( -

{emailErrors[i]}

+

{emailErrors[i]}

)}
))}
@@ -799,7 +799,7 @@ export function ContactForm({ contact, addressBooks, allKeywords, defaultAddress
))}
@@ -855,7 +855,7 @@ export function ContactForm({ contact, addressBooks, allKeywords, defaultAddress
))}
@@ -884,7 +884,7 @@ export function ContactForm({ contact, addressBooks, allKeywords, defaultAddress
))}
@@ -916,7 +916,7 @@ export function ContactForm({ contact, addressBooks, allKeywords, defaultAddress
))}
@@ -957,7 +957,7 @@ export function ContactForm({ contact, addressBooks, allKeywords, defaultAddress
))}
@@ -1145,7 +1145,7 @@ function CategoryComboBox({
@@ -134,7 +134,7 @@ export function ContactGroupForm({ type="button" onClick={() => toggleMember(contact.id)} className={cn( - "w-full flex items-center gap-3 px-3 py-2.5 text-left transition-colors", + "w-full flex items-center gap-3 px-3 py-2.5 text-start transition-colors", "hover:bg-muted", isSelected && "bg-primary/5" )} diff --git a/components/contacts/contact-group-list.tsx b/components/contacts/contact-group-list.tsx index 0c3127b1..e5193e9d 100644 --- a/components/contacts/contact-group-list.tsx +++ b/components/contacts/contact-group-list.tsx @@ -45,7 +45,7 @@ export function ContactGroupList({
@@ -69,7 +69,7 @@ export function ContactGroupList({ key={group.id} onClick={() => onSelectGroup(group.id)} className={cn( - "w-full flex items-center px-4 text-left transition-colors", + "w-full flex items-center px-4 text-start transition-colors", "hover:bg-muted", group.id === selectedGroupId && "bg-accent text-accent-foreground" )} diff --git a/components/contacts/contact-import-dialog.tsx b/components/contacts/contact-import-dialog.tsx index d62e6dcf..8e8331a1 100644 --- a/components/contacts/contact-import-dialog.tsx +++ b/components/contacts/contact-import-dialog.tsx @@ -187,7 +187,7 @@ export function ContactImportDialog({ type="button" onClick={() => toggleSelect(idx)} className={cn( - "w-full flex items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-muted", + "w-full flex items-center gap-3 px-3 py-2.5 text-start transition-colors hover:bg-muted", isSelected && "bg-primary/5" )} > diff --git a/components/contacts/contact-list.tsx b/components/contacts/contact-list.tsx index 04eac01b..0d9f23c7 100644 --- a/components/contacts/contact-list.tsx +++ b/components/contacts/contact-list.tsx @@ -310,7 +310,7 @@ export function ContactList({ placeholder={t("search_placeholder")} value={searchQuery} onChange={(e) => onSearchChange(e.target.value)} - className={cn("pl-9 h-9", searchQuery && "pr-8")} + className={cn("ps-9 h-9", searchQuery && "pe-8")} /> {searchQuery && ( )} @@ -538,7 +538,7 @@ export function ContactList({

{t("empty_state_title")}

{t("empty_state_subtitle")}

diff --git a/components/contacts/contacts-sidebar.tsx b/components/contacts/contacts-sidebar.tsx index 1fd36bb8..80fc4b40 100644 --- a/components/contacts/contacts-sidebar.tsx +++ b/components/contacts/contacts-sidebar.tsx @@ -287,14 +287,14 @@ export function ContactsSidebar({ className="absolute right-0 top-full mt-1 w-44 rounded-md border border-border bg-background text-foreground shadow-md z-50 py-1" > {onCreateAddressBook && ( @@ -355,7 +355,7 @@ export function ContactsSidebar({
{expanded && ( -
+
{owned.length > 0 && (
@@ -418,7 +418,7 @@ export function ContactsSidebar({
@@ -505,7 +505,7 @@ export function ContactsSidebar({
@@ -561,7 +561,7 @@ export function ContactsSidebar({
@@ -847,7 +847,7 @@ function AddressBookItem({ onDragLeave={handleDragLeave} onDrop={handleDrop} className={cn( - "w-full flex items-center gap-2 pl-5 pr-3 text-sm transition-colors", + "w-full flex items-center gap-2 ps-5 pe-3 text-sm transition-colors", isActive ? "bg-accent text-accent-foreground font-medium" : "text-foreground/80 hover:bg-muted", @@ -858,11 +858,11 @@ function AddressBookItem({ {book.name} {!book.isShared && Object.keys(book.shareWith || {}).length > 0 && ( - + )} 0) && "ml-auto" + !(!book.isShared && Object.keys(book.shareWith || {}).length > 0) && "ms-auto" )}> {contactCount} diff --git a/components/email/calendar-invitation-banner.tsx b/components/email/calendar-invitation-banner.tsx index 64359948..e3293e2c 100644 --- a/components/email/calendar-invitation-banner.tsx +++ b/components/email/calendar-invitation-banner.tsx @@ -1053,7 +1053,7 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp setShowCalendarPicker(false); handleImport(cal.id); }} - className="w-full px-3 py-1.5 text-sm text-left hover:bg-muted flex items-center gap-2" + className="w-full px-3 py-1.5 text-sm text-start hover:bg-muted flex items-center gap-2" > + )}
)} diff --git a/components/email/email-composer.tsx b/components/email/email-composer.tsx index c2b231f1..5a62c335 100644 --- a/components/email/email-composer.tsx +++ b/components/email/email-composer.tsx @@ -1990,7 +1990,7 @@ export function EmailComposer({
{/* Right-side composer sidebar slot is rendered after the main content div below. */}
- + {t('send')}
@@ -2445,7 +2445,7 @@ export function EmailComposer({ )} )} @@ -2699,7 +2699,7 @@ export function EmailComposer({ {t('discard')}
@@ -2718,7 +2718,7 @@ export function EmailComposer({
); @@ -2743,7 +2743,7 @@ const AutocompleteDropdown = React.forwardRef { diff --git a/components/email/email-context-menu.tsx b/components/email/email-context-menu.tsx index 9a66f9b6..f8229ca1 100644 --- a/components/email/email-context-menu.tsx +++ b/components/email/email-context-menu.tsx @@ -334,7 +334,7 @@ export function EmailContextMenu({
)} {node.children.length > 0 && ( -
+
{renderNodes(node.children)}
)} @@ -377,7 +377,7 @@ export function EmailContextMenu({ role="menuitem" onClick={() => handleAction(() => onSetColorTag?.(option.value))} className={cn( - "w-full px-3 py-1.5 text-sm text-left flex items-center gap-2 hover:bg-muted cursor-pointer", + "w-full px-3 py-1.5 text-sm text-start flex items-center gap-2 hover:bg-muted cursor-pointer", isActive && "bg-accent font-medium" )} > diff --git a/components/email/email-hover-actions.tsx b/components/email/email-hover-actions.tsx index 7bb4a734..a2691da7 100644 --- a/components/email/email-hover-actions.tsx +++ b/components/email/email-hover-actions.tsx @@ -181,16 +181,17 @@ export function EmailHoverActions({ return (
-
+
{actionButtons}
diff --git a/components/email/email-list-item.tsx b/components/email/email-list-item.tsx index 31e4d8e9..44c7e915 100644 --- a/components/email/email-list-item.tsx +++ b/components/email/email-list-item.tsx @@ -176,7 +176,7 @@ export function EmailListItem({ email, selected, onClick, onDoubleClick, onConte {/* Unread indicator */} {isUnread && ( -
+
)} diff --git a/components/email/email-list.tsx b/components/email/email-list.tsx index cab95c82..3df7753d 100644 --- a/components/email/email-list.tsx +++ b/components/email/email-list.tsx @@ -417,9 +417,9 @@ export function EmailList({ className="text-destructive border-destructive/30 hover:bg-destructive/10 text-xs" > {isProcessing ? ( - + ) : ( - + )} {t('empty_folder.button')} diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index 4f078c5b..7055e928 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -333,7 +333,7 @@ function renderClickableRecipients( return ( - {index > 0 && ,} + {index > 0 && ,} +
{/* Header */}

{t('contact_sidebar.title')}

@@ -607,7 +607,7 @@ function SidebarSection({ icon: Icon, title, children }: { icon: React.Component

{title}

-
{children}
+
{children}
); } @@ -2637,7 +2637,7 @@ export function EmailViewer({

{tDemoWelcome('title')}

{tDemoWelcome('description')}

-
+
{tDemoWelcome('feature_email')} @@ -2679,7 +2679,7 @@ export function EmailViewer({

{t('no_conversation_description')}

{onCompose && ( )} @@ -2704,7 +2704,7 @@ export function EmailViewer({ variant="ghost" size="icon" onClick={onBack} - className="h-9 w-9 flex-shrink-0 -ml-1" + className="h-9 w-9 flex-shrink-0 -ms-1" aria-label={t('back_to_list')} > @@ -2839,7 +2839,7 @@ export function EmailViewer({ {showToolbarLabels && {t('move')}} {moveMenuOpen && ( -
+
{(() => { const renderNodes = (nodes: MailboxNode[], depth = 0) => { return nodes.map((node) => { @@ -2850,7 +2850,7 @@ export function EmailViewer({ {isTarget ? ( {tagMenuOpen && ( -
+
{colorOptions.map((option) => { const isActive = currentColors.includes(option.value); return ( @@ -2918,13 +2918,13 @@ export function EmailViewer({ key={option.value} onClick={() => { if (email) onSetColorTag?.(email.id, option.value); setTagMenuOpen(false); }} className={cn( - "w-full px-3 py-1.5 text-sm text-left hover:bg-muted flex items-center gap-2", + "w-full px-3 py-1.5 text-sm text-start hover:bg-muted flex items-center gap-2", isActive && "bg-accent font-medium" )} > {option.name} - {isActive && } + {isActive && } ); })} @@ -2933,7 +2933,7 @@ export function EmailViewer({
{moreMenuOpen && !isMobile && ( -
+
{/* Star toggle */} {moreMenuSub === 'move' && ( -
+
{(() => { const renderMobileNodes = (nodes: MailboxNode[], depth = 0) => { return nodes.map((node) => { @@ -3104,7 +3104,7 @@ export function EmailViewer({ {isTarget ? ( {moreMenuSub === 'tag' && ( -
+
{colorOptions.map((option) => { const isActive = currentColors.includes(option.value); return ( @@ -3153,13 +3153,13 @@ export function EmailViewer({ key={option.value} onClick={() => { if (email) onSetColorTag?.(email.id, option.value); setMoreMenuOpen(false); setMoreMenuSub(null); }} className={cn( - "w-full px-3 py-1.5 text-sm text-left hover:bg-muted flex items-center gap-2", + "w-full px-3 py-1.5 text-sm text-start hover:bg-muted flex items-center gap-2", isActive && "bg-accent font-medium" )} > {option.name} - {isActive && } + {isActive && } ); })} @@ -3168,7 +3168,7 @@ export function EmailViewer({
); })} {currentColors.length > 0 && (
{/* Date/time on the right of subject row - hidden on mobile, shown next to sender */} -
+
{formatDateTime(email.receivedAt, timeFormat, { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric' })} @@ -3569,7 +3569,7 @@ export function EmailViewer({ name={sender?.name} email={sender.email} onViewContact={handleViewContactSidebar} - className="font-semibold text-left" + className="font-semibold text-start" /> ) : ( {t('unknown_sender')} @@ -3633,7 +3633,7 @@ export function EmailViewer({ )}
{/* Date/time + size on the right (mobile) */} -
+
{formatDateTime(email.receivedAt, timeFormat, { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric' })} @@ -4006,7 +4006,7 @@ export function EmailViewer({ email={sender?.email || ''} displayLabel={sender?.name && sender?.email ? `${sender.name} <${sender.email}>` : undefined} onViewContact={handleViewContactSidebar} - className="text-sm text-left" + className="text-sm text-start" />
@@ -4534,7 +4534,7 @@ export function EmailViewer({ {getAttachmentDisplayName(attachment.name, attachment.type)} - + {formatFileSize(attachment.size)}
@@ -4672,7 +4672,7 @@ export function EmailViewer({ {getAttachmentDisplayName(attachment.name, attachment.type)} - + {formatFileSize(attachment.size)}
@@ -4815,7 +4815,7 @@ export function EmailViewer({ disabled={isSendingQuickReply} className="text-muted-foreground" > - + {t('more_options')} @@ -488,13 +488,13 @@ function EmailCard({
{/* Card Header - Always visible */} )} @@ -671,7 +671,7 @@ function EmailCard({ }} className="flex-1" > - + {t("email_viewer.reply_all")} )} @@ -685,7 +685,7 @@ function EmailCard({ }} className="flex-1" > - + {t("email_viewer.forward")} )} diff --git a/components/email/thread-email-item.tsx b/components/email/thread-email-item.tsx index 11cfd637..bd35f4c5 100644 --- a/components/email/thread-email-item.tsx +++ b/components/email/thread-email-item.tsx @@ -86,8 +86,8 @@ export function ThreadEmailItem({ {...longPressHandlers} className={cn( "relative cursor-pointer select-none transition-all duration-150", - "pl-12 pr-4", - "border-l-2 border-l-transparent", + "ps-12 pe-4", + "border-s-2 border-l-transparent", selected ? "bg-selection border-l-primary" : "hover:bg-muted/50", @@ -130,7 +130,7 @@ export function ThreadEmailItem({ {/* Unread indicator */} {isUnread && ( -
+
)} diff --git a/components/email/thread-list-item.tsx b/components/email/thread-list-item.tsx index 27a81714..8e44f2a2 100644 --- a/components/email/thread-list-item.tsx +++ b/components/email/thread-list-item.tsx @@ -218,7 +218,7 @@ const SingleEmailItem = React.forwardRef( )} {isUnread && ( -
+
)} @@ -643,7 +643,7 @@ export const ThreadListItem = React.forwardRef +
)} @@ -896,7 +896,7 @@ export const ThreadListItem = React.forwardRef {isLoading ? (
- + {t('loading')}
) : ( diff --git a/components/email/unsubscribe-banner.tsx b/components/email/unsubscribe-banner.tsx index 1f63a0e4..00736e69 100644 --- a/components/email/unsubscribe-banner.tsx +++ b/components/email/unsubscribe-banner.tsx @@ -96,7 +96,7 @@ export function UnsubscribeBanner({ if (success) { return ( - + {t(unsubMethod === 'http' @@ -110,7 +110,7 @@ export function UnsubscribeBanner({ if (error) { return ( - +
@@ -34,13 +34,13 @@ export function PageErrorFallback({ error: _error, resetError, t }: FallbackProp */ export function SidebarErrorFallback({ resetError, t }: FallbackProps) { return ( -
+

{t("sidebar_error")}

@@ -58,7 +58,7 @@ export function EmailListErrorFallback({ resetError, t }: FallbackProps) { {t("email_list_error")}

@@ -81,7 +81,7 @@ export function EmailViewerErrorFallback({ resetError, t }: FallbackProps) { {t("viewer_error_description")}

@@ -119,7 +119,7 @@ export function SettingsErrorFallback({ resetError, t }: FallbackProps) { {t("settings_error_description")}

diff --git a/components/files/file-browser.tsx b/components/files/file-browser.tsx index b712c158..10fbd330 100644 --- a/components/files/file-browser.tsx +++ b/components/files/file-browser.tsx @@ -821,8 +821,8 @@ export function FileBrowser({ const SortIndicator = ({ column }: { column: SortKey }) => { if (sortKey !== column) return null; return sortDir === "asc" - ? - : ; + ? + : ; }; // Keyboard shortcuts @@ -942,7 +942,7 @@ export function FileBrowser({ @@ -1003,7 +1003,7 @@ export function FileBrowser({ className="h-8" onClick={onPaste} > - + {t("paste")} ({clipboard.names.length}) )} @@ -1157,7 +1157,7 @@ export function FileBrowser({ className="h-7 text-destructive hover:text-destructive shrink-0" onClick={onRefresh} > - + {t("retry")}
@@ -1181,12 +1181,12 @@ export function FileBrowser({ {t("uploading")} {uploadProgress.name} {uploadProgress.totalFiles > 1 && ( - + ({uploadProgress.current}/{uploadProgress.totalFiles}) )} - + {uploadProgress.total > 0 ? `${Math.round((uploadProgress.loaded / uploadProgress.total) * 100)}%` : "…"} @@ -1267,7 +1267,7 @@ export function FileBrowser({ )} {/* Favorites & Recent sidebar (when layout is inline) */} {folderLayout === "inline" && (favorites.length > 0 || recentFiles.length > 0) && ( -
+
{favorites.length > 0 && (

@@ -1280,7 +1280,7 @@ export function FileBrowser({ key={fav} onClick={() => onNavigate(fav)} className={cn( - "w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-left", + "w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm hover:bg-muted transition-colors text-start", currentPath === fav && "bg-muted font-medium" )} > @@ -1301,7 +1301,7 @@ export function FileBrowser({ {recentFiles.slice(0, 10).map((recent) => (