aboutsummaryrefslogtreecommitdiffstats
path: root/desktop/island/README.md
blob: 2b2c9fe65e845b4381c50fab36963d15c46af37f (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
# island

A thin neutral bar at the top centre of `DP-1`. Point at it and two pills
slide out of it: a clock pill on the left and a status pill on the right. It
replaced waybar, both bars. It is not a separate shell: `desktop/shell.qml`
instantiates it next to the drawer, so it starts with `qs -p ./desktop` and
shares the drawer's IPC and keepalive window.

## Running it

    qs -p ./desktop

The pure logic (volume icon thresholds, VM dot colours, flick text) lives in
`Logic.js` and has one check, which needs only node:

    node desktop/island/test_logic.js

## States

**Hidden.** Only the sliver shows, in `Theme.overlay`. While anything is
capturing it takes the capture colours instead, split into equal segments, one
per active kind: mic green, camera yellow, location blue, screen red.

**Revealed.** The pointer enters the sliver and the pills slide down and apart
in one move, driven by a single `reveal` amount, so an interrupted reveal turns
around from wherever it is. They hide 400ms after the pointer leaves both,
unless a panel is open.

**Panel open.** Clicking the volume icon morphs the status pill into the sound
panel (output and input sliders with mute, and an arrow to the drawer's sound
page). An outside click or Escape closes it.

**Flick.** On a workspace change or an app opening, and not on a plain focus
change, the clock pill shows `<desktop icon> <app icon> <app name>` for 1.5s.
If the island is hidden, the clock pill alone drops out for the flick and
retracts. Events in the first 1.5s after startup are ignored, because Hyprland
reports its initial state as events. The desktop icons come from
`shared/Workspaces.qml`, the same ones the window switcher uses.

## Clock pill

`Wed 07/10  15:07`, then one dot per VM that is running, paused, suspended,
saved or crashed (a plainly shut-off VM gets none). States come from the
drawer's `Virsh` service, whose lifecycle event stream runs whether the drawer
is open or not.

## Status pill

Left to right: volume icon, tray, layout flag.

- **Volume icon** shows the default sink (muted, low, medium, high). Click
  opens the sound panel.
- **Tray** shows `SystemTray` items. Left click activates, right click opens
  the item's menu. The island is the session's StatusNotifier host. A tray
  menu is a separate popup, so the island holds itself shown after `display()`
  until the pointer returns.
- **Layout flag** shows the current keyboard layout, driven by Hyprland's
  `activelayout` event. Scroll switches layout with
  `hyprctl switchxkblayout current next|prev`.

## Privacy

`privacy.sh` prints `mic cam loc scr`, each 0 or 1, and `Privacy.qml` polls it
every 2s. It polls because Quickshell's `PwNode` exposes no running state,
which is what tells a capturing source from an idle one, so mic and screen go
through `pw-dump`, and camera and location through `fuser` and `pgrep`. A
failed run prints nothing and the last reading is kept, since no dump is a
broken run and not "nothing is capturing".

## Theming

Colours come only from `Theme`, which follows the unified-desktop-theme
palette live. Icons are freedesktop names resolved with
`Quickshell.iconPath(name, true)` through the active icon theme, so switching
icon theme in the appearance panel changes the island. Nerd Font glyphs, in
`Theme.iconFamily`, only where no named icon exists. The island defines no
colour and no icon theme of its own. The idle sliver is never the accent,
because udt can snap the accent to the same colour as a capture kind.

## Blur

The window sets namespace `quickshell-island`. For frosting, add to
`~/.config/hypr/sections/decorations.lua`:

    hl.layer_rule({
        name         = "blur-island",
        match        = { namespace = "^(quickshell-island)$" },
        blur         = true,
        xray         = false,
        ignore_alpha = 0.1,
    })

Without it the island renders flat translucent.