aboutsummaryrefslogtreecommitdiffstats
path: root/docs/superpowers/specs/2026-10-15-drawer-control-center-design.md
blob: 47eda7645a34fd1c54d863af2ece55ba46d14565 (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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Drawer as the control center

Sub-project 6 of the roadmap in `2026-10-07-island-core-design.md`. The
drawer keeps its look, a notification space above a tile grid, and becomes
the one control surface: the island's controls panel goes, the drawer takes
its SUPER bind, and three things it lacked arrive as a performance page, a
clipboard page and a quiet mode.

## Drawer shell

**Keybinds**, in the live `~/.config/hypr/sections/keybindings.lua`, outside
this repo, shown to the user before editing:

- SUPER alone (the existing `SUPER + Super_L` release bind) runs `ipc call
  drawer toggle`.
- SUPER+Return is removed and left free.
- CTRL+SHIFT+L runs `ipc call drawer open clipboard`, replacing the
  `cliphist-rofi` command.

**The controls panel goes.** Its seven tiles each opened a drawer page that
already exists, and the user does not use it. Removed: `ControlsPanel.qml`,
`ControlTile.qml`, `Controls.js`, `test_controls.js`, the controls icon and
`openControls` in `StatusPill.qml`, `toggleControls` and the `"controls"`
panel in `Island.qml`, the `island` IPC target in `shell.qml`, and the
controls list in `Prefs.qml` (`pinnedIp` stays). The island README loses its
controls section; `AGENTS.md` notes where SUPER alone now goes.

**The tile grid scrolls on its own.** The `Flow` sits inside a `Flickable`
whose height is capped at three rows. Past that, the tiles scroll inside the
box and the notification space above keeps its size. A thin accent scrollbar
shows only when there is overflow, so a fourth row is discoverable.

**Order:** Sound, Network, Bluetooth, KDE Connect, Mail, Status, Performance,
Clipboard, VMs, AI, Appearance. The everyday ones fill the first three rows.

## Performance module

`modules/perf/PerfModule.qml`, name `perf`.

- Tile line: `CPU 12% · RAM 45%`, with a dot coloured by `Perf.severity`
  when it is not ok, as on the island icon.
- Page: the island's `PerfPanel` inside a `Page`. The page sets
  `Perf.detailed` while loaded and clears it on destruction.
- `Perf.qml` and `PerfPanel.qml` stay in `island/perf/` and are imported
  from there.

Ceiling, as a `ponytail:` comment: `Perf.detailed` has one writer at a time.
The island panel and the drawer page both open, then one closed, would stop
the detailed sampling under the other. The drawer covers the screen, so the
two are not open together in practice.

## Clipboard module

`modules/clipboard/`, name `clipboard`, no service running while the drawer
is closed, no tile line.

- Opening the page runs `cliphist list` once (750 entries measured, 42 of
  them images).
- A search field holds focus on open and filters case-insensitively as you
  type.
- A `ListView`, newest first. A text row is one elided line. An image row
  (`[[ binary data ... ]]`) shows a thumbnail, decoded with `cliphist decode`
  into `$XDG_RUNTIME_DIR/qs-clip/<id>.png` the first time the row is created
  and reused after; the directory is tmpfs, cleared by a reboot.
- Up and Down move the highlight, Enter copies the highlighted entry, Escape
  behaves as on every page.
- A click or Enter copies and closes the drawer:
  `sh -c 'printf %s "$1" | cliphist decode | wl-copy' sh <line>`. The entry
  is an argument, never part of the command string: clipboard text is
  untrusted.
- An `x` on each row runs `cliphist delete` with the line on stdin the same
  way, and the row goes.
- A "Clear all" button in the page header becomes "Confirm?" for 3 seconds;
  the second click runs `cliphist wipe` and empties the list.

## Quiet mode

A new mode in the status registry: mutes the Claude and opencode sounds and
leaves their popups alone.

- `shared/Status.qml`: a `ModeFile` on `status.quiet` and `Status.quiet`.
  Presentation saves quiet and sets it on, and restores it when it ends,
  like dnd and nolock.
- `statusctl`: `quiet` joins `MODES`. `test-statusctl.sh` gains a quiet check.
- Status page: a "Quiet" row, "Mutes Claude and opencode sounds. Popups still
  appear." The presentation row's description becomes "Do not disturb,
  quiet, no screen lock, breaktimer paused."

`desktop/modules/status/notify-sound`, symlinked into `~/bin` like
`statusctl`. It exits at once when `statusctl quiet get` prints 1; otherwise
it maps its argument to a file under `~/Music/notify/` and plays it with
`paplay`:

| argument | sound |
| --- | --- |
| `claude` | `claude-attention.opus` |
| `permission` | `opencode-perms.opus` |
| `complete`, `error`, `question` | `opencode-attention.opus` |
| anything else | nothing |

Ignoring unknown arguments matters: the opencode notifier runs its command for
every event, `session_started` and `user_message` included.

Live configs, outside this repo, shown to the user before editing:

- `~/.claude/settings.json`: the `Notification` hook command becomes
  `notify-sound claude`.
- `~/.config/opencode/opencode-notifier.json`: `"sound": false` on
  permission, complete, error and question, and
  `"command": { "enabled": true, "path": "notify-sound", "args": ["{event}"] }`.
  The plugin reloads this file on every event, so no restart is needed.
  Notifications stay as they are.

Behaviour change: error and question played the plugin's bundled default
sound and now play `opencode-attention.opus`.

## Testing

- `desktop/modules/status/test-statusctl.sh`, with the quiet check.
- `desktop/modules/status/test-notify-sound.sh`: a fake `paplay` first on
  `PATH` records its argument; asserts the file per argument, nothing for an
  unknown one, nothing while quiet is 1. `XDG_RUNTIME_DIR` points at a temp
  directory so the real mode is untouched.
- On screen, by the user: tile scrolling, SUPER alone, the performance page,
  clipboard search, copy (text and image), delete and clear all, the quiet
  row against a real Claude notification, presentation turning quiet on.

New files and directories under `desktop/` need `./reload.sh desktop`, not a
hot reload, and so does the `shared/Status.qml` edit.