aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-10-07 19:13:16 +0200
committerDanilo M. <danix@danix.xyz>2026-10-07 19:13:16 +0200
commita55464e179a3f82aa46866564878ad35e11e0ae5 (patch)
tree7e604416cb2c6c1a22f633a8dc6158d4d30d7e1f
parentfe80b03136a141fb6832474e7dd6dc0ba822e454 (diff)
downloadquickshell-a55464e179a3f82aa46866564878ad35e11e0ae5.tar.gz
quickshell-a55464e179a3f82aa46866564878ad35e11e0ae5.zip
docs: the island, and waybar gone
AGENTS.md describes the hybrid model (desktop/ hosts drawer and island) and gains the gotchas found while building the island: UseQApplication for tray menus, WheelHandler vs MouseArea, accent colliding with green, xkb layout switching, and the tray popup un-hovering the island. The Blur waybar measurement becomes history. READMEs drop their present-tense waybar references. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--AGENTS.md57
-rw-r--r--README.md8
-rw-r--r--appearance/README.md8
-rw-r--r--desktop/README.md45
-rw-r--r--desktop/island/README.md93
-rw-r--r--desktop/modules/mail/README.md67
-rw-r--r--desktop/modules/status/README.md23
-rw-r--r--window-switcher/README.md14
8 files changed, 209 insertions, 106 deletions
diff --git a/AGENTS.md b/AGENTS.md
index d81c83c..a6fb434 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -4,16 +4,20 @@ Guidance for agents working in this repository.
## What this is
-Quickshell components for a Hyprland desktop, one per directory, each a
-complete shell in its own right. They are not modules of a single bar: any of
-them runs alone, and running one does not require the others. One of them,
-`desktop/`, is itself a host for modules.
+Quickshell components for a Hyprland desktop. The repo is a hybrid: `desktop/`
+is the integrated process, hosting the parts that need to talk to each other,
+the drawer and the island now and a control panel later. The other directories
+stay separate shells, each complete on its own: any of them runs alone, and
+running one does not require the others. `desktop/` is itself a host for
+modules.
desktop/ the drawer: sound, mail, VMs, appearance
+ island/ top-centre pills on DP-1: clock, volume, tray, layout, privacy
appearance/ wallpaper picker and colour scheme switcher
window-switcher/ open windows as live previews in a grid, on ALT+TAB
keybinds/ live keybind reminder, on SUPER+k
assistant/ overlay for the voice assistant, fed by its event socket
+ notifications/ balloons for the notification daemon's published files
They are started from `~/.config/hypr/sections/autostart.lua` and keep running
for the whole session.
@@ -122,6 +126,28 @@ changing that component. The ones that generalise:
`--name`, which is the form the panel lists with, so detection is `virsh
dominfo <vm>` grepped for `Managed save: yes`, polled per VM the way the
agent rows are. It measured 6ms.
+- **Tray menus need `//@ pragma UseQApplication`** at the top of the root QML
+ file. Without it `SystemTrayItem.display()` only logs a qCritical ("not
+ started in QApplication mode") and nothing opens. Pragmas are read at launch,
+ before the first import, so adding or changing one needs a restart, not a hot
+ reload. `desktop/shell.qml` carries it.
+- **A tray menu is a separate popup, and hovering it un-hovers the island.**
+ The pills lose the pointer, so the hide timer would close the island under
+ the open menu. `display()` reports no close, so the island holds itself shown
+ after it until the pointer returns (a ponytail ceiling in `Island.qml`).
+- **A `WheelHandler` on an `Image` inside the island's status pill never
+ fired**; `MouseArea { onWheel }` in the same place works, and `SoundPage`
+ already used it. Prefer `MouseArea.onWheel`.
+- **udt can snap the accent to the same colour as a palette colour used for
+ meaning.** Measured: accent and green were both `#36d190`. The island's idle
+ sliver is therefore `Theme.overlay`, never the accent; anything that signals
+ state by colour must not use the accent as its neutral.
+- **`hyprctl switchxkblayout current next|prev` targets the main keyboard**, so
+ no device lookup is needed. Hyprland's `activelayout` raw event fires on
+ every switch, so the layout flag is event-driven rather than polled.
+- **Waybar is gone, both bars.** Its DP-3 functions moved to the island. The
+ mail count is drawer-only: `Accounts.qml` watches the xapian directory
+ itself, and `waybar-mail.sh` was deleted.
- **Hyprland 0.56.2 evaluates dispatch arguments as Lua.** `dispatch
focuswindow address:0x...` is a syntax error rather than a command, and it
fails silently unless stderr is read. The working form is `dispatch
@@ -296,6 +322,11 @@ keeps the palette parsed rather than imported; quickshell follows the link and
resolves the singleton with no qmldir and no consumer change. Editing any
component's `Theme.qml` edits all three. Do not replace a link with a copy.
+`shared/Workspaces.qml`, the workspace icon map, is linked the same way, from
+`desktop/` and `window-switcher/`, so the island's flick and the switcher's
+cards show the same icons. `Status.qml` and `Notify.qml` are shared links too.
+Hot reload does not follow any of them (see Reloading).
+
Glyphs are Nerd Font private-use codepoints and render with `Theme.iconFamily`,
pinned to Inconsolata Nerd Font, not `Theme.fontFamily` ("Noto Sans" has no
glyphs). Without the pin, fontconfig falls back per codepoint and the same
@@ -314,16 +345,18 @@ frosting needs its own `hl.layer_rule` and a distinct
`WlrLayershell.namespace`. Without the rule it still works, rendering flat
translucent.
-A panel that should sit below waybar rather than over it wants
-`exclusionMode: ExclusionMode.Normal` on its window, which respects waybar's
-exclusive zone without the component knowing the bar's height. Measured with
-`hyprctl layers`: waybar at `y=-540 h=42`, a `Normal` overlay on the same
-screen at `y=-498 h=1038`, starting exactly where the bar ends, so the
-backdrop never dims it. The `desktop` drawer does this; components that cover
-the whole screen use `ExclusionMode.Ignore`.
+History: a panel that should sit below waybar rather than over it used
+`exclusionMode: ExclusionMode.Normal`, which respected waybar's exclusive zone
+without the component knowing the bar's height. Measured with `hyprctl layers`
+while waybar existed: waybar at `y=-540 h=42`, a `Normal` overlay on the same
+screen at `y=-498 h=1038`, starting exactly where the bar ended, so the
+backdrop never dimmed it. Waybar is gone, so the `desktop` drawer now starts at
+the top edge of DP-1 and keeps `Normal` so a bar added later is respected.
+Components that cover the whole screen use `ExclusionMode.Ignore`.
`window-switcher` has namespace `quickshell-window-switcher` and a
-`blur-window-switcher` rule of its own.
+`blur-window-switcher` rule of its own. The island has namespace
+`quickshell-island` and a `blur-island` rule.
## Reloading
diff --git a/README.md b/README.md
index 75059dd..12d7ed7 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,10 @@
# quickshell
Quickshell components for a Hyprland desktop, each one self-contained in its
-own directory. They are separate shells, not modules of a single bar: any of
-them can run alone, and running one does not require the others. One of them,
-`desktop/`, is itself a host for modules, which its own README covers.
+own directory. `desktop/` is the integrated one: a single process hosting the
+drawer and the island, and later a control panel, which its own README covers.
+The rest are separate shells: any of them can run alone, and running one does
+not require the others.
Visually they follow [unified-desktop-theme][udt]: Catppuccin Macchiato as the
fixed base, Noto Sans for UI text, and an accent that tracks the wallpaper.
@@ -15,6 +16,7 @@ repos stay independent, this one has no build-time dependency on that one.
## Implementations
desktop/ the drawer: sound, mail, VMs, appearance
+ island/ top-centre pills: clock, volume, tray, layout, privacy
appearance/ wallpaper picker and colour scheme switcher
window-switcher/ open windows as live previews in a grid, on ALT+TAB
keybinds/ live keybind reminder, on SUPER+k
diff --git a/appearance/README.md b/appearance/README.md
index 7f69b97..7524322 100644
--- a/appearance/README.md
+++ b/appearance/README.md
@@ -62,9 +62,9 @@ are needed to see the rest.
An icon switch also regenerates the two consumers that resolve icons by name
when they are generated rather than when they run: `udt-accent` rewrites the
-SDDM power icons, and waybar-theme-udt's `install.sh` rewrites the taskbar
-theme and the module icons, then waybar is sent `SIGUSR2`. Both are chained
-after the `gsettings` write in one shell, since both read it.
+SDDM power icons. It is chained after the `gsettings` write in one shell,
+since it reads it. Waybar is gone, so the icon switch no longer reruns
+waybar-theme-udt's `install.sh` or signals waybar.
## Wallpapers
@@ -135,7 +135,7 @@ startup would go stale the first time anything was added.
Applying writes the `scheme =` line in `~/.config/udt/roles.conf` and runs
`install.sh`, which regenerates every themed config and reloads Hyprland,
-waybar, kitty and conky. Its whole output becomes the `install` tab of the
+kitty and conky. Its whole output becomes the `install` tab of the
steps window below. The notice names what is left: Qt and GTK apps only reread
a theme when they restart, and neovim needs `:colorscheme udt`.
diff --git a/desktop/README.md b/desktop/README.md
index f51feb4..5536d7d 100644
--- a/desktop/README.md
+++ b/desktop/README.md
@@ -1,7 +1,8 @@
# desktop
One drawer, left of DP-1, hosting the things a desktop lets you adjust.
-Reached from a launcher at the left end of waybar.
+Reached over IPC (`ipc call drawer toggle`), from a Hyprland bind.
+The same process also hosts the island, see `island/README.md`.
The drawer opens as a grid of tiles, and clicking a tile either opens that
module's page or fires an action. A module's back arrow, and Escape while a
@@ -89,15 +90,16 @@ while the page is closed. Pages are lazily loaded in every module.
## Geometry
-The panel is 600px wide and full height, on `DP-1`. Waybar runs there and the
-launcher sits at its left end, while conky holds the right side of the screen,
-so the drawer belongs on the left. It falls back to the first screen when
+The panel is 600px wide and full height, on `DP-1`. Conky holds the right side
+of the screen, so the drawer belongs on the left. It falls back to the first screen when
`DP-1` is not connected, so the drawer is never invisible.
-The window uses `ExclusionMode.Normal`, not `Ignore`. Waybar claims an
-exclusive zone at the top of the screen, so respecting it puts the drawer below
-the bar without this code knowing the bar's height, and the bar stays visible
-and clickable while the drawer is open. The whole surface is an overlay so a
+The window uses `ExclusionMode.Normal`, not `Ignore`. Waybar used to claim
+an exclusive zone at the top of the screen, and respecting it put the drawer
+below the bar without this code knowing the bar's height. Waybar is gone, so
+the drawer now starts at the top edge of `DP-1`; `Normal` stays so a bar added
+later is respected. The island is an overlay with no exclusive zone, so it
+floats over the drawer's top edge. The whole surface is an overlay so a
click outside closes it.
The top of the panel is an empty reserved area for the notification engine. It
@@ -107,12 +109,9 @@ fixed, never-scrolled `Flow` grid: three columns at 180px minimum, wrapping
and adding rows up to a 3x3 ceiling for the modules that exist, in the order
Sound, Network, Bluetooth, KDE Connect, Mail, Appearance, Machines.
-## Hyprland and waybar
+## Hyprland
-Nothing here is in the repo's control, and the drawer works without it. Two
-pieces of outside configuration belong to it.
-
-Frosting is the compositor's, matched on the namespace this window sets. The
+Nothing here is in the repo's control, and the drawer works without it. Frosting is the compositor's, matched on the namespace this window sets. The
drawer sets `quickshell-desktop`, so it needs:
hl.layer_rule({
@@ -125,23 +124,15 @@ drawer sets `quickshell-desktop`, so it needs:
The sound module's OSD owns its own window and keeps its old
`quickshell-volume-osd` namespace, so the existing `blur-volume-osd` rule still
-matches and stays. Without any of these the surfaces still render, just flat
+matches and stays. Without these the surfaces still render, just flat
translucent.
-The waybar launcher is a static `custom` module with no `exec`, so it cannot
-show whether the drawer is open. Its `on-click` runs the zero-argument toggle:
-
- "on-click": "qs -p ~/Programming/GIT/quickshell/desktop ipc call drawer toggle"
-
-The mail module's waybar count keeps its own `custom` module, whose click
-deep-links to the mail page with `ipc call drawer open mail`. `SUPER+v` reaches
-the vm page the same way, with `ipc call drawer open vm`. The full list of
-edits to the live Hyprland and waybar configuration is Task 10 of the plan in
-`docs/superpowers/plans/2026-09-14-desktop-shell.md`.
+`SUPER+v` reaches the vm page with `ipc call drawer open vm`, and the drawer
+toggles with `ipc call drawer toggle`. The island's sound panel opens the
+drawer's sound page through the same `show` the IPC uses.
-The drawer is the only place network and Bluetooth are managed. The drawer
-replaces waybar's own network and Bluetooth indicators; removing them is a
-live-configuration change outside this repo.
+The drawer is the only place network and Bluetooth are managed. Waybar's own
+indicators for both went with waybar.
The drawer is the only KDE Connect surface, so the `kdeconnect-indicator`
autostart line was removed in the same change. The `kdeconnectd` daemon is a
diff --git a/desktop/island/README.md b/desktop/island/README.md
new file mode 100644
index 0000000..2b2c9fe
--- /dev/null
+++ b/desktop/island/README.md
@@ -0,0 +1,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.
diff --git a/desktop/modules/mail/README.md b/desktop/modules/mail/README.md
index ced87f3..b6a3159 100644
--- a/desktop/modules/mail/README.md
+++ b/desktop/modules/mail/README.md
@@ -7,8 +7,8 @@
> `blur-mail` namespace in the Theme section below is historical, since this
> page now rides the drawer's `quickshell-desktop` surface.
-Unread mail across every account, as one waybar number and a drawer behind it.
-Clicking the icon opens the drawer; Escape or a click outside closes it.
+Unread mail across every account, as a tile and a page in the drawer.
+Opening the page shows the accounts; Escape or a click outside closes it.
┌──────────────────────────────────────────────────┐
│ Mail 33 unread │
@@ -33,12 +33,12 @@ Clicking the icon opens the drawer; Escape or a click outside closes it.
qs -p ./desktop
It is started from `autostart.lua` and reached over IPC, so the shell has to be
-running for the waybar click to do anything:
+running for a deep link to do anything:
qs -p ./desktop ipc call drawer open mail
-Write that path out in full in the real config. Waybar's `exec` and `on-click`
-have no shell to expand `~`, and neither do Hyprland's Lua strings.
+Write that path out in full in the real config. Hyprland's Lua strings have no
+shell to expand `~`.
## The accounts are not listed here
@@ -77,7 +77,7 @@ account with `path:"<maildir>/**"`, and copying that query would be wrong for
this panel. notmuch deduplicates by message id, so a message that arrived at
two of the configured addresses is one message with two file paths: a glob
counts it under both accounts, and the rows then sum to more than the total the
-waybar icon shows. Measured here, five accounts summed to 102 against a global
+header shows. Measured here, five accounts summed to 102 against a global
total of 101. The tag is a property of the message, so it is singular and the
rows always sum to the header. The cost is that such a message appears under
only the account the `post-new` hook attributed it to, which is the right trade
@@ -104,35 +104,18 @@ An unknown count shows a dash, never a zero, for the same reason the VM panel
refuses to substitute libvirt's host-side figures: a number that is actually a
failure reads as a fact.
-## The waybar module
+## The count
-A `custom` module in **continuous mode**: the script never exits, prints one
-JSON line per database commit, and waybar redraws on each line. There is no
-`interval`, and no daemon to supervise, because waybar owns the process.
+The drawer's `Accounts.qml` watches the xapian directory itself, so the count
+drops the moment mail is read in qtmaildir and rises the moment mbsync commits.
+The count lives only in the drawer: the old bar module is gone with waybar.
- "custom/mail": {
- "format": "<span font='18px'>󰇮</span> {}",
- "return-type": "json",
- "exec": "~/Programming/GIT/quickshell/desktop/modules/mail/waybar-mail.sh",
- "on-click": "qs -p ~/Programming/GIT/quickshell/desktop ipc call drawer open mail",
- "on-click-right": "~/bin/mailsync.sh",
- "tooltip": false
- }
-
-The count drops the moment mail is read in qtmaildir and rises the moment
-mbsync commits, because both write the database and the watcher sees either.
-
-Two details in that loop are load-bearing. The watch is on the **xapian
-directory**, not on a file inside it: a commit replaces files, and a watch held
-on a filename dies with it. And a short sleep coalesces the several writes of
-one commit, without which the module redraws three or four times per sync with
+Two details in the watch are load-bearing. It is on the **xapian directory**,
+not on a file inside it: a commit replaces files, and a watch held on a
+filename dies with it. And a short debounce coalesces the several writes of one
+commit, without which the page redraws three or four times per sync with
intermediate counts.
-The old setup this replaces ran three copies of a python script polling the
-Gmail API with stored credentials, covered three of the five accounts, and
-opened Thunderbird. Everything it fetched over the network was already in the
-local index.
-
## Notifications on arrival
`mail-notify.sh` sends one notification per account when mail lands, with the
@@ -143,10 +126,9 @@ Started from `autostart.lua`, it runs for the whole session.
/home/you/Programming/GIT/quickshell/desktop/modules/mail/mail-notify.sh
-It watches the same xapian directory the waybar module does, for the same
-reason and with the same 0.3s debounce. It is a **separate process rather
-than part of `waybar-mail.sh`**, which already has that edge: waybar owns
-that script's process, so a bar restart would stop notifications with nothing
+It watches the same xapian directory `Accounts.qml` does, for the same reason
+and with the same 0.3s debounce. It is a **separate process rather than part of
+the shell**, so a shell restart does not stop notifications with nothing
reporting it.
**A commit is not the same as new mail.** Reading a message in qtmaildir drops
@@ -171,7 +153,7 @@ from the old one means nothing.
revision and notifying nothing. Without that floor, `lastmod:0..` matches every
unread message ever: 101 of them here, which is a wall of popups at every
login. The same applies to a restart, so mail that arrived while it was down is
-never notified. The waybar count is still right and the drawer still shows it,
+never notified. The drawer count is still right and still shows it,
so nothing is lost but a stale popup.
The notification is sent with dunstify. The icon is passed as an **absolute
@@ -197,10 +179,12 @@ anchored to its top right, which is the idiom the vm and appearance panels
both use and what gives click-outside-to-close and a focusable item for Escape.
It differs from those two in one property: `exclusionMode` is `Normal`, not
-`Ignore`. Waybar claims an exclusive zone at the top of this screen, so
-respecting it places the overlay below the bar with no height constant here to
-drift. Measured: waybar at `y=-540 h=42`, this overlay at `y=-498 h=1038`,
-starting exactly where the bar ends. The backdrop therefore never dims waybar.
+`Ignore`. Waybar claimed an exclusive zone at the top of this screen, so
+respecting it placed the overlay below the bar with no height constant here to
+drift. Measured then: waybar at `y=-540 h=42`, this overlay at `y=-498 h=1038`,
+starting exactly where the bar ended. Waybar is gone, so the overlay now starts
+at the top edge of the screen; `Normal` stays, so a bar added later is still
+respected.
**Key events reach a focused item, not a window.** Setting
`WlrLayershell.keyboardFocus` is necessary but not sufficient, and
@@ -219,8 +203,7 @@ possibly running client.
"Open qtmaildir" launches the client. There is no "Sync now" button: the
watcher triggers a sync the moment mail lands, the cron tick is the backstop,
-and `on-click-right` on the waybar module runs `~/bin/mailsync.sh` for a manual
-pull.
+and `~/bin/mailsync.sh` is a manual pull.
## The watcher status dot
diff --git a/desktop/modules/status/README.md b/desktop/modules/status/README.md
index e435d94..b25505f 100644
--- a/desktop/modules/status/README.md
+++ b/desktop/modules/status/README.md
@@ -28,7 +28,7 @@ mechanism, which is why it keeps working while quickshell is down.
statusctl <mode> get prints 0 or 1
statusctl <mode> set 0|1
statusctl <mode> toggle
- statusctl <mode> watch waybar JSON on every change
+ statusctl <mode> watch JSON on every change (no consumer now)
The repo copy is the source; `~/bin/statusctl` is a symlink to it. `watch` watches
the directory rather than the file, because an atomic write replaces the file
@@ -70,8 +70,8 @@ The traffic runs both ways, and only one way writes.
$XDG_RUNTIME_DIR/breaktimer.remain seconds left in the phase
exposed as `Status.btState`, `btPhase` and `btRemain`, with `btRunning` and
-`btPaused` derived from the first. `waybar-breaktimer.sh` reads the same three
-files and the two consumers do not know about each other.
+`btPaused` derived from the first. The old bar script read the same three
+files; the two consumers never knew about each other.
**Writing: never.** The daemon owns those files and rewrites state and phase on
every transition, so a second writer would race its loop. Every control calls a
@@ -83,7 +83,7 @@ end the daemon write `stopped` there: `stop_daemon`, and the `cleanup` trap on
`TERM`. A daemon lost to `KILL` leaves a stale `running` and the drawer shows a
frozen countdown, a visible wrong answer the Start button resolves, which is
cheaper than a liveness probe on every repaint. QML cannot send a signal, so
-the `kill -0` check the waybar module uses is not available here anyway.
+the `kill -0` check the old bar module used is not available here anyway.
**The countdown counts in five second steps**, because that is the daemon's
tick and the shell does not interpolate between its writes. A local one second
@@ -123,16 +123,17 @@ runs, since `status.gaming` holds it on until the game exits.
`status.gaming` is never set by `statusctl`; the detector owns it. The effects
are unchanged, because they key off the combined `presentation` value.
-## Waybar
+## The watch (no consumer now)
-`custom/presentation` reads `statusctl presentation watch`. It replaces
-waybar's built-in `idle_inhibitor`, which cannot be kept alongside it: that
-module owns its own inhibitor object, so both would have to be released
-before the screen could lock.
+`statusctl presentation watch` fed waybar's `custom/presentation` module, which
+replaced waybar's built-in `idle_inhibitor`. Waybar is gone, so the watch has
+no consumer; the command stays. Presentation is toggled from the drawer's
+status page, and later from the control panel. The island shows no presentation
+state at rest.
The watch emits three classes, not two. `status.gaming` turns the mode on
-without touching the manual half, so a game would otherwise leave the widget
-reading `deactivated` while the effects were asserted. `gaming` is its own
+without touching the manual half, so a bar widget would otherwise read
+`deactivated` while the effects were asserted. `gaming` is its own
class with its own icon, and `toggle` is refused while it holds: the detector
reasserts the file within its poll, so the write would not stick. `set` is
still allowed, since the manual half is worth setting for when the game exits.
diff --git a/window-switcher/README.md b/window-switcher/README.md
index 0f1cb90..516d8dd 100644
--- a/window-switcher/README.md
+++ b/window-switcher/README.md
@@ -23,12 +23,12 @@ The cards are ordered most recently used first, so the window you just left is
the first one, the same order ALT+TAB implies. Windows on a `special:`
workspace are the scratchpad, which has its own bind, and are left out.
-Each card's desktop marker is the same icon waybar draws for that workspace, so
-the switcher and the bar agree rather than showing a bare number. waybar keeps
-the eight icons as CSS background images keyed by workspace number
-(`styles/modules.css` in `waybar-theme-udt`), because its `format-icons` takes
-text and not paths; here the same names are resolved through the active icon
-theme with `Quickshell.iconPath`. A named workspace, or one past eight, has no
+Each card's desktop marker is an icon for that workspace rather than a bare
+number. The eight icons come from `Workspaces.qml`, a singleton in `shared/`
+that this component links, as it does `Theme.qml`, and that the island's flick
+uses too, so the two agree. The names are resolved through the active icon
+theme with `Quickshell.iconPath`. Hot reload does not follow the symlink, so
+after editing `Workspaces.qml` reload both. A named workspace, or one past eight, has no
icon and falls back to `[name]`.
With nothing to switch to it says so, because a full-screen dim with nothing in
@@ -155,5 +155,5 @@ Frosting is Hyprland's, matched on this window's namespace:
})
Without the rule it still works, rendering flat translucent. The overlay covers
-the whole screen, waybar included, so unlike `mail-overview` it uses
+the whole screen, so unlike `mail-overview` it uses
`ExclusionMode.Ignore`.