diff --git a/scripts/lib/upstream-patches.mjs b/scripts/lib/upstream-patches.mjs index 7b9608f..a4156a1 100644 --- a/scripts/lib/upstream-patches.mjs +++ b/scripts/lib/upstream-patches.mjs @@ -26,7 +26,7 @@ export async function patchUpstreamApp(stageAppDir) { const mainBundlePath = path.join(buildDir, mainBundleName); const source = await fs.readFile(mainBundlePath, "utf8"); - const patched = patchLinuxOpenTargetsSource(source); + const patched = patchUpstreamMainSource(source); if (patched === source) { return; @@ -35,6 +35,15 @@ export async function patchUpstreamApp(stageAppDir) { await fs.writeFile(mainBundlePath, patched); } +export function patchUpstreamMainSource(source) { + let patched = source; + + patched = patchLinuxOpenTargetsSource(patched); + patched = patchDisableTransparencySource(patched); + + return patched; +} + export function patchLinuxOpenTargetsSource(source) { let patched = source; @@ -54,6 +63,74 @@ export function patchLinuxOpenTargetsSource(source) { return patched; } +export function patchDisableTransparencySource(source) { + let patched = source; + + patched = patchLinuxWindowBackground(patched); + patched = patchLinuxWindowTransparency(patched); + + return patched; +} + +function patchLinuxWindowBackground(source) { + if ( + /backgroundColor:[A-Za-z_$][\w$]*===`linux`\?\([A-Za-z_$][\w$]*\?[A-Za-z_$][\w$]*:[A-Za-z_$][\w$]*\):[A-Za-z_$][\w$]*,backgroundMaterial:null/.test(source) + ) { + return source; + } + + const match = source.match( + /function ([A-Za-z_$][\w$]*)\(\{platform:([A-Za-z_$][\w$]*),appearance:([A-Za-z_$][\w$]*),opaqueWindowsEnabled:([A-Za-z_$][\w$]*),prefersDarkColors:([A-Za-z_$][\w$]*)\}\)\{return \4&&!([A-Za-z_$][\w$]*)\(\3\)&&\(\2===`darwin`\|\|\2===`win32`\)\?\{backgroundColor:\5\?([A-Za-z_$][\w$]*):([A-Za-z_$][\w$]*),backgroundMaterial:\2===`win32`\?`none`:null\}:\2===`win32`&&!\6\(\3\)\?\{backgroundColor:([A-Za-z_$][\w$]*),backgroundMaterial:`mica`\}:\{backgroundColor:([A-Za-z_$][\w$]*),backgroundMaterial:null\}\}/ + ); + + if (!match) { + throw new Error("Unable to apply upstream patch; missing window background helper"); + } + + const [ + anchor, + , + platformVar, + , + , + prefersDarkVar, + , + darkColorVar, + lightColorVar, + , + fallbackColorVar + ] = match; + const fallback = `{backgroundColor:${fallbackColorVar},backgroundMaterial:null}`; + const replacement = anchor.replace( + fallback, + `{backgroundColor:${platformVar}===\`linux\`?(${prefersDarkVar}?${darkColorVar}:${lightColorVar}):${fallbackColorVar},backgroundMaterial:null}` + ); + + return replaceOnce(source, anchor, replacement); +} + +function patchLinuxWindowTransparency(source) { + if (/transparent:[A-Za-z_$][\w$]*===`linux`\?!1:[A-Za-z_$][\w$]*,hasShadow:/.test(source)) { + return source; + } + + const match = source.match( + /function ([A-Za-z_$][\w$]*)\(\{alwaysOnTop:([A-Za-z_$][\w$]*),hasShadow:([A-Za-z_$][\w$]*)=!0,platform:([A-Za-z_$][\w$]*),resizable:([A-Za-z_$][\w$]*),thickFrame:([A-Za-z_$][\w$]*),transparent:([A-Za-z_$][\w$]*)=!0\}\)\{return\{frame:!1,transparent:\7,hasShadow:\3,/ + ); + + if (!match) { + throw new Error("Unable to apply upstream patch; missing window transparency helper"); + } + + const [anchor, , , shadowVar, platformVar, , , transparentVar] = match; + const replacement = anchor.replace( + `transparent:${transparentVar},hasShadow:${shadowVar},`, + `transparent:${platformVar}===\`linux\`?!1:${transparentVar},hasShadow:${shadowVar},` + ); + + return replaceOnce(source, anchor, replacement); +} + function patchOpenTargetMap(source) { if (source.includes("appPath:process.platform===`linux`")) { return source; diff --git a/test/upstream-patches.test.mjs b/test/upstream-patches.test.mjs index 4972cc6..5096793 100644 --- a/test/upstream-patches.test.mjs +++ b/test/upstream-patches.test.mjs @@ -1,7 +1,10 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { patchLinuxOpenTargetsSource } from "../scripts/lib/upstream-patches.mjs"; +import { + patchDisableTransparencySource, + patchLinuxOpenTargetsSource +} from "../scripts/lib/upstream-patches.mjs"; test("patchLinuxOpenTargetsSource adds Linux editor targets and exposes app paths", () => { const source = [ @@ -109,3 +112,33 @@ test("patchLinuxOpenTargetsSource preserves native target spread variable", () = ); assert.match(patched, /\}\)\),\.\.\.h\]/); }); + +test("patchDisableTransparencySource disables Linux BrowserWindow transparency and background", () => { + const source = [ + "function A2(e){return e===`avatarOverlay`||e===`browserCommentPopup`}", + "function I2({platform:e,appearance:t,opaqueWindowsEnabled:n,prefersDarkColors:r}){return n&&!A2(t)&&(e===`darwin`||e===`win32`)?{backgroundColor:r?a2:o2,backgroundMaterial:e===`win32`?`none`:null}:e===`win32`&&!A2(t)?{backgroundColor:i2,backgroundMaterial:`mica`}:{backgroundColor:i2,backgroundMaterial:null}}", + "function R2({alwaysOnTop:e,hasShadow:t=!0,platform:n,resizable:r,thickFrame:i,transparent:a=!0}){return{frame:!1,transparent:a,hasShadow:t,resizable:r,minimizable:!1,maximizable:!1,fullscreenable:!1,skipTaskbar:!0,...e?{alwaysOnTop:!0}:{},...n===`win32`?{accentColor:!1,roundedCorners:!1,...i==null?{}:{thickFrame:i}}:{},...n===`darwin`?{type:`panel`}:{}}}", + "function z2({appearance:e,platform:n}){switch(e){case`browserCommentPopup`:return R2({hasShadow:!1,platform:n,resizable:!1,thickFrame:!1,transparent:!0});case`avatarOverlay`:return R2({platform:n,resizable:!1})}}" + ].join(";"); + + const patched = patchDisableTransparencySource(source); + + assert.match(patched, /backgroundColor:e===`linux`\?\(r\?a2:o2\):i2,backgroundMaterial:null/); + assert.doesNotMatch(patched, /\{backgroundColor:i2,backgroundMaterial:null\}/); + assert.match(patched, /transparent:n===`linux`\?!1:a,hasShadow:t/); + assert.doesNotMatch(patched, /return\{frame:!1,transparent:a,hasShadow:t/); +}); + +test("patchDisableTransparencySource is idempotent", () => { + const source = [ + "function A2(e){return e===`avatarOverlay`||e===`browserCommentPopup`}", + "function I2({platform:e,appearance:t,opaqueWindowsEnabled:n,prefersDarkColors:r}){return n&&!A2(t)&&(e===`darwin`||e===`win32`)?{backgroundColor:r?a2:o2,backgroundMaterial:e===`win32`?`none`:null}:e===`win32`&&!A2(t)?{backgroundColor:i2,backgroundMaterial:`mica`}:{backgroundColor:i2,backgroundMaterial:null}}", + "function R2({alwaysOnTop:e,hasShadow:t=!0,platform:n,resizable:r,thickFrame:i,transparent:a=!0}){return{frame:!1,transparent:a,hasShadow:t,resizable:r,minimizable:!1,maximizable:!1,fullscreenable:!1,skipTaskbar:!0,...e?{alwaysOnTop:!0}:{},...n===`win32`?{accentColor:!1,roundedCorners:!1,...i==null?{}:{thickFrame:i}}:{},...n===`darwin`?{type:`panel`}:{}}}", + "function z2({appearance:e,platform:n}){switch(e){case`browserCommentPopup`:return R2({hasShadow:!1,platform:n,resizable:!1,thickFrame:!1,transparent:!0});case`avatarOverlay`:return R2({platform:n,resizable:!1})}}" + ].join(";"); + + const patched = patchDisableTransparencySource(source); + const repatched = patchDisableTransparencySource(patched); + + assert.equal(repatched, patched); +});