# Roadmap (dev-side, detailed) Long-form version of the roadmap section in [README.md](https://github.com/Delido/signalrgb-wallpaper/blob/main/README.md). Items are grouped by impact-to-effort ratio. Each entry has an effort estimate (maintainer's order-of-magnitude β€” not a contract), status, and any notes on architecture / dependencies. Status legend: - πŸ”² not started - 🚧 in progress - βœ… shipped (these usually get moved to `CHANGELOG.md` instead) - πŸ…ΏοΈ parked / blocked --- ## πŸ–ΌοΈ Workflow polish β€” Gallery + Builder + multi-monitor Identified during v0.8.2-beta testing: the "find a wallpaper β†’ cut transparency into it β†’ use it on a screen" loop was too much friction, and multi-monitor users had to redo settings on every tab manually. Shipped across the v0.8.3 β†’ v0.8.7 beta cycle: - **v0.8.3-beta** β€” Gallery + Builder bridge (hover preview, click-to-preview + Undo, right-click menu, Builder open/save library, ?library deep-link) - **v0.8.4-beta** β€” Pin + sort + drag-reorder, Builder glow preview - **v0.8.5-beta** β€” Bug fixes, Builder crop tool, tab labels with resolution, library picker on Builder merge slots - **v0.8.6-beta** β€” Installer-overwrite hotfix (library.json) - **v0.8.7-beta** β€” Apply-to-all per section, overview card with mini-monitor thumbnails - **v0.8.8-beta** β€” Mirror mode, Builder 2Γ—2-grid merge, Tool-options column widened Workflow-polish slice complete. βœ… ### βœ… Gallery: hover-preview large + RGB-mock glow behind β€” shipped v0.8.3-beta Hovering a Library tile pops a larger preview (around 800 Γ— 450) with an animated RGB-cycle gradient behind the transparent cut-outs (or the live `currentTintCss` value if a wallpaper page is already running). You see what the wallpaper *actually looks like* before you commit. Click anywhere outside or press Esc to dismiss; Apply button inside the preview to commit. ### βœ… Gallery: click is preview, Apply is separate + 5 s Undo-Toast β€” shipped v0.8.3-beta Currently a single click wipes the screen's existing background. Split into a *preview* click (popup as above) + a deliberate *Apply* button inside. Plus: after Apply, a 5-second *"Undo β€” restore previous background"* toast at the bottom of the Configurator. Reverts via the same `POST /screen/N/background` path using a cached prev-bg blob. ### βœ… Gallery: right-click context menu β€” shipped v0.8.3-beta `contextmenu` event on Library tiles β†’ custom menu (Configurator already has the styling chops for it): - Apply (default left-click action; here just for symmetry) - Edit in Builder β†’ opens `/builder` in a new tab with the image's path as a query parameter (see Builder "Open from library" below) - Rename β†’ prompts for a new label, renames the file + regenerates `library.json` - Duplicate β†’ copies the PNG with a "-copy" suffix + new catalogue entry - Delete β†’ same path as the existing hover-Γ— button ### βœ… Gallery: sort + pin favourites β€” shipped v0.8.4-beta `library.json` gains optional `pinned: true` and `addedAt` timestamps. Render order: pinned first β†’ built-in starters β†’ user uploads sorted by addedAt descending. Right-click β†’ Pin / Unpin toggle. ### βœ… Gallery: drag-and-drop reorder β€” shipped v0.8.4-beta HTML5 drag API on Library tiles. On drop: bridge gets a `POST /library/reorder` with the new `order` array; persisted as an `order` field per entry in `library.json`. Render order falls back to addedAt when `order` is absent (backwards-compatible). ### βœ… Builder: "Open from library" picker β€” shipped v0.8.3-beta Currently Builder only accepts *Choose image…* + drag-and-drop. Adds a dropdown next to those that lists every Library item; pick one and the Builder loads it as the active image. Also honours `?image=` query string so Configurator's *Edit in Builder* context-menu entry can deep-link. ### βœ… Builder: "Save to library" button β€” shipped v0.8.3-beta New action next to *Apply to Screen N* / *Save as PNG*: - *Save to library as…* β†’ prompts for label, creates a new Library entry from the current canvas - *Update library entry* β†’ only enabled when the user opened this image from Library; overwrites in place ### βœ… Builder: live RGB preview behind the canvas β€” shipped v0.8.4-beta Toggle in the Builder's top bar: *"Show glow preview"*. When on, a CSS layer underneath the canvas runs an animated RGB cycle (re-uses the same gradient style the Library preview uses), so the user sees what their cut-outs look like against actual shifting colour as they work. Defaults to off to keep the edit canvas clean. ### βœ… Configurator: tab labels with resolution β€” shipped v0.8.5-beta Tab text becomes "Screen 1 β€” 3840Γ—1080" using `viewportW/H` from the screen's settings (the bridge already tracks this for the plugin's Auto-aspect-ratio feature). On screens that haven't connected a wallpaper page yet, falls back to just "Screen N". ### βœ… Configurator: mirror mode per tab β€” shipped v0.8.8-beta Generalised beyond the original "Mirror Screen 1" β€” any screen can mirror any other (cycle/self detection at activation). Bridge enforces the invariant via `_block_if_mirror` on every per-screen mutation path (`setting-update`, widgets, presets, background) and `_replicate_to_mirrors` fans changes out from source to mirrors. ### βœ… Configurator: "Apply to all screens" button per section β€” shipped v0.8.7-beta Small button at the right of each settings section header (Background, Glow, Effects, Widgets): *"Apply to all screens"*. Copies this screen's section's values to every other screen in one shot. Quick-config instead of N-times manual setting. ### βœ… Configurator: overview card with mini-thumbnails β€” shipped v0.8.7-beta ### βœ… Builder: Monitor Wall as primary right-panel nav β€” shipped v0.9.11-beta Wall promoted to the top of the right panel; *Apply to Screen N* + *Multi-monitor split* sections removed (folded into the Wall via *Use current canvas* on each frame). Frames pre-fill with the screen's current `bgImage` via the `/image?path=` proxy. Per-frame click opens a 4-item menu (πŸ“ Choose file, πŸ“š From library, πŸ–ΌοΈ Use current canvas, βœ• Clear). Apply Wall re-loads `/config` after success so frames immediately show the just-applied backgrounds. Horizontal layout's `nowrap; overflow-x: auto` fix from v0.9.9 carried forward. ### βœ… Builder: ⇔ Span canvas across monitors β€” shipped v0.9.13-beta Single button in the Wall toolbar slices the current canvas into one chunk per monitor (sized proportionally to each screen's physical width) and stages every frame at once. Closes the merge β†’ wall workflow gap: a 7680Γ—2160 canvas built from two photos can now go onto a 2 Γ— 2560Γ—1440 wall in one click instead of manual per-frame cropping. Hint under the wall canvas lights up whenever the canvas's aspect ratio is within 5 % of the wall's combined aspect so the shortcut is discoverable. Shipped alongside a tray *Reload wallpaper pages* command for future hot-reload of the wallpaper JS without re-import. ### βœ… Builder: right-panel rework (Source β†’ Wall β†’ Output) β€” shipped v0.9.14-beta Section order corrected so the user's natural flow (load / merge first, then push to the wall) maps to top-to-bottom panel scrolling. Two-image / 2Γ—2 merge controls collapsed into a `
` since the single-image happy path doesn't need four file-pick slots in view. *Wand anwenden* promoted to a full-width primary button with Span / Clear in a secondary row; *Clear* now disables itself when nothing is staged. Staged-ready hint replaces the *try Span* banner the moment any slot fills, so the UI no longer suggests an action the user has just performed. A new card at the top of the Configurator, above the tab bar: horizontal row of N small monitor-frame thumbnails (matching the screen count), each showing the current background image of its screen. Click a thumbnail β†’ jumps to that Screen's tab. Visual overview of which monitor shows what, without having to flip through tabs. ### βœ… Builder: Ctrl + Wheel zoom β€” shipped (pre-0.8.3-beta) Already implemented in `builder.html` β€” `canvasArea` listens for `wheel` events and zooms in/out by 1.1Γ— when `ctrlKey` is held. ### βœ… Builder: crop tool β€” shipped v0.8.5-beta New toolbox entry: *Crop*. Drag a rectangle; on confirm, the canvas resizes to that rectangle. Pre-fills the rectangle to match the target screen's aspect ratio when known (we have `viewportW/H` from the screen the user came from). Useful for 3840 Γ— 2160 source images that need to fit a 3840 Γ— 1080 ultrawide. --- ## 🎯 Tier 1 β€” Setup polish (biggest UX wins for least effort) These directly reduce the "I installed it and nothing happens / I broke something" support surface. Tier 1 = ~10 hours of work total for a massive UX bump. ### βœ… Setup health check in the tray β€” shipped v0.8.9-beta Tray entry *System status…* opens a Tk dialog with five rows: SignalRGB plugin file present, SignalRGB.exe running, bridge port reachable, wallpaper pages connected, LHM reachable (only if a Hardware-sensor widget exists). Each red row offers a contextual Fix button: open plugins folder, download SignalRGB, open Help, download LHM. ### βœ… Backup + restore config β€” shipped v0.8.9-beta *Export everything…* in the Configurator (new *Backup & Restore* card) downloads a `signalrgb-wallpaper-backup-.zip` via `GET /backup` β€” contains `config.json` + the full `library/` and `screens/` dirs. *Restore from ZIP…* uploads to `POST /restore`; bridge swaps in the config, merges library/screens files on top of the live dirs (won't nuke unmatched local files), rebuilds the library catalogue, and pushes new settings to every screen. `help_images/` not yet included since users don't customise it. ### βœ… Reset + undo β€” shipped v0.8.9-beta + v0.9.10-beta *Reset this screen to defaults* button in the mirror bar (v0.8.9-beta). **Ctrl+Z undo** + **Ctrl+Y / Ctrl+Shift+Z redo** across the last 20 setting changes per screen (v0.9.10-beta) β€” per-screen ring buffer, captured in `setSetting` before each write. Manual edits invalidate the redo stack (linear-history model). Doesn't cover widgets / presets / mirror / cycle, which have their own scoped flows. ### βœ… First-run onboarding tour β€” shipped v0.9.10-beta Configurator-side overlay that fires on first WS settings push when `signalrgb.tour_seen` isn't set in `localStorage`. Seven steps (Welcome β†’ Tabs β†’ Overview β†’ Background β†’ Presets β†’ Builder β†’ Done), each with a spotlight ring + floating tooltip on the live DOM element. Skip / Esc / overlay click dismiss; *Tour* button in the header replays it on demand. Tier 1 complete. --- ## ✨ Tier 2 β€” High-visibility user features These are the features that get screenshotted and shared. Higher ratio of "wow factor" to implementation effort. ### βœ… Wallpaper shuffle / cycle β€” shipped v0.9.2-beta Per-screen *Auto-cycle* block inside the Background card. Configurable: enable, interval (1-720 min), pool (all library / pinned only), order (sequential / random). `CycleScheduler` background thread runs a 30 s tick; mirror screens are skipped since the source's cycle propagates to them via the existing mirror-replication path. Time-of-day pool (dawn / day / dusk / night) deferred to a follow-up. ### βœ… Preset hotkeys β€” shipped v0.9.3-beta Global `Ctrl+Shift+1..4` applies preset slot N on every active screen. `HotkeyListener` runs on its own thread, uses `RegisterHotKey` for each hotkey and a GetMessage loop for dispatch. Tray toggle under Advanced flips `config.presetHotkeysEnabled`; off by default so we don't grab shortcuts the user might already be using. ### βœ… Per-app / per-game profiles β€” shipped v0.9.5-beta `ProfileWatcher` polls foreground at 1 Hz via `GetForegroundWindow β†’ QueryFullProcessImageNameW`, matches basename against `config.profiles` rules (case-insensitive), and applies the rule's preset slot to the chosen screen(s). Snapshots prior state on activation; reverts to it when the foreground changes away. Only one rule active at a time. Configurator's *Per-app profiles* card adds / edits / removes rules; CRUD over new `profile-add`/`profile-update`/`profile-remove` WS commands. ### βœ… Now-playing widget β€” shipped v0.9.4-beta `NowPlayingPoller` reads Windows SMTC via the `winrt-Windows.Media. Control` package (split-package successor of legacy `winsdk`), runs on a dedicated asyncio-loop thread, and merges its snapshot into the existing 1 Hz `sysstats` WS push. Widget rendering on the wallpaper page shows title + artist + optional progress bar; tints the bar with the live glow colour when *Tint* is on. --- ## πŸ› οΈ Tier 3 β€” Power-user / polish ### βœ… Builder: Auto cut tool β€” shipped v0.9.16-beta, finalised v0.9.20-beta ✨ icon in the toolbox. Two modes share the same `clicks` storage and replay path so undo / redo / refine-with-brushes work like any other operation: - **Auto saliency (instant)** β€” frequency-tuned saliency *(Achanta, Hemami, Estrada, SΓΌsstrunk 2009, published academic algorithm)*. For each pixel: Euclidean colour distance from the image's mean RGB plus a brightness-above-mean premium; adaptive threshold. Pure JS, ~50 ms on a typical canvas, offline, no licence concerns. Strong on the neon / UI-overlay / glowing-edge case because those regions are precisely where colour deviates most from the image's overall palette. - **Brightness (Otsu)** β€” Otsu's method on a luma histogram for cases where pure-brightness thresholding fits better. Threshold slider biases the cutoff; Invert toggle flips the mask. Rotation handler updates the stored mask in place so *Rotate 90Β°* keeps the cut aligned with the canvas. **Power-user opt-in**: setting `localStorage["builder.aiEnabled"] = "1"` (or supplying a URL via `["builder.aiModelUrl"]`) injects a third *Custom ONNX model* entry into the dropdown that lazy-loads `onnxruntime-web` from jsDelivr and runs the user's model. Hidden by default after the v0.9.16 β†’ v0.9.20 default-URL saga (RMBG-1.4 was non-commercial; subsequent Apache-2.0 URLs either 404'd or referenced external-data files ORT couldn't auto-resolve). Going classical for the default case solved all three constraints β€” works offline, licence-clean, zero download β€” in one shot. ### 🚧 Winget package + auto-update β€” auto-update finalised v0.9.19-beta In-app auto-update is done. Tray entry *"⬇ Download + install {tag}"* streams the installer into `%TEMP%`, spawns it via `ShellExecuteW` (`/SILENT /SUPPRESSMSGBOXES /NORESTART`), then `os._exit(0)`s. The installer has `CloseApplications=force` so it kills the running bridge cleanly before overwriting `SignalRGBBridge.exe`; the `[Run]` section relaunches the new exe silently. Each step writes to `%TEMP%/signalrgb-update.log` for post-mortem diagnosis. Progress shown via a small Tk window during download. Originally shipped v0.9.8 with `subprocess.Popen(..., DETACHED_PROCESS)`; v0.9.17 swapped that for `ShellExecuteW` after reports of the spawned installer dying with the parent; v0.9.19 added `CloseApplications=force` after the `/SUPPRESSMSGBOXES` plus `CloseApplications=yes` interaction was found to deadlock the silent path (Inno waits on a user-confirm dialog that's already been killed). Three-step debugging β€” kept the changes documented in the changelog so future regressions in this area have a clear diff to look at. Still πŸ”²: Winget manifest submission to `microsoft/winget-pkgs` β€” needs a PR through their submission flow + ongoing manifest updates per release. Left as a manual task for the maintainer when there's audience for it. ### 🚧 Ambient effects: port MIT-licensed CodePen pens β€” first batch v0.9.12-beta v0.9.12-beta added **Constellation** + **Fireflies** ambient presets, written from scratch in the project's own `AMBIENT_PRESETS` shape so no per-pen licence verification was needed. Renderer learned an optional `def.after(ctx, particles, tint)` post-pass hook for effects that draw across the whole particle set (used by Constellation's connecting lines). Further direct ports from individual MIT-licensed CodePen pens are an open menu β€” picked on visual fit (looks great as a wallpaper backdrop, plays well with the live RGB glow), not on a single author / catalogue. CodePen's default licence is MIT but CodePen Pro users can override per-pen, so the licence MUST be verified per pen before porting (the pen's *Settings β†’ License* field is authoritative). Candidate effect types β€” useful as a search lens when browsing CodePen, not a fixed shopping list: - Particle drift / swarm / boids - Geometric flow fields, wave fields - Audio-reactive visualisers (would combine with our existing `lastAudio` FFT bins) - Plasma / fluid / metaball blobs (in addition to the existing Plasma preset) - Generative line art, vector noise fields - Star-field / nebula / cosmic backdrops - Matrix-rain style cascades - Lightning / electric arcs - Water ripples / pond-surface effects Per-pen workflow (each port): 1. Confirm per-pen licence is MIT (or another permissive licence compatible with our MIT distribution). CodePen Pro accounts can override the default β€” check *Settings β†’ License* on the pen. 2. Adapt to our `ambient` IIFE pattern: `#ambient-canvas` element, `targetCount` / `spawn` / `step` / `render` / optional `after` hooks matching the `AMBIENT_PRESETS` shape, start/stop based on user toggle, viewport-resize handler, tintFromGlow option. 3. Add an entry to `docs/credits.md` with: author, pen URL, licence, optional attribution string for the wallpaper credits / About dialog. 4. Add a per-file MIT notice comment block in the ported code. If a pen's licence is non-permissive or unverified, the alternative is what v0.9.12 and v0.9.15 did: write a fresh implementation *inspired by* the visual style, in our own `AMBIENT_PRESETS` shape, with no copied code. That's licence-free by construction and was the right call for those five effects. --- ## 🎨 Post-v1.0 β€” Widget design system refresh The v0.7 β†’ v1.0 arc added widgets incrementally β€” each one got built when the feature was needed, with its own one-off visual style. The result is a set of eleven+ widgets that all *work*, but read as disconnected: different paddings, different header treatments, different background tints, different type scales, different glass / solid / outlined chromes. On a 4-monitor wall with 8-12 widgets visible, that visual inconsistency is the dominant noise. 🚧 **Goal:** unify every widget into a single "tile" design system so the wallpaper reads as one coherent UI surface rather than as "a collection of independent gadgets glued onto a background". ### What "tile" means here A reusable container shell every widget renders into. Properties the shell owns (not the individual widget): - **Background** β€” single source of truth for the tile's fill. Frosted-glass / acrylic look as the default (semi-transparent + blur), with a clear-glass and a solid-fill variant the user can pick per-tile or globally. - **Border / corner radius** β€” uniform across every widget. Single CSS variable so the user can dial it from boxy to fully rounded. - **Shadow / depth** β€” subtle drop shadow to lift the tile off the wallpaper without competing with the SignalRGB glow underneath. - **Header bar** β€” optional, configurable per widget: icon + title on the left, action buttons on the right (settings, refresh, close). Consistent height + type size everywhere. - **Padding + spacing tokens** β€” every widget uses the same scale (e.g. 8 / 12 / 16 / 24 px) so internal layouts line up across tiles when placed side by side. - **Type scale** β€” three sizes top: title (header), primary (body / big numbers), secondary (labels / units). Picked once, applied everywhere. - **Tint integration** β€” accent colour pulls from the live glow colour by default, so widgets visually belong to the wallpaper they sit on. User can override with a fixed accent. - **Interaction states** β€” hover lift, drag-mode outline, snap guides β€” defined once, applied identically across every widget. ### What the widgets contribute Each widget is then just the *body content* inside the shell: - Clock β€” the time text - Weather β€” temp / condition / forecast strip - CPU/RAM meters β€” bars + percentages - Now-playing β€” title + artist + progress bar - Hardware sensor β€” sensor name + value + unit - … No widget owns its own border, background, padding, or header chrome anymore. They all inherit from the shell. ### Implementation notes - Shell component lives in `wallpaper/index.html` as a single CSS class (`.widget-tile`) plus optional modifier classes (`.widget-tile--glass`, `--solid`, `--clear`, `--no-header`). - Each existing widget's CSS gets trimmed down to ONLY the content-layout rules β€” every container / border / background / padding line gets deleted and moved to the shell. - Builder-side widget catalogue (Configurator) gets a single "Tile style" panel that controls the shell variant + accent source globally; per-widget overrides via right-click menu on a tile. - Type tokens live in CSS custom properties at the wallpaper-page root so the user can A/B different scales without rebuilding. - Drag-and-resize behaviour (interact.js) is already widget-level; the new shell wraps that without changing the drag API. ### Why this is post-v1.0 not pre-v1.0 A design-system refresh of this size touches every widget's CSS plus the Configurator's preview canvas plus the Builder's drag-overlay. That's the kind of change that bricks at least one widget on the first iteration. Better done in a focused v1.1.x cycle than as a last-minute v1.0 scramble. The widgets all work correctly today β€” they just don't look like they belong to one product yet. Effort estimate: **~12-16 h** end-to-end (shell design + every widget's CSS rewrite + Configurator integration + Builder preview update + DE/EN strings for the new "Tile style" controls). --- ## 🧩 Post-v1.0 β€” Background Fit: add tile / repeat mode The Background card's *Fit* dropdown currently offers three modes: `cover (crop to fill)`, `contain (letterbox)`, `fill (stretch)`. Missing the obvious fourth one: **tile** β€” repeat a small image across the canvas as a pattern, the way browser-style backgrounds do. Users with seamless / pattern wallpapers (carbon fibre, hex grids, dot patterns, abstract textures, retro 90s tile art) currently have no way to use those at their native scale. ### What gets added Three new dropdown entries: - **tile** β€” repeat the image in both X and Y. CSS: `background-repeat: repeat; background-size: auto;` - **tile X** β€” repeat horizontally only, image fills the screen height (`background-repeat: repeat-x; background-size: auto 100%;`). - **tile Y** β€” repeat vertically only, image fills the screen width (`background-repeat: repeat-y; background-size: 100% auto;`). ### Architecture Current implementation uses `` with `object-fit: cover/contain/fill`. `object-fit` has no tile / repeat mode, so the tile variants need CSS `background-image` on a `
` instead. Two cleanest options: 1. **Single element, CSS-only:** swap `` for `
` and drive everything via `background-image` + `background-size` + `background-repeat`. Same DOM count, same GPU cost, supports every existing mode plus the new ones. Affects the fade-on-load transition logic since `background-image` doesn't fire `load` events the way `` does β€” would need to preload via `new Image()` then swap. 2. **Two-element hybrid:** keep `` for cover/contain/fill, add a hidden `
` for tile modes, toggle visibility based on bgFit value. Lower regression risk on the existing modes, but doubles the DOM + makes the fade-on-load transition asymmetric. Recommended: option 1, with a `new Image()` preload to keep the fade-on-load UX from regressing. ### Per-tile scale (optional follow-up) A second slider β€” *Tile scale* (10 % – 200 %) β€” lets the user resize the pattern without re-uploading a different-sized source image. Drives `background-size: % auto` for *tile X*, `auto %` for *tile Y*, and `% %` for *tile*. ### Where it lives in the code - `wallpaper_bridge/bridge.py` β€” `BG_FIT_CHOICES` (line 692ish); add `"tile"`, `"tile-x"`, `"tile-y"`. Defaults stay `"cover"`. - `wallpaper_bridge/configurator.html` β€” three new `