fix(fonts): prefer installed JetBrains Mono/Inter over bundled variable webfonts (#2311)

* fix(fonts): prefer installed JetBrains Mono/Inter over bundled variable webfonts

When the user has JetBrains Mono or Inter installed, the system outline
fonts carry TrueType hinting bytecode that the bundled fontsource variable
webfonts lack, so the installed face renders crisper at editor sizes and
matches IntelliJ IDEA. Keep the variable faces as fallbacks so machines
without a system install keep rendering (#26).

Fixes #2305

* test(e2e): expect installed Inter before bundled Inter Variable (#2305)

* test(e2e): match computed-style serialization in the Inter font assertion (#2305)

* chore: trigger CI (no runs created for 950cb4b4)
Цей коміт міститься в:
arookieofc authored and GitHub committed 2026-11-09 19:43:48 +08:00
1 джерело 1ae8572021
коміт f72e3a9a8a
5 files changed
+34 -30

No files matched your search

+9 -9
Переглянути файл
@@ -302,7 +302,7 @@ describe("split layout", () => {
});
describe("terminalFont", () => {
const BASE = '"JetBrains Mono Variable", "JetBrains Mono", ui-monospace, Menlo, monospace';
const BASE = '"JetBrains Mono", "JetBrains Mono Variable", ui-monospace, Menlo, monospace';
const t = {
useEditorFont: true,
fontFamily: "JetBrains Mono",
@@ -320,13 +320,13 @@ describe("terminalFont", () => {
"editor font, default family",
t,
editor,
`"JetBrains Mono Variable", "JetBrains Mono", ${BASE.replace(", monospace", "")}, `,
`"JetBrains Mono", "JetBrains Mono Variable", ${BASE.replace(", monospace", "")}, `,
],
[
"editor font, no editor settings",
t,
undefined,
`"JetBrains Mono Variable", "JetBrains Mono", ${BASE.replace(", monospace", "")}, `,
`"JetBrains Mono", "JetBrains Mono Variable", ${BASE.replace(", monospace", "")}, `,
],
[
"editor font, custom family",
@@ -338,13 +338,13 @@ describe("terminalFont", () => {
"editor font, editor fallback",
t,
{ ...editor, fallbackFont: "PingFang SC" },
`"JetBrains Mono Variable", "JetBrains Mono", "PingFang SC", ${BASE.replace(", monospace", "")}, `,
`"JetBrains Mono", "JetBrains Mono Variable", "PingFang SC", ${BASE.replace(", monospace", "")}, `,
],
[
"own font, default family",
{ ...t, useEditorFont: false },
editor,
`"JetBrains Mono Variable", "JetBrains Mono", ${BASE.replace(", monospace", "")}, `,
`"JetBrains Mono", "JetBrains Mono Variable", ${BASE.replace(", monospace", "")}, `,
],
[
"own font, custom + fallback",
@@ -360,14 +360,14 @@ describe("terminalFont", () => {
});
it("resolves bundled Inter in inherited, independent and fallback selections", () => {
expect(terminalFont(t, { ...editor, fontFamily: "Inter" }, BASE).family).toMatch(/^"Inter Variable", "Inter",/);
expect(terminalFont(t, { ...editor, fontFamily: "Inter" }, BASE).family).toMatch(/^"Inter", "Inter Variable",/);
expect(terminalFont({ ...t, useEditorFont: false, fontFamily: "Inter" }, editor, BASE).family).toMatch(
/^"Inter Variable", "Inter",/,
/^"Inter", "Inter Variable",/,
);
expect(terminalFont({ ...t, fallbackFontFamily: "Inter" }, editor, BASE).family).toContain(
'"Inter Variable", "Inter"',
'"Inter", "Inter Variable"',
);
expect(terminalFont(t, { ...editor, fallbackFont: "Inter" }, BASE).family).toContain('"Inter Variable", "Inter"');
expect(terminalFont(t, { ...editor, fallbackFont: "Inter" }, BASE).family).toContain('"Inter", "Inter Variable"');
});
it("prefers the terminal's fallback over the editor's", () => {
+14 -13
Переглянути файл
@@ -6,14 +6,15 @@ const BASE = "var(--font-mono)";
describe("monoFontFamily", () => {
it.each([
// #26: the default family names the bundled face, so it renders without a system install.
["JetBrains Mono", "", `"JetBrains Mono Variable", "JetBrains Mono", ${BASE}`],
["", "", `"JetBrains Mono Variable", "JetBrains Mono", ${BASE}`],
[undefined, "", `"JetBrains Mono Variable", "JetBrains Mono", ${BASE}`],
["JetBrains Mono", "PingFang SC", `"JetBrains Mono Variable", "JetBrains Mono", "PingFang SC", ${BASE}`],
["Inter", "", `"Inter Variable", "Inter", ${BASE}`],
["Fira Code", "Inter", `"Fira Code", "Inter Variable", "Inter", ${BASE}`],
["Inter", "JetBrains Mono", `"Inter Variable", "Inter", "JetBrains Mono Variable", "JetBrains Mono", ${BASE}`],
["inter", "jetbrains mono", `"Inter Variable", "Inter", "JetBrains Mono Variable", "JetBrains Mono", ${BASE}`],
// #2305: the installed family comes first, so its hinted outlines render like IDEA's.
["JetBrains Mono", "", `"JetBrains Mono", "JetBrains Mono Variable", ${BASE}`],
["", "", `"JetBrains Mono", "JetBrains Mono Variable", ${BASE}`],
[undefined, "", `"JetBrains Mono", "JetBrains Mono Variable", ${BASE}`],
["JetBrains Mono", "PingFang SC", `"JetBrains Mono", "JetBrains Mono Variable", "PingFang SC", ${BASE}`],
["Inter", "", `"Inter", "Inter Variable", ${BASE}`],
["Fira Code", "Inter", `"Fira Code", "Inter", "Inter Variable", ${BASE}`],
["Inter", "JetBrains Mono", `"Inter", "Inter Variable", "JetBrains Mono", "JetBrains Mono Variable", ${BASE}`],
["inter", "jetbrains mono", `"Inter", "Inter Variable", "JetBrains Mono", "JetBrains Mono Variable", ${BASE}`],
["Fira Code", "", `"Fira Code", ${BASE}`],
['"Cascadia Code"', " Microsoft YaHei ", `"Cascadia Code", "Microsoft YaHei", ${BASE}`],
])("%j + fallback %j", (family, fallback, expected) => {
@@ -36,11 +37,11 @@ it("escapes CSS special characters without turning a family into a list", () =>
describe("uiFontFamily", () => {
it.each([
["Inter", "", '"Inter Variable", "Inter", system-ui, sans-serif'],
["JetBrains Mono", "", '"JetBrains Mono Variable", "JetBrains Mono", system-ui, sans-serif'],
["Inter", "", '"Inter", "Inter Variable", system-ui, sans-serif'],
["JetBrains Mono", "", '"JetBrains Mono", "JetBrains Mono Variable", system-ui, sans-serif'],
[' "PingFang SC" ', "", '"PingFang SC", system-ui, sans-serif'],
["Inter", "思源黑体", '"Inter Variable", "Inter", "思源黑体", system-ui, sans-serif'],
["Inter", " ", '"Inter Variable", "Inter", system-ui, sans-serif'],
["Inter", "思源黑体", '"Inter", "Inter Variable", "思源黑体", system-ui, sans-serif'],
["Inter", " ", '"Inter", "Inter Variable", system-ui, sans-serif'],
])("%j + %j", (family, fallback, expected) => {
expect(uiFontFamily(family, fallback)).toBe(expected);
});
@@ -131,7 +132,7 @@ describe("macOS user font resolver", () => {
it("loads a UI fallback through the shared macOS user font resolver", async () => {
const { fonts, invoke, registered } = await resolver(false);
const family = fonts.uiFontFamily("Inter", "Maple Mono NF CN");
expect(family).toBe('"Inter Variable", "Inter", "OpenIDEA User Font 0", "Maple Mono NF CN", system-ui, sans-serif');
expect(family).toBe('"Inter", "Inter Variable", "OpenIDEA User Font 0", "Maple Mono NF CN", system-ui, sans-serif');
expect(fonts.monoFontFamily("Inter", "Maple Mono NF CN")).toContain('"OpenIDEA User Font 0"');
await fonts.fontsLoaded(family, 13);
expect(invoke).toHaveBeenCalledExactlyOnceWith("user_font_data", { family: "Maple Mono NF CN" });
+8 -6
Переглянути файл
@@ -4,20 +4,22 @@ import { guessOs } from "./platformInfo";
/**
* Monospace font-family lists shared by every consumer of the editor / terminal
* font. "JetBrains Mono" is the bundled default, but the bundle registers it as
* "JetBrains Mono Variable" (`@fontsource-variable/jetbrains-mono`); a list that
* only names "JetBrains Mono" falls through to the system's generic monospace
* wherever the font isn't installed (SimSun on Chinese Windows).
* "JetBrains Mono Variable" (`@fontsource-variable/jetbrains-mono`); the bundled
* variable face stays in the list so the family renders without a system install
* (SimSun on Chinese Windows otherwise, #26), but the installed family comes
* first: its hinted static outlines render like IntelliJ IDEA's, while the
* unhinted variable webfont looks blurred next to them at editor sizes (#2305).
*/
export const BUNDLED_MONO_FAMILY = "JetBrains Mono";
export const BUNDLED_UI_FAMILY = "Inter";
const BUNDLED_FACES: ReadonlyMap<string, readonly string[]> = new Map([
[BUNDLED_MONO_FAMILY.toLowerCase(), ['"JetBrains Mono Variable"', '"JetBrains Mono"']],
[BUNDLED_MONO_FAMILY.toLowerCase(), ['"JetBrains Mono"', '"JetBrains Mono Variable"']],
["jetbrains mono variable", ['"JetBrains Mono Variable"']],
["inter variable", ['"Inter Variable"']],
[BUNDLED_UI_FAMILY.toLowerCase(), ['"Inter Variable"', '"Inter"']],
[BUNDLED_UI_FAMILY.toLowerCase(), ['"Inter"', '"Inter Variable"']],
]);
const MONO_STACK_FALLBACK = '"JetBrains Mono Variable", "JetBrains Mono", ui-monospace, Menlo, monospace';
const MONO_STACK_FALLBACK = '"JetBrains Mono", "JetBrains Mono Variable", ui-monospace, Menlo, monospace';
/** Quote one CSS family safely (including quotes, backslashes and control characters). */
export const quoteFontFamily = (family: string) =>
+1 -1
Переглянути файл
@@ -247,7 +247,7 @@ html {
--font-ui: "Inter Variable", Inter, system-ui, -apple-system, sans-serif;
/* Shortcut glyphs (⌘⇧⌥⌃↵) render best in the system UI font, as in macOS menus. */
--font-keys: -apple-system, BlinkMacSystemFont, "Segoe UI Symbol", system-ui, sans-serif;
--font-mono: "JetBrains Mono Variable", "JetBrains Mono", ui-monospace, Menlo, monospace;
--font-mono: "JetBrains Mono", "JetBrains Mono Variable", ui-monospace, Menlo, monospace;
--fs-ui: 13px;
--fs-small: 12px;
--fs-tiny: 11px;