aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-24 11:50:54 +0200
committerDanilo M. <danix@danix.xyz>2026-10-24 11:50:54 +0200
commit2f800ea58f91faa9bd48d77d45fc00f7dbc24633 (patch)
tree2f66738b1cf2281559becbb9405cf0f9a38a84c6 /docs
parentc083b40500b0496e1e8e1a32fc31b70b5e9b960d (diff)
downloadquickshell-2f800ea58f91faa9bd48d77d45fc00f7dbc24633.tar.gz
quickshell-2f800ea58f91faa9bd48d77d45fc00f7dbc24633.zip
docs: companion seat on the assistant's tall card, design
She sits on the chat card's top edge rather than standing on the bar or the file card, because standing frames face the viewer and reading the text below her feet would need art of its own. The tall card's top is fixed, so the seat is a margin change made through the monitor hop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs')
-rw-r--r--docs/superpowers/specs/2026-10-24-companion-seat-design.md101
1 files changed, 101 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..b858e5a
--- /dev/null
+++ b/docs/superpowers/specs/2026-10-24-companion-seat-design.md
@@ -0,0 +1,101 @@
+# Companion seat: design
+
+Date: 2026-10-24. Status: designed, not implemented.
+
+## 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
+
+- **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 centre 60 px right of the screen's centre: the
+window stays anchored right and bottom, `margins.bottom = screenH - cardTop
+- 35`, `margins.right = screenW / 2 - 60 - seatWidth / 2`. 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. The canvas holds the keyboard
+`Exclusive`; Hyprland binds should still fire, since the compositor
+matches binds before delivering keys. Checked first, by typing into the
+canvas with `qs log` open. If they do not fire, the card's input
+`onTextChanged` sets `typingAt`, and a shorter text sets the mock.
+
+## 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.