aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-11-09 18:10:22 +0100
committerDanilo M. <danix@danix.xyz>2026-11-09 18:10:22 +0100
commit4b98da80bdfd5016ad9f5858310d4b8bacafe7bb (patch)
tree83c9eb6bf503fffe565af32f68de817c3f4a0a9b /docs/superpowers/specs
downloaddeck-it-4b98da80bdfd5016ad9f5858310d4b8bacafe7bb.tar.gz
deck-it-4b98da80bdfd5016ad9f5858310d4b8bacafe7bb.zip
docs: add deck-it design and implementation plan
Diffstat (limited to 'docs/superpowers/specs')
-rw-r--r--docs/superpowers/specs/2026-11-09-deck-it-design.md133
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.