Skip to content

Commit fe04368

Browse files
committed
fix: rebase the i18n changes onto current main
The first commit was built from a stale source snapshot, so several files silently reverted recent upstream work - most notably the separate-fonts handling in startClientAppearance, which broke typecheck (missing required 'separate' in AppearanceFontPreferences) and made the Separate fonts setting stop affecting the composer font and prompt size. This restores every file to the current upstream main and reapplies only the i18n changes on top: the uiLanguage setting, the language picker, translated settings chrome (sidebar nav, breadcrumb, command palette entries), and the translated Appearance, typography, and thread-details rows. Repo-wide typecheck passes again and the settings-search, command-palette, and i18n tests stay green. Powered by GLM-5.3-Flash (bigmodel) via ZCode.
1 parent 4b3a023 commit fe04368

8 files changed

Lines changed: 100 additions & 89 deletions

File tree

‎apps/web/src/appInit.tsx‎

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { scopedThreadKey, scopeThreadRef } from "@t3tools/client-runtime/environment";
22
import type { ClientSettings } from "@t3tools/contracts/settings";
33
import { createBrowserHistory, createHashHistory } from "@tanstack/react-router";
4+
import * as Schema from "effect/Schema";
45

56
import { applyAppearanceContrast } from "./appearanceContrast";
6-
import { applyAppearanceFontVariables } from "./appearanceFonts";
7+
import { applyAppearanceFontVariables, TYPOGRAPHY_ADVANCED_STORAGE_KEY } from "./appearanceFonts";
78
import { startElectronBrowserHost } from "./browser/ElectronBrowserHost";
89
import { startBrowserProfileReporting } from "./browser/BrowserProfileReporter";
910
import { startPreviewTitleRecording } from "./browserHistoryStore";
@@ -31,6 +32,7 @@ import {
3132
import { startDefaultThemeAdoption } from "./hooks/useDefaultTheme";
3233
import { startEnvironmentThemeRepaint } from "./hooks/useEnvironmentTheme";
3334
import { getUiLocale } from "./hooks/useI18n";
35+
import { readLocalStorageValue, subscribeLocalStorageKey } from "./hooks/useLocalStorage";
3436
import {
3537
getClientSettings,
3638
startModelPreferencesMigration,
@@ -59,10 +61,18 @@ import { resolveThreadRouteTarget } from "./threadRoutes";
5961
function startClientAppearance(): void {
6062
const root = document.documentElement;
6163
let applied: ClientSettings | null = null;
64+
let appliedSeparateFonts: boolean | null = null;
6265
const apply = () => {
6366
const settings = getClientSettings();
6467
const previous = applied;
6568
applied = settings;
69+
const separateFonts = readLocalStorageValue(
70+
TYPOGRAPHY_ADVANCED_STORAGE_KEY,
71+
false,
72+
Schema.Boolean,
73+
);
74+
const previousSeparateFonts = appliedSeparateFonts;
75+
appliedSeparateFonts = separateFonts;
6676
if (previous?.diffColorScheme !== settings.diffColorScheme) {
6777
root.dataset.diffColorScheme = settings.diffColorScheme;
6878
}
@@ -87,7 +97,8 @@ function startClientAppearance(): void {
8797
previous.fontSizeInterface !== settings.fontSizeInterface ||
8898
previous.fontSizePrompt !== settings.fontSizePrompt ||
8999
previous.fontSizeCode !== settings.fontSizeCode ||
90-
previous.fontSmoothing !== settings.fontSmoothing
100+
previous.fontSmoothing !== settings.fontSmoothing ||
101+
previousSeparateFonts !== separateFonts
91102
) {
92103
applyAppearanceFontVariables(root, {
93104
sans: settings.fontFamilySans,
@@ -97,10 +108,12 @@ function startClientAppearance(): void {
97108
sizePrompt: settings.fontSizePrompt,
98109
sizeCode: settings.fontSizeCode,
99110
smoothing: settings.fontSmoothing,
111+
separate: separateFonts,
100112
});
101113
}
102114
};
103115
subscribeClientSettings(apply);
116+
subscribeLocalStorageKey(TYPOGRAPHY_ADVANCED_STORAGE_KEY, apply);
104117
apply();
105118
}
106119

‎apps/web/src/components/CommandPalette.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ import { useAtomValue } from "@effect/atom-react";
8888
import { isDesktopLocalConnectionTarget } from "../connection/desktopLocal";
8989
import { useDesktopLocalBootstraps } from "../connection/useDesktopLocalBootstraps";
9090
import { useHandleNewThread } from "../hooks/useHandleNewThread";
91+
import { useT } from "../hooks/useI18n";
9192
import { useOpenPanelPullRequestUrl } from "../hooks/useOpenPanelPullRequestUrl";
9293
import { copyThreadReference } from "../lib/copyThreadReference";
9394
import {
@@ -99,7 +100,6 @@ import {
99100
copyPromptLink,
100101
promptLinkRepositoryOf,
101102
} from "./promptLinks/copyPromptLink";
102-
import { useT } from "../hooks/useI18n";
103103
import { useClientSettings } from "../hooks/useSettings";
104104
import { useStableCallback } from "../hooks/useStableCallback";
105105
import { readLocalApi } from "../localApi";

‎apps/web/src/components/settings/AppearanceSettingsPanel.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,8 @@ export function AppearanceSettingsPanel() {
469469
title={t("settings.interface.environmentIdentification.title")}
470470
description={
471471
environmentStageVariant
472-
? t("settings.interface.environmentIdentification.description.stages")
473-
: t("settings.interface.environmentIdentification.description.header")
472+
? "Choose how Dev and Nightly environments are identified."
473+
: "Show Dev or Nightly artwork in the sidebar header."
474474
}
475475
resetAction={
476476
settings.environmentIdentificationMode !== DEFAULT_ENVIRONMENT_IDENTIFICATION_MODE ? (

‎apps/web/src/components/settings/SettingsBreadcrumb.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import type { MessageKey } from "@t3tools/client-runtime/i18n";
22

3-
import { useT } from "../../hooks/useI18n";
43
import {
54
WorkspaceBreadcrumb,
65
WorkspaceBreadcrumbItem,
76
WorkspaceBreadcrumbSeparator,
87
} from "../WorkspaceBreadcrumb";
8+
import { useT } from "../../hooks/useI18n";
99
import { SETTINGS_SECTION_LABEL_KEYS } from "./settingsSearch";
1010

1111
const SETTINGS_BREADCRUMB_LABEL_KEYS: Readonly<Record<string, MessageKey>> = {

‎apps/web/src/components/settings/SettingsSidebarNav.tsx‎

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ import {
3333
XIcon,
3434
} from "lucide-react";
3535
import { useLocation, useNavigate } from "@tanstack/react-router";
36-
import type { MessageKey, TranslateFn } from "@t3tools/client-runtime/i18n";
3736

38-
import { useT } from "../../hooks/useI18n";
3937
import { cn } from "../../lib/utils";
4038
import { Button } from "../ui/button";
4139
import { Kbd } from "../ui/kbd";
@@ -57,6 +55,8 @@ import {
5755
type SettingsPath,
5856
type SettingsSearchItem,
5957
} from "./settingsSearch";
58+
import { useT } from "../../hooks/useI18n";
59+
import type { MessageKey } from "@t3tools/client-runtime/i18n";
6060
import { useAvailableSettingsSearchItems } from "./useAvailableSettingsSearchItems";
6161
import { validateSettingsScopeSearch } from "./settingsScope";
6262
import { useSettingsSectionNavigation } from "./useSettingsSectionNavigation";
@@ -124,12 +124,6 @@ const SETTINGS_GROUP_HEADING_KEYS: Readonly<Record<string, MessageKey>> = {
124124
Connections: "settings.nav.group.connections",
125125
};
126126

127-
function settingsGroupHeading(heading: string | null, t: TranslateFn): string | undefined {
128-
if (heading === null) return undefined;
129-
const key = SETTINGS_GROUP_HEADING_KEYS[heading];
130-
return key === undefined ? heading : t(key);
131-
}
132-
133127
// Attached only to the highlighted result, so it runs whenever the highlight lands on a row.
134128
function scrollResultIntoView(row: HTMLElement | null) {
135129
row?.scrollIntoView({ block: "nearest" });
@@ -395,53 +389,63 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
395389
aria-label={t("settings.nav.sectionsAria")}
396390
className="flex flex-col gap-4"
397391
>
398-
{SETTINGS_NAV_GROUPS.map((group, index) => (
399-
<div
400-
key={group.heading ?? index}
401-
role="group"
402-
aria-label={settingsGroupHeading(group.heading, t)}
403-
className="flex flex-col gap-1 max-md:border-t max-md:border-sidebar-border max-md:pt-4 max-md:first:border-t-0 max-md:first:pt-0"
404-
>
405-
{group.heading ? (
406-
// The group carries this label; a heading is not a valid tree child.
407-
<h3
408-
aria-hidden="true"
409-
className={cn(
410-
"px-2 text-xs font-medium text-sidebar-muted-foreground/80",
411-
!compactSearchOpen && "max-md:hidden",
412-
)}
413-
>
414-
{settingsGroupHeading(group.heading, t)}
415-
</h3>
416-
) : null}
417-
<SidebarMenu role="presentation">
418-
{group.paths.map((to) => {
419-
const Icon = SETTINGS_SECTION_ICONS[to];
420-
const isActive = activePath === to || activePath.startsWith(`${to}/`);
421-
return (
422-
<SidebarMenuItem key={to} role="presentation">
423-
<SidebarMenuButton
424-
data-settings-section={to}
425-
role="treeitem"
426-
tabIndex={sectionNavigation.tabIndexFor(to)}
427-
aria-selected={isActive}
428-
aria-current={isActive ? "page" : undefined}
429-
isActive={isActive}
430-
onClick={() => handleSectionClick(to)}
431-
>
432-
<Icon />
433-
<span
434-
className={cn("truncate", !compactSearchOpen && "max-md:sr-only")}
392+
{SETTINGS_NAV_GROUPS.map((group, index) => {
393+
const heading =
394+
group.heading === null
395+
? undefined
396+
: SETTINGS_GROUP_HEADING_KEYS[group.heading];
397+
const headingLabel = heading ? t(heading) : group.heading;
398+
return (
399+
<div
400+
key={group.heading ?? index}
401+
role="group"
402+
aria-label={headingLabel ?? undefined}
403+
className="flex flex-col gap-1 max-md:border-t max-md:border-sidebar-border max-md:pt-4 max-md:first:border-t-0 max-md:first:pt-0"
404+
>
405+
{headingLabel ? (
406+
// The group carries this label; a heading is not a valid tree child.
407+
<h3
408+
aria-hidden="true"
409+
className={cn(
410+
"px-2 text-xs font-medium text-sidebar-muted-foreground/80",
411+
!compactSearchOpen && "max-md:hidden",
412+
)}
413+
>
414+
{headingLabel}
415+
</h3>
416+
) : null}
417+
<SidebarMenu role="presentation">
418+
{group.paths.map((to) => {
419+
const Icon = SETTINGS_SECTION_ICONS[to];
420+
const isActive = activePath === to || activePath.startsWith(`${to}/`);
421+
return (
422+
<SidebarMenuItem key={to} role="presentation">
423+
<SidebarMenuButton
424+
data-settings-section={to}
425+
role="treeitem"
426+
tabIndex={sectionNavigation.tabIndexFor(to)}
427+
aria-selected={isActive}
428+
aria-current={isActive ? "page" : undefined}
429+
isActive={isActive}
430+
onClick={() => handleSectionClick(to)}
435431
>
436-
{t(SETTINGS_SECTION_LABEL_KEYS[to])}
437-
</span>
438-
</SidebarMenuButton>
439-
</SidebarMenuItem>
440-
);
441-
})}
442-
</SidebarMenu>
443-
</div>
444-
))}
432+
<Icon />
433+
<span
434+
className={cn(
435+
"truncate",
436+
!compactSearchOpen && "max-md:sr-only",
437+
)}
438+
>
439+
{t(SETTINGS_SECTION_LABEL_KEYS[to])}
440+
</span>
441+
</SidebarMenuButton>
442+
</SidebarMenuItem>
443+
);
444+
})}
445+
</SidebarMenu>
446+
</div>
447+
);
448+
})}
445449
</div>
446450
</div>
447451
)}

‎apps/web/src/components/settings/TypographySettings.tsx‎

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,15 @@ import {
4545
} from "./settingsSearch";
4646

4747
function useFontDefaultFamilies() {
48-
const t = useT();
4948
const settings = useScopedSettings();
50-
// The family resolution probes canvas metrics, so run it once per mount.
51-
const [resolved] = useState(() => ({
52-
sans: resolveDefaultFamilyLabel(DEFAULT_SANS_FONT_STACK),
53-
code: resolveDefaultFamilyLabel(DEFAULT_CODE_FONT_STACK),
49+
const [defaults] = useState(() => ({
50+
sans: resolveDefaultFamilyLabel(DEFAULT_SANS_FONT_STACK) ?? "System default",
51+
code: resolveDefaultFamilyLabel(DEFAULT_CODE_FONT_STACK) ?? "System monospace",
5452
}));
55-
const sans = resolved.sans ?? t("settings.typography.systemDefault");
56-
const code = resolved.code ?? t("settings.typography.systemMonospace");
5753
return {
58-
sans,
59-
code,
60-
interfaceFamily: settings.fontFamilySans.trim() || sans,
54+
sans: defaults.sans,
55+
code: defaults.code,
56+
interfaceFamily: settings.fontFamilySans.trim() || defaults.sans,
6157
};
6258
}
6359

@@ -71,8 +67,7 @@ function InterfaceFontRow({ preview }: { preview?: ReactNode }) {
7167
{...searchableSetting("interface-font")}
7268
title={t("settings.typography.interfaceFont.title")}
7369
description={t("settings.typography.interfaceFont.description")}
74-
familyAriaLabel={t("settings.typography.interfaceFont.familyA11y")}
75-
resetLabel="interface font"
70+
familyA11yLabel={t("settings.typography.interfaceFont.familyA11y")}
7671
defaultFamily={defaults.sans}
7772
defaultValue={DEFAULT_UNIFIED_SETTINGS.fontFamilySans}
7873
value={settings.fontFamilySans}
@@ -106,8 +101,7 @@ function PromptFontRow() {
106101
{...searchableSetting("prompt-font")}
107102
title={t("settings.typography.promptFont.title")}
108103
description={t("settings.typography.promptFont.description")}
109-
familyAriaLabel={t("settings.typography.promptFont.familyA11y")}
110-
resetLabel="prompt font"
104+
familyA11yLabel={t("settings.typography.promptFont.familyA11y")}
111105
defaultFamily={defaults.interfaceFamily}
112106
defaultValue={DEFAULT_UNIFIED_SETTINGS.fontFamilyComposer}
113107
value={settings.fontFamilyComposer}
@@ -134,10 +128,12 @@ function PromptFontRow() {
134128
function CodeFontRow({
135129
title,
136130
description,
131+
familyA11yLabel,
137132
preview,
138133
}: {
139134
title?: string;
140135
description?: string;
136+
familyA11yLabel?: string;
141137
preview?: ReactNode;
142138
}) {
143139
const t = useT();
@@ -147,10 +143,9 @@ function CodeFontRow({
147143
return (
148144
<FontFamilySettingsRow
149145
{...searchableSetting("code-font")}
150-
title={title ?? t("settings.typography.codeFont.title")}
146+
{...(title !== undefined ? { title } : {})}
151147
description={description ?? t("settings.typography.codeFont.description")}
152-
familyAriaLabel={t("settings.typography.codeFont.familyA11y")}
153-
resetLabel="code font"
148+
familyA11yLabel={familyA11yLabel ?? t("settings.typography.codeFont.familyA11y")}
154149
defaultFamily={defaults.code}
155150
defaultValue={DEFAULT_UNIFIED_SETTINGS.fontFamilyCode}
156151
value={settings.fontFamilyCode}
@@ -185,8 +180,7 @@ function TerminalFontRow() {
185180
{...searchableSetting("terminal-font")}
186181
title={t("settings.typography.terminalFont.title")}
187182
description={t("settings.typography.terminalFont.description")}
188-
familyAriaLabel={t("settings.typography.terminalFont.familyA11y")}
189-
resetLabel="terminal font"
183+
familyA11yLabel={t("settings.typography.terminalFont.familyA11y")}
190184
defaultFamily={defaults.code}
191185
defaultValue={DEFAULT_UNIFIED_SETTINGS.fontFamilyTerminal}
192186
value={settings.fontFamilyTerminal}
@@ -323,6 +317,7 @@ function SimpleFontRows() {
323317
<CodeFontRow
324318
title={t("settings.typography.monospaceFont.title")}
325319
description={t("settings.typography.monospaceFont.description")}
320+
familyA11yLabel={t("settings.typography.monospaceFont.familyA11y")}
326321
preview={
327322
<>
328323
<CodeFontPreview />
@@ -379,9 +374,8 @@ export function TypographySection() {
379374
function FontFamilySettingsRow({
380375
id,
381376
title,
377+
familyA11yLabel,
382378
description,
383-
familyAriaLabel,
384-
resetLabel,
385379
defaultFamily,
386380
defaultValue,
387381
preview,
@@ -393,9 +387,8 @@ function FontFamilySettingsRow({
393387
}: {
394388
id?: string;
395389
title: string;
390+
familyA11yLabel: string;
396391
description: string;
397-
familyAriaLabel: string;
398-
resetLabel: string;
399392
defaultFamily: string;
400393
defaultValue: string;
401394
preview?: ReactNode;
@@ -461,14 +454,14 @@ function FontFamilySettingsRow({
461454
};
462455
const resetAction =
463456
value !== defaultValue || size.value !== size.defaultValue ? (
464-
<SettingResetButton label={resetLabel} onClick={resetToDefault} />
457+
<SettingResetButton label={title.toLowerCase()} onClick={resetToDefault} />
465458
) : null;
466459
const fontEnumeration = useFontEnumeration();
467460
const [inputFocused, setInputFocused] = useState(false);
468461
const familyControl =
469462
fontEnumeration.status === "granted" ? (
470463
<FontFamilyPicker
471-
ariaLabel={familyAriaLabel}
464+
ariaLabel={familyA11yLabel}
472465
defaultFamily={defaultFamily}
473466
selectedFamily={trimmed}
474467
requireMonospace={requireMonospace}
@@ -478,7 +471,7 @@ function FontFamilySettingsRow({
478471
) : (
479472
<Input
480473
size="sm"
481-
aria-label={familyAriaLabel}
474+
aria-label={familyA11yLabel}
482475
aria-invalid={draftPending || undefined}
483476
autoCapitalize="off"
484477
autoComplete="off"

0 commit comments

Comments
 (0)