@@ -22,6 +22,15 @@ import type {
2222import { formatDownloadSize } from "../../../utils/formatting" ;
2323import { buildShortcutValue , formatShortcut } from "../../../utils/shortcuts" ;
2424import { 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
2635const 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