Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions scripts/patch-linux-window-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ const {
applyBrowserAnnotationScreenshotPatch,
applyLinuxAppSunsetPatch,
applyLinuxConfigWriteVersionConflictPatch,
applyLinuxI18nGatePatch,
applyLinuxProfileSettingsMenuPatch,
applyLinuxOpaqueWindowsDefaultPatch,
applyLinuxFastModeModelGuardPatch,
applySubagentNicknameMetadataPatch,
Expand Down Expand Up @@ -168,6 +170,8 @@ module.exports = {
applyLinuxChromeNativeHostRuntimePatch,
applyLinuxChromePluginAutoInstallPatch,
applyLinuxConfigWriteVersionConflictPatch,
applyLinuxI18nGatePatch,
applyLinuxProfileSettingsMenuPatch,
applyLinuxComputerUseFeaturePatch,
applyLinuxComputerUseInstallFlowPatch,
applyLinuxComputerUsePluginGatePatch,
Expand Down
52 changes: 52 additions & 0 deletions scripts/patch-linux-window-ui.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ const {
applyPersistentRateLimitFooterPatch,
applyLinuxAppServerFeatureEnablementPatch,
applyLinuxConfigWriteVersionConflictPatch,
applyLinuxI18nGatePatch,
applyLinuxProfileSettingsMenuPatch,
applyLinuxSafeMonospaceFontStackPatch,
} = require("./patches/webview-assets.js");
const { patchAssetFiles } = require("./patches/shared.js");
Expand Down Expand Up @@ -562,6 +564,8 @@ test("default core patch descriptors are grouped and unique", () => {
"linux-launch-actions",
"linux-hotkey-window-prewarm",
"linux-git-origins-source-fallback",
"linux-i18n-gate",
"linux-profile-settings-menu",
"automation-schedule-multi-time-rrule",
"linux-app-sunset-gate",
"linux-app-server-feature-enablement",
Expand Down Expand Up @@ -1958,6 +1962,54 @@ test("warns when the app sunset key is present but the gate shape drifts", () =>
]);
});

test("allows explicit locale overrides through the webview i18n provider gate on Linux", () => {
const source =
"function eP(e){let a=Ma(`72216192`),o;o=a?.get(`enable_i18n`,!1);let c=o,l=a?.get(`locale_source`,`IDE`),u=js(s.localeOverride);return c?u:null}";

const patched = applyPatchTwice(applyLinuxI18nGatePatch, source);

assert.match(patched, /o=a\?\.get\(`enable_i18n`,!1\);let l=a\?\.get\(`locale_source`,`IDE`\),u=js\(s\.localeOverride\),c=o\|\|u!=null/);
assert.equal((patched.match(/js\(s\.localeOverride\)/g) ?? []).length, 1);
assert.match(patched, /localeOverride/);
});

test("keeps React compiler cache hook order in the webview i18n provider gate patch", () => {
const source =
"function eP(e){let t=(0,Z.c)(21),a=Ma(`72216192`),o;t[0]===a?o=t[1]:(o=a?.get(`enable_i18n`,!1),t[0]=a,t[1]=o);let c=o,l=a?.get(`locale_source`,`IDE`),u=js(s.localeOverride),d=r?.ideLocale;return c?u:d}";

const patched = applyPatchTwice(applyLinuxI18nGatePatch, source);

assert.match(
patched,
/o=a\?\.get\(`enable_i18n`,!1\),t\[0\]=a,t\[1\]=o\);let l=a\?\.get\(`locale_source`,`IDE`\),u=js\(s\.localeOverride\),c=o\|\|u!=null/,
);
assert.equal((patched.match(/js\(s\.localeOverride\)/g) ?? []).length, 1);
});

test("allows explicit locale overrides through the settings language row i18n gate on Linux", () => {
const source =
"function Or(){let r=F(),i=re(`72216192`)?.get(`enable_i18n`,!0),s=H(t.localeOverride);if(!i)return null;return r.locale+s}";

const patched = applyPatchTwice(applyLinuxI18nGatePatch, source);

assert.match(
patched,
/i=re\(`72216192`\)\?\.get\(`enable_i18n`,!0\),s=H\(t\.localeOverride\);i=i\|\|s!=null;if\(!i\)/,
);
assert.equal((patched.match(/H\(t\.localeOverride\)/g) ?? []).length, 1);
});

test("shows the profile dropdown settings route on Linux", () => {
const source =
"function E(){let Ct=se(`4166894088`),Pt=Ct,Ft=f(De,`settings`),U=Pt&&(0,C.jsx)(S,{LeftIcon:ye,keyboardShortcut:Ft,onClick:()=>{v(`/settings/general-settings`)},children:(0,C.jsx)(g,{id:`codex.profileDropdown.settingsPage`,defaultMessage:`Settings`})});return U}";

const patched = applyPatchTwice(applyLinuxProfileSettingsMenuPatch, source);

assert.match(patched, /let Ct=!0,Pt=Ct,Ft=f\(De,`settings`\)/);
assert.match(patched, /\/settings\/general-settings/);
assert.match(patched, /codex\.profileDropdown\.settingsPage/);
});

