From 371cc26ca17f9fc17531f02ef4f06cdad0f1a034 Mon Sep 17 00:00:00 2001 From: BingZ Date: Mon, 10 Aug 2026 04:31:22 +0800 Subject: [PATCH] fix(settings): emit Windows font family names as UTF-8 (#12602) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(settings): emit Windows font family names as UTF-8 Windows PowerShell 5.1 can write localized font names with the console code page while Node always decodes stdout as UTF-8, which garbles Korean and other non-ASCII family names in the font picker. Force UTF-8 OutputEncoding before enumerating InstalledFontCollection (#12590). * test(settings): assert UTF-8 pin precedes Windows font enumeration Lock script order so OutputEncoding is set before InstalledFontCollection enumeration, preventing a silent regression of the mojibake fix. * refactor(settings): cut the Windows font UTF-8 pin to the standard shape `$OutputEncoding` only governs bytes piped to a native executable's stdin; this script pipes to ForEach-Object, so it was inert. Drop it, and drop the script-builder export whose only consumer was a test — the one-shot `-Command` shape now matches windows-foreground-process-rows and ssh-browse, while the BOM-less `UTF8Encoding::new($false)` spelling matches powershell-osc133-bootstrap and antigravity/hook-service. The test reaches the script through the public listSystemFontFamilies path and pins the assignment as the script's first statement, so it fails on removal, on a stdout write above it, and on a swapped encoding. --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> --- src/main/system-fonts.test.ts | 19 +++++++++++++++++++ src/main/system-fonts.ts | 3 +++ 2 files changed, 22 insertions(+) diff --git a/src/main/system-fonts.test.ts b/src/main/system-fonts.test.ts index c6c5e0e42c4..d25ef630f86 100644 --- a/src/main/system-fonts.test.ts +++ b/src/main/system-fonts.test.ts @@ -67,6 +67,25 @@ describe('listSystemFontFamilies', () => { killMock.mockReset() }) + it('sets UTF-8 stdout encoding as the first statement of the Windows font script', async () => { + await withPlatform('win32', async () => { + execFileMock.mockImplementation((_cmd, _args, _opts, cb) => { + cb(null, 'Consolas\n') + return { kill: killMock } + }) + const { listSystemFontFamilies } = await import('./system-fonts') + await listSystemFontFamilies() + + const args = (execFileMock.mock.calls[0]?.[1] ?? []) as string[] + const script = args[args.indexOf('-Command') + 1] ?? '' + // Why: match the whole statement, not a substring — anything emitted above it + // still leaves in the OEM code page, and a swapped encoding must not slip by. + expect(script.trim().split(/\r?\n/)[0]).toBe( + '[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)' + ) + }) + }) + it('falls back when the platform font command never exits', async () => { vi.useFakeTimers() execFileMock.mockReturnValue({ kill: killMock }) diff --git a/src/main/system-fonts.ts b/src/main/system-fonts.ts index 20a8764548c..ccc51dae767 100644 --- a/src/main/system-fonts.ts +++ b/src/main/system-fonts.ts @@ -77,7 +77,10 @@ function listLinuxFonts(): Promise { } function listWindowsFonts(): Promise { + // Why: PowerShell 5.1 emits redirected stdout in the OEM code page; pin UTF-8 + // before the first name is written or localized families arrive as mojibake (#12590). const script = ` +[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) Add-Type -AssemblyName System.Drawing $fonts = New-Object System.Drawing.Text.InstalledFontCollection $fonts.Families | ForEach-Object { $_.Name }