476 lines
24 KiB
Markdown
476 lines
24 KiB
Markdown
|
|
# socktop-swipe v2 — release plan
|
|||
|
|
|
|||
|
|
Status: **draft for review**, 2026-09-09. Nothing implemented yet.
|
|||
|
|
|
|||
|
|
v1 is three POSIX shell scripts driving lisgd and tmux. It works and is running on
|
|||
|
|
the rack display today, but it hard-codes a single linear carousel of socktop hosts,
|
|||
|
|
and every new kind of screen is a special case (`SOCKTOP_AUX_CMD` is the aux screen
|
|||
|
|
because there was nowhere else to put it). v2 makes the layout a first-class,
|
|||
|
|
user-authored thing and rewrites the logic in Rust.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Decisions locked in
|
|||
|
|
|
|||
|
|
| Question | Decision |
|
|||
|
|
| --- | --- |
|
|||
|
|
| Rust scope | One Rust binary owns config, the grid model, gesture input (evdev, replacing lisgd) and drives tmux. **tmux stays the pane engine**; zellij shelved (§4.3) |
|
|||
|
|
| Grid coordinates | Sparse ordinals — they define *order and relative position*, not physical slots |
|
|||
|
|
| socktop group | **One cell**, regardless of host count; expands into a sub-sequence internally |
|
|||
|
|
| Payload TUIs | Installer *offers* socktop / uptime-kuma-status / unifly, never requires them |
|
|||
|
|
| Distribution | **Source only for now.** Clone from Gitea, `cargo build --release`. No prebuilt binaries, no apt repo yet — revisit after feeling the pain on the Wyse |
|
|||
|
|
| v1 config | **Clean break.** No `.env` reader, no `migrate` command. Rack display's YAML gets hand-written once |
|
|||
|
|
| Canonical repo | `gt.wittyoneoff.com/jason/socktop-swipe` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. The grid model
|
|||
|
|
|
|||
|
|
### 2.1 Coordinates
|
|||
|
|
|
|||
|
|
Every screen declares `at: "<row>x<col>"`.
|
|||
|
|
|
|||
|
|
- **row** increases *downward*: `-1x0` is above `0x0`, `1x0` is below it.
|
|||
|
|
- **col** increases *rightward*: `0x1` is right of `0x0`.
|
|||
|
|
- `0x0` is where you start.
|
|||
|
|
|
|||
|
|
Coordinates are **sparse ordinals**. Only the ordering matters:
|
|||
|
|
|
|||
|
|
- Rows sort ascending. A vertical swipe moves to the next *defined* row in that
|
|||
|
|
direction, not literally `row ± 1`. Rows `-1, 0, 1` and rows `-7, 0, 42` behave
|
|||
|
|
identically.
|
|||
|
|
- Within a row, cells sort by column ascending. A horizontal swipe moves to the next
|
|||
|
|
*defined* cell. `0x1` and `0x5` are interchangeable as long as the ordering is right.
|
|||
|
|
|
|||
|
|
This is the "ezpz for users" property: you never have to count how many screens a
|
|||
|
|
socktop group will produce in order to place the thing next to it.
|
|||
|
|
|
|||
|
|
### 2.2 Horizontal movement
|
|||
|
|
|
|||
|
|
A socktop cell with four hosts is one cell containing a sub-sequence of five screens:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
cell 0x0 (socktop, 4 hosts) cell 0x1 (socktop, 2 hosts)
|
|||
|
|
┌───────────────────────────────────┐ ┌──────────────────────┐
|
|||
|
|
│ tiled → host1 → host2 → host3 → h4│→ │ tiled → host1 → host2│
|
|||
|
|
└───────────────────────────────────┘ └──────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Swiping forward walks the sub-sequence, and only leaves the cell after its last
|
|||
|
|
sub-screen. Non-socktop cells have a sub-sequence of length 1, so they are entered
|
|||
|
|
and left in a single swipe.
|
|||
|
|
|
|||
|
|
**Entry direction sets the landing sub-screen:**
|
|||
|
|
|
|||
|
|
- entering a cell from the left → its **first** sub-screen (the tiled overview)
|
|||
|
|
- entering a cell from the right → its **last** sub-screen
|
|||
|
|
|
|||
|
|
So swiping back left from `0x1` lands you on `0x0`'s last host, not its overview.
|
|||
|
|
That is what v1 does today and it is what makes the carousel feel continuous.
|
|||
|
|
|
|||
|
|
No wrap-around at either end of a row — same reasoning as v1: on a wall display,
|
|||
|
|
wrapping makes it impossible to tell where you are.
|
|||
|
|
|
|||
|
|
### 2.3 Vertical movement
|
|||
|
|
|
|||
|
|
1. Find the next defined row in that direction. If there is none, do nothing.
|
|||
|
|
2. **If you have been in that row before, go back to exactly where you were** — same
|
|||
|
|
cell, same sub-screen. No calculation.
|
|||
|
|
3. Only if the row has never been visited: go to the cell at the same column if one
|
|||
|
|
exists, otherwise snap to the **nearest defined column** in that row. (Ties break
|
|||
|
|
toward the lower column — see open question Q1.)
|
|||
|
|
|
|||
|
|
Return memory always wins over snapping. Each row remembers its last position, and only
|
|||
|
|
a *horizontal* move within that row updates it. This is what makes "swipe up to unifly,
|
|||
|
|
swipe down, you're back on the same Pi" work — and it holds for wider grids too: from
|
|||
|
|
`0x1`, up to `-1x0`, then down returns you to `0x1`, not `0x0`.
|
|||
|
|
|
|||
|
|
Snapping therefore only runs on the first entry into a row, and a layout where every
|
|||
|
|
row has a cell in the same column never runs it at all.
|
|||
|
|
|
|||
|
|
### 2.4 The default layout, walked through
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌──────────────┐
|
|||
|
|
row -1 │ unifly │ -1x0
|
|||
|
|
└──────────────┘
|
|||
|
|
↕
|
|||
|
|
┌───────────────────────────────────┐ ┌──────────────────────┐
|
|||
|
|
row 0 │ 4 Pis: tiled → each Pi zoomed │→ │ orangepi + trixie │
|
|||
|
|
└───────────────────────────────────┘ └──────────────────────┘
|
|||
|
|
0x0 0x1
|
|||
|
|
↕
|
|||
|
|
┌──────────────┐
|
|||
|
|
row 1 │ uptime-kuma │ 1x0
|
|||
|
|
└──────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Swipe **up** from any of the five `0x0` screens → unifly.
|
|||
|
|
- Left/right on unifly → nothing; it is the only cell in row -1.
|
|||
|
|
- Swipe **down** from unifly → back into `0x0`, on the exact Pi you left.
|
|||
|
|
- Swipe **down** again → uptime-kuma-status.
|
|||
|
|
|
|||
|
|
From `0x1` (orangepi + trixie) the same holds: up snaps to `-1x0` the first time, and
|
|||
|
|
down brings you back to `0x1` on the host you left.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Configuration
|
|||
|
|
|
|||
|
|
### 3.1 Format and location
|
|||
|
|
|
|||
|
|
YAML. Resolution order:
|
|||
|
|
|
|||
|
|
1. `--config <path>`
|
|||
|
|
2. `$XDG_CONFIG_HOME/socktop-swipe/config.yaml` (i.e. `~/.config/socktop-swipe/config.yaml`)
|
|||
|
|
3. `/etc/socktop-swipe/config.yaml`
|
|||
|
|
|
|||
|
|
Per-user first, because the display box runs as one dedicated user and that keeps the
|
|||
|
|
file editable without sudo. `/etc` stays supported so a future `.deb` has somewhere
|
|||
|
|
sensible to drop a default.
|
|||
|
|
|
|||
|
|
`socktop-swipe validate` parses the file, resolves the grid, checks every referenced
|
|||
|
|
binary exists, and prints the resulting map. It is the thing to run after every edit,
|
|||
|
|
and the README will say so.
|
|||
|
|
|
|||
|
|
### 3.2 Schema
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# ── how the session is built ────────────────────────────────────────────────
|
|||
|
|
session: socktop-swipe # tmux session name
|
|||
|
|
terminal: alacritty # used only by the autostart the installer writes
|
|||
|
|
|
|||
|
|
# Full paths solve the "i3 has no ~/.cargo/bin on PATH" problem in one place.
|
|||
|
|
# ~ is expanded. Anything already on PATH can be given as a bare name.
|
|||
|
|
binaries:
|
|||
|
|
socktop: ~/.cargo/bin/socktop
|
|||
|
|
uptime-kuma-status: ~/.cargo/bin/uptime-kuma-status
|
|||
|
|
unifly: ~/Documents/GitHub/unifly/target/release-small/unifly
|
|||
|
|
|
|||
|
|
# ── touch panel ─────────────────────────────────────────────────────────────
|
|||
|
|
touch:
|
|||
|
|
device: /dev/input/by-id/usb-ILITEK_ILITEK-TOUCH-event-if00
|
|||
|
|
width: 1280 # the PANEL's resolution, not the X screen
|
|||
|
|
height: 720
|
|||
|
|
grab: true # exclusive grab; see §5
|
|||
|
|
threshold: 80 # px of travel before a drag counts as a swipe
|
|||
|
|
leniency: 30 # degrees off-axis tolerated
|
|||
|
|
fingers: [1, 2, 3] # contact counts accepted
|
|||
|
|
|
|||
|
|
gestures:
|
|||
|
|
forward: RL # right-to-left finger motion moves forward
|
|||
|
|
back: LR
|
|||
|
|
up: DU
|
|||
|
|
down: UD
|
|||
|
|
|
|||
|
|
# ── the grid ────────────────────────────────────────────────────────────────
|
|||
|
|
screens:
|
|||
|
|
- at: -1x0
|
|||
|
|
type: unifly
|
|||
|
|
|
|||
|
|
- at: 0x0
|
|||
|
|
type: socktop
|
|||
|
|
socktop_group: [rpi-master, rpi-worker-1, rpi-worker-2, rpi-worker-3]
|
|||
|
|
layout: tiled
|
|||
|
|
|
|||
|
|
- at: 0x1
|
|||
|
|
type: socktop
|
|||
|
|
socktop_group: "orangepi, trixie" # CSV string also accepted
|
|||
|
|
layout: even-vertical
|
|||
|
|
|
|||
|
|
- at: 1x0
|
|||
|
|
type: uptime-kuma-status
|
|||
|
|
url: https://status.wittyoneoff.com/status/wittyoneoff
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 Monitor types
|
|||
|
|
|
|||
|
|
Each type knows how to turn its parameters into one or more tmux panes. Every type
|
|||
|
|
also accepts `command:` to override the generated command outright, and `title:` to
|
|||
|
|
override the pane border label — so nobody is ever blocked by a type not yet
|
|||
|
|
supporting a parameter they need.
|
|||
|
|
|
|||
|
|
| Type | Parameters | Panes | Generated command |
|
|||
|
|
| --- | --- | --- | --- |
|
|||
|
|
| `socktop` | `socktop_group` (list or CSV, **required**), `layout` (default `tiled`) | one per profile, plus the tiled overview as sub-screen 0 | `<socktop> -P <profile>` |
|
|||
|
|
| `uptime-kuma-status` | `url` | 1 | `<uptime-kuma-status> <url>` |
|
|||
|
|
| `unifly` | *(none yet)* — `site`/`controller` carved out for later | 1 | `<unifly> tui` |
|
|||
|
|
| `generic` | `command` (**required**), `title` | 1 | as given |
|
|||
|
|
|
|||
|
|
`layout` accepts the tmux layout names (`tiled`, `even-horizontal`, `even-vertical`,
|
|||
|
|
`main-horizontal`, `main-vertical`).
|
|||
|
|
|
|||
|
|
Only `socktop` produces a multi-screen sub-sequence today. The internal representation
|
|||
|
|
is a plain `Vec<Pane>` per cell plus an optional overview, so if a future type wants
|
|||
|
|
the same treatment it is a data change, not a redesign.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Architecture
|
|||
|
|
|
|||
|
|
One binary, one process at runtime.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
src/
|
|||
|
|
main.rs CLI dispatch
|
|||
|
|
config/
|
|||
|
|
mod.rs serde types, defaults, path resolution
|
|||
|
|
coord.rs Coord parsing ("0x-1"), Grid construction, neighbour lookup
|
|||
|
|
grid.rs navigation state machine — pure, no I/O, unit tested
|
|||
|
|
monitor.rs MonitorType → panes + command lines
|
|||
|
|
input.rs evdev reader → Swipe { direction, fingers }
|
|||
|
|
session/mod.rs Multiplexer trait — the ONLY module that knows tmux vs zellij (§4.3)
|
|||
|
|
session/tmux.rs build the session, select window/pane, zoom
|
|||
|
|
doctor.rs diagnostics (replaces tools/diag.sh and tools/find-device.sh)
|
|||
|
|
ui.rs optional on-screen position indicator (§8, cuttable)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4.1 Why this is better than v1 even before the grid
|
|||
|
|
|
|||
|
|
v1 runs lisgd as a daemon which shells out to `socktop-swipe next` per gesture. That
|
|||
|
|
script has no memory, so on every single swipe it re-derives "where am I" by
|
|||
|
|
interrogating tmux (`list-windows`, `display-message`, `window_zoomed_flag`). v2 holds
|
|||
|
|
the cursor in memory: one gesture becomes two or three tmux calls instead of six, and
|
|||
|
|
the navigation logic becomes a pure function over a struct that can be unit tested
|
|||
|
|
without a display, a touch panel or a tmux server. That test suite is the main reason
|
|||
|
|
the grid model is worth attempting at all.
|
|||
|
|
|
|||
|
|
### 4.2 CLI
|
|||
|
|
|
|||
|
|
| Command | What it does |
|
|||
|
|
| --- | --- |
|
|||
|
|
| `socktop-swipe run` | Build the tmux session, spawn the terminal attached to it, run the gesture loop, exit when the session exits. **The single autostart line.** |
|
|||
|
|
| `socktop-swipe daemon` | Gesture loop only, against an existing session (for a systemd-user split) |
|
|||
|
|
| `socktop-swipe attach` | Build and attach the session only |
|
|||
|
|
| `socktop-swipe validate` | Parse config, resolve the grid, check binaries, print the map |
|
|||
|
|
| `socktop-swipe doctor` | Interactive touch diagnostics; list candidate devices; report what the panel actually sends |
|
|||
|
|
| `socktop-swipe next\|back\|up\|down` | Drive a running instance over its control socket — for keybindings and testing without a panel |
|
|||
|
|
|
|||
|
|
`run` as one process also kills the v1 double-instance footgun by construction: the
|
|||
|
|
second one fails to grab the device and exits with a clear message.
|
|||
|
|
|
|||
|
|
### 4.3 Multiplexer: tmux, with zellij shelved
|
|||
|
|
|
|||
|
|
**tmux stays.** `session/` is a `Multiplexer` trait with a tmux implementation behind
|
|||
|
|
it, so the question can be reopened cheaply, but it is not being evaluated for v2.
|
|||
|
|
|
|||
|
|
Shelved rather than rejected. The reasoning, recorded in `notes/DESIGN.md` so it is not
|
|||
|
|
re-litigated: "available as a crate" does not buy in-process integration —
|
|||
|
|
`zellij-server`/`zellij-utils` are workspace crates for the binary, not a library
|
|||
|
|
surface, so it would still be a subprocess driven over a CLI exactly like tmux. The
|
|||
|
|
blocking issue is addressing: the grid needs *"focus cell 0x1, sub-screen 3, zoomed"* as
|
|||
|
|
one deterministic call, which tmux gives directly as `select-pane -t session:window.3`
|
|||
|
|
plus `resize-pane -Z`, whereas zellij's CLI is direction-oriented (`move-focus left`).
|
|||
|
|
Footprint also runs the wrong way on 2 GB boxes, and a zellij source build on an Atom is
|
|||
|
|
a much worse story for the install guide than `apt install tmux`.
|
|||
|
|
|
|||
|
|
The one idea worth keeping on the shelf: running the navigation state machine as a
|
|||
|
|
**WASM plugin inside zellij** via `zellij-tile`, which would give real event
|
|||
|
|
subscriptions instead of CLI polling. Revisit only if tmux becomes the bottleneck.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Input: evdev instead of lisgd
|
|||
|
|
|
|||
|
|
`input.rs` opens the event device and reads multitouch **protocol B** (`ABS_MT_SLOT`,
|
|||
|
|
`ABS_MT_TRACKING_ID`, `ABS_MT_POSITION_X/Y`), tracking per-slot start and end points.
|
|||
|
|
A swipe fires when the dominant axis exceeds `threshold`, the off-axis angle is within
|
|||
|
|
`leniency`, and the peak simultaneous contact count is in `fingers`.
|
|||
|
|
|
|||
|
|
What this buys:
|
|||
|
|
|
|||
|
|
- **Drops the C toolchain from the install path.** No `libinput-dev`, no `libX11-dev`,
|
|||
|
|
no `git clone git.sr.ht/~mil/lisgd`, no `make`. On the Wyse's eMMC that is real.
|
|||
|
|
- **`grab: true` replaces `--xignore`.** `EVIOCGRAB` takes the device exclusively, so X
|
|||
|
|
never sees the touches at all — which is what the `Option "Ignore"` InputClass was
|
|||
|
|
faking. That removes an `/etc/X11/xorg.conf.d` file *and* the logout/login step it
|
|||
|
|
required. The X rule stays documented in `notes/` as a fallback for anyone who wants
|
|||
|
|
touch to reach X for other apps.
|
|||
|
|
- **Compositor-agnostic for free.** Reading `/dev/input` directly means nothing in the
|
|||
|
|
gesture path is X11-specific. This is not a Wayland port — the terminal and tmux
|
|||
|
|
still are what they are — but it removes one of the reasons v1 could not be one.
|
|||
|
|
- **The `fingers: [1,2,3]` workaround becomes honest.** Instead of binding three
|
|||
|
|
separate lisgd gestures per direction, the peak contact count is just a field on the
|
|||
|
|
detected swipe, and `doctor` can print it.
|
|||
|
|
|
|||
|
|
Still required: read access to the device. Installer offers the `input` group (needs a
|
|||
|
|
relogin) or a udev rule granting the display user access to that one device (no
|
|||
|
|
relogin). Prefer the udev rule and say why.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Installer
|
|||
|
|
|
|||
|
|
`install.sh` stays POSIX sh — it has to run before any Rust exists.
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
curl -fsSL https://gt.wittyoneoff.com/jason/socktop-swipe/raw/branch/main/install.sh | sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Piping to `sh` leaves no stdin for prompts, so the script reads answers from
|
|||
|
|
`/dev/tty` explicitly. If there is no tty it falls back to defaults and says so.
|
|||
|
|
`--yes` takes every default non-interactively; `--no-<thing>` declines individually.
|
|||
|
|
|
|||
|
|
### 6.1 Dependency preflight
|
|||
|
|
|
|||
|
|
Every dependency is **checked before anything is installed**, and the whole report is
|
|||
|
|
printed at once so you can see the full cost up front rather than discovering it one
|
|||
|
|
prompt at a time. Each missing item comes with a concrete offered fix; anything with no
|
|||
|
|
known fix on the detected distro is reported as such rather than silently skipped, with
|
|||
|
|
the manual step spelled out.
|
|||
|
|
|
|||
|
|
| Checked | How | If missing |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| distro + package manager | `/etc/os-release`, which of `apt-get`/`dnf`/`pacman`/`zypper` | unsupported → print manual package list and continue in degraded mode |
|
|||
|
|
| `tmux` (or `zellij`, per §4.3) | `command -v` + version | offer the distro package |
|
|||
|
|
| terminal emulator | probe `alacritty`, `foot`, `kitty`, `xterm` | offer to install one; explain that it must be GPU/GL-capable-ish and legible at panel size |
|
|||
|
|
| `cargo` / rustup | `command -v cargo` | offer rustup, stating the disk cost (~1.2 GB toolchain + target dir) before asking |
|
|||
|
|
| C toolchain | only if something still needs building | offer build-essential equivalent |
|
|||
|
|
| window manager | probe i3, detect a running WM | needed only for the `--i3`-equivalent autostart; skip that step cleanly if absent |
|
|||
|
|
| display manager | `/etc/lightdm`, `/etc/gdm3`, `/etc/sddm.conf` | autologin is only offered for the one detected; unknown DM → print the manual snippet |
|
|||
|
|
| screen locker | probe `xss-lock`, `light-locker`, `i3lock`, `xscreensaver` | offer to disable, naming the specific unit or process found |
|
|||
|
|
| touch device | enumerate `/dev/input/by-id/*event*` with touchscreen capabilities | none found → point at `socktop-swipe doctor` and stop before writing a broken config |
|
|||
|
|
| device read access | try opening the chosen device | offer udev rule (default) or `input` group |
|
|||
|
|
| `git` | `command -v` | needed only for the unifly source build |
|
|||
|
|
| disk space | `df` on `/usr/local`, `$HOME`, and the cargo target location | warn *before* starting a build that will not fit — this is the Wyse's 8 GB eMMC case specifically |
|
|||
|
|
|
|||
|
|
The report ends with a single summary — *"3 things will be installed, 2 files changed,
|
|||
|
|
~1.4 GB of disk used. Continue? [y/N]"* — and nothing before that point has modified
|
|||
|
|
the system.
|
|||
|
|
|
|||
|
|
### 6.2 Flow
|
|||
|
|
|
|||
|
|
1. **Preflight** (§6.1). Print the full report, get one confirmation.
|
|||
|
|
2. **Toolchain** — if no cargo: *"Rust is needed to build from source. Install rustup? [Y/n]"*
|
|||
|
|
3. **Build and install** socktop-swipe from source to `/usr/local/bin`.
|
|||
|
|
4. **Touch panel** — enumerate `/dev/input/by-id/*` touch devices, show them, ask which,
|
|||
|
|
write it into the config. Offer a live confirm ("swipe now") before committing.
|
|||
|
|
5. **Optional payload TUIs**, each a separate prompt, all skippable:
|
|||
|
|
- `socktop` — `cargo install socktop`
|
|||
|
|
- `uptime-kuma-status` — `cargo install uptime-kuma-status`
|
|||
|
|
- `unifly` — clone `github.com/jasonwitty/unifly`, `cargo build --profile release-small -p unifly`, record the resulting path in `binaries:`
|
|||
|
|
6. **Device access** — udev rule (default) or `input` group.
|
|||
|
|
7. **Autostart?** — i3 / systemd user unit / none.
|
|||
|
|
8. **Autologin?** — lightdm drop-in, with the physical-access warning stated at the
|
|||
|
|
prompt, not buried in the README.
|
|||
|
|
9. **Never blank, never sleep, never lock?** — Xorg `ServerFlags` snippet + `xset` in
|
|||
|
|
the autostart + disable any detected screen locker. (Keeping v1's hard-won note that
|
|||
|
|
`sudo xset` silently targets root's X connection and does nothing.)
|
|||
|
|
10. **Write a starter config** if none exists — one socktop group with a placeholder
|
|||
|
|
profile, commented, and a pointer to `socktop-swipe validate`.
|
|||
|
|
11. **Print exactly what changed**, file by file, and what to do next.
|
|||
|
|
|
|||
|
|
`uninstall.sh` reverses each of the above and, as in v1, prints what it deliberately
|
|||
|
|
left alone.
|
|||
|
|
|
|||
|
|
Cheap future-proofing: add `[package.metadata.deb]` to `Cargo.toml` now (paths
|
|||
|
|
`/usr/bin`, `/etc/socktop-swipe/`, `/lib/udev/rules.d/`). It costs about fifteen lines
|
|||
|
|
and means the apt-repo follow-up is `cargo deb` rather than a packaging project.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. README rewrite
|
|||
|
|
|
|||
|
|
Written for someone who has just found the repo and does not know what socktop is.
|
|||
|
|
Current README leads with mechanism ("the carousel is tmux pane zoom, not extra
|
|||
|
|
instances") — correct, and it moves to `notes/DESIGN.md`.
|
|||
|
|
|
|||
|
|
New order:
|
|||
|
|
|
|||
|
|
1. **What this is** — one paragraph, then the rack photo and the swipe video.
|
|||
|
|
2. **What you're looking at** — annotated: the panel, the 19" adapter, what is on each
|
|||
|
|
screen and why.
|
|||
|
|
3. **Hardware** — tested table (LattePanda, Wyse 3040), the GeeekPi 9" panel, and a link
|
|||
|
|
across to [`cad/`](../cad) for the adapter that mounts it in a 19" rack.
|
|||
|
|
4. **Quick start** — the curl one-liner and what it will ask you.
|
|||
|
|
5. **Your first config** — build it up: one socktop group → add unifly above → add
|
|||
|
|
uptime-kuma above/below. Each step shows the YAML *and* the resulting map.
|
|||
|
|
6. **The grid** — §2 of this document, with the diagram.
|
|||
|
|
7. **Monitor types** — the §3.3 table.
|
|||
|
|
8. **Configuration reference** — full key table.
|
|||
|
|
9. **Unattended operation** — autologin, blanking, phantom outputs, does it come back
|
|||
|
|
after a power cut.
|
|||
|
|
10. **Troubleshooting** — lead with `socktop-swipe doctor`, then the specific failures.
|
|||
|
|
11. **Uninstall.**
|
|||
|
|
12. **Related projects** — socktop, uptime-kuma-status, unifly, Uptime Kuma.
|
|||
|
|
|
|||
|
|
The Wyse 3040 install is done by **following this README and nothing else**, and every
|
|||
|
|
place it is wrong or incomplete is a bug fixed before release.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. Wayfinding indicator (proposed, cuttable)
|
|||
|
|
|
|||
|
|
A 2D grid is harder to stay oriented in than v1's single line, and there is no wrap to
|
|||
|
|
tell you you have reached an end. Proposal: a small persistent indicator in the tmux
|
|||
|
|
status line or pane border showing position, e.g. `unifly ▲ · rpi-worker-2 (3/5) · ▼ kuma`
|
|||
|
|
— the current screen plus what is above and below. Off by default, `indicator: true`
|
|||
|
|
to enable. Flagging it as a distinct decision rather than assuming it.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Repo layout after v2
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
socktop-swipe/
|
|||
|
|
├── README.md rewritten (§7)
|
|||
|
|
├── Cargo.toml / Cargo.lock
|
|||
|
|
├── src/ §4
|
|||
|
|
├── config.example.yaml the default rack layout, heavily commented
|
|||
|
|
├── install.sh uninstall.sh
|
|||
|
|
├── packaging/
|
|||
|
|
│ ├── socktop-swipe.service systemd user unit
|
|||
|
|
│ └── 70-socktop-swipe.rules udev
|
|||
|
|
├── cad/ unchanged
|
|||
|
|
├── media/ unchanged
|
|||
|
|
└── notes/
|
|||
|
|
├── PLAN-v2.md this document
|
|||
|
|
├── DESIGN.md why tmux, why evdev, the grid model's reasoning
|
|||
|
|
├── HARDWARE-NOTES.md ILITEK contact counts, phantom DSI-1, the sudo-xset
|
|||
|
|
│ trap, multi-monitor SCREEN_W, LattePanda specifics,
|
|||
|
|
│ and: screen size and rack size are separate measurements
|
|||
|
|
│ (9" screen, 10" mini-rack mount, 19" rack via the adapter)
|
|||
|
|
├── V1-BASH.md what v1 did and where to find it (tag v1.2)
|
|||
|
|
└── TODO.md
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Tag the current tree `v1.2` before deleting the shell scripts, so the working bash
|
|||
|
|
version stays recoverable.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Work order
|
|||
|
|
|
|||
|
|
Each milestone ends somewhere testable.
|
|||
|
|
|
|||
|
|
| # | Milestone | Done when |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| 1 | Tag `v1.2`. Scaffold the crate. Config types + `coord.rs` + `validate` | `socktop-swipe validate` prints the rack layout map from YAML |
|
|||
|
|
| 2 | `grid.rs` navigation state machine, no I/O | Unit tests cover: sparse columns, sub-sequence entry from left vs right, vertical snap, cursor persistence, both ends of every row |
|
|||
|
|
| 3 | `session/tmux.rs` + `attach` | Session builds correctly from YAML on the desktop; drive it with `next`/`back`/`up`/`down` by hand — no touch panel needed |
|
|||
|
|
| 4 | `input.rs` evdev + `doctor` | `doctor` correctly reports direction and contact count on the LattePanda's ILITEK panel |
|
|||
|
|
| 5 | `run` wires it together | LattePanda runs the full default grid by touch, unifly above and kuma below |
|
|||
|
|
| 6 | Installer rewrite | Clean-VM install works, prompts behave when piped from curl, `--yes` works |
|
|||
|
|
| 7 | README + notes/ split | Reads correctly to someone who has not seen the project |
|
|||
|
|
| 8 | **Wyse 3040 validation** | Debian minimal → working rack display, following only the README. Every stumble is a fix, then re-run |
|
|||
|
|
| 9 | Release | Tag v2.0, `config.example.yaml` matches the rack, `cargo clippy` clean with no `#[allow]` |
|
|||
|
|
|
|||
|
|
Milestone 5 is the point at which the LattePanda switches over. Its v1 install stays
|
|||
|
|
untouched until then — v2 uses a different config path and a different tmux session
|
|||
|
|
name, so both can be installed side by side during milestone 3–5 testing.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. Open questions
|
|||
|
|
|
|||
|
|
**Q1 — column tie-break on snap.** From `0x2`, if the row above has cells at `-1x1`
|
|||
|
|
and `-1x3`, both are distance 1. Plan says lower column (left) wins. Fine? This only
|
|||
|
|
ever fires on the first entry into a row, so it is low-stakes.
|
|||
|
|
|
|||
|
|
**Q2 — the indicator (§8).** Build it, or cut it from v2?
|
|||
|
|
|
|||
|
|
**Q3 — repo visibility.** Distribution is Gitea-source-only for now, which is settled.
|
|||
|
|
But the README is being written for a public audience and the Wyse is reference
|
|||
|
|
hardware for a public guide. Is the Gitea repo publicly readable without a login? If
|
|||
|
|
not, the curl one-liner in the README will not work for anyone but you.
|