# dsh-mpkg-wallpaper — Wallpaper Engine mpkg Background Plugin [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com) [中文](README.md) | [English](README.en.md) Adds background wallpapers to the [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) Web UI (`dsh web`): **Wallpaper Engine `.mpkg` parsing, Steam Workshop folders, video/web/image wallpapers, time-of-day switching for Time Variation wallpapers, a full-screen blur system, theme colours and glass surfaces, a local wallpaper library, scheduled rotation, a Now playing widget and one-click update**. Almost every appearance detail is adjustable. > Version scope: this document describes the implementation shipped as **`3.10.0`** in `package.json`. The publish surface is **15 files** (`lib/` runtime files + `package.json`, `icon.svg`, `cordis.patch.yml`, `README.md`, `README.en.md`, `THIRD-PARTY.md`, `LICENSE`; measured with `npm pack --dry-run`: 15 files / 2,088,240 B unpacked); `lib/liquid-glass/**`, `lib/liquid-glass-bundle.js`, `dist/`, `tools/` and `docs/` never enter the npm package (`package.json:8-21`). Defaults that changed: **`npNowPlaying` off → on** (since 3.8.0) and **`powPauseHidden` off → on** (since 3.9.0, migrating only profiles that never set it; see [What changed in this version](#what-changed-in-this-version)). --- ## Download · Install The plugin is published on npm (`dsh-mpkg-wallpaper`). Four ways to load it — pick one from this table, then read its section: | Option | Who it is for | How to update | Client UI | |---|---|---|---| | 1 `dsh plugin add` (recommended) | the default choice; the market shows "installed" | `dsh plugin --profile web update …` | full | | 2 pnpm manual install | you manage the profile's dependency table yourself | same (through the dependency table) | full | | 3 Git clone | developers / offline / you want to edit the code | `git pull` | full | | 4 single-file bundle | offline emergencies; reusing the routes from a non-DSH host | regenerate and replace that `.mjs` | **host half only** | ### Option 1: `dsh plugin add` (recommended, market-recognized) ```bash dsh plugin --profile web add dsh-mpkg-wallpaper # restart dsh web, then Ctrl+F5 in the browser ``` ### Option 2: pnpm manual install ```bash pnpm --dir $DSH_HOME/profiles/ add dsh-mpkg-wallpaper # restart dsh web, then Ctrl+F5 ``` Same source as option 1, just without the `dsh plugin` wrapper. ### Option 3: Git clone (developers / offline) ```bash git clone https://github.com/XHR666/dsh-mpkg-wallpaper.git $DSH_HOME/profiles//node_modules/dsh-mpkg-wallpaper # then register in the profile's cordis.patch.yml: # - insert: # - id: dsh-mpkg-wallpaper # name: dsh-mpkg-wallpaper # restart to take effect ``` > Option 3 writes no dependency entry ⇒ the market does not show "installed" (display only, functionality unaffected). ### Option 4: single-file bundle (offline / drop-in; **host half only**) Inline the host half into a self-contained ESM and register that: ```bash cd /path/to/dsh-mpkg-wallpaper node tools/build-bundle.mjs # output: dist/dsh-mpkg-wallpaper.bundle.mjs (measured 449,671 B / 439.1KB; the gate output is authoritative) node tools/build-bundle.mjs --check # cross-check against the source: export surface / route table / ping JSON shape (20 assertions) node tools/bundle-equivalence-test.mjs # the fuller equivalence gate (38 assertions; gate step 11) ``` Copy `dist/dsh-mpkg-wallpaper.bundle.mjs` anywhere (e.g. `~/.dsh/plugins/`), register it by **absolute path** in the profile's `cordis.patch.yml`, then restart `dsh web`: ```yaml # $DSH_HOME/profiles//cordis.patch.yml - insert: - id: dsh-mpkg-wallpaper name: /absolute/path/dsh-mpkg-wallpaper.bundle.mjs # ← points at the .mjs file itself ``` **What this path loads and what it does not** (code and gate facts): | Item | Option 4 behaviour | Evidence | |---|---|---| | Host half (upload/Range streaming, scene extraction, audio lists, settings persistence, diag reporting — **41 routes**) | **Complete** (four host modules inlined; node builtins only) | route table identical `[41 entries]` | | `/api/mpkg-wallpaper/ping` | `{ok, version, betterSidebar, betterSidebarVersion}` key set identical to the source | `build-bundle.mjs --check` | | **Client half (settings panel / wallpaper layer / frost / Now playing)** | **Not loaded** — only the host export surface (`apply` / `inject` / `__mpwTest`) is present | DSH finds the client half **per package** (Loader entries declaring `dsh.client` → `exports["./client"]`); a bare `.mjs` has no package.json | | `GET /api/mpkg-wallpaper/lg/*` (legacy WebGL hosting route, no client caller) | **404** unless a `liquid-glass/` folder sits next to the bundle | the route resolves `liquid-glass/` from `import.meta.url` (`lib/index.js:3453`) | | `ping.version` | `null` when the parent directory has no `package.json` (version display only) | `new URL('../package.json', import.meta.url)` (`lib/index.js:1622`) | | "Check for updates / one-click update" | No companion `package.json` ⇒ `update-check` 500s and `update-apply` writes next to the bundle: **not recommended here** | `lib/index.js:1792-1860` | | Uninstall | Delete that `.mjs` and its line in `cordis.patch.yml` | — | > Conclusion: **option 4 is a degraded "host capability only" load** (handy offline, as an emergency path, or when reusing the routes from a non-DSH host); use options 1/2/3 for the full UI. The artifact is **not committed** (`dist/` is in `.gitignore`: it is a pure derivative of `lib/*.js`, two builds are byte-identical — section ② of `tools/bundle-equivalence-test.mjs`; generate it at release time and publish the hash). ### Update ```bash # options 1 / 2: through npm's latest tag dsh plugin --profile web update dsh-mpkg-wallpaper # option 3: inside the clone git pull # option 4: regenerate and replace that .mjs node tools/build-bundle.mjs ``` Every route ends the same way: restart `dsh web`, then press `Ctrl+F5`. ### Uninstall Options 1/2/3: `dsh plugin --profile web remove dsh-mpkg-wallpaper`. Option 4: delete the `.mjs` and its line in `cordis.patch.yml`. Leftover data (optional cleanup): browser `localStorage['dsh.mpkg-wallpaper.v2']` and the host directory `~/.dsh-mpkg-wallpaper/` (`settings.json`, `web-store.json`, `media-audio.json`, uploaded mpkg files, transcode cache, `diag-*.json`). ## 30-second quick start The shortest path from installed to wallpaper on screen — three steps. 1. **Install and restart**: pick any option in the previous section, restart `dsh web`, press `Ctrl+F5`. 2. **Open the panel**: left sidebar → Settings → *MPKG Wallpaper*. 3. **Pick a wallpaper**, any one of: - drop in an `.mpkg` file (video wallpapers play directly; scene wallpapers use the static frame / layer composite) - choose a local image/video, or paste an image URL - *Custom local wallpaper folder*: point it at any folder (the Steam `steamapps/workshop/content/431960` root works — every subfolder counts as one wallpaper) The defaults are already usable: master switch on, hybrid mode on, unified blur on (30px), Now playing mounted in the left sidebar. To fine-tune, three controls are enough to start: **Wallpaper → Frosted blur** (0–40), **Surface unify → Full-screen blur degree** (0–40), **Wallpaper → Lens zoom** (10–2000%). > If nothing happens, press **one-click diagnostics report** in the *Other* tab first (it downloads a JSON file when the host is unavailable), then take it to [Bug reports](#bug-reports). ## Core features This section groups features by **what you can perceive** (sources, time variation, blur, appearance, playback, library and rotation, safety, backup) rather than by code module. **📦 Wallpaper sources** - **Wallpaper Engine `.mpkg`**: the container is parsed in the browser (nothing is uploaded to a third party); video wallpapers play their embedded mp4 / video textures, scene wallpapers have their assets extracted, and **Time Variation** wallpapers pick the material for the current time slot - **Steam Workshop folders**: Wallpaper Engine installs are discovered automatically (registry + `libraryfolders.vdf`, non-default drives included) and listed as `video / web / scene`; the **workshop root** (`steamapps/workshop/content/431960`) can also be set as a custom folder — every subfolder is auto-detected as one wallpaper - **Video**: `.mp4/.webm/.mov/.m4v` play directly; **Web**: HTML loads in a sandboxed iframe (with a risk pre-scan); **Image/animation/link**: local images or URLs (including `data:image`) - **Custom folder**: any folder; `.mpkg` files, workshop subfolders, images, videos and `scene.pkg` can be mixed freely **⏰ Time Variation wallpapers** - Detects the WE time-variation properties (`morningtime / daytime / dusktime / nighttime / timevarying`, default hours 5/8/17/20, `lib/client.js:10419-10423`) - **Lazy loading**: only the current slot is extracted (tens of MB per slot); other slots are read when you switch, so importing every slot at once cannot OOM - **Manual slot lock**: slot buttons appear only for slots that actually exist inside the container (`lib/client.js:12797-12813`), stored in `timeOverride`; "Auto" returns to time-based switching - **No cross-talk**: switching wallpapers clears the previous wallpaper's slot cache **🌊 Full-screen blur (frost) system** - **Unified blur**: one slider drives the wallpaper's blur plus the sidebar/title-bar frost thickness; whether the chat area and the new-chat button follow is controlled separately - **UI blur (independent switch + amount each)**: dialogs (generic centre windows + chat input), settings panel, download/confirm popups, popovers (menus/dropdowns/tooltips), mask (full-screen backdrop), left sidebar frost - **Show wallpaper**: left sidebar / title bar / right sidebar dock are independent, and the title-bar frost radius can be set separately **🎨 Theme colours and glass surfaces (Aqua experiments default to off)** - **Theme colour (`themeColor`)**: colour picker + presets, driving the sidebar/title bar/new-chat button/settings dialog base colour; **Accent (`accent`)** drives the brand interaction colour (buttons/sliders/selection/links/send button) - **Panel colours match wallpaper (`aquaTint`)**: samples the wallpaper's dominant colour (video/GIF refresh every 2 s); plus **unified fog** (full-screen tinted mask), **adaptive text colour + blue cleanup**, **dark-background text readability**, **task list frost** - **Liquid glass (CSS/SVG)**: `lgCss` + refraction amount on the composer / left sidebar / title bar; the WebGL runtime in `lib/liquid-glass/` is not part of the build (see [File structure](#file-structure)) **🧩 dsh-better-sidebar compatibility (shown once that plugin is detected)** - When installed, the *Other* tab gains a **compatibility section**: master switch `bsCompat` (**default on**) plus `bsFloat` (floating two-layer fix: 14px rounded shell + transparent inner layer + zero margin + the resize strip moved inside the panel) / `bsFont` / `bsReveal` + `bsRevealAlpha` / `bsAqua` - The host `/ping` returns `{ok, version, betterSidebar, betterSidebarVersion}`; the client writes `body[data-mpw-bs-version]` and version-specific rules are gated with `[data-mpw-bs-version^="…"]` (`lib/index.js:1626-1643`, `lib/client.js:255-269`) - Details in [`docs/BETTER-SIDEBAR-COMPAT.md`](docs/BETTER-SIDEBAR-COMPAT.md) and [`docs/BETTER-SIDEBAR-DOM-CONTRACT-0.19.1.md`](docs/BETTER-SIDEBAR-DOM-CONTRACT-0.19.1.md); regression: `node tools/better-sidebar-compat-test.mjs` **⏯️ Playback control and power saving** - Video/web wallpapers can be **paused/played** from the *Wallpaper* tab; the button reflects the real playback state, and **changing unrelated settings does not restart playback** (the `video.src` equality test was fixed) - **Three power-saving switches**: pause when the page is hidden/switched away, pause on window blur, pause on battery (`getBattery`; silently skipped when unavailable). Any one of them pauses, all must recover to resume, and they share one gating model with manual pause **🚀 Large-file hybrid mode (hybrid, on by default)** - The mpkg is **streamed** to the DSH host → stored on disk → played back over HTTP Range (`lib/index.js:1688-1728`, `:1730-1790`); **>600MB works** because the bytes never sit in memory. Turning it off falls back to browser-only mode (600MB ceiling) **🖼️ Local library and rotation** - Steam auto-discovery + custom folder (cross-platform folder picker); WE playlists (`general.playlists` in `config.json`) are imported as rotation lists - Previous/next switching and scheduled rotation (`rotate` + `rotateMin`, 1–120 minutes); ticking items does not make the list jump to the top **🛡️ Safety and coexistence** - **Conflict detection**: other wallpaper/theme plugins disable this feature automatically (it can be forced back on, which writes `forceEnabled`) - `.exe/application` wallpapers are excluded outright (`lib/web-wallpaper.js:101`, `:199`); custom folders only read media files; host routes validate against path traversal; web wallpapers are isolated in a sandboxed iframe **💾 Backup, restore and settings persistence** - **Backup & restore** in the *Other* tab exports appearance settings as shareable JSON (`BACKUP_FIELDS`, `lib/client.js:11979-11993`) and restores them on import - Besides browser `localStorage` (key `dsh.mpkg-wallpaper.v2`, `lib/client.js:48`), settings are also stored on the host as `~/.dsh-mpkg-wallpaper/settings.json`, so they survive port changes and cleared browser data ## Supported types and boundaries This section answers "does my material work, and how well"; the boundary list under the table explains why some things are out of reach. | Type | Status | What it can / cannot control | |---|---|---| | **mpkg (video)** | ✅ Full | Embedded mp4 / video textures play directly; mute, speed, pause, blur/zoom/brightness all adjustable | | **mpkg (scene)** | 🟡 Compromise | Static frame / layer composite / embedded video slots; **Live2D puppets, shaders and scripts are out of reach** | | **Time Variation** | ✅ Multi-slot | Automatic switching + manual lock; only the current slot is extracted | | **Video (mp4/webm/mov/m4v)** | ✅ Full | Plays directly; fps/resolution caps require ffmpeg transcoding | | **Web (HTML)** | 🟡 Experimental | Sandboxed iframe + WE API shim; **Live2D-class settings are editable**; external-SDK / heavily interactive ones are not adapted | | **scene.pkg loose folder** | 🟡 Compromise | Same as mpkg scene wallpapers | | **preview.gif / image / animation** | ✅ Full | Falls back to the author's preview animation when a scene has nothing better (`lib/client.js:887`, `:12272`) | | **Application (exe)** | ❌ Excluded | Content detection yields `unknown/excluded-application`; never read, never executed (`lib/web-wallpaper.js:199`) | | **Custom folder (mixed)** | ✅ Full | mpkg files and workshop subfolders mixed; bounded scan (depth ≤4, ≤4000 entries, `lib/web-wallpaper.js:213-232`) | **Hard boundaries** (facts about the current implementation, not "later" items): - **Full dynamic reconstruction of scene wallpapers is impossible** — the MDL puppet skeleton has no public format documentation and shaders/scripts have no Web runtime (see [Scene adaptation](#scene-wallpaper-adaptation)) - **The pure-Scene renderer selector is not wired up**: `sceneRender` (webgl/static/elysia) in the *Other* tab has buttons and writes a value, but nothing in the repository reads it. The real path is decided by renderer availability (online → `:8899` iframe, offline → static-frame fallback, `lib/client.js:3133-3138`) - Web wallpapers **cannot** reproduce CSS `:hover/:active`, `isTrusted:true`, in-frame `contextmenu` or pointer lock/fullscreen/download popups (inherent limits of synthetic events, `docs/WEB-WALLPAPER.md` §11.4) - Assets above 600MB work only in **hybrid mode**; browser-only mode additionally caps video textures at 250MB, images at 200MB and local image files at 100MB ## Settings reference (7 tabs) This is the **authoritative table of every setting**: each row = panel label + internal key + default + what it does + how to turn it off. The tab order matches the panel and is fixed: `TAB_ORDER = ["source","wallpaper","appearance","unify","blur","other","liquid"]` (`lib/client.js:10730`), labelled **Background source / Wallpaper / Appearance / Surface unify / UI blur / Other / Liquid Glass (test)**. ### 1. Background source (source) | Label | Key | Default | Purpose | Off / rollback | |---|---|---|---|---| | Enable mpkg background | `enabled` | on | Master switch; off applies no background at all | off | | Upload to dsh for streaming playback | `hybrid` | on | Large-file hybrid mode (host upload → disk → Range playback, no 600MB ceiling) | off = browser-only mode | | mpkg / image / video file / image URL | — | — | File pickers and URL input (`http(s)` or `data:image`) | "Clear wallpaper" | | Custom local wallpaper folder | `customDirPath` | empty | Any folder; can point at the workshop root, every subfolder becomes one wallpaper | clear the field | | Local wallpaper library (Steam scan) | — | — | Scans the WE install and Workshop, importing wallpapers and `config.json` playlists | rescan replaces it | | Rotation | `rotate` / `rotateMin` | off / 5 min | Switches to the next wallpaper on a timer (1–120 minutes) | off | | Time slot | `timeOverride` | auto | Manually locks morning/day/dusk/night; buttons list only slots that really exist | "Auto" | | Current wallpaper card | — | — | Preview, display name, container file name, type; pause/play, refresh, clear | — | ### 2. Wallpaper (wallpaper) Contains the *Wallpaper picture* and *Power saving* sub-sections. | Label | Key | Default | Purpose | Off / rollback | |---|---|---|---|---| | Mute (web wallpapers) | `mute` | on | Web-wallpaper audio; off lets the wallpaper make sound | off | | **Now playing (above Settings in the sidebar)** | `npNowPlaying` | **on** | Left-sidebar player whose transport row controls wallpaper sound. **Off = zero injection**; yields to another plugin (`data-mpw-np-yield`) | off / restore defaults | | Play/pause also controls the wallpaper | `npLinkWallpaper` | on | Now playing's play/pause and position also drive the wallpaper itself; off = only this plugin's player (when the wallpaper media is the only source the controls are **honestly disabled**) | off | | Horizontal flip (mirror) | `flipX` | off | Mirrors the wallpaper horizontally | off | | Vertical flip (mirror) | `flipY` | off | Mirrors the wallpaper vertically | off | | Decode fps cap | `fpsCap` | unlimited | Host ffmpeg frame extraction when the source exceeds the cap (24/30/48/60) | "unlimited" | | Resolution cap | `resMax` | source resolution | ffmpeg downscaling (720p/1080p/2K, aspect kept) | "source resolution" | | Playback speed | `playbackRate` | 1x | 0.5–2x (steps 0.5/0.75/1/1.25/1.5/2) | 1x | | ffmpeg status | — | — | Shows system/cached/env source; download when missing, uninstall the cached copy only (never touches the system one) | — | | Adjustable options (collapsed) | `propEdits` | empty | mpkg read-only display; web wallpapers are editable (resolution/language/volume, below) | per-wallpaper reset | | Frosted blur | `blur` | 12px | Wallpaper-layer blur (0–40) | 0 | | Lens zoom | `zoom` | 100% | 10–2000% | 100% | | Brightness | `brightness` | 100% | 50–150% filter | 100% | | Lens position (pan) | `lensX` / `lensY` | 0 / 0 | Horizontal/vertical pan, ±2000 each | 0 | | Power save · pause when hidden | `powPauseHidden` | off | `visibilitychange` | off | | Power save · pause on blur | `powPauseBlur` | off | `blur/focus` | off | | Power save · pause on battery | `powPauseBattery` | off | `getBattery`; silently skipped when unavailable | off | ### 3. Appearance (appearance) Contains the *Show wallpaper* sub-section. | Label | Key | Default | Purpose | Off / rollback | |---|---|---|---|---| | Floating cards | `float` | off | Left sidebar/title bar become floating cards (radius + shadow + wallpaper showing through) | off | | Theme colour | `themeColor` | empty | Base tint of sidebar/title bar/new-chat/settings dialog (picker + presets) | empty = disabled | | Panel colors match wallpaper | `aquaTint` | off | Samples the wallpaper's dominant colour for panel bases (video/GIF refresh every 2 s) | off = use the picker | | Accent | `accent` | empty | Brand interaction colour (buttons/sliders/selection/links/send) | empty = DSH default | | Mask custom colour | `aquaColor` | empty | Custom colour for unified fog / panel tint | empty = grey | | Custom grey text colour | `fontColorGray` | off | Grey text uses a custom colour (`fontColorGrayColor`) | off | | Show wallpaper in left sidebar | `sidebar` | on | off = solid opaque sidebar | off | | Left sidebar frost | `sidebarBlur` | off | `backdrop-filter` on the sidebar itself; removed automatically while popups are open | off (requires sidebar reveal) | | Left sidebar frost amount | `sidebarBlurAmount` | 14px | 0–40; taken over while unified blur is on | — | | Show wallpaper behind the title bar | `headerBg` | on | off = plain white title bar | off | | Frost the title bar | `headerBlur` | on | Taken over while unified blur is on | off | | Title bar frost amount | `headerBlurAmount` | 0% | Frost thickness 0–100% (default 0 = transparent) | 0 | | Set title bar frost separately | `headerFrostOwn` | off | on = `headerFrostAmount` overrides the frost radius | off | | Title bar frost strength | `headerFrostAmount` | 30px | 0–60 | 0 | | Right sidebar / dock blur | `rightSidebarBlur` | on | The DSH right sidebar and bottom dock | off | | Blur amount / surface opacity | `rightSidebarBlurAmount` / `rightSidebarAlpha` | 14px / 45% | 0–40 / 0–100% | — | ### 4. Surface unify (unify) | Label | Key | Default | Purpose | Off / rollback | |---|---|---|---|---| | Unify blur | `unifyTint` | on | One slider drives the whole-screen blur; takes over sidebar/title-bar/right-sidebar frost | off | | Full-screen blur degree | `unifyAmount` | 30px | 0–40 (drives the wallpaper-layer blur) | — | | Left sidebar / title-bar opacity | `sidebarAlpha` | 35% | Frost thickness 0–100% | — | | Chat follows full-screen blur | `chatFollow` | on | off = the chat area is driven by the *Frosted blur* slider | off | | New-chat button follows panel opacity | `sessionFollow` | on | off = back to the host's original colour | off | | Unified fog (full-screen mask) | `aquaMask` | off | All surfaces share one fog colour (moved here from the Aqua section) | off | | Unified fog strength | `aquaMaskAlpha` | 82% | 0–100% | — | ### 5. UI blur (blur) | Label | Key | Default | Amount key / default | Off | |---|---|---|---|---| | Blur dialogs | `dialogBlur` | on | `dialogAmount` 14px | off | | Blur settings panel | `settingsBlur` | on | `settingsAmount` 14px | off | | Blur download/confirm popups | `confirmBlur` | on | `confirmAmount` 12px | off | | Blur popovers | `popoverBlur` | on | `popoverAmount` 10px; plus `popoverAlpha` 94% surface opacity | off | | Blur mask (full-screen backdrop) | `maskBlur` | on | `maskAmount` 8px | off | ### 6. Other (other) | Label | Key | Default | Purpose | Off / rollback | |---|---|---|---|---| | Light sharpening | `sharp` | on | Improves low-quality GIFs; turn it off if animations stutter | off | | Deep diving background box | `thinkBg` | off | on = the thinking state gets a blurred background box | off | | Task list frost | `todoBlur` | off | Blurs todo-card backgrounds | off | | Third-party UI radius compat | `roundCompat` | off | Radius compatibility for third-party plugins | off | | Adaptive text color + blue cleanup | `aquaInk` | off | Text colour follows mask luminance + brand colours are unified (`aquaInkColor`) | off | | Dark-background text readability | `aquaTextEnhance` | off | Two-colour text outline (an approximation) | off | | Pure-Scene renderer selection | `sceneRender` | — | ⚠ Has buttons and a write path, **but no reader** (not wired up) | — | | Scene render reporting (troubleshooting) | `sceneReport` | on | The wallpaper writes renderer state into `reports/` every 10 s | off | | Scene first-frame watchdog | `sceneWatchdog` | on | Falls back to the static frame when no frame arrives in time; `sceneWatchdogSecs` 8 s (3–30) | off | | Retry renderer / renderer debug params / extension hook URL | `sceneDebugParams` / `sceneExtUrl` | empty | Allow-listed pass-through (`ln/eye/audit/isolate/parallax/…`); the extension hook is appended as `extbase` | clear / clear all | | One-click diagnostics report | — | — | Collects subsystem state → `POST /diag`; downloads a JSON file when the host is unavailable | — | | Diagnostics switch cheat sheet (renderer) | — | — | The 10 common renderer flags + "copy" for the URL fragment | — | | better-sidebar compatibility | `bsCompat` / `bsFloat` / `bsFont` / `bsReveal` / `bsRevealAlpha` / `bsAqua` | on / off / off / off / 62% / off | See above; shown only when better-sidebar is detected | master off = none apply | | Backup & restore | — | — | Exports/imports the `BACKUP_FIELDS` allow-list (current wallpaper and scan folders excluded) | — | | Restore all defaults / feedback / check for updates | — | — | Resets appearance values; one-click update pulls the latest code from GitHub | — | ### 7. Liquid Glass (test) (liquid) | Label | Key | Default | Purpose | Off / rollback | |---|---|---|---|---| | Test mode master switch | `lgTest` | off | Keeps only wallpaper + floating + layout, disabling every appearance feature | off | | Liquid glass (CSS) | `lgCss` | off | Pure CSS/SVG refraction + specular edges (no WebGL context, can run alongside scene wallpapers) | off / URL `?lgcss=off` | | Refraction | `lgCssAmount` | 14px | 0–40 (0 = plain blur) | — | | Composer / left sidebar / title bar liquid glass | `lgComposer` / `lgSidebar` / `lgHeader` | off | Each selects its own target (JS applies a `[data-mpw-lg-css]` marker) | off | | Standalone demo page (port 3081) | — | — | The WebGL2 demo under `tools/liquid-demo/` (a separate service; it does not affect the plugin) | — | ### Keys without a panel control These keys **exist and participate in the logic** but have no widget on the settings page; they are reachable only through a backup import, by editing `localStorage` directly, or via URL parameters (`lib/client.js:101-232` holds every default; `tools/switch-wiring-test.mjs:41-64` is the "runtime only" allow-list with the reason for each entry). | Key | Default | Notes | |---|---|---| | `clock` / `clock24h` / `clockSec` / `clockDate` / `clockPos` / `clockSize` | off / on / off / off / `tr` / 40 | Runtime compatibility item: old configurations still render the clock, the settings page has no toggle | | `bsAlpha` | off | better-sidebar panels follow the theme base colour (CSS reads it, no widget) | | `bsBottomAvoid` | off | A settled **deliberate no-op** (alignment belongs to better-sidebar's ResizeObserver) | | `newStyle` | off | Only changes settings-page widget looks; never enters `buildCss` | | `forceEnabled` | off | Runtime priority flag for forcing the feature back on past conflict detection | | `opacity` | 82 | The "panel opacity" slider was removed (unified blur uses `sidebarAlpha`); the value is still read | | `aquaTintStrength` | 45 | How much of the wallpaper colour is mixed into panel tints; read at runtime | | `glassColor` / `glassAlpha` | empty / 12 | Early WebGL liquid-glass leftovers: backup/restore only, **no widget and no reader** | | `webInteraction` | `pointer` | Web-wallpaper interaction mode (`off`/`pointer`/`full`); use `?mpwinteract=…` or write the stored settings | | `sceneRendererUrl` | `http://127.0.0.1:8899/` | Scene-renderer address, overridable | | `npVolume` | 100 | The **volume level** in the Now playing card (0..100, applied to the real element); the default profile **never writes element volume**, only your own change does | | `glassWindow` | — | **Retired and deleted** (2026-09-19): no widget, no reader; covered by `settingsBlur` + `dialogBlur`/`popoverBlur` | ## Now playing and wallpaper sound This section covers **where the sidebar player mounts, what it shows and what it can control** — its source is the wallpaper's own sound. > Location convention: this section refers to the implementation by **symbol name** (`resolveAnchor` / `shouldHide` / `occupantOf` / `PlayMark` / `markYield` in `lib/now-playing.js`, `opsX` and friends in `lib/now-playing-math.js`, `npResolveMedia` / `npActiveVideo` / `npAudioScope` / `npApplyMute` / `applyNowPlaying` in `lib/client.js`) — **line numbers drift between versions; symbols are authoritative**. The shape is "source + generated inline": `lib/now-playing-math.js` + `lib/now-playing.js` are inlined byte-for-byte into the `MPW-NP-GEN-START/END` region of `lib/client.js` by `tools/build-now-playing.mjs`. - **Mount point**: the host slot `sidebar.footer.action` (the `createSlotAction` registration in `lib/client.js`: `id:"mpw-now-playing"`, `order:60`). When the slot is unavailable, `resolveAnchor()` falls back by mode: `slot` → `settings-slot` (before the host's settings cell) → `settings-area` (before `[class*="settingsArea"]`) → `foot` (first position in `[class*="footArea"]`); if none holds it **creates no node at all** and logs a `console.warn`. **A late anchor still gets mounted**: when no landing spot exists it watches the document and mounts itself as soon as the host's slot outlet renders (the old code only warned and returned, so after a wallpaper switch on a real machine the widget never came back). - **Yielding (the counterpart of being on by default)**: `occupantOf(container, mode, selfNode)` walks the container's children and lets three kinds through — our own nodes, host-owned nodes (the slot outlet / settings cell) and effectively empty nodes; the first remaining element counts as an **occupant** ⇒ we do not mount (before mounting) or we retract (after mounting, via `MutationObserver` with `subtree:true`), write `data-mpw-np-yield="foreign-occupant"` and log one readable warning; when the occupant leaves we come back. The criteria are **two-way**: neither our own nodes nor the host's cells may be misread as occupants. - **Hidden when the sidebar is collapsed**: `data-mpw-np-hidden` + CSS `display:none`. The criterion is **physical width first** (`shouldHide(width, hostCollapsed)`: a measured width ≥ `NP_COLLAPSE_MAX_W = 96` forbids hiding); host signals (slot `wide` / `data-sidebar-collapsed` / root class `collapsed`) are a fallback only when the width **cannot be measured**, and there is a **one-shot** re-check after the anchor moves. When space is tight the whole widget scales by `--mpw-np-fit = clamp(avail/260, 0.5, 1)`, where `avail` measures **our own container** (not `[class*="sidebarCol"]` — that class name appears more than once on a real machine). - **Shape**: one pill that expands into a card — artwork (the current wallpaper's thumbnail), title/byline, progress rail + clock and a full-card hit target. **Four keys when expanded**: previous / play-pause / next / mute-unmute; **three keys when collapsed** — the mute key appears with the card, because the collapsed transport row is positioned as an 88px three-key row at `opsX(0) = 206` and forcing a fourth key in would overflow the right padding. Expanding is a self-stopping 0→1 tween (no resident rAF); play/pause is **not a swapped icon** but the pair of eight-point quads, now shaped by a **tween of the playback state itself** (`mark`: 0 = paused, 1 = playing), while the morph progress `p` only drives size and position. - **Data sources** (`npResolveMedia`: **we only report what we actually know**): | Current wallpaper | What NP shows | What it can control | |---|---|---| | Video wallpaper (the `