diff options
Diffstat (limited to 'docs/superpowers/specs/2026-10-24-companion-seat-design.md')
| -rw-r--r-- | docs/superpowers/specs/2026-10-24-companion-seat-design.md | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-10-24-companion-seat-design.md b/docs/superpowers/specs/2026-10-24-companion-seat-design.md new file mode 100644 index 0000000..3e7fb0f --- /dev/null +++ b/docs/superpowers/specs/2026-10-24-companion-seat-design.md @@ -0,0 +1,103 @@ +# Companion seat: design + +Date: 2026-10-24. Status: implemented; the sitting art is generated by the user. + +## Goal + +While the assistant canvas shows its tall card (a chat, or the history, +picker or models panel), the companion sits on the card's top edge, right +of centre, legs dangling over the empty middle of the header. Typing makes +her lean over and read; BackSpace makes her point and smirk from the ledge; +thinking, listening and the reply wave happen there too. The bar and the +file card do not seat her: she stays in her corner, since standing on a +short card would need its own art to read anything below her feet. + +## Verified before designing + +- **Hyprland's typing binds fire under the canvas.** With the canvas open + and holding the keyboard `Exclusive`, socket2 carried + `custom>>keystone-typing` and `custom>>keystone-delete` for every burst + typed into it, so the companion needs no input of the canvas's own. +- **The tall card has a fixed top.** `Card.tall` is `mode === "chat" || + panel`, and while tall its height is `fullHeight`, set by the window to + 70% of the screen height. The card is centred, so its top is + `(screenH - fullHeight) / 2` and only changes on entering or leaving the + tall state. Nothing needs to follow a moving edge. +- **The header's middle is empty.** The model name is on the left, the + context meter and buttons on the right, both in a 32 px row inside a + 14 px margin. Her legs fall in the gap between them. +- **Sitting art exists.** `base-sitting.png` in the art directory is the + original rest pose: front view, sitting, legs dangling. `dress/base` was + made from it, so the new sitting frames edit it directly. +- **Opening the canvas already remaps her.** `openlayer` for + `keystone-assistant` unmaps and maps her again above the dim, with a drop + out and a drop in. A seat change made while she is unmapped needs no + animation of its own. + +## Behaviour + +### Seat + +`Assistant.qml` gains `readonly property bool tall`, the same expression +as `Card.tall`, and `readonly property real cardTop`, the card's top on +its screen. The canvas owns both so the companion does not reach into the +card. + +The companion's `seated` is `asst.open && asst.tall`. Seated, her window +is on `asst.screenObj` and its margins put her seat's bottom 35 px below +the card's top, with her right edge 90 px right of the screen's centre: +the window stays anchored right and bottom, `margins.bottom = screenH - +cardTop - 35`, `margins.right = screenW / 2 - 90`. Her right edge rather +than her centre, because frames differ in width and the window would move +with every frame. Both numbers were checked by eye. Not seated, the +margins are today's 24 and 16. + +`seated` changing runs the monitor hop's path: she drops out, the window +moves when the exit has settled, and the entrance plays. The first seat on +opening a chat coincides with the remap and is hidden by it. While seated, +a focused monitor change does not hop her; on unseating she goes to the +focused monitor. + +Balloons do not shove her while seated: their stack is in a screen corner, +not on the card. + +### Frames + +Seated, the frame Mood picks goes through `Mood.seatFrame(frame)`: + +| Standing | Sitting | +| --- | --- | +| `watch` | `sit-read`: leaning forward, looking down at the card | +| `point` | `sit-point` | +| `smirk` | `sit-smirk` | +| `think` | `sit-think` | +| `listen` | `sit-listen` | +| `wave`, `wave-l`, `wave-r` | `sit-wave` | +| anything else | `sit` | + +Cycles (the goodbye wave, dj, headbang) map frame by frame, so the +goodbye shows `sit-wave` throughout and dj shows `sit`. `Mood.pick` is +unchanged: typing still gives `watch`, delete still gives the mock. + +Seven new frames in each outfit, from `base-sitting` in `frames.txt`, with +a sitting counterpart of `POSE` and the leotard stanzas like the others. +`gen-frames` then `prep-frames`, by the user, as with every other frame. +A missing frame falls back to `base`, standing, which is how an +ungenerated set shows. + +### Inputs + +Typing and BackSpace keep arriving as `keystone-typing` and +`keystone-delete` from Hyprland, which fire under the canvas (see above). + +## Check + +`test_mood.js` gains `seatFrame` cases: each row of the table, a cycle +frame, and an unknown frame to `sit`. The seat position and the bind check +are by hand. + +## Not doing + +- Standing on the bar or the file card: needs new art. +- Following the card while it animates: the tall card does not move. +- Sitting on ordinary windows. |
