aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/companion/README.md
blob: e0eaaf385e992624918427d68779d1f3ea435156 (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
# 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.
- 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.
- `IdleMonitor.enabled` read false on creation when left out, so each of
  the three sets it.
- 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