Skip to content

Commit 34f9af1

Browse files
committed
fix: restore font settings
1 parent 2842818 commit 34f9af1

2 files changed

Lines changed: 244 additions & 0 deletions

File tree

src/features/settings/components/SettingsView.test.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,69 @@ describe("SettingsView Display", () => {
178178
});
179179
});
180180

181+
it("commits font family changes on blur and enter", async () => {
182+
const onUpdateAppSettings = vi.fn().mockResolvedValue(undefined);
183+
renderDisplaySection({ onUpdateAppSettings });
184+
185+
const uiFontInput = screen.getByLabelText("UI font family");
186+
fireEvent.change(uiFontInput, { target: { value: "Avenir, sans-serif" } });
187+
fireEvent.blur(uiFontInput);
188+
189+
await waitFor(() => {
190+
expect(onUpdateAppSettings).toHaveBeenCalledWith(
191+
expect.objectContaining({ uiFontFamily: "Avenir, sans-serif" }),
192+
);
193+
});
194+
195+
const codeFontInput = screen.getByLabelText("Code font family");
196+
fireEvent.change(codeFontInput, {
197+
target: { value: "JetBrains Mono, monospace" },
198+
});
199+
fireEvent.keyDown(codeFontInput, { key: "Enter" });
200+
201+
await waitFor(() => {
202+
expect(onUpdateAppSettings).toHaveBeenCalledWith(
203+
expect.objectContaining({ codeFontFamily: "JetBrains Mono, monospace" }),
204+
);
205+
});
206+
});
207+
208+
it("resets font families to defaults", async () => {
209+
const onUpdateAppSettings = vi.fn().mockResolvedValue(undefined);
210+
renderDisplaySection({ onUpdateAppSettings });
211+
212+
const resetButtons = screen.getAllByRole("button", { name: "Reset" });
213+
fireEvent.click(resetButtons[1]);
214+
fireEvent.click(resetButtons[2]);
215+
216+
await waitFor(() => {
217+
expect(onUpdateAppSettings).toHaveBeenCalledWith(
218+
expect.objectContaining({
219+
uiFontFamily: expect.stringContaining("SF Pro Text"),
220+
}),
221+
);
222+
expect(onUpdateAppSettings).toHaveBeenCalledWith(
223+
expect.objectContaining({
224+
codeFontFamily: expect.stringContaining("SF Mono"),
225+
}),
226+
);
227+
});
228+
});
229+
230+
it("updates code font size from the slider", async () => {
231+
const onUpdateAppSettings = vi.fn().mockResolvedValue(undefined);
232+
renderDisplaySection({ onUpdateAppSettings });
233+
234+
const slider = screen.getByLabelText("Code font size");
235+
fireEvent.change(slider, { target: { value: "14" } });
236+
237+
await waitFor(() => {
238+
expect(onUpdateAppSettings).toHaveBeenCalledWith(
239+
expect.objectContaining({ codeFontSize: 14 }),
240+
);
241+
});
242+
});
243+
181244
it("toggles notification sounds", async () => {
182245
const onUpdateAppSettings = vi.fn().mockResolvedValue(undefined);
183246
renderDisplaySection({

src/features/settings/components/SettingsView.tsx

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ import type {
2222
import { formatDownloadSize } from "../../../utils/formatting";
2323
import { buildShortcutValue, formatShortcut } from "../../../utils/shortcuts";
2424
import { clampUiScale } from "../../../utils/uiScale";
25+
import {
26+
DEFAULT_CODE_FONT_FAMILY,
27+
DEFAULT_UI_FONT_FAMILY,
28+
CODE_FONT_SIZE_DEFAULT,
29+
CODE_FONT_SIZE_MAX,
30+
CODE_FONT_SIZE_MIN,
31+
clampCodeFontSize,
32+
normalizeFontFamily,
33+
} from "../../../utils/fonts";
2534

2635
const DICTATION_MODELS = [
2736
{ id: "tiny", label: "Tiny", size: "75 MB", note: "Fastest, least accurate." },
@@ -203,6 +212,11 @@ export function SettingsView({
203212
const [scaleDraft, setScaleDraft] = useState(
204213
`${Math.round(clampUiScale(appSettings.uiScale) * 100)}%`,
205214
);
215+
const [uiFontDraft, setUiFontDraft] = useState(appSettings.uiFontFamily);
216+
const [codeFontDraft, setCodeFontDraft] = useState(appSettings.codeFontFamily);
217+
const [codeFontSizeDraft, setCodeFontSizeDraft] = useState(
218+
appSettings.codeFontSize,
219+
);
206220
const [overrideDrafts, setOverrideDrafts] = useState<Record<string, string>>({});
207221
const [groupDrafts, setGroupDrafts] = useState<Record<string, string>>({});
208222
const [newGroupName, setNewGroupName] = useState("");
@@ -259,6 +273,18 @@ export function SettingsView({
259273
setScaleDraft(`${Math.round(clampUiScale(appSettings.uiScale) * 100)}%`);
260274
}, [appSettings.uiScale]);
261275

276+
useEffect(() => {
277+
setUiFontDraft(appSettings.uiFontFamily);
278+
}, [appSettings.uiFontFamily]);
279+
280+
useEffect(() => {
281+
setCodeFontDraft(appSettings.codeFontFamily);
282+
}, [appSettings.codeFontFamily]);
283+
284+
useEffect(() => {
285+
setCodeFontSizeDraft(appSettings.codeFontSize);
286+
}, [appSettings.codeFontSize]);
287+
262288
useEffect(() => {
263289
setShortcutDrafts({
264290
model: appSettings.composerModelShortcut ?? "",
@@ -393,6 +419,48 @@ export function SettingsView({
393419
});
394420
};
395421

422+
const handleCommitUiFont = async () => {
423+
const nextFont = normalizeFontFamily(
424+
uiFontDraft,
425+
DEFAULT_UI_FONT_FAMILY,
426+
);
427+
setUiFontDraft(nextFont);
428+
if (nextFont === appSettings.uiFontFamily) {
429+
return;
430+
}
431+
await onUpdateAppSettings({
432+
...appSettings,
433+
uiFontFamily: nextFont,
434+
});
435+
};
436+
437+
const handleCommitCodeFont = async () => {
438+
const nextFont = normalizeFontFamily(
439+
codeFontDraft,
440+
DEFAULT_CODE_FONT_FAMILY,
441+
);
442+
setCodeFontDraft(nextFont);
443+
if (nextFont === appSettings.codeFontFamily) {
444+
return;
445+
}
446+
await onUpdateAppSettings({
447+
...appSettings,
448+
codeFontFamily: nextFont,
449+
});
450+
};
451+
452+
const handleCommitCodeFontSize = async (nextSize: number) => {
453+
const clampedSize = clampCodeFontSize(nextSize);
454+
setCodeFontSizeDraft(clampedSize);
455+
if (clampedSize === appSettings.codeFontSize) {
456+
return;
457+
}
458+
await onUpdateAppSettings({
459+
...appSettings,
460+
codeFontSize: clampedSize,
461+
});
462+
};
463+
396464
const handleComposerPresetChange = (preset: ComposerPreset) => {
397465
const config = COMPOSER_PRESET_CONFIGS[preset];
398466
void onUpdateAppSettings({
@@ -937,6 +1005,119 @@ export function SettingsView({
9371005
</button>
9381006
</div>
9391007
</div>
1008+
<div className="settings-field">
1009+
<label className="settings-field-label" htmlFor="ui-font-family">
1010+
UI font family
1011+
</label>
1012+
<div className="settings-field-row">
1013+
<input
1014+
id="ui-font-family"
1015+
type="text"
1016+
className="settings-input"
1017+
value={uiFontDraft}
1018+
onChange={(event) => setUiFontDraft(event.target.value)}
1019+
onBlur={() => {
1020+
void handleCommitUiFont();
1021+
}}
1022+
onKeyDown={(event) => {
1023+
if (event.key === "Enter") {
1024+
event.preventDefault();
1025+
void handleCommitUiFont();
1026+
}
1027+
}}
1028+
/>
1029+
<button
1030+
type="button"
1031+
className="ghost settings-button-compact"
1032+
onClick={() => {
1033+
setUiFontDraft(DEFAULT_UI_FONT_FAMILY);
1034+
void onUpdateAppSettings({
1035+
...appSettings,
1036+
uiFontFamily: DEFAULT_UI_FONT_FAMILY,
1037+
});
1038+
}}
1039+
>
1040+
Reset
1041+
</button>
1042+
</div>
1043+
<div className="settings-help">
1044+
Applies to all UI text. Leave empty to use the default system font stack.
1045+
</div>
1046+
</div>
1047+
<div className="settings-field">
1048+
<label className="settings-field-label" htmlFor="code-font-family">
1049+
Code font family
1050+
</label>
1051+
<div className="settings-field-row">
1052+
<input
1053+
id="code-font-family"
1054+
type="text"
1055+
className="settings-input"
1056+
value={codeFontDraft}
1057+
onChange={(event) => setCodeFontDraft(event.target.value)}
1058+
onBlur={() => {
1059+
void handleCommitCodeFont();
1060+
}}
1061+
onKeyDown={(event) => {
1062+
if (event.key === "Enter") {
1063+
event.preventDefault();
1064+
void handleCommitCodeFont();
1065+
}
1066+
}}
1067+
/>
1068+
<button
1069+
type="button"
1070+
className="ghost settings-button-compact"
1071+
onClick={() => {
1072+
setCodeFontDraft(DEFAULT_CODE_FONT_FAMILY);
1073+
void onUpdateAppSettings({
1074+
...appSettings,
1075+
codeFontFamily: DEFAULT_CODE_FONT_FAMILY,
1076+
});
1077+
}}
1078+
>
1079+
Reset
1080+
</button>
1081+
</div>
1082+
<div className="settings-help">
1083+
Applies to git diffs and other mono-spaced readouts.
1084+
</div>
1085+
</div>
1086+
<div className="settings-field">
1087+
<label className="settings-field-label" htmlFor="code-font-size">
1088+
Code font size
1089+
</label>
1090+
<div className="settings-field-row">
1091+
<input
1092+
id="code-font-size"
1093+
type="range"
1094+
min={CODE_FONT_SIZE_MIN}
1095+
max={CODE_FONT_SIZE_MAX}
1096+
step={1}
1097+
className="settings-input settings-input--range"
1098+
value={codeFontSizeDraft}
1099+
onChange={(event) => {
1100+
const nextValue = Number(event.target.value);
1101+
setCodeFontSizeDraft(nextValue);
1102+
void handleCommitCodeFontSize(nextValue);
1103+
}}
1104+
/>
1105+
<div className="settings-scale-value">{codeFontSizeDraft}px</div>
1106+
<button
1107+
type="button"
1108+
className="ghost settings-button-compact"
1109+
onClick={() => {
1110+
setCodeFontSizeDraft(CODE_FONT_SIZE_DEFAULT);
1111+
void handleCommitCodeFontSize(CODE_FONT_SIZE_DEFAULT);
1112+
}}
1113+
>
1114+
Reset
1115+
</button>
1116+
</div>
1117+
<div className="settings-help">
1118+
Adjusts code and diff text size.
1119+
</div>
1120+
</div>
9401121
<div className="settings-subsection-title">Sounds</div>
9411122
<div className="settings-subsection-subtitle">
9421123
Control notification audio alerts.

0 commit comments

Comments
 (0)