diff --git a/docs/evidence/framey-module-lookup-2026-10-26.json b/docs/evidence/framey-module-lookup-2026-10-26.json new file mode 100644 index 0000000..fdfc124 --- /dev/null +++ b/docs/evidence/framey-module-lookup-2026-10-26.json @@ -0,0 +1,186 @@ +{ + "schemaVersion": 1, + "timeUtc": "2026-10-26T03:29:05.328492+00:00", + "baseCommit": "c89ddff", + "scope": "Finite Framey-local DFL component/CSS registry and explicit unsupported optional UI-audio export", + "upstreamReferences": [ + "https://github.com/SteamDeckHomebrew/decky-frontend-lib/blob/main/src/webpack.ts", + "https://github.com/SteamDeckHomebrew/decky-frontend-lib/blob/main/src/class-mapper.ts" + ], + "plugins": [ + { + "id": 16, + "name": "Audio Loader", + "version": "1.6.1", + "sha256": "fd5a090c2e2cd4da6723d7bd08937ab31b9f8d4663510431713da531343e303f", + "status": "broken", + "reason": "This plugin needs root and is not supported by the Frame user service. Steam UI sound lookup is present as explicitly unsupported; the frontend still requires Steam m_GamepadUIAudioStore.m_AudioPlaybackManager, which is unavailable in the isolated panel.", + "browser": { + "id": 16, + "name": "Audio Loader", + "ready": false, + "error": "Cannot read properties of undefined (reading 'm_AudioPlaybackManager')", + "logs": [ + { + "kind": "warn", + "message": "Resolve failed: [object Object] promise [object Promise]" + } + ], + "methods": [ + "store_read" + ] + } + }, + { + "id": 36, + "name": "SteamGridDB", + "version": "1.8.1", + "sha256": "e84f5b1f111af8862b06218479491fbf61036e1bfd9f16f93f9901e9b9ebc5e0", + "status": "broken", + "reason": "Local module mapping is available; frontend still requires the Steam LibraryContextMenu implementation, which has no target in the isolated Framey panel.", + "browser": { + "id": 36, + "name": "SteamGridDB", + "ready": false, + "error": "Cannot convert undefined or null to object", + "logs": [], + "methods": [] + } + }, + { + "id": 61, + "name": "TabMaster", + "version": "2.15.1", + "sha256": "e08333859a32b0c177b8e9a7959c5eeeafe38f946578f7f1d161c721f0e05da5", + "status": "partial", + "reason": "Local Chromium rendered the QAM and navigable Overview/Filters documentation with an actual Decky 3.3.0 amd64 backend. Library-tab editing remains unavailable: Steam login/library patches and MobX reactions have no target. ARM64 and worn VR unverified.", + "browser": { + "id": 61, + "name": "TabMaster", + "ready": true, + "error": null, + "logs": [ + { + "kind": "error", + "message": "%c TabMaster %c ERROR %c background: #ff6d05; color: black; background: #c70808; color: black; background: transparent; Could not find mobx.reaction" + } + ], + "methods": [ + "log_message" + ] + } + }, + { + "id": 84, + "name": "Junk-Store", + "version": "1.1.13", + "sha256": "f7ad2a0103c85bf308929b2e96ec9aeeb0d76bcc6ae97eb1e4b5f31ffba0cbb1", + "status": "broken", + "reason": "Local CSS lookup fixes the loading view crash. Actual Decky 3.3.0 amd64 startup/static InitActions return a script execution/JSON error; the published scripts are not executable after extraction. Store install/login/game launch and ARM64 remain unverified.", + "browser": { + "id": 84, + "name": "Junk-Store", + "ready": true, + "error": null, + "logs": [], + "methods": [ + "fetch_rss_feed", + "execute_action" + ] + } + }, + { + "id": 102, + "name": "DeckSP", + "version": "1.1.3", + "sha256": "d6cbedcd64430e1f94a2b12caf936ba270f1ceafe77c6252808ff39c88640b40", + "status": "broken", + "reason": "Optional UI-audio lookup succeeds, but the published frontend raises \"Cannot access PluginManager before initialization\" when Steam MobX reaction lookup is unavailable. JamesDSP installation/audio routing and ARM64 remain unverified.", + "browser": { + "id": 102, + "name": "DeckSP", + "ready": false, + "error": "Cannot access 'PluginManager' before initialization", + "logs": [], + "methods": [] + } + } + ], + "fullCatalogFrontend": { + "attempted": 116, + "ready": 102, + "failed": 14, + "previousReady": 101, + "newlyReady": [ + 61 + ], + "regressions": [], + "backendAndNetworkDenied": true, + "library": "Private detached real Frame snapshot replay, not live Steam or backend verification" + }, + "focusedBrowser": { + "host": "Actual ui/headset.html, React 18.3.1, opaque allow-scripts iframe, exact official bundles", + "installedPluginAndStatusResponses": "Fixture-shaped; no headset HTTP endpoint exercised", + "backend": "Actual official Decky 3.3.0 amd64 in unprivileged network-none container; no host audio/device/data mounts", + "deckySha256": "e80d03e641d1766a33a817cb27ab8d2e0a4633685e5d27e4de948a6687526181", + "allowedMethods": { + "TabMaster": [ + "log_message", + "get_setting", + "get_users_dict", + "get_tags" + ], + "Junk-Store": [ + "execute_action only init/InitActions" + ] + }, + "checks": [ + "TabMaster QAM renders; library editing controls remain disabled", + "TabMaster Open Docs -> Overview renders", + "TabMaster Filters tab renders", + "Back to plugin returns to QAM", + "Junk-Store static InitActions via real backend returns Type Error; not promoted" + ], + "denied": [ + "External RSS/network", + "Other execute_action arguments", + "Login, installation, game launch, library patching, audio playback" + ], + "directBackendReads": { + "TabMaster.get_setting": "default string returned", + "TabMaster.get_users_dict": {}, + "TabMaster.get_tags": [], + "Junk-Store.scriptMode": "0644, Python shebang present" + } + }, + "headset": { + "newPluginDeviceExecution": false, + "lockProbe": "Normal atomic lock acquired/released for read-only dependency availability probe; no service or power changes", + "ARM64OfficialBackend": "Not exercised in this theme", + "wornVR": false + }, + "verification": { + "frameyTests": 42, + "fullServerTests": { + "count": 1620, + "skipped": 16, + "result": "OK" + }, + "siteTestFilesPassed": 2, + "fakeFrame": { + "attempted": 116, + "installedRemoved": 60, + "expectedBlockers": 56, + "backendExecuted": false, + "privateProject": "framey-catalog-6456d3d939", + "projectRemoved": true + } + }, + "storeRefresh": { + "timeUtc": "2026-10-26T03:29:46.606326+00:00", + "currentCount": 116, + "pinnedCount": 116, + "unchanged": true, + "changedIds": [] + } +} diff --git a/docs/evidence/framey-module-lookup-2026-10-26.md b/docs/evidence/framey-module-lookup-2026-10-26.md new file mode 100644 index 0000000..5b70e9e --- /dev/null +++ b/docs/evidence/framey-module-lookup-2026-10-26.md @@ -0,0 +1,60 @@ +# Framey-local module lookup: official plugin checks + +TabMaster 2.15.1 now renders its QAM and bundled documentation in local Chromium. +Opening its docs, selecting Overview and Filters, and returning to the panel +passed. Its status moves from **broken to partial**: library editing controls +remain disabled because Steam login, live MobX reactions and library-page +patches are unavailable. This is not working library-tab customization. + +The [observations](framey-module-lookup-2026-10-26.json) pin each tested official +archive and the actual Decky 3.3.0 amd64 binary. The complete current official +store was refreshed: all 116 IDs, versions and digests match the catalog. + +| Plugin | Result | Remaining limitation | +| --- | --- | --- | +| TabMaster 61 | Partial: QAM and documentation navigation render | Steam library editing unavailable; ARM64 and worn VR unverified | +| Junk-Store 84 | Loading and backend-error views render without the CSS lookup crash | Actual startup/static InitActions return an execution/JSON error; extracted scripts lack executable permission; remains broken | +| SteamGridDB 36 | Module mapping is callable | Still needs Steam's LibraryContextMenu implementation; remains broken | +| DeckSP 102 | Optional UI-audio lookup succeeds | Published bundle accesses PluginManager before initialization when MobX reaction is absent; remains broken | +| Audio Loader 16 | Optional UI-audio lookup succeeds | Still requires Steam's mutable audio playback store; remains broken | + +## What ran + +The host exposes a finite registry of its own components and styled CSS maps, +with DFL module/export/class lookup and mapping semantics. Unknown matches return +undefined or an empty mapping. An optional audio export explicitly reports +unavailable; invoking playback throws. No Steam webpack cache, function-source +signature, audio store or library patch was manufactured. + +The full exact-bundle browser pass ran with backend and network calls denied. +It completed 102 initializers and failed 14, versus 101/15 before this theme. +Initializer readiness alone does not establish rendered or useful behavior. + +Focused checks used actual `ui/headset.html`, React 18.3.1 and opaque +`allow-scripts` iframes. Status/installed-plugin responses were fixture-shaped, +and the private detached library snapshot from the earlier Frame SSH check was +replayed. The relay allowed only TabMaster log/settings reads and Junk-Store's +static `init/InitActions` action. TabMaster's official backend returned the +default setting, empty users dictionary and empty tags. Junk-Store returned a +structured Type Error, which its panel displayed. RSS/network, login, installs, +launch actions and other backend methods were denied. + +The unmodified Python backends ran in actual Decky 3.3.0 inside a disposable +unprivileged amd64 container with no network, host audio, devices or user-data +mounts. This bypasses Steam CEF and is not a headset HTTP/RPC check. The normal +headset lock was briefly acquired and released for read-only dependency/service +availability; no official plugin ran there in this theme. No ARM64 or worn-VR +result is claimed. + +## Regression checks + +- 1,620 server tests passed, 16 skipped; 42 focused Framey tests passed. +- Both site test files passed. +- All 116 exact archives passed the fake-Frame audit: 60 install/remove flows + and 56 expected blockers. Official backend execution remained stubbed. +- The unique fake-Frame project and its volumes were removed. The focused + backend container and both loopback helpers were removed after the checks. + +The catalog and generated compatibility table use the same status/reasons as +both store views. The result is **1 works / 49 partial / 55 broken / 11 N/A**; +the majority goal remains open. diff --git a/docs/framey-compatibility.md b/docs/framey-compatibility.md index 411005e..5ed68a8 100644 --- a/docs/framey-compatibility.md +++ b/docs/framey-compatibility.md @@ -4,7 +4,7 @@ Snapshot: 2026-10-25T23:19:48.854504+00:00; 116 current visible official plugins This table and both store views use [`ui/framey-compatibility.json`](../ui/framey-compatibility.json). Results apply only to the exact store ID, version and SHA-256. A changed digest/version is **unverified**. Regenerate this document with `python3 scripts/framey-compatibility-doc.py`; use `--check` in review. -**Works: 1 · Partial: 48 · Broken: 56 · N/A: 11.** +**Works: 1 · Partial: 49 · Broken: 55 · N/A: 11.** - **Works** requires verified useful plugin behavior with its real backend. Read the reason for the verified scope and remaining limits; it does not imply worn-VR verification. - **Partial** means only a subset or a local fixture scope was verified, often just frontend/settings initialization. The remaining backend/device functionality is unverified; read the reason for the exact scope. @@ -27,7 +27,7 @@ Open the browser audit URL in local Chromium, run `runAudit()` and inspect/downl The current published digests are pinned in the JSON; if the store changed, retain/download those exact archives for reproduction. Audit downloads permit larger ZIPs for inspection; production installer limits remain 50 MiB ZIP / 150 MiB unpacked / 30 MiB per member. -After the VR job released the shared lock, this audit acquired it and used a private temporary Decky 3.3.0 ARM64 user service. Read-only ProtonDB `get_setting` and Steam Status `get_settings` calls succeeded through the live Steam CEF websocket. The service and private base were removed and the lock released; normal managed services stayed inactive, battery was 100 → 99, and no power setting was changed. [Redacted results](evidence/framey-official-backend-2026-10-25.json) record exactly what ran. A [2026-10-26 follow-up](evidence/framey-events-backend-2026-10-26.json) verified default Notebook drawing/save/reopen across a real backend restart and Simple Timer start/cancel, countdown/completion events, a visible toast and persisted recents/subtle mode. Only private test settings were written. Simple Timer remains partial because its own expired-timer startup raises a missing `timer_task` error; sound/suspend are unsupported. A [local audio follow-up](evidence/framey-audio-backends-2026-10-26.md) exercised Volume Mixer, Volume Boost and Mixer through real Decky 3.3.0 amd64 backends on private silent virtual audio targets. Sliders, numeric volume entry, independent server readbacks and Mixer filter persistence across a runtime restart passed. These three stay partial: real ARM64 Frame audio dependencies/session mapping remain unverified because the shared headset lock was occupied. A [library context follow-up](evidence/framey-library-context-2026-10-26.md) read 1,106 app overviews, 895 game IDs and the current public Steam ID from real Frame CEF under the shared lock. Local Chromium replayed this detached snapshot to render Deck Settings game selection, Steamback's empty save panel and PlayTime statistics/settings. Steamback and PlayTime used actual Decky 3.3.0 amd64 backends in an isolated container; their ARM64 backends and automatic game hooks remain unverified. Shotty's frontend now starts, but its published backend fails on missing `click`; it stays broken. Other official backends remain unexecuted. The separate VR job proves Frame Control/sample compositor rendering, not any official plugin. No worn-VR official-plugin result is claimed. A [local UI follow-up](evidence/framey-ui-fallbacks-2026-10-26.json) rendered Pause Games and Tailscale Control settings with all backend/network actions denied; it does not verify process pausing or Tailscale changes. A [read-only Steam bridge follow-up](evidence/framey-steam-reads-2026-10-26.json) verified actual Frame country, install folders and system information, plus IsThereAnyDeal's country dropdown in local Chromium. The installed-plugin status was fixture-shaped in that relay; plugin backends stayed denied and the deployed headset HTTP endpoint remains unverified. Earlier focused checks cover [ProtonDB](evidence/framey-browser-2026-10-25.md), [Steam Status/cache](evidence/framey-cache-browser-2026-10-25.md), [Storage Cleaner](evidence/framey-api-v1-browser-2026-10-25.md), and [legacy MusicControl](evidence/framey-legacy-2026-10-25.md). +After the VR job released the shared lock, this audit acquired it and used a private temporary Decky 3.3.0 ARM64 user service. Read-only ProtonDB `get_setting` and Steam Status `get_settings` calls succeeded through the live Steam CEF websocket. The service and private base were removed and the lock released; normal managed services stayed inactive, battery was 100 → 99, and no power setting was changed. [Redacted results](evidence/framey-official-backend-2026-10-25.json) record exactly what ran. A [2026-10-26 follow-up](evidence/framey-events-backend-2026-10-26.json) verified default Notebook drawing/save/reopen across a real backend restart and Simple Timer start/cancel, countdown/completion events, a visible toast and persisted recents/subtle mode. Only private test settings were written. Simple Timer remains partial because its own expired-timer startup raises a missing `timer_task` error; sound/suspend are unsupported. A [local audio follow-up](evidence/framey-audio-backends-2026-10-26.md) exercised Volume Mixer, Volume Boost and Mixer through real Decky 3.3.0 amd64 backends on private silent virtual audio targets. Sliders, numeric volume entry, independent server readbacks and Mixer filter persistence across a runtime restart passed. These three stay partial: real ARM64 Frame audio dependencies/session mapping remain unverified because the shared headset lock was occupied. A [library context follow-up](evidence/framey-library-context-2026-10-26.md) read 1,106 app overviews, 895 game IDs and the current public Steam ID from real Frame CEF under the shared lock. Local Chromium replayed this detached snapshot to render Deck Settings game selection, Steamback's empty save panel and PlayTime statistics/settings. Steamback and PlayTime used actual Decky 3.3.0 amd64 backends in an isolated container; their ARM64 backends and automatic game hooks remain unverified. Shotty's frontend now starts, but its published backend fails on missing `click`; it stays broken. A [local module lookup follow-up](evidence/framey-module-lookup-2026-10-26.md) supplies a finite registry of Framey-local controls and CSS. TabMaster QAM/docs now render; Steam library-tab editing remains unavailable. Junk-Store loading/error views render, but its real backend cannot execute the extracted startup script, so it stays broken. SteamGridDB still requires Steam context-menu internals and DeckSP still fails its published MobX fallback. TabMaster/Junk-Store ran in actual isolated amd64 Decky; no ARM64 or worn-VR result is claimed. Beyond the documented checks, official backends remain unexecuted. The separate VR job proves Frame Control/sample compositor rendering, not any official plugin. No worn-VR official-plugin result is claimed. A [local UI follow-up](evidence/framey-ui-fallbacks-2026-10-26.json) rendered Pause Games and Tailscale Control settings with all backend/network actions denied; it does not verify process pausing or Tailscale changes. A [read-only Steam bridge follow-up](evidence/framey-steam-reads-2026-10-26.json) verified actual Frame country, install folders and system information, plus IsThereAnyDeal's country dropdown in local Chromium. The installed-plugin status was fixture-shaped in that relay; plugin backends stayed denied and the deployed headset HTTP endpoint remains unverified. Earlier focused checks cover [ProtonDB](evidence/framey-browser-2026-10-25.md), [Steam Status/cache](evidence/framey-cache-browser-2026-10-25.md), [Storage Cleaner](evidence/framey-api-v1-browser-2026-10-25.md), and [legacy MusicControl](evidence/framey-legacy-2026-10-25.md). ## Every current plugin @@ -42,7 +42,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say | 12: MusicControl 1.1.6 | legacy / legacy | partial | Legacy media controls initialize; player discovery and playback are unverified. | static archive, local browser, fake-Frame | | 13: Pause Games 1.0.3 | 1 / 2 | partial | Settings render locally with inert system-key subscriptions and empty detached Steam context. Live-game discovery, process pause/resume and suspend integration are unsupported/unverified; no backend actions ran. | static archive, local browser, fake-Frame | | 14: ProtonDB Badges 1.3.4 | 2 / 2 | partial | Settings render locally and read-only get_setting RPC returned the default through real Decky 3.3.0 on ARM64 Frame. Steam game-page badges have no patch target; live writes and VR controls unverified. | static archive, local browser, fake-Frame, real Frame SSH | -| 16: Audio Loader 1.6.1 | legacy / legacy | broken | This plugin needs root and is not supported by the Frame user service. Frontend audit failed: Cannot read properties of null (reading 'm_GamepadUIAudioStore'). | static archive, local browser, fake-Frame | +| 16: Audio Loader 1.6.1 | legacy / legacy | broken | This plugin needs root and is not supported by the Frame user service. Steam UI sound lookup is present as explicitly unsupported; the frontend still requires Steam m_GamepadUIAudioStore.m_AudioPlaybackManager, which is unavailable in the isolated panel. | static archive, local browser, fake-Frame | | 18: Bluetooth 3.1.1 | 1 / 2 | partial | Controls render with fixture replies; actual Bluetooth discovery, labels and connections remain unverified. | static archive, local browser, fake-Frame | | 19: Notebook 0.1.5 | legacy / legacy | works | Default notebook drawing, save and reopen verified in Chromium with real Decky 3.3.0 ARM64, including backend restart. Per-game auto-selection and background artwork unavailable; worn-VR input unverified. | static archive, local browser, fake-Frame, real Frame SSH | | 21: PowerTools 2.0.3 | legacy / legacy | N/A | Not supported on Frame: Targets AMD Ryzen power controls; Frame uses an ARM64 SoC. | static archive, local browser, fake-Frame | @@ -55,7 +55,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say | 30: TunnelDeck 1.0.4 | legacy / legacy | broken | This plugin needs root and is not supported by the Frame user service. | static archive, local browser, fake-Frame | | 32: AutoFlatpaks 1.6.8 | legacy / legacy | broken | This plugin needs root and is not supported by the Frame user service. | static archive, local browser, fake-Frame | | 35: MoonDeck 1.13.0 | 1 / 2 | broken | Plugin contains a non-ARM64 Linux binary: moondeck/python/externals/yarl/_quoting_c.cpython-313-x86_64-linux-gnu.so. Frontend initializes; native CPython wheels and executables in the published archive are x86_64. | static archive, local browser, fake-Frame | -| 36: SteamGridDB 1.8.1 | 1 / 2 | broken | Frontend audit failed: DFL.createModuleMapping is not a function. | static archive, local browser, fake-Frame | +| 36: SteamGridDB 1.8.1 | 1 / 2 | broken | Local module mapping is available; frontend still requires the Steam LibraryContextMenu implementation, which has no target in the isolated Framey panel. | static archive, local browser, fake-Frame | | 37: Discord Status 1.5.0 | 1 / 2 | broken | Detached app overviews are now available, but RunningApps/lifetime observations and Steam artwork helpers still lack a live mapping. Discord activity integration remains unavailable/unverified. | static archive, local browser, fake-Frame, real Frame SSH | | 38: Volume Mixer 1.2.1-1 | legacy / legacy | partial | Local Chromium discovers and changes a private silent stream through the actual Decky 3.3.0 Linux backend, with independent Pulse readback. Frame ARM64 libraries/audio session and Bluetooth playback controls remain unverified. | static archive, local browser, fake-Frame, isolated Linux backend | | 40: Controller Tools 2.0.2 | 1 / 2 | broken | Plugin contains a non-ARM64 Linux binary: ControllerTools/bin/backend. Frontend audit failed: Audit blocks WebSocket. | static archive, local browser, fake-Frame | @@ -71,7 +71,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say | 58: Volume Boost 0.0.7 | legacy / legacy | partial | Local Chromium sync/separate sliders, numeric volume entry and cache reopen pass through the actual Decky 3.3.0 backend on a private virtual stereo sink. Frame pactl/default sink, boost above 100% and physical audio remain unverified. | static archive, local browser, fake-Frame, isolated Linux backend | | 59: Web Browser 1.4.3 | legacy / legacy | broken | Frontend audit failed: deckyFrontendLib.getReactRoot is not a function. | static archive, local browser, fake-Frame | | 60: DeckyFileServer 1.2.0 | 1 / 2 | broken | Plugin contains a non-ARM64 Linux binary: DeckyFileServer/bin/backend. | static archive, local browser, fake-Frame | -| 61: TabMaster 2.15.1 | 1 / 2 | broken | Frontend audit failed: Cannot read properties of null (reading 'GamepadUIAudio'). | static archive, local browser, fake-Frame | +| 61: TabMaster 2.15.1 | 1 / 2 | partial | Local Chromium rendered the QAM and navigable Overview/Filters documentation with an actual Decky 3.3.0 amd64 backend. Library-tab editing remains unavailable: Steam login/library patches and MobX reactions have no target. ARM64 and worn VR unverified. | static archive, local browser, fake-Frame | | 62: TS3 QuickAccess 1.2.0 | legacy / legacy | broken | Plugin contains a non-ARM64 Linux binary: ts3-qs4sd/bin/ts3-qs4sd.so. | static archive, local browser, fake-Frame | | 63: Free Loader 1.6.0 | 1 / 2 | broken | Unsafe plugin archive member. | static archive, local browser, fake-Frame | | 64: Syncthing 0.2.7 | legacy / legacy | broken | Plugin contains a non-ARM64 Linux binary: decky-syncthing/bin/decky-syncthing-watchdog. | static archive, local browser, fake-Frame | @@ -91,7 +91,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say | 80: MagicBlack 2.0.2 | 1 / 2 | N/A | Not supported on Frame: Targets a global Steam Deck screen-black overlay; the isolated panel cannot black out the Frame compositor. | static archive, local browser, fake-Frame | | 81: IsThereAnyDeal for Deck 1.1.1 | legacy / legacy | partial | Country settings render and select the real Frame Steam country through the read-only bridge. Settings writes, deal lookups and Steam page integration remain unverified; plugin backend calls were denied. | static archive, local browser, fake-Frame, real Frame SSH | | 82: MagicPods 2.0.21 | 1 / 2 | broken | Plugin contains a non-ARM64 Linux binary: MagicPodsDecky/bin/magicpodscore. Frontend audit failed: Audit blocks WebSocket. | static archive, local browser, fake-Frame | -| 84: Junk-Store 1.1.13 | legacy / legacy | broken | Route registration succeeds; rendering fails on a missing Steam ContainerBackground component. | static archive, local browser, fake-Frame | +| 84: Junk-Store 1.1.13 | legacy / legacy | broken | Local CSS lookup fixes the loading view crash. Actual Decky 3.3.0 amd64 startup/static InitActions return a script execution/JSON error; the published scripts are not executable after extraction. Store install/login/game launch and ARM64 remain unverified. | static archive, local browser, fake-Frame | | 85: Decky-Undervolt 1.0.8 | 1 / 2 | N/A | Not supported on Frame: RyzenAdj Curve Optimizer requires an AMD CPU; Frame is ARM64. | static archive, local browser, fake-Frame | | 87: ScreenSaver 1.1.2 | legacy / legacy | partial | Initializer succeeds; global screen overlay and idle/suspend integration have no verified VR target. | static archive, local browser, fake-Frame | | 88: steamdeck-input-disabler 1.0.2 | legacy / legacy | N/A | Not supported on Frame: Targets the built-in Steam Deck USB gamepad; Frame has different controllers. | static archive, local browser, fake-Frame | @@ -108,7 +108,7 @@ The API column shows manifest / actual bundled frontend API. Many manifests say | 99: Decky Zerotier 0.3.1 | 1 / 2 | broken | This plugin needs root and is not supported by the Frame user service. | static archive, local browser, fake-Frame | | 100: Virtual Surround Sound 0.0.5 | 1 / 2 | partial | Initializer succeeds; avatar proxy is unavailable and live audio-routing backend unverified. | static archive, local browser, fake-Frame | | 101: XIVOmega 1.0.2 | 1 / 2 | broken | This plugin needs root and is not supported by the Frame user service. | static archive, local browser, fake-Frame | -| 102: DeckSP 1.1.3 | 1 / 2 | broken | Frontend audit failed: Cannot read properties of null (reading 'GamepadUIAudio'). | static archive, local browser, fake-Frame | +| 102: DeckSP 1.1.3 | 1 / 2 | broken | Optional UI-audio lookup succeeds, but the published frontend raises "Cannot access PluginManager before initialization" when Steam MobX reaction lookup is unavailable. JamesDSP installation/audio routing and ARM64 remain unverified. | static archive, local browser, fake-Frame | | 103: Decky Ludusavi 1.2.2 | 1 / 2 | partial | Initializer succeeds; Flatpak Ludusavi discovery and backup/restore unverified. | static archive, local browser, fake-Frame | | 104: Game Sync 1.0.4 | 1 / 2 | partial | Backend get_config is intentionally rejected by the audit before initialization completes; actual backend compatibility is unknown. | static archive, local browser, fake-Frame | | 105: WiFi Locker 1.0.2 | 1 / 2 | broken | This plugin needs root and is not supported by the Frame user service. | static archive, local browser, fake-Frame | diff --git a/docs/framey-loader.md b/docs/framey-loader.md index 211f7d0..43facd2 100644 --- a/docs/framey-loader.md +++ b/docs/framey-loader.md @@ -70,6 +70,15 @@ countdown, completion toast and persisted recents/subtle mode worked. Simple Timer remains partial: its own expired-timer startup raises a missing `timer_task` error, sound/suspend are unsupported, and worn VR was not checked. +DFL module and class lookup searches a finite registry of Framey's own controls, +CSS maps and an explicitly unsupported optional UI-audio player. It follows DFL's +module/export filter and mapping contracts; unknown Steam modules return +`undefined`, and unknown module mappings are empty. Returned IDs describe local +Framey exports, not Steam webpack IDs. Sound playback throws an unsupported +error, and Steam audio stores, function-source signatures and global patches are +not fabricated. The [module lookup checks](evidence/framey-module-lookup-2026-10-26.md) +record the resulting TabMaster documentation support and remaining blockers. + The host currently supplies sections, rows, buttons, toggles, text fields, sliders, grouped dropdowns, scroll containers, a modal, locale information, local panel routes and toasts, diff --git a/scripts/framey-compatibility-doc.py b/scripts/framey-compatibility-doc.py index 1545900..feee806 100644 --- a/scripts/framey-compatibility-doc.py +++ b/scripts/framey-compatibility-doc.py @@ -60,7 +60,7 @@ def render(data): 'to render Deck Settings game selection, Steamback\'s empty save panel and PlayTime statistics/settings. ' 'Steamback and PlayTime used actual Decky 3.3.0 amd64 backends in an isolated container; their ARM64 backends and automatic game hooks remain unverified. ' 'Shotty\'s frontend now starts, but its published backend fails on missing `click`; it stays broken. ' - 'Other official backends remain unexecuted. ' + 'A [local module lookup follow-up](evidence/framey-module-lookup-2026-10-26.md) supplies a finite registry of Framey-local controls and CSS. TabMaster QAM/docs now render; Steam library-tab editing remains unavailable. Junk-Store loading/error views render, but its real backend cannot execute the extracted startup script, so it stays broken. SteamGridDB still requires Steam context-menu internals and DeckSP still fails its published MobX fallback. TabMaster/Junk-Store ran in actual isolated amd64 Decky; no ARM64 or worn-VR result is claimed. Beyond the documented checks, official backends remain unexecuted. ' 'The separate VR job proves Frame Control/sample compositor rendering, not any official plugin. ' 'No worn-VR official-plugin result is claimed. ' 'A [local UI follow-up](evidence/framey-ui-fallbacks-2026-10-26.json) rendered Pause Games and Tailscale Control settings ' diff --git a/tests/page/framey_actions.mjs b/tests/page/framey_actions.mjs index 5a2f177..64ea74a 100644 --- a/tests/page/framey_actions.mjs +++ b/tests/page/framey_actions.mjs @@ -165,7 +165,35 @@ const undo=api.routerHook.addRoute('/plugin/details',LocalRoute); assert.equal(window.DFL.Navigation.Navigate('/plugin/details'),true); assert.equal(nodes(window.__surface({plugin}),LocalRoute).length,1); assert.equal(window.DFL.Navigation.Navigate('/decky/store'),false); -assert.equal(window.DFL.findModuleChild(()=>true),null); +// Bounded lookup uses DFL's module/export semantics, with only local exports. +const dfl=window.DFL; +const spinnerClasses=dfl.findClassModule(module=>!!module.SpinnerLoaderContainer); +assert.equal(spinnerClasses.ContainerBackground,'framey-loading'); +assert.equal(dfl.findClass('framey/spinner','Medium'),'framey-medium'); +assert.equal(dfl.findClassByName('LoadingStatus'),'framey-status'); +assert.equal(dfl.unminifyClass('framey-loading'),'ContainerBackground'); +assert.equal(dfl.findClassModule(module=>module.SteamOnlyClass),undefined); +assert.equal(dfl.findClass('steam/unknown','Medium'),undefined); +assert.equal(dfl.findClassByName('SteamOnlyClass'),undefined); +const details=dfl.findModuleDetailsByExport((value,name)=>name==='Spinner' && typeof value==='function'); +assert.equal(details[0].Spinner,dfl.Spinner); +assert.equal(details[1],dfl.Spinner); +assert.equal(details[2],'Spinner');assert.equal(details[3],'framey/components'); +assert.equal(dfl.findModule(module=>module.Spinner),details[0]); +assert.equal(dfl.findModuleByExport((value,name)=>name==='Spinner'),details[0]); +assert.equal(dfl.findModuleChild(module=>module.Spinner),dfl.Spinner); +assert.equal(dfl.findModuleExport((value,name)=>name==='Spinner',1000),undefined); +assert.equal(dfl.findModuleExport(value=>value.prototype.nonexistent),undefined); +assert.equal(dfl.findModuleChild(()=>false),undefined); +const mapping=dfl.createModuleMapping(module=>!!module.SpinnerLoaderContainer); +assert.equal(mapping.size,1);assert.equal(mapping.get('framey/spinner'),spinnerClasses); +mapping.clear();assert.equal(dfl.findAllModules(module=>!!module.SpinnerLoaderContainer).length,1); +assert.equal(dfl.createModuleMapping(module=>module.SteamOnlyClass).size,0); +const audio=dfl.findModuleExport(value=>value.GamepadUIAudio).GamepadUIAudio; +assert.equal(audio.available,false); +assert.throws(()=>audio.AudioPlaybackManager.PlayAudioURL('file:///sound.wav'),/not supported in Framey/); +assert.equal(dfl.findModuleExport(value=>value.m_GamepadUIAudioStore),undefined); +assert.equal(dfl.findModuleExport(value=>value.toString?.().includes('().LibraryContextMenu')),undefined); window.DFL.Navigation.CloseSideMenus(); assert.equal(nodes(window.__surface({plugin}),LocalRoute).length,0); undo();assert.equal(window.DFL.Navigation.Navigate('/plugin/details'),false); diff --git a/tests/page/framey_hltb.mjs b/tests/page/framey_hltb.mjs index 179f68c..cacc481 100644 --- a/tests/page/framey_hltb.mjs +++ b/tests/page/framey_hltb.mjs @@ -49,7 +49,7 @@ context.SP_JSX=window.SP_JSX; vm.runInContext(official,context,{timeout:2000}); const plugin=window.__official(); assert.equal(typeof window.DFL.findModuleChild,'function'); -assert.equal(window.DFL.findModuleChild(()=>{}),null); // Steam has no matching module in the VR panel. +assert.equal(window.DFL.findModuleChild(()=>{}),undefined); // Steam has no matching module in the VR panel. assert.equal(plugin.content?.type?.name,'QuickAccessView'); const panel=plugin.content.type(); assert.ok(panel?.type); // Read-only settings render with empty local data. diff --git a/ui/framey-compatibility.json b/ui/framey-compatibility.json index 692433e..938d734 100644 --- a/ui/framey-compatibility.json +++ b/ui/framey-compatibility.json @@ -793,7 +793,7 @@ "https://github.com/SteamDeckHomebrew/decky-loader" ], "status": "broken", - "reason": "This plugin needs root and is not supported by the Frame user service. Frontend audit failed: Cannot read properties of null (reading 'm_GamepadUIAudioStore').", + "reason": "This plugin needs root and is not supported by the Frame user service. Steam UI sound lookup is present as explicitly unsupported; the frontend still requires Steam m_GamepadUIAudioStore.m_AudioPlaybackManager, which is unavailable in the isolated panel.", "blockInstall": true, "verification": [ "static archive", @@ -804,7 +804,7 @@ "id": 16, "name": "Audio Loader", "ready": false, - "error": "Cannot read properties of null (reading 'm_GamepadUIAudioStore')", + "error": "Cannot read properties of undefined (reading 'm_AudioPlaybackManager')", "logs": [ { "kind": "warn", @@ -2784,7 +2784,7 @@ "https://github.com/SteamGridDB/decky-steamgriddb" ], "status": "broken", - "reason": "Frontend audit failed: DFL.createModuleMapping is not a function.", + "reason": "Local module mapping is available; frontend still requires the Steam LibraryContextMenu implementation, which has no target in the isolated Framey panel.", "blockInstall": false, "verification": [ "static archive", @@ -2795,7 +2795,7 @@ "id": 36, "name": "SteamGridDB", "ready": false, - "error": "DFL.createModuleMapping is not a function", + "error": "Cannot convert undefined or null to object", "logs": [], "methods": [] }, @@ -4403,8 +4403,8 @@ "https://github.com/Tormak9970/TabMaster", "https://github.com/Tormak9970/TabMaster.git" ], - "status": "broken", - "reason": "Frontend audit failed: Cannot read properties of null (reading 'GamepadUIAudio').", + "status": "partial", + "reason": "Local Chromium rendered the QAM and navigable Overview/Filters documentation with an actual Decky 3.3.0 amd64 backend. Library-tab editing remains unavailable: Steam login/library patches and MobX reactions have no target. ARM64 and worn VR unverified.", "blockInstall": false, "verification": [ "static archive", @@ -4414,10 +4414,17 @@ "browser": { "id": 61, "name": "TabMaster", - "ready": false, - "error": "Cannot read properties of null (reading 'GamepadUIAudio')", - "logs": [], - "methods": [] + "ready": true, + "error": null, + "logs": [ + { + "kind": "error", + "message": "%c TabMaster %c ERROR %c background: #ff6d05; color: black; background: #c70808; color: black; background: transparent; Could not find mobx.reaction" + } + ], + "methods": [ + "log_message" + ] }, "archiveValidation": { "id": 61, @@ -6462,7 +6469,7 @@ "https://github.com/ebenbruyns/junkstore.git" ], "status": "broken", - "reason": "Route registration succeeds; rendering fails on a missing Steam ContainerBackground component.", + "reason": "Local CSS lookup fixes the loading view crash. Actual Decky 3.3.0 amd64 startup/static InitActions return a script execution/JSON error; the published scripts are not executable after extraction. Store install/login/game launch and ARM64 remain unverified.", "blockInstall": false, "verification": [ "static archive", @@ -6474,14 +6481,10 @@ "name": "Junk-Store", "ready": true, "error": null, - "logs": [ - { - "kind": "error", - "message": "TypeError: Cannot read properties of null (reading 'ContainerBackground')" - } - ], + "logs": [], "methods": [ - "fetch_rss_feed" + "fetch_rss_feed", + "execute_action" ] }, "archiveValidation": { @@ -8473,7 +8476,7 @@ "https://github.com/jessebofill/DeckSP.git" ], "status": "broken", - "reason": "Frontend audit failed: Cannot read properties of null (reading 'GamepadUIAudio').", + "reason": "Optional UI-audio lookup succeeds, but the published frontend raises \"Cannot access PluginManager before initialization\" when Steam MobX reaction lookup is unavailable. JamesDSP installation/audio routing and ARM64 remain unverified.", "blockInstall": false, "verification": [ "static archive", @@ -8484,7 +8487,7 @@ "id": 102, "name": "DeckSP", "ready": false, - "error": "Cannot read properties of null (reading 'GamepadUIAudio')", + "error": "Cannot access 'PluginManager' before initialization", "logs": [], "methods": [] }, diff --git a/ui/framey-host.js b/ui/framey-host.js index 7346904..abc921a 100644 --- a/ui/framey-host.js +++ b/ui/framey-host.js @@ -288,6 +288,50 @@ R.createElement('button', {type:'button',onClick:closeModal}, 'Close'), element)); return {Close:closeModal}; }; + // These are Framey's own styles and components, never Steam's webpack cache. + // Keep the registry finite: an unknown Steam implementation stays unavailable. + const classModules = new Map(Object.entries({ + 'framey/spinner': {SpinnerLoaderContainer:'framey-spinner', ContainerBackground:'framey-loading', + LoadingStatus:'framey-status', Medium:'framey-medium'}, + 'framey/footer': {QuickAccessFooter:'framey-footer', BasicFooter:'framey-footer'}, + 'framey/app-details': {AppDetailSectionList:'framey-sections', AppActionButton:'framey-action'}, + 'framey/play-button': {PlayButtonContainer:'framey-actions', ButtonChild:'framey-button-child', + Green:'framey-green', PlayButton:'framey-action'}, + 'framey/library-image': {LongTitles:'framey-long-title', GreyBackground:'framey-muted', Image:'framey-image'}, + 'framey/library': {GamepadLibrary:'framey-library'}, + 'framey/focus': {FocusRingRoot:'framey-focus', FocusRing:'framey-focus'}, + }).map(([id, classes]) => [id, Object.freeze(classes)])); + const localModules = new Map(classModules); + const moduleVariants = module => [module?.default, module].filter(value => value != null); + function findModule(filter) { + for (const module of localModules.values()) + for (const value of moduleVariants(module)) if (filter(value)) return value; + } + function findModuleDetailsByExport(filter, minExports) { + for (const [id, module] of localModules) + for (const value of moduleVariants(module)) { + if (typeof value !== 'object' || (minExports && Object.keys(value).length < minExports)) continue; + for (const name in value) { + if (!value[name]) continue; + // DFL tolerates filters that dereference an incompatible export. + try { if (filter(value[name], name)) return [value, value[name], name, id]; } catch (_) {} + } + } + return [undefined, undefined, undefined, undefined]; + } + function findModuleChild(filter) { + for (const module of localModules.values()) + for (const value of moduleVariants(module)) { + const result = filter(value); + if (result) return result; + } + } + function createModuleMapping(filter) { + const mapping = new Map(); + for (const [id, module] of localModules) + for (const value of moduleVariants(module)) if (filter(value)) mapping.set(id, value); + return mapping; + } const DFL = window.DFL = { definePlugin: init => init, PanelSection, PanelSectionRow: passthrough('div'), Field, ToggleField, DropdownItem, @@ -301,6 +345,7 @@ DialogHeader:passthrough('h2'), DialogSubHeader:passthrough('h3'), DialogFooter:passthrough('footer'), DialogLabel:passthrough('label'), DialogControlsSection:passthrough('section'), DialogControlsSectionHeader:passthrough('h3'), ModalRoot:passthrough('section'), + ModalPosition:passthrough('div'), Menu:passthrough('div'), MenuGroup:passthrough('section'), MenuItem, ProgressBar, ProgressBarWithInfo:ProgressBar, gamepadDialogClasses:{}, quickAccessMenuClasses:{}, gamepadTabbedPageClasses:{}, @@ -314,12 +359,30 @@ showModal, showContextMenu:showModal, // Steam app, library, and store patches have no target inside this panel. afterPatch:noPatch, beforePatch:noPatch, replacePatch:noPatch, createReactTreePatcher:noPatch, - findInReactTree: () => null, findModuleExport: () => null, findModuleChild: () => null, - findModule:()=>null, findModuleByExport:()=>null, findClassModule:()=>null, - findClass:()=>null, findSP:()=>null, fakeRenderComponent:()=>null, + findInReactTree: () => null, findModule, findModuleChild, findModuleDetailsByExport, + findModuleByExport:(filter, minExports)=>findModuleDetailsByExport(filter,minExports)[0], + findModuleExport:(filter, minExports)=>findModuleDetailsByExport(filter,minExports)[1], + createModuleMapping, findAllModules:filter=>[...createModuleMapping(filter).values()], + findClassModule:filter=>[...classModules.values()].find(filter), + findClass:(id,name)=>classModules.get(id)?.[name], + findClassByName:name=>[...classModules.values()].find(classes=>classes[name])?.[name], + unminifyClass:token=>{ + for(const classes of classModules.values()) + for(const name of Object.keys(classes)) if(classes[name]===token)return name; + }, + findSP:()=>null, fakeRenderComponent:()=>null, appDetailsClasses: {}, appDetailsHeaderClasses: {}, useParams: () => currentParams, }; + localModules.set('framey/components', Object.freeze(Object.fromEntries( + Object.entries(DFL).filter(([name,value])=>typeof value==='function' && /^[A-Z]/.test(name))))); + // Some panels eagerly look up the optional Steam UI sound player. Allow the + // lookup, but never claim playback or expose Steam's mutable audio store. + localModules.set('framey/ui-audio', Object.freeze({UIAudio:Object.freeze({ + GamepadUIAudio:Object.freeze({available:false, AudioPlaybackManager:Object.freeze({ + PlayAudioURL:()=>{throw Error('Steam UI sound playback is not supported in Framey');}, + })}), + })})); // No Steam game is attached to an isolated panel. Subscriptions have a // disposable handle but emit no fabricated game, power, or input events. const subscription = () => {const dispose=()=>{};dispose.unregister=dispose;return dispose;}; diff --git a/ui/framey_panel.py b/ui/framey_panel.py index 56ea646..76edbf0 100644 --- a/ui/framey_panel.py +++ b/ui/framey_panel.py @@ -70,7 +70,13 @@ def panel(plugin_id):