From a5ecbad76d5b23f113c92c36ff88505617f86f4d Mon Sep 17 00:00:00 2001 From: Pierre Kisters <1524059+lhns@users.noreply.github.com> Date: Tue, 29 Sep 2026 01:32:41 +0200 Subject: [PATCH] lib: shared resolvePatch, ensureStyle, logger; legacy wrapper code removed - finders.js (VERSION 2): resolvePatch (resolveAll or the "signature not found, ... left unpatched" status), ensureStyle and logger, used by the patches instead of their own copies. - hooks.js (VERSION 2) no longer unwinds per-patch wrappers of old patch versions; dashboard-windows and steam-close-button drop their handling of versions <= 2 / <= 6 (in-page only, gone with any SteamVR restart). - VERSIONs: dashboard-windows 4, steam-close-button 9, window-curvature 21, frame-controls 7, steam-keyboard-patch 22, launcher-menu grid 7, pinned-desktop 4, show-all 2. --- modules/dashboard-windows/patch.js | 15 +++------- modules/dashboard-windows/unpatch.js | 11 +++---- modules/frame-controls/patch.js | 22 ++++---------- modules/launcher-menu/grid/patch.js | 10 ++----- modules/launcher-menu/pinned-desktop/patch.js | 8 ++--- modules/launcher-menu/show-all/patch.js | 10 ++----- modules/lib/finders.js | 30 ++++++++++++++++++- modules/lib/hooks.js | 10 ++----- modules/steam-close-button/patch.js | 22 +++++--------- modules/steam-keyboard-patch/patch.js | 16 ++++------ modules/window-curvature/patch.js | 22 ++++---------- 11 files changed, 68 insertions(+), 108 deletions(-) diff --git a/modules/dashboard-windows/patch.js b/modules/dashboard-windows/patch.js index f574af8..dcdfd91 100644 --- a/modules/dashboard-windows/patch.js +++ b/modules/dashboard-windows/patch.js @@ -23,13 +23,11 @@ // scheduler) applies new limits at once. // // State: window.__sfuiDashboardWindows (proto, resend, options id, counters in -// .hits); hook name NAME. (VERSION 2 wrapped SendMessage itself and read -// state.rewrite, no longer set, so a leftover wrapper is inert; hooks.js -// unwinds one on top.) Signature mismatch -> error, nothing changed. Same +// .hits); hook name NAME. Signature mismatch -> error, nothing changed. Same // VERSION and options -> "unchanged", else the rewrite is replaced in place. ((find, sigs, opts, hooks) => { const NAME = 'dashboard-windows'; - const VERSION = 3; + const VERSION = 4; const GRAB = { world: { type: 'grab-scale', min: 0.25, max: 5 }, theater: { type: 'grab-transform', min: 1, max: 6 }, @@ -49,12 +47,8 @@ let st = window.__sfuiDashboardWindows; if (!st?.proto) { - let mods; - try { - mods = find.resolveAll(find.getWebpackRequire('webpackChunkvrwebui'), sigs); - } catch (e) { - return `signature not found, dashboard left unpatched: ${e.message}`; - } + const mods = find.resolvePatch('webpackChunkvrwebui', sigs); + if (typeof mods === 'string') return mods; // Mailbox: SendMessage(target, msg) -> WebSocketSend("mailbox_send ..."). // resend: debounced "send the scene graph again" (0 args, NextSGID(), setTimeout). st = window.__sfuiDashboardWindows = { @@ -62,7 +56,6 @@ id: null, hits: {}, }; } - delete st.rewrite; // VERSION 2's wrapper reads it: keep that inert const active = maxScale !== null || rules.length > 0; if (st.version === VERSION && st.id === id && active === hooks.has(st.proto, 'SendMessage', NAME)) return 'unchanged'; diff --git a/modules/dashboard-windows/unpatch.js b/modules/dashboard-windows/unpatch.js index a4642e0..d020481 100644 --- a/modules/dashboard-windows/unpatch.js +++ b/modules/dashboard-windows/unpatch.js @@ -1,14 +1,11 @@ -// Reverts dashboard-windows/patch.js: removes its SendMessage hook (and a -// VERSION 2 own wrapper if on top) and resends the scene graph so the stock -// limits apply at once. Safe when not patched. +// Reverts dashboard-windows/patch.js: removes its SendMessage hook and +// resends the scene graph so the stock limits apply at once. Safe when not +// patched. (() => { - const NAME = 'dashboard-windows'; const st = window.__sfuiDashboardWindows; if (!st) return 'not patched'; delete window.__sfuiDashboardWindows; - window.__sfuiHooks?.remove(st.proto, 'SendMessage', NAME); - const f = st.proto.SendMessage; - if (f?.__sfuiPatch === NAME) st.proto.SendMessage = f.__sfuiOrig; + window.__sfuiHooks?.remove(st.proto, 'SendMessage', 'dashboard-windows'); try { st.resend?.(); } catch { /* ignore */ } return 'unpatched'; })() diff --git a/modules/frame-controls/patch.js b/modules/frame-controls/patch.js index 8574770..ffc0b22 100644 --- a/modules/frame-controls/patch.js +++ b/modules/frame-controls/patch.js @@ -68,7 +68,7 @@ // setPlacement(name or "icon:N", 'bar' | 'menu' | null), reset(). ((find, sigs, opts) => { const NAME = 'frame-controls'; - const VERSION = 6; + const VERSION = 7; const T_SPACER = 1, T_ACTION = 2; // Action icon enums (sigs.controls anchors them): names for the options. const NAMES = { keyboard: 22, float: 26, dashboard: 27, theater: 28, dockLeft: 29, dockRight: 30, close: 31, curvature: 40 }; @@ -96,12 +96,8 @@ const FS = window.FrameStore; if (!FS) return 'not patched: window.FrameStore missing'; - let mods; - try { - mods = find.resolveAll(find.getWebpackRequire('webpackChunkvrwebui'), sigs); - } catch (e) { - return `signature not found, dashboard left unpatched: ${e.message}`; - } + const mods = find.resolvePatch('webpackChunkvrwebui', sigs); + if (typeof mods === 'string') return mods; const mx = mods.mobx.module; const P = mods.frame.exports.Frame.prototype; const actions = mods.actions.exports.store; @@ -122,10 +118,7 @@ S = window.__sfuiFrameControlsState = { schema: 1, placement: ok ? { ...saved.placement } : {}, nixSeen: ok ? { ...saved.nixSeen } : {}, log: [] }; } const logBuf = S.log ??= []; - const log = (msg, data) => { - logBuf.push({ t: new Date().toISOString().slice(11, 23), msg, ...(data !== undefined ? { data } : {}) }); - if (logBuf.length > 200) logBuf.splice(0, logBuf.length - 200); - }; + const log = find.logger(logBuf); if (restored) log('state restored', { from: restored, popup: S.placement }); let unsaved = true; // last save not sent (no injector binding): retried by check() const save = () => { @@ -342,12 +335,7 @@ border: solid #fff; border-width: 0 3px 3px 0; transform: rotate(45deg); } .sfui-fc-pressing { position: relative; } `; - const ensureStyle = () => { - if (document.getElementById(STYLE_ID)) return; - const s = document.createElement('style'); - s.id = STYLE_ID; s.textContent = CSS; - document.head.appendChild(s); - }; + const ensureStyle = () => find.ensureStyle(document, STYLE_ID, CSS); const PANEL_RE = /^legacy-frame-controls-(additional-options-)?(\d+)$/; // The control under an event target: { frame, actionId, where, button, panel } or null. diff --git a/modules/launcher-menu/grid/patch.js b/modules/launcher-menu/grid/patch.js index a9c22c8..65eb185 100644 --- a/modules/launcher-menu/grid/patch.js +++ b/modules/launcher-menu/grid/patch.js @@ -23,7 +23,7 @@ // unpatch.js (or a new VERSION/options) calls __sfuiLauncherGrid.stop(). ((find, sigs, opts) => { const NAME = 'launcher-menu-grid'; - const VERSION = 6; + const VERSION = 7; const COLS = opts.columns; const ROWS = opts.maxRows ?? null; if (!(Number.isInteger(COLS) && COLS >= 1) || !(ROWS === null || (Number.isInteger(ROWS) && ROWS >= 1))) @@ -258,13 +258,7 @@ ${PINNED} > [role=button] * { flex-grow: 0 !important; text-align: center !impor const attach = (d) => { if (docs.has(d) || !d?.body) return; - let s = d.getElementById(STYLE_ID); - if (!s) { - s = d.createElement('style'); - s.id = STYLE_ID; - (d.head ?? d.documentElement).appendChild(s); - } - if (s.textContent !== CSS) s.textContent = CSS; + find.ensureStyle(d, STYLE_ID, CSS); const obs = new MutationObserver(guard(() => update(d))); obs.observe(d.body, { childList: true, subtree: true, characterData: true }); const ro = new d.defaultView.ResizeObserver(guard(() => regionsOf(d).forEach(layout))); diff --git a/modules/launcher-menu/pinned-desktop/patch.js b/modules/launcher-menu/pinned-desktop/patch.js index 6da2331..7b46466 100644 --- a/modules/launcher-menu/pinned-desktop/patch.js +++ b/modules/launcher-menu/pinned-desktop/patch.js @@ -13,7 +13,7 @@ // unpatch.js (or a new VERSION/position) calls __sfuiPinnedDesktop.stop(). ((find, sigs, opts) => { const NAME = 'launcher-menu-pinned-desktop'; - const VERSION = 3; + const VERSION = 4; const POS = opts.position === 'top' ? 'top' : 'bottom'; const KEY = 'steamos-nested-desktop'; const HIDDEN = 'data-sfui-desktop-hidden'; @@ -109,11 +109,7 @@ const attach = (d) => { if (docs.has(d) || !d?.body) return; - if (!d.getElementById(STYLE_ID)) { - const s = d.createElement('style'); - s.id = STYLE_ID; s.textContent = CSS; - (d.head ?? d.documentElement).appendChild(s); - } + find.ensureStyle(d, STYLE_ID, CSS); const obs = new MutationObserver(guard(() => update(d))); obs.observe(d.body, { childList: true, subtree: true, characterData: true }); docs.set(d, obs); diff --git a/modules/launcher-menu/show-all/patch.js b/modules/launcher-menu/show-all/patch.js index 5854a1a..24a2c77 100644 --- a/modules/launcher-menu/show-all/patch.js +++ b/modules/launcher-menu/show-all/patch.js @@ -11,13 +11,9 @@ // nothing while its contents (and the signature) stay intact and the setting // itself is untouched. Remembered as window.__sfuiShowAllApps for unpatch.js. ((find, sigs) => { - const VERSION = 1; - let mods; - try { - mods = find.resolveAll(find.getWebpackRequire('webpackChunksteamui'), sigs); - } catch (e) { - return `signature not found, Steam left unpatched: ${e.message}`; - } + const VERSION = 2; + const mods = find.resolvePatch('webpackChunksteamui', sigs); + if (typeof mods === 'string') return mods; const list = mods.devModeApps.exports.devModeOnly; if (!Array.isArray(list)) return 'Developer Mode app list is not an array, Steam left unpatched'; const prev = window.__sfuiShowAllApps; diff --git a/modules/lib/finders.js b/modules/lib/finders.js index 47a04d7..088ccd1 100644 --- a/modules/lib/finders.js +++ b/modules/lib/finders.js @@ -3,6 +3,8 @@ // ids or minified names (which change with every Steam update). Like Decky // Loader's @decky/ui finders and Vencord's `find`. // +// Also small helpers every patch needs (resolvePatch, ensureStyle, logger). +// // A single expression: installs window.__sfuiFind (unless an equal or newer // VERSION is there) and evaluates to it; mkPatch passes it as `find`. // scripts/check-signatures.mjs evaluates the same file in Node, so the @@ -39,7 +41,7 @@ // Every lookup must match exactly once; otherwise a FinderError names the // signature, the part that failed and the candidates. (() => { - const VERSION = 1; + const VERSION = 2; const G = globalThis; const have = G.__sfuiFind; if (have && have.version >= VERSION) return have; @@ -259,10 +261,36 @@ return null; } + // ---- patch helpers ------------------------------------------------------------ + // resolveAll on the page's webpack chunk array, or the status string + // "signature not found, left unpatched: ". + function resolvePatch(chunkGlobal, sigs) { + try { return resolveAll(getWebpackRequire(chunkGlobal), sigs); } catch (e) { + return `signature not found, ${chunkGlobal === 'webpackChunkvrwebui' ? 'dashboard' : 'Steam'} left unpatched: ${e.message}`; + } + } + //