Files
wehub-resource-sync 3e076d4dd9
Deploy Worker / deploy (push) Failing after 1s
Shellcheck / Check shell scripts (push) Failing after 1s
CI / Build Packages (amd64 - appimage) (push) Has been skipped
CI / Build Packages (amd64 - deb) (push) Has been skipped
CI / Build Packages (amd64 - rpm) (push) Has been skipped
CI / Build Packages (arm64 - appimage) (push) Has been skipped
CI / Build Packages (arm64 - deb) (push) Has been skipped
CI / Test Flags Parsing (push) Failing after 1s
BATS Tests / BATS unit tests (push) Failing after 1s
CI / Build Packages (arm64 - rpm) (push) Has been skipped
CI / Test Build Artifacts (amd64) (push) Has been skipped
CI / Test Build Artifacts (arm64) (push) Has been skipped
CI / Update APT Repository (push) Has been cancelled
CI / Mirror Official .deb to Release (push) Has been cancelled
CI / Update DNF Repository (push) Has been cancelled
CI / Update AUR Package (push) Has been cancelled
CI / Create Release (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:29:21 +08:00

165 lines
6.9 KiB
Bash

#===============================================================================
# Quick-window patches: KDE-gated blur/focus workarounds for the pop-up menu
# so the main window reappears after quick-entry submit.
#
# Sourced by: build.sh
# Sourced globals: main_js (optional — the resolved main chunk; set by
# patch_app_asar. Falls back to .vite/build/index.js for older bundles.)
# Modifies globals: (none)
#===============================================================================
patch_quick_window() {
echo 'Patching quick window for Linux...'
local index_js="${main_js:-app.asar.contents/.vite/build/index.js}"
# Extract the quick window variable name from the unique "pop-up-menu"
# setAlwaysOnTop call, e.g.: Sa.setAlwaysOnTop(!0,"pop-up-menu")
local quick_var
quick_var=$(grep -oP '[$\w]+(?=\.setAlwaysOnTop\(\s*!0\s*,\s*"pop-up-menu"\))' \
"$index_js" | head -1)
if [[ -z $quick_var ]]; then
echo 'WARNING: Could not extract quick window variable name'
echo '##############################################################'
return
fi
echo " Found quick window variable: $quick_var"
local quick_var_re="${quick_var//\$/\\$}"
# Part 1: Add blur() before hide() on the quick window so that
# isFocused() returns false after hiding (Electron Linux bug on KDE).
# The hide call sits after || (e.g. GUARD()||VAR.hide()), so both
# calls must be wrapped in parens to preserve short-circuit semantics.
# Gated to KDE only: on GNOME/Ubuntu the blur() regresses quick entry
# (see #393), and the focus-stale bug doesn't manifest there.
local de_check='(process.env.XDG_CURRENT_DESKTOP||"")'
de_check+='.toLowerCase().includes("kde")'
if grep -qF "${quick_var}.blur(),${quick_var}.hide()" "$index_js"; then
echo ' Quick window blur already patched'
elif grep -qP "\|\|\s*${quick_var_re}\.hide\(\)" "$index_js"; then
sed -i -E \
"s/\|\|\s*${quick_var_re}\.hide\(\)/||(${de_check}?(${quick_var}.blur(),${quick_var}.hide()):${quick_var}.hide())/g" \
"$index_js"
echo ' Added KDE-gated blur() before hide() on quick window'
else
echo ' WARNING: Could not find quick window hide() call'
fi
# Part 2: Fix main window not appearing after quick entry submit.
# On KDE, isFocused() can return stale true after hiding, causing
# FOCUS_CHECK()||Lt.show() to skip the show. Gate the visibility-check
# replacement to KDE only: on GNOME, the original focus check works
# and replacing it regresses quick entry (see #393).
if INDEX_JS="$index_js" node << 'QUICK_WINDOW_PATCH'
const fs = require('fs');
const indexJs = process.env.INDEX_JS;
let code = fs.readFileSync(indexJs, 'utf8');
let patchCount = 0;
// Find the minified isWindowFocused function via its named property
// export: isWindowFocused: () => !!NAME()
const focusedPropRe = /isWindowFocused:\s*\(\)\s*=>\s*!!([\w$]+)\(\)/;
const focusedMatch = code.match(focusedPropRe);
if (!focusedMatch) {
console.log(' WARNING: Could not find isWindowFocused function');
process.exit(1);
}
const focusFn = focusedMatch[1];
console.log(' Found focus check function: ' + focusFn);
// Find the sibling isVisible function defined near the focus function.
// Tolerate the optional `var <name>(,<name>)*;` declaration the
// minifier hoists when the function body uses optional chaining
// (1.3883.0+ shape: `function aZA(){var e;return!Qt...}`). Older
// builds don't declare anything before `return!`. The non-capturing
// group keeps the prefix optional in either case.
//
// The window handle churns: a bare minified local (`Qt`, `it`) through
// 1.18286.0, a property access (`exports.mainWindow`) from 1.19367.0.
// [\w$]+(?:\.[\w$]+)* covers both; the fn-name capture is [\w$]+ (not
// \w+) so a `$`-prefixed name isn't silently truncated
// (docs/learnings/patching-minified-js.md).
const focusFnIdx = code.indexOf('function ' + focusFn + '(');
const nearbyCode = code.substring(focusFnIdx, focusFnIdx + 500);
const win = String.raw`[\w$]+(?:\.[\w$]+)*`;
const visFnRe = new RegExp(
String.raw`function ([\w$]+)\(\)\{(?:var [\w$]+(?:,[\w$]+)*;)?` +
`return!${win}\\|\\|${win}\\.isDestroyed\\(\\)\\?!1:` +
`${win}\\.isVisible\\(\\)`
);
const visMatch = nearbyCode.match(visFnRe);
if (!visMatch) {
console.log(' WARNING: Could not find visibility function near ' +
focusFn);
process.exit(1);
}
const visFn = visMatch[1];
console.log(' Found visibility check function: ' + visFn);
// Anchor on unique QuickEntry log strings to patch only the right sites
const anchors = [
'Navigating to existing chat',
'Creating new chat with submit_quick_entry',
];
const escapeRegExp = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
for (const anchor of anchors) {
const anchorIdx = code.indexOf(anchor);
if (anchorIdx === -1) {
console.log(' WARNING: anchor not found: ' + anchor);
continue;
}
// Search region after anchor (1500 chars covers promise chains)
const region = code.substring(anchorIdx, anchorIdx + 1500);
// Idempotency: if region already contains the DE gate, skip
if (region.indexOf('XDG_CURRENT_DESKTOP') !== -1) {
console.log(' Quick entry show() already patched near "' +
anchor.substring(0, 30) + '..."');
continue;
}
// matches: <focusFn>()||<win>.show(), where <win> is a bare local
// (older builds) or a property access like exports.mainWindow
// (1.19367.0+). Capturing the whole handle keeps the .show() call
// pointed at the real window after the rewrite.
const showRe = new RegExp(
escapeRegExp(focusFn) + String.raw`\(\)\|\|(${win})\.show\(\)`
);
const showMatch = region.match(showRe);
if (showMatch) {
const oldStr = showMatch[0];
const mainWin = showMatch[1];
// Gate the visibility check to KDE only; fall back to original
// focus check on GNOME/other so #390 doesn't regress them (#393).
const deCheck = '(process.env.XDG_CURRENT_DESKTOP||"")' +
'.toLowerCase().includes("kde")';
const newStr = '(' + deCheck + '?' + visFn + '():' +
focusFn + '())||' + mainWin + '.show()';
if (oldStr !== newStr) {
const absIdx = anchorIdx + region.indexOf(oldStr);
code = code.substring(0, absIdx) + newStr +
code.substring(absIdx + oldStr.length);
console.log(' KDE-gated ' + focusFn + '()/' + visFn +
'() for show() near "' + anchor.substring(0, 30) + '..."');
patchCount++;
}
} else {
console.log(' WARNING: show() pattern not found near "' +
anchor + '"');
}
}
if (patchCount > 0) {
fs.writeFileSync(indexJs, code);
console.log(' Patched ' + patchCount +
' quick entry show() calls to use visibility check');
} else {
console.log(' WARNING: No quick entry show() calls patched');
}
QUICK_WINDOW_PATCH
then
echo 'Quick window patches applied'
else
echo 'WARNING: Quick window show patch failed' >&2
fi
echo '##############################################################'
}