Derive the terminal IME platform from the renderer's own mapping

Enumerating platform names in a second place is what let one user agent
read as Linux everywhere except the policy that cared.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Neil
2026-08-01 22:21:13 -07:00
co-authored by Orca
parent 4fafbf5fe6
commit 85e54fd573
3 changed files with 47 additions and 14 deletions
@@ -1,4 +1,5 @@
import { describe, expect, it } from 'vitest'
import { resolveShortcutPlatform } from '@/lib/shortcut-platform'
import { resolveTerminalImePlatform } from './terminal-ime-platform'
const DESKTOP_LINUX =
@@ -11,6 +12,8 @@ const MACOS =
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'
const WINDOWS =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'
const FREEBSD =
'Mozilla/5.0 (X11; FreeBSD amd64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'
describe('resolveTerminalImePlatform', () => {
it('treats desktop Linux as both Chromium-family and ibus/fcitx-bearing', () => {
@@ -44,4 +47,25 @@ describe('resolveTerminalImePlatform', () => {
isDesktopLinux: false
})
})
// Only Windows has a reason to suppress a standalone 229, so an unrecognised desktop
// belongs on the passing side rather than in whichever bucket a UA guess lands it in.
it('treats an unrecognised desktop as Linux', () => {
expect(resolveTerminalImePlatform(FREEBSD)).toEqual({
isMac: false,
isLinux: true,
isDesktopLinux: true
})
})
it.each([DESKTOP_LINUX, CHROME_OS, ANDROID, MACOS, WINDOWS, FREEBSD])(
'agrees with the renderer-wide platform mapping (%#)',
(userAgent) => {
const { isMac, isLinux } = resolveTerminalImePlatform(userAgent)
expect({ isMac, isLinux }).toEqual({
isMac: resolveShortcutPlatform(userAgent) === 'darwin',
isLinux: resolveShortcutPlatform(userAgent) === 'linux'
})
}
)
})
@@ -1,30 +1,34 @@
import { resolveShortcutPlatform } from '@/lib/shortcut-platform'
export type TerminalImePlatform = {
isMac: boolean
/** Chromium on a Linux kernel — ChromeOS and Android included. */
/** Everything that is neither macOS nor Windows, matching `getShortcutPlatform`. */
isLinux: boolean
/** Desktop Linux only — the platforms actually running ibus or fcitx. */
isDesktopLinux: boolean
}
// ChromeOS reports `X11; CrOS x86_64`, with no "Linux" anywhere in it, so a plain
// substring test puts it on the Windows side of every policy below.
const LINUX_KERNEL_PLATFORM = /Linux|CrOS/
const NON_DESKTOP_LINUX_PLATFORM = /Android|CrOS/
/**
* Splits the two platform questions the terminal's IME policy asks, which are not the
* same question and had been sharing one answer.
*
* Standalone keydown 229 is Chromium-family behavior, so ChromeOS and Android belong
* with desktop Linux. The candidate-key guards read ibus/fcitx specifics that neither
* shares, so they are scoped tighter. Deriving both here keeps one user agent from
* getting two different platform verdicts in two different files.
* Standalone keydown 229 is really a question about Windows, whose preedit-diff race is
* the only reason to suppress it, so every other platform passes. Sharing the renderer's
* one user-agent mapping is what keeps that set from drifting: enumerating Linux-ish
* user agents here instead is how ChromeOS — which reports `X11; CrOS x86_64`, with no
* "Linux" in it — ended up on the Windows side of this policy while the rest of the
* renderer treated it as Linux.
*
* The candidate-key guards read ibus/fcitx specifics that ChromeOS and Android do not
* share, so they are scoped tighter. That narrowing is the only intended difference.
*/
export function resolveTerminalImePlatform(userAgent: string): TerminalImePlatform {
const isMac = userAgent.includes('Mac')
const isLinux = !isMac && LINUX_KERNEL_PLATFORM.test(userAgent)
const platform = resolveShortcutPlatform(userAgent)
const isLinux = platform === 'linux'
return {
isMac,
isMac: platform === 'darwin',
isLinux,
isDesktopLinux: isLinux && !NON_DESKTOP_LINUX_PLATFORM.test(userAgent)
}
+8 -3
View File
@@ -1,6 +1,11 @@
export function getShortcutPlatform(): NodeJS.Platform {
if (navigator.userAgent.includes('Mac')) {
/** Maps a user agent to the platform whose conventions the renderer should follow. */
export function resolveShortcutPlatform(userAgent: string): NodeJS.Platform {
if (userAgent.includes('Mac')) {
return 'darwin'
}
return navigator.userAgent.includes('Windows') ? 'win32' : 'linux'
return userAgent.includes('Windows') ? 'win32' : 'linux'
}
export function getShortcutPlatform(): NodeJS.Platform {
return resolveShortcutPlatform(navigator.userAgent)
}