authorgravatar for 71498452+StiensWout@users.noreply.github.comWout Stiens <71498452+StiensWout@users.noreply.github.com> 2026-08-11 17:21:58+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-11 17:21:58+02:00
logac4780f451f98c10d5b518f2bfa3d035b46645df
tree528e761c98f4ed04201efac5f84a430476c51bf7
parent3da7f9c5c35d0c1f2a1b4420fbc10016e11ce069
signature Signed by PGP key B5690EEEBB952194

fix(web): restore typography font sizes to defaults (#6172)


3 files changed, 110 insertions(+), 16 deletions(-)

apps/web/src/components/settings/SettingsPanels.logic.test.ts+14
...@@ -12,12 +12,26 @@ import {...@@ -12,12 +12,26 @@ import {
12 backgroundActivitySharedPolicySettings,12 backgroundActivitySharedPolicySettings,
13 buildProviderInstanceUpdatePatch,13 buildProviderInstanceUpdatePatch,
14 formatDiagnosticsDescription,14 formatDiagnosticsDescription,
15 getChangedTypographySettingLabels,
15 hasChangedBackgroundActivitySettings,16 hasChangedBackgroundActivitySettings,
16 isProjectGroupingEnabled,17 isProjectGroupingEnabled,
17 projectGroupingModeFromToggle,18 projectGroupingModeFromToggle,
18 resolveBackgroundActivityProfileOption,19 resolveBackgroundActivityProfileOption,
19} from "./SettingsPanels.logic";20} from "./SettingsPanels.logic";
2021
22describe("typography settings restore", () => {
23 it("detects family and size changes by font row", () => {
24 expect(getChangedTypographySettingLabels(DEFAULT_UNIFIED_SETTINGS)).toEqual([]);
25 expect(
26 getChangedTypographySettingLabels({
27 ...DEFAULT_UNIFIED_SETTINGS,
28 fontSizeInterface: 18,
29 fontFamilyCode: "Fira Code",
30 }),
31 ).toEqual(["Interface font", "Code font"]);
32 });
33});
34
21describe("background activity settings restore", () => {35describe("background activity settings restore", () => {
22 it("detects legacy interval values even when the structured setting is at its default", () => {36 it("detects legacy interval values even when the structured setting is at its default", () => {
23 expect(37 expect(
apps/web/src/components/settings/SettingsPanels.logic.ts+34
...@@ -74,6 +74,40 @@ export function hasChangedBackgroundActivitySettings(...@@ -74,6 +74,40 @@ export function hasChangedBackgroundActivitySettings(
74 );74 );
75}75}
7676
77type TypographySettings = Pick<
78 UnifiedSettings,
79 | "fontFamilySans"
80 | "fontFamilyComposer"
81 | "fontFamilyCode"
82 | "fontFamilyTerminal"
83 | "fontSizeInterface"
84 | "fontSizePrompt"
85 | "fontSizeCode"
86 | "fontSizeTerminal"
87>;
88
89/** Labels the font rows whose family or size differs from the defaults. */
90export function getChangedTypographySettingLabels(settings: TypographySettings): string[] {
91 return [
92 ...(settings.fontFamilySans !== DEFAULT_UNIFIED_SETTINGS.fontFamilySans ||
93 settings.fontSizeInterface !== DEFAULT_UNIFIED_SETTINGS.fontSizeInterface
94 ? ["Interface font"]
95 : []),
96 ...(settings.fontFamilyComposer !== DEFAULT_UNIFIED_SETTINGS.fontFamilyComposer ||
97 settings.fontSizePrompt !== DEFAULT_UNIFIED_SETTINGS.fontSizePrompt
98 ? ["Prompt font"]
99 : []),
100 ...(settings.fontFamilyCode !== DEFAULT_UNIFIED_SETTINGS.fontFamilyCode ||
101 settings.fontSizeCode !== DEFAULT_UNIFIED_SETTINGS.fontSizeCode
102 ? ["Code font"]
103 : []),
104 ...(settings.fontFamilyTerminal !== DEFAULT_UNIFIED_SETTINGS.fontFamilyTerminal ||
105 settings.fontSizeTerminal !== DEFAULT_UNIFIED_SETTINGS.fontSizeTerminal
106 ? ["Terminal font"]
107 : []),
108 ];
109}
110
77export function resolveBackgroundActivityProfileOption(111export function resolveBackgroundActivityProfileOption(
78 settings: ServerSettings,112 settings: ServerSettings,
79): BackgroundActivityProfile | "advanced" {113): BackgroundActivityProfile | "advanced" {
apps/web/src/components/settings/SettingsPanels.tsx+62-16
...@@ -122,6 +122,7 @@ import {...@@ -122,6 +122,7 @@ import {
122 backgroundActivitySharedPolicySettings,122 backgroundActivitySharedPolicySettings,
123 durationToSeconds,123 durationToSeconds,
124 formatDiagnosticsDescription,124 formatDiagnosticsDescription,
125 getChangedTypographySettingLabels,
125 normalizeIntervalSeconds,126 normalizeIntervalSeconds,
126 PROVIDER_HEALTH_INTERVAL_STEP_SECONDS,127 PROVIDER_HEALTH_INTERVAL_STEP_SECONDS,
127 hasChangedBackgroundActivitySettings,128 hasChangedBackgroundActivitySettings,
...@@ -493,16 +494,7 @@ export function useSettingsRestore(onRestored?: () => void) {...@@ -493,16 +494,7 @@ export function useSettingsRestore(onRestored?: () => void) {
493 ? ["Auto-settle inactive threads"]494 ? ["Auto-settle inactive threads"]
494 : []),495 : []),
495 ...(settings.wordWrap !== DEFAULT_UNIFIED_SETTINGS.wordWrap ? ["Word wrap"] : []),496 ...(settings.wordWrap !== DEFAULT_UNIFIED_SETTINGS.wordWrap ? ["Word wrap"] : []),
496 ...(settings.fontFamilySans !== DEFAULT_UNIFIED_SETTINGS.fontFamilySans497 ...getChangedTypographySettingLabels(settings),
497 ? ["Interface font"]
498 : []),
499 ...(settings.fontFamilyComposer !== DEFAULT_UNIFIED_SETTINGS.fontFamilyComposer
500 ? ["Prompt font"]
501 : []),
502 ...(settings.fontFamilyCode !== DEFAULT_UNIFIED_SETTINGS.fontFamilyCode ? ["Code font"] : []),
503 ...(settings.fontFamilyTerminal !== DEFAULT_UNIFIED_SETTINGS.fontFamilyTerminal
504 ? ["Terminal font"]
505 : []),
506 ...(settings.diffIgnoreWhitespace !== DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace498 ...(settings.diffIgnoreWhitespace !== DEFAULT_UNIFIED_SETTINGS.diffIgnoreWhitespace
507 ? ["Diff whitespace changes"]499 ? ["Diff whitespace changes"]
508 : []),500 : []),
...@@ -652,6 +644,10 @@ export function useSettingsRestore(onRestored?: () => void) {...@@ -652,6 +644,10 @@ export function useSettingsRestore(onRestored?: () => void) {
652 fontFamilyComposer: DEFAULT_UNIFIED_SETTINGS.fontFamilyComposer,644 fontFamilyComposer: DEFAULT_UNIFIED_SETTINGS.fontFamilyComposer,
653 fontFamilyCode: DEFAULT_UNIFIED_SETTINGS.fontFamilyCode,645 fontFamilyCode: DEFAULT_UNIFIED_SETTINGS.fontFamilyCode,
654 fontFamilyTerminal: DEFAULT_UNIFIED_SETTINGS.fontFamilyTerminal,646 fontFamilyTerminal: DEFAULT_UNIFIED_SETTINGS.fontFamilyTerminal,
647 fontSizeInterface: DEFAULT_UNIFIED_SETTINGS.fontSizeInterface,
648 fontSizePrompt: DEFAULT_UNIFIED_SETTINGS.fontSizePrompt,
649 fontSizeCode: DEFAULT_UNIFIED_SETTINGS.fontSizeCode,
650 fontSizeTerminal: DEFAULT_UNIFIED_SETTINGS.fontSizeTerminal,
655 });651 });
656 onRestored?.();652 onRestored?.();
657 }, [653 }, [
...@@ -1105,13 +1101,21 @@ function InterfaceFontRow({ preview }: { preview?: ReactNode }) {...@@ -1105,13 +1101,21 @@ function InterfaceFontRow({ preview }: { preview?: ReactNode }) {
1105 {...searchableSetting("interface-font")}1101 {...searchableSetting("interface-font")}
1106 description="Everything outside code blocks and the terminal."1102 description="Everything outside code blocks and the terminal."
1107 defaultFamily={defaults.sans}1103 defaultFamily={defaults.sans}
1104 defaultValue={DEFAULT_UNIFIED_SETTINGS.fontFamilySans}
1108 value={settings.fontFamilySans}1105 value={settings.fontFamilySans}
1109 onValueChange={(fontFamilySans) => updateSettings({ fontFamilySans })}1106 onValueChange={(fontFamilySans) => updateSettings({ fontFamilySans })}
1107 onReset={() =>
1108 updateSettings({
1109 fontFamilySans: DEFAULT_UNIFIED_SETTINGS.fontFamilySans,
1110 fontSizeInterface: DEFAULT_UNIFIED_SETTINGS.fontSizeInterface,
1111 })
1112 }
1110 size={{1113 size={{
1111 label: "Interface font size",1114 label: "Interface font size",
1112 min: MIN_INTERFACE_FONT_SIZE,1115 min: MIN_INTERFACE_FONT_SIZE,
1113 max: MAX_INTERFACE_FONT_SIZE,1116 max: MAX_INTERFACE_FONT_SIZE,
1114 value: settings.fontSizeInterface,1117 value: settings.fontSizeInterface,
1118 defaultValue: DEFAULT_UNIFIED_SETTINGS.fontSizeInterface,
1115 onChange: (fontSizeInterface) => updateSettings({ fontSizeInterface }),1119 onChange: (fontSizeInterface) => updateSettings({ fontSizeInterface }),
1116 }}1120 }}
1117 {...(preview !== undefined ? { preview } : {})}1121 {...(preview !== undefined ? { preview } : {})}
...@@ -1128,13 +1132,21 @@ function PromptFontRow() {...@@ -1128,13 +1132,21 @@ function PromptFontRow() {
1128 {...searchableSetting("prompt-font")}1132 {...searchableSetting("prompt-font")}
1129 description="Only the box you write prompts in. Mono works well here."1133 description="Only the box you write prompts in. Mono works well here."
1130 defaultFamily={defaults.interfaceFamily}1134 defaultFamily={defaults.interfaceFamily}
1135 defaultValue={DEFAULT_UNIFIED_SETTINGS.fontFamilyComposer}
1131 value={settings.fontFamilyComposer}1136 value={settings.fontFamilyComposer}
1132 onValueChange={(fontFamilyComposer) => updateSettings({ fontFamilyComposer })}1137 onValueChange={(fontFamilyComposer) => updateSettings({ fontFamilyComposer })}
1138 onReset={() =>
1139 updateSettings({
1140 fontFamilyComposer: DEFAULT_UNIFIED_SETTINGS.fontFamilyComposer,
1141 fontSizePrompt: DEFAULT_UNIFIED_SETTINGS.fontSizePrompt,
1142 })
1143 }
1133 size={{1144 size={{
1134 label: "Prompt font size",1145 label: "Prompt font size",
1135 min: MIN_PROMPT_FONT_SIZE,1146 min: MIN_PROMPT_FONT_SIZE,
1136 max: MAX_PROMPT_FONT_SIZE,1147 max: MAX_PROMPT_FONT_SIZE,
1137 value: settings.fontSizePrompt,1148 value: settings.fontSizePrompt,
1149 defaultValue: DEFAULT_UNIFIED_SETTINGS.fontSizePrompt,
1138 onChange: (fontSizePrompt) => updateSettings({ fontSizePrompt }),1150 onChange: (fontSizePrompt) => updateSettings({ fontSizePrompt }),
1139 }}1151 }}
1140 preview={<PromptFontPreview />}1152 preview={<PromptFontPreview />}
...@@ -1160,14 +1172,22 @@ function CodeFontRow({...@@ -1160,14 +1172,22 @@ function CodeFontRow({
1160 {...(title !== undefined ? { title } : {})}1172 {...(title !== undefined ? { title } : {})}
1161 description={description}1173 description={description}
1162 defaultFamily={defaults.code}1174 defaultFamily={defaults.code}
1175 defaultValue={DEFAULT_UNIFIED_SETTINGS.fontFamilyCode}
1163 value={settings.fontFamilyCode}1176 value={settings.fontFamilyCode}
1164 onValueChange={(fontFamilyCode) => updateSettings({ fontFamilyCode })}1177 onValueChange={(fontFamilyCode) => updateSettings({ fontFamilyCode })}
1178 onReset={() =>
1179 updateSettings({
1180 fontFamilyCode: DEFAULT_UNIFIED_SETTINGS.fontFamilyCode,
1181 fontSizeCode: DEFAULT_UNIFIED_SETTINGS.fontSizeCode,
1182 })
1183 }
1165 requireMonospace1184 requireMonospace
1166 size={{1185 size={{
1167 label: "Code font size",1186 label: "Code font size",
1168 min: MIN_CODE_FONT_SIZE,1187 min: MIN_CODE_FONT_SIZE,
1169 max: MAX_CODE_FONT_SIZE,1188 max: MAX_CODE_FONT_SIZE,
1170 value: settings.fontSizeCode,1189 value: settings.fontSizeCode,
1190 defaultValue: DEFAULT_UNIFIED_SETTINGS.fontSizeCode,
1171 onChange: (fontSizeCode) => updateSettings({ fontSizeCode }),1191 onChange: (fontSizeCode) => updateSettings({ fontSizeCode }),
1172 }}1192 }}
1173 preview={preview ?? <CodeFontPreview />}1193 preview={preview ?? <CodeFontPreview />}
...@@ -1184,14 +1204,22 @@ function TerminalFontRow() {...@@ -1184,14 +1204,22 @@ function TerminalFontRow() {
1184 {...searchableSetting("terminal-font")}1204 {...searchableSetting("terminal-font")}
1185 description="Terminal output, independent from code blocks and diffs."1205 description="Terminal output, independent from code blocks and diffs."
1186 defaultFamily={defaults.code}1206 defaultFamily={defaults.code}
1207 defaultValue={DEFAULT_UNIFIED_SETTINGS.fontFamilyTerminal}
1187 value={settings.fontFamilyTerminal}1208 value={settings.fontFamilyTerminal}
1188 onValueChange={(fontFamilyTerminal) => updateSettings({ fontFamilyTerminal })}1209 onValueChange={(fontFamilyTerminal) => updateSettings({ fontFamilyTerminal })}
1210 onReset={() =>
1211 updateSettings({
1212 fontFamilyTerminal: DEFAULT_UNIFIED_SETTINGS.fontFamilyTerminal,
1213 fontSizeTerminal: DEFAULT_UNIFIED_SETTINGS.fontSizeTerminal,
1214 })
1215 }
1189 requireMonospace1216 requireMonospace
1190 size={{1217 size={{
1191 label: "Terminal font size",1218 label: "Terminal font size",
1192 min: MIN_TERMINAL_FONT_SIZE,1219 min: MIN_TERMINAL_FONT_SIZE,
1193 max: MAX_TERMINAL_FONT_SIZE,1220 max: MAX_TERMINAL_FONT_SIZE,
1194 value: settings.fontSizeTerminal,1221 value: settings.fontSizeTerminal,
1222 defaultValue: DEFAULT_UNIFIED_SETTINGS.fontSizeTerminal,
1195 onChange: (fontSizeTerminal) => updateSettings({ fontSizeTerminal }),1223 onChange: (fontSizeTerminal) => updateSettings({ fontSizeTerminal }),
1196 }}1224 }}
1197 preview={1225 preview={
...@@ -1369,9 +1397,11 @@ function FontFamilySettingsRow({...@@ -1369,9 +1397,11 @@ function FontFamilySettingsRow({
1369 title,1397 title,
1370 description,1398 description,
1371 defaultFamily,1399 defaultFamily,
1400 defaultValue,
1372 preview,1401 preview,
1373 value,1402 value,
1374 onValueChange,1403 onValueChange,
1404 onReset,
1375 requireMonospace = false,1405 requireMonospace = false,
1376 size,1406 size,
1377}: {1407}: {
...@@ -1380,11 +1410,21 @@ function FontFamilySettingsRow({...@@ -1380,11 +1410,21 @@ function FontFamilySettingsRow({
1380 description: string;1410 description: string;
1381 /** What an unset preference renders as, e.g. "Menlo". */1411 /** What an unset preference renders as, e.g. "Menlo". */
1382 defaultFamily: string;1412 defaultFamily: string;
1413 /** The persisted family value supplied by the unified settings defaults. */
1414 defaultValue: string;
1383 preview?: ReactNode;1415 preview?: ReactNode;
1384 value: string;1416 value: string;
1385 onValueChange: (value: string) => void;1417 onValueChange: (value: string) => void;
1418 onReset: () => void;
1386 requireMonospace?: boolean;1419 requireMonospace?: boolean;
1387 size: { label: string; min: number; max: number; value: number; onChange: (v: number) => void };1420 size: {
1421 label: string;
1422 min: number;
1423 max: number;
1424 value: number;
1425 defaultValue: number;
1426 onChange: (v: number) => void;
1427 };
1388}) {1428}) {
1389 const trimmed = value.trim();1429 const trimmed = value.trim();
1390 // The fallback input edits a draft; the preference only commits once typing1430 // The fallback input edits a draft; the preference only commits once typing
...@@ -1431,12 +1471,18 @@ function FontFamilySettingsRow({...@@ -1431,12 +1471,18 @@ function FontFamilySettingsRow({
1431 // Flag an unknown name only once typing pauses, and never for an empty1471 // Flag an unknown name only once typing pauses, and never for an empty
1432 // field - that is the starting state, not a rejected entry.1472 // field - that is the starting state, not a rejected entry.
1433 const draftPending = draftSettled && draftTrimmed.length > 0 && draftTrimmed !== trimmed;1473 const draftPending = draftSettled && draftTrimmed.length > 0 && draftTrimmed !== trimmed;
1474 const resetToDefault = () => {
1475 if (commitTimerRef.current !== null) {
1476 window.clearTimeout(commitTimerRef.current);
1477 commitTimerRef.current = null;
1478 }
1479 setDraft(defaultValue);
1480 setDraftSettled(true);
1481 onReset();
1482 };
1434 const resetAction =1483 const resetAction =
1435 trimmed.length > 0 ? (1484 value !== defaultValue || size.value !== size.defaultValue ? (
1436 <SettingResetButton1485 <SettingResetButton label={title.toLowerCase()} onClick={resetToDefault} />
1437 label={`${title.toLowerCase()} family`}
1438 onClick={() => onValueChange("")}
1439 />
1440 ) : null;1486 ) : null;
1441 const fontEnumeration = useFontEnumeration();1487 const fontEnumeration = useFontEnumeration();
1442 // Everyone starts on the plain input; focusing it is the user gesture that1488 // Everyone starts on the plain input; focusing it is the user gesture that