aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/companion/README.md
blob: cfa3ffc9a0e606287e64eca672374bbab0ed6cff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# companion

A chibi 2B in the bottom-right corner of the focused monitor, on SUPER+\.
The design, with what was measured on the way, is
`docs/superpowers/specs/2026-10-23-companion-design.md`.

## Files

| File | |
| --- | --- |
| `Companion.qml` | the window, inputs, timers, speech, IPC (`companion toggle/show/hide`) |
| `Figure.qml` | draws a frame: crossfade, bob, drop out and back, explosion |
| `Mood.js` | which state wins, from a snapshot; all the timings are its `T` |
| `Lines.js` | her lines and the opening monologue |
| `prep-frames` | generated images to display frames |
| `gen-frames`, `frames.txt` | the generated images, through sd-server |

`Figure.qml` is not `Sprite.qml` because QtQuick has a `Sprite` type, and
it wins over the directory's own file (see AGENTS.md).

## Art

Not in the repo: the character is Square Enix's and this repo is GPLv2.
It lives in `~/.local/share/keystone/companion/2b/`:

    dress/<name>.png, leotard/<name>.png   generated, 1024x1024 on white
    frames/<outfit>/<name>.png             what she shows, from prep-frames
    recipe.txt                             how each was made, and what failed

Run `prep-frames` after adding or replacing any generated image. A missing
frame falls back to its outfit's `base`.

## Inputs

- Typing and BackSpace come from `sections/companion.lua` in the
  hypr-theme repo, as `custom>>keystone-typing` and
  `custom>>keystone-delete` on socket2. Nothing else about the keyboard
  reaches her. The BackSpace bind is `non_consuming`; checked by hand that
  it still deletes in a terminal and in the browser.
- The typing pulse skips modifiers and any key pressed with Ctrl, Alt or
  Super held, so a shortcut is not typing; Shift and AltGr still count.
  She watches only from the second pulse within 2.5 s, so a lone key is
  not typing either.
- She is on the Overlay layer and remaps whenever another layer surface
  opens (Hyprland's `openlayer` event: the assistant's dim, rofi, the
  keybind reminder), vanishing and hopping back in, because Hyprland
  stacks a later surface above an earlier one on a layer, and 0.56.2 has
  no layer rule to order them (no `order` key in the binary; an unknown
  key raises no config error).
- She only feels the pointer while it is over her: Quickshell has no
  global pointer and Hyprland sends no motion events. Hence the 0.6 s
  dodge rather than moving away as the pointer approaches.
- Idle stages are 20, 40 and 70% of wscreensavers' `idleTimeoutMinutes`,
  read live from its config, so she lies down before the saver starts.
- `IdleMonitor.enabled` read false on creation when left out, so each of
  the three sets it. It also reads false at `Component.onCompleted` even
  when set, and true a moment later; do not trust that first reading.
- The monitors ignore inhibitors. No lock holds an idle inhibitor for as
  long as it is on, and with `respectInhibitors: true` she never idled
  (measured: a 5 s monitor respecting them never fired, one ignoring them
  did). Presentation and gaming mode keep her awake by name instead.
- Fullscreen is `lastIpcObject.fullscreen === 2`. Maximised is 1, and
  most windows here are maximised.
- The lock screen covers every layer, so locking needs no code.

## Check

    node desktop/companion/test_mood.js