diff options
Diffstat (limited to 'docs/superpowers/specs')
| -rw-r--r-- | docs/superpowers/specs/2026-11-09-deck-it-design.md | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-11-09-deck-it-design.md b/docs/superpowers/specs/2026-11-09-deck-it-design.md new file mode 100644 index 0000000..fad1e4f --- /dev/null +++ b/docs/superpowers/specs/2026-11-09-deck-it-design.md @@ -0,0 +1,133 @@ +# deck-it — design + +**Date:** 2026-11-09 +**Status:** approved (design), pending implementation plan + +## Intent + +An always-on tablet dashboard, stream-deck, and second monitor for this desktop: +Slackware-current, Hyprland with the Lua config (hypr-theme), UDT-themed. It +reuses tabdeck's plumbing (Python stdlib HTTP server, a web page in the tablet's +browser over `adb reverse`, and a virtual monitor streamed with wayvnc) but +replaces tabdeck's fixed stats/keys/mull surface with a module system, and swaps +tabdeck's Arch/systemd assumptions for Slackware's. + +## Goals + +1. Always-on touch control on the Android tablet over USB; Wi-Fi optional. +2. Second monitor: virtual Hyprland output + wayvnc, shown in AVNC. +3. Stream-deck control of OBS: recording, scenes, audio channels, transitions, + sources. +4. Read-only dashboards: system stats, music/volume, windows/workspaces/launch, + claude-code + opencode usage (via llm-cost-dashboard), sb-builder progress. +5. Modular: a new app is a new module, with no core edits. +6. UDT-themed: the palette is read, never restated; a scheme switch recolours + the tablet. + +## Non-goals + +- Not a replacement for quickshell / rofi-scripts. They stay for the static + desktop approach; this is the fast always-on surface. Existing commands + (playerctl, wpctl, hyprctl, rofi scripts) are reused, not reimplemented. +- Not a general automation engine. Only declared actions run. +- No cloud, no account, no app-store install. +- Assistant control: deferred. A module slot is reserved. + +## Anti-goals (failures to avoid) + +- The tablet can ever run an arbitrary command. +- A scheme or wallpaper change leaves the tablet stale or desynced. +- Adding a module means editing the core server. +- Anything requires systemd. + +## Constraints + +- Slackware-current: SysV init, eudev, no systemd. Hyprland with the Lua config. +- Python stdlib only, with one exception: `websocket-client` for obs-websocket. +- Tablet: Android + AVNC + a Chromium browser. +- UDT is the single source of theme truth. + +## Module contract + +A module contributes: +- **Backend** — a `make(cfg) -> Module` factory returning a `Module` with a + `state()` callable (GET) and an `actions` dict of allow-listed verbs (POST). + The client sends `{module, action, ...}`; the server maps ids to code and the + client never sends a command. +- **Frontend** — `static/modules/<name>.js`, a plain object + `{title, interval, mount(el), update(state)}` rendered into the shell grid. + +The core provides the HTTP server, auth/trust, the adb tunnel, theming, and the +module registry. Modules are enabled in config under `[modules]`. + +## Theming + +At startup `deckit/theme.py` reads: +- `~/.config/rofi/udt/palette.rasi` — the structural palette, entries + `name: #rrggbb[aa]` (base, mantle, crust, surface0-2, text, subtext0-1, + overlay0-2, red, green, yellow, teal, blue, lavender, peach, mauve, termblue, + scrim). +- `~/.cache/udt/udt-accent.rasi` — `* { accent: #rrggbb; }`. + +It serves `/theme.css` with `:root { --udt-<name>: #...; --udt-accent: #...; }`. +The frontend styles only through those variables. When UDT regenerates a scheme +(`install.sh`) or accent (`udt-accent` on wallpaper change), the server re-reads +on an mtime poll and a page refresh picks it up. A missing palette falls back to +a built-in Catppuccin Macchiato set so the page always renders. Fonts: Noto Sans +for UI, Inconsolata Nerd Font Mono for numbers. + +## Slackware wiring + +No systemd. `deck-it` is a bash launcher with `start` (default), `stop`, `pair`, +`wifi`, `install`, `uninstall`, `env`, `setup-display`. + +- Start from Hyprland via hypr-theme's `exec-once` (the compositor must be up for + the virtual output and wayvnc); `install` prints the exact line to add. +- Plug-in autostart: an eudev rule in `/etc/udev/rules.d/70-deck-it.rules` with a + non-blocking `RUN+` that setsids `deck-it start`. A login-time check covers an + already-connected tablet. +- No `loginctl`; lock/leave stays in the existing rofi powermenu. + +## Second monitor + +Reuse tabdeck's `setup_display`: create a headless Hyprland output sized to the +tablet (`hyprctl output create headless`, then the Lua `hl.monitor` table), run +wayvnc with TLS + a random password in a private dir, and tunnel the VNC port to +the tablet with `adb reverse tcp:5900 localfilesystem:<run>/vnc.sock`. AVNC is +opened with the login in the link. wayvnc works on Hyprland, which implements +the wlroots screencopy and virtual-input protocols it needs. + +## Security + +Reuse tabdeck's model unchanged in spirit: +- Unix socket in a 0700 `$XDG_RUNTIME_DIR/deck-it`, exposed only by `adb reverse`. +- Token cookie (HttpOnly, SameSite=Strict) unlocks the API. +- Host/Origin checks block DNS rebinding and cross-site writes. +- Client sends ids, never commands. OBS's password and the OBS connection stay + server-side. + +## Per-module specifics + +| Module | Source | Actions | +|---|---|---| +| stats | /proc, sysfs, nvidia-smi | none (read) | +| music | playerctl + wpctl | play/pause, prev/next, volume ±/mute | +| windows | hyprctl clients/activewindow/workspaces | workspace switch, focus/close, launch (config commands) | +| obs | obs-websocket v5 client | set scene, toggle record/stream, input volume/mute, set/trigger transition, toggle source | +| llm | relay llm-cost-dashboard `/api/summary`, `/api/today`, `/api/claude_limits`, `/api/claude_session`, `/api/opencode_agents` | none (read) | +| sbbuilder | `ssh <host> sbb-remote jobs` (read-only) | none (read) | +| assistant | deferred | — | + +## Build order + +1. Core — scaffold, config, theming, module registry, server + auth, frontend + shell, launcher, second monitor. +2. First modules — stats, music, windows (proves the contract). +3. OBS — the stream-deck. +4. Read-only feeds — llm + sbbuilder. +5. Assistant — deferred. + +## Open calls (settled in the plan) + +- OBS client: `websocket-client` is the single non-stdlib dependency. +- sb-builder bridge: `sbb-remote jobs` over ssh; no change to sb-builder. |
