v2.13.1 — Post-v2.13 UX patches
v2.13.1 — Post-v2.13 UX patches
Two follow-ups after the v2.13.0 visual identity pass: a regression that had been silently broken for three releases, plus a UX clarification for Pi-health diagnostics. No new features — just polish that should have been there.
🐛 Boot init regression — three releases broken
Commit 8d47b55 (v2.10 "web-ui catches up") accidentally removed the page-load boot block:
- loadScenes();
- checkStatus();
- setInterval(checkStatus, 15000);Anyone with a long-running browser session kept seeing the rig work because the old JS was still in memory. The bug was latent for v2.10 → v2.11 → v2.12 → v2.13 — fresh page loads showed "Checking services…" forever, empty Quick Commands scene grid, and missing fixture data on first paint.
Fixed by restoring the init as an IIFE at the end of the script with a comment explaining the history so it doesn't get orphaned again.
✨ UX polish bundled with the boot-init fix (PR #39)
- Desktop tabs row — hide the ugly default scrollbar cross-browser (
scrollbar-width: none,::-webkit-scrollbar,-ms-overflow-style). Tighter padding so all 7 tabs fit comfortably in the 900px container without horizontal scroll on desktop. - Copy button on Service logs — uses the async Clipboard API where available, falls back to
textarea+execCommandfor plain-http LAN access (the Pi is served ashttp://lights.local:5000, wherenavigator.clipboardis blocked). Visual "Copied ✓" confirmation that resets after 1.4s. - Default landing tab → Quick Commands. Chat moves to slot 2, still one click away. Chat history still pre-renders into the hidden DOM so it feels instant on first activation.
🩺 Diagnostics — distinguish "not installed" from "inactive" (PR #41)
The Pi-health card has been reporting lighting-mcp: inactive whether the systemd unit was stopped or missing entirely — systemctl is-active returns "inactive" for both. Result: fresh Pis showed the same red warning as crashed Pis, with no signal that the user just needed to run the installer.
Backend:
- New
_parse_systemd_load_state()+_systemd_unit_state()helpers — combineLoadState(existence) +is-active(runtime) into one of:active·inactive·failed·activating·deactivating·masked·not_installed·unknown. Short-circuits onLoadState=not-foundso it doesn't waste a second subprocess call.
UI:
- Services card now partitions installed vs. not-installed entries.
- "2/3" becomes "running / installed" — missing optional units don't drag the count down. If you've installed lighting-control + qlcplus-web and never installed lighting-mcp, you'll see
2/2now (the previous behaviour showed2/3and looked like something was broken). - Not-installed entries render muted + italic with a tooltip pointing at
./lightsctl.sh mcp-install. - Broken installed services render in
--signal-redso they stand out from optional-missing. - The card only flips to
warnstyling when an installed service is actually broken.
✅ Tests
17 new tests in tests/test_systemd_state.py covering the LoadState parser + the state resolver (mock-based exec_fn so they never shell out). Suite is now 195 tests, still sub-second.
Upgrade
From your Mac (the actual deploy path — ./lightsctl.sh update only runs apt):
git pull origin main
bash scripts/deploy.shThen hard-refresh the browser (⌘⇧R / Ctrl⇧R) to bust the cached CSS/JS.
What's next
- PR #40 (tab URL persistence —
?tab=<name>so refresh lands you on the same tab) is still open as of this release. Roll it into a v2.13.2 or queue it for v2.14. - The feature backlog (#24–#37) is the main thrust from here: MIDI controller input, OSC, authentication, integration tests, audio reactivity.