test("removes unsupported features from default app-server feature sync", () => {
const source = [
"var GF=[`apps`,`auth_elicitation`,`enable_mcp_apps`,`memories`,`mentions_v2`,`plugins`,`remote_control`,`tool_call_mcp_elicitation`,`tool_search`,`tool_suggest`,te];",
Expand Down
16 changes: 16 additions & 0 deletions scripts/patches/core/all-linux/webview/i18n-gate/patch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use strict";

const { applyLinuxI18nGatePatch } = require("../../../../webview-assets.js");

module.exports = [
{
id: "linux-i18n-gate",
phase: "webview-asset",
order: 1042,
ciPolicy: "optional",
pattern: /^(app-main|general-settings)-.*\.js$/,
missingDescription: "i18n-gated webview bundle",
skipDescription: "Linux i18n gate patch",
apply: applyLinuxI18nGatePatch,
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use strict";

const { applyLinuxProfileSettingsMenuPatch } = require("../../../../webview-assets.js");

module.exports = [
{
id: "linux-profile-settings-menu",
phase: "webview-asset",
order: 1043,
ciPolicy: "optional",
pattern: /^profile-dropdown-.*\.js$/,
missingDescription: "profile dropdown webview bundle",
skipDescription: "Linux profile settings menu patch",
apply: applyLinuxProfileSettingsMenuPatch,
},
];
78 changes: 78 additions & 0 deletions scripts/patches/webview-assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,82 @@ function applyLinuxAppServerFeatureEnablementPatch(currentSource) {
].join("");
}

function applyLinuxI18nGatePatch(currentSource) {
let patchedSource = currentSource.replace(
/([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*\?\.get\(`enable_i18n`,!1\)(?:,[^;]+?)?);let ([A-Za-z_$][\w$]*)=\1,([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*\?\.get\(`locale_source`,`IDE`\)),([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)\(([A-Za-z_$][\w$]*)\.localeOverride\)/g,
(
_match,
gateVar,
gateExpression,
enabledVar,
localeSourceVar,
localeSourceExpression,
localeOverrideVar,
readLocaleOverrideVar,
settingsVar,
) =>
`${gateVar}=${gateExpression};let ${localeSourceVar}=${localeSourceExpression},${localeOverrideVar}=${readLocaleOverrideVar}(${settingsVar}.localeOverride),${enabledVar}=${gateVar}||${localeOverrideVar}!=null`,
);

patchedSource = patchedSource.replace(
/([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*\([^)]*\)\?\.get\(`enable_i18n`,!0\))((?:,\[[^\]]+\]=[^;]+?)),([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)\(([A-Za-z_$][\w$]*)\.localeOverride\),([A-Za-z_$][\w$]*);/g,
(
_match,
gateVar,
gateExpression,
betweenGateAndOverride,
localeOverrideVar,
readLocaleOverrideVar,
settingsVar,
nextVar,
) =>
`${gateVar}=${gateExpression}${betweenGateAndOverride},${localeOverrideVar}=${readLocaleOverrideVar}(${settingsVar}.localeOverride);${gateVar}=${gateVar}||${localeOverrideVar}!=null;let ${nextVar};`,
);

patchedSource = patchedSource.replace(
/([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*\([^)]*\)\?\.get\(`enable_i18n`,!0\)),([A-Za-z_$][\w$]*)=([A-Za-z_$][\w$]*)\(([A-Za-z_$][\w$]*)\.localeOverride\);/g,
(
match,
gateVar,
gateExpression,
localeOverrideVar,
readLocaleOverrideVar,
settingsVar,
offset,
source,
) => {
const appliedMarker = `${gateVar}=${gateVar}||${localeOverrideVar}!=null;`;
if (source.startsWith(appliedMarker, offset + match.length)) {
return match;
}
return `${gateVar}=${gateExpression},${localeOverrideVar}=${readLocaleOverrideVar}(${settingsVar}.localeOverride);${appliedMarker}`;
},
);

if (currentSource.includes("enable_i18n") && patchedSource === currentSource) {
console.warn("WARN: Could not find i18n gate needle — skipping Linux i18n gate patch");
}

return patchedSource;
}

function applyLinuxProfileSettingsMenuPatch(currentSource) {
if (!currentSource.includes("codex.profileDropdown.settingsPage")) {
return currentSource;
}

const patchedSource = currentSource.replace(
/([A-Za-z_$][\w$]*)=[A-Za-z_$][\w$]*\(`4166894088`\)/g,
"$1=!0",
);

if (currentSource.includes("4166894088") && patchedSource === currentSource) {
console.warn("WARN: Could not find profile settings menu gate needle — skipping Linux settings menu patch");
}

return patchedSource;
}

function applyLinuxConfigWriteVersionConflictPatch(currentSource) {
if (!currentSource.includes("expectedVersion:")) {
return currentSource;
Expand Down Expand Up @@ -966,6 +1042,8 @@ module.exports = {
applyBrowserAnnotationScreenshotPatch,
applyLinuxAppServerFeatureEnablementPatch,
applyLinuxConfigWriteVersionConflictPatch,
applyLinuxI18nGatePatch,
applyLinuxProfileSettingsMenuPatch,
applyPersistentRateLimitFooterPatch,
applyLinuxAppSunsetPatch,
applyLinuxOpaqueWindowsDefaultPatch,
Expand Down
Loading