feat: mobile web UI + network access toggle - #231
Merged
Merged
Conversation
Add a phone-optimized web UI and let other devices on the LAN reach a StemDeck instance, so the app is usable end-to-end from a phone. Mobile UI (static/mobile/, vanilla JS to match the stack): - Library, Mixer, and Extract screens wired to the real API. Library lists /api/jobs with swipe-to-delete; Mixer reuses the desktop Web Audio engine (audioEngine.js, now accepting a shared gesture-unlocked AudioContext for iOS) with faders/mute/solo/seek, real analysis, and mixdown/MP4 export; Extract submits URL/upload and follows SSE progress. - Served by a user-agent check on "/" (phones get mobile, everyone else the DAW; ?ui= overrides). Shared DOM-free helpers in static/js/shared/jobs.js. - Ported from the design prototype kept under design/mobile/. Network access (app/core/settings.py, app/main.py): - Backend always binds 0.0.0.0; a runtime gate decides whether non-host requests are served (default off, opt-in). The host machine (loopback or its own LAN IP) is always allowed, so it can't be locked out. - Settings dialog reorganized into General / Advanced tabs: General holds max track length (<=20 min) and MP4 video quality; Advanced holds the network toggle (with the LAN address list) and out-of-sync resync. - Runtime settings (allow_network, max_duration_sec, video_max_height) are persisted and read live via GET/POST /api/settings, no restart needed. Performance: stem MP3s are transcoded once and cached on disk (was re-encoded on every request), so loading a track on mobile is fast and re-loads instant. Desktop: start_backend binds 0.0.0.0; adds a local_ip command.
…k design refs - _local_ips() and settings _load()/_save(): replace bare `except: pass` with an explanatory comment + logging.debug/warning(exc_info=True); behavior unchanged (still best-effort). - _load(): handle the no-file case explicitly (FileNotFoundError) vs. logging genuinely corrupt files. - Untrack design/ (the imported Claude Design prototype) and gitignore it — it's a local spec reference, not shipped code, and the static analyzer's "no-effect expression" flags on its <x-dc> template bindings were false positives.
thcp
added a commit
that referenced
this pull request
Jun 27, 2026
Follow-ups to the mobile UI (#231): - Mixer waveform now fills yellow as playback progresses (the played bars, not just the playhead), and repaints on seek. - Library/Mixer/mini-player show the real YouTube/SoundCloud thumbnail when available (layered over the gradient as a fallback), not just a letter. - Configurable port (Settings -> Advanced): default 8080, persisted, read by the desktop launcher before spawning the backend (falls back to a free port if taken). A stable port means a stable phone URL. Applies on restart. - Settings General tab: number fields are digit-only text inputs (no spinner arrows), length-capped; max track length capped at 20 min with the limit noted in the description; controls aligned. Added a Done button. Co-authored-by: Thales <>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A phone-optimized web UI for StemDeck, plus an opt-in "Make StemDeck available on your network" setting so other devices on the LAN can reach an instance — making the app usable end-to-end from a phone.
Mobile UI (
static/mobile/, vanilla JS to match the stack)/api/jobs, swipe-left to delete, "Load" into the Mixer.audioEngine.js, now accepting a shared gesture-unlocked AudioContext for iOS) with faders / mute / solo / seek, real analysis, and mixdown / MP4 export.POST /api/jobs, with live SSE progress./(phones get mobile, everyone else the DAW;?ui=mobile/?ui=desktopoverride). Shared DOM-free helpers instatic/js/shared/jobs.js.design/mobile/.Network access (
app/core/settings.py,app/main.py)0.0.0.0; a runtime gate decides whether non-host requests are served (default off, opt-in). The host machine (loopback or its own LAN IP) is always allowed, so it can never lock itself out.allow_network,max_duration_sec,video_max_height) persist tosettings.jsonand are read live viaGET/POST /api/settings— no restart.Performance
Desktop
start_backendbinds0.0.0.0; adds alocal_ipcommand. Validated withcargo clippy.Tests
tests/test_mobile_routing.pyandtests/test_network_gate.py(UA routing, the host/LAN-IP gate, settings round-trip + clamps), plustests/conftest.pyisolating the gate. 127 passed, ruff + format clean.