diff --git a/src/renderer/src/components/settings/FontAutocomplete.tsx b/src/renderer/src/components/settings/FontAutocomplete.tsx
index 10aadb07022..6e39e9dadeb 100644
--- a/src/renderer/src/components/settings/FontAutocomplete.tsx
+++ b/src/renderer/src/components/settings/FontAutocomplete.tsx
@@ -7,6 +7,27 @@ import { ScrollArea } from '../ui/scroll-area'
import { filterFontSuggestions, getRenderedFontSuggestions } from './settings-form-option-filter'
import { useInstalledFontsUnavailable } from './installed-font-list'
import { translate } from '@/i18n/i18n'
+import { buildAppFontFamily } from '@/lib/app-font-family'
+
+// Why: these OS-shipped fonts draw pictographs or Greek for Latin letters, so their own name would be unreadable.
+const SYMBOL_FONT_FAMILIES = new Set([
+ 'webdings',
+ 'wingdings',
+ 'wingdings 2',
+ 'wingdings 3',
+ 'marlett',
+ 'symbol',
+ 'mt extra',
+ 'bookshelf symbol 7',
+ 'd050000l',
+ 'standard symbols ps'
+])
+
+function fontPreviewStyle(font: string): React.CSSProperties | undefined {
+ return font && !SYMBOL_FONT_FAMILIES.has(font.toLowerCase())
+ ? { fontFamily: buildAppFontFamily(font) }
+ : undefined
+}
type FontAutocompleteProps = {
value: string
@@ -354,13 +375,16 @@ export function FontAutocomplete({
onMouseDown={(e) => e.preventDefault()}
onMouseEnter={() => setHighlightedIndex(sourceIndex)}
onClick={() => commitValue(font)}
- className={`flex w-full items-center justify-between rounded-sm px-3 py-2 text-left text-sm transition-colors ${
+ // Why content-visibility: each row loads its own font, so skip offscreen rows until scrolled to; 20px is the line height inside py-2.
+ className={`flex w-full items-center justify-between rounded-sm px-3 py-2 text-left text-sm transition-colors [contain-intrinsic-size:auto_20px] [content-visibility:auto] ${
sourceIndex === highlightedIndex
? 'bg-accent text-accent-foreground'
: 'hover:bg-muted/60'
}`}
>
- {font || defaultOptionLabel}
+
+ {font || defaultOptionLabel}
+
{font === value ? : null}
))
diff --git a/src/renderer/src/components/settings/SettingsFormControls.font-autocomplete.test.tsx b/src/renderer/src/components/settings/SettingsFormControls.font-autocomplete.test.tsx
index 7e45aaf7853..dfdbb68e419 100644
--- a/src/renderer/src/components/settings/SettingsFormControls.font-autocomplete.test.tsx
+++ b/src/renderer/src/components/settings/SettingsFormControls.font-autocomplete.test.tsx
@@ -148,6 +148,33 @@ describe('FontAutocomplete', () => {
expectOptionsToBePortaled()
})
+ it('renders each option name in its own typeface and leaves the input in the UI font', async () => {
+ const fonts = ['Arial', 'Fira Code', 'Georgia']
+
+ function Harness(): ReactNode {
+ const [value, setValue] = useState('Arial')
+ return
+ }
+
+ await act(async () => {
+ root.render()
+ })
+ await act(async () => {
+ getInput().focus()
+ })
+
+ const optionFamilies = Array.from(
+ document.querySelectorAll('[role="option"]')
+ ).map((option) => option.querySelector('span')?.style.fontFamily ?? '')
+
+ const primaryFamilies = optionFamilies.map((family) =>
+ family.split(',')[0]?.replaceAll('"', '')
+ )
+
+ expect(primaryFamilies).toEqual(fonts)
+ expect(getInput().style.fontFamily).toBe('')
+ })
+
it('bounds the portaled list to the available popover height', async () => {
function Harness(): ReactNode {
const [value, setValue] = useState('Cascadia Mono')