From 54f028ed563daed9b49bf1fabee9d47060335304 Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Mon, 14 Sep 2026 12:36:03 +0200 Subject: docs: the desktop shell and what the merge changed Five components become three, and the Theme symlink count with them. Two new notes: a pragma Singleton in a subdirectory resolves through a plain directory import with no qmldir, and Virsh.sampling gates only the stats poll, not the lifecycle stream that keeps the tile correct while the page is closed. --- AGENTS.md | 25 +++++--- README.md | 22 +++---- desktop/README.md | 133 ++++++++++++++++++++++++++++++++++++++++ desktop/modules/mail/README.md | 7 +++ desktop/modules/sound/README.md | 7 +++ desktop/modules/vm/README.md | 6 ++ 6 files changed, 180 insertions(+), 20 deletions(-) create mode 100644 desktop/README.md diff --git a/AGENTS.md b/AGENTS.md index ee37cf7..67603e2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,12 +6,11 @@ Guidance for agents working in this repository. 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. +them runs alone, and running one does not require the others. One of them, +`desktop/`, is itself a host for modules. - volume-osd/ volume for output and input, plus what is playing - vm-manager/ libvirt drawer: state, live stats, snapshots + desktop/ the drawer: sound, mail, VMs, appearance appearance/ wallpaper picker and colour scheme switcher - mail-overview/ notmuch unread counts per account, waybar icon and drawer window-switcher/ open windows as live previews in a grid, on ALT+TAB They are started from `~/.config/hypr/sections/autostart.lua` and keep running @@ -137,6 +136,14 @@ changing that component. The ones that generalise: not `(0, 0)`. An aspect ratio guard has to test for a positive height: the obvious rewrites, `!== 0` or a truthiness check, all pass on `-1` and produce a negative ratio. +- **A `pragma Singleton` in a subdirectory needs no `qmldir`.** A plain + directory import resolves it, the same way quickshell resolves the + `Theme.qml` symlink. Tested while merging the components: the control was a + reference to an undefined type, which warns `ReferenceError: is not + defined`, and the singleton case produced no such warning. +- **`Virsh.sampling` gates the 2s stats poll, not the whole service.** The + lifecycle event stream runs unconditionally, which is what keeps the VM list + and the tile's dots current while the page is closed. ## Theme @@ -163,7 +170,7 @@ they never had. A symlink rather than a shared import path because a singleton outside the config directory needs a `qmldir`, which is the same friction that 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 five. Do not replace a link with a copy. +component's `Theme.qml` edits all three. Do not replace a link with a copy. ## Blur @@ -179,11 +186,11 @@ A panel that should sit below waybar rather than over it wants 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. `mail-overview` does this; the other four use -`ExclusionMode.Ignore` and cover the whole screen. +backdrop never dims it. The `desktop` drawer does this; components that cover +the whole screen use `ExclusionMode.Ignore`. -`window-switcher` is the fifth, with namespace `quickshell-window-switcher` and -a `blur-window-switcher` rule of its own. +`window-switcher` has namespace `quickshell-window-switcher` and a +`blur-window-switcher` rule of its own. ## Reloading diff --git a/README.md b/README.md index a74de8a..7a6cbee 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # 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. +own directory. They are three 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. 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. @@ -13,29 +14,28 @@ repos stay independent, this one has no build-time dependency on that one. ## Implementations - volume-osd/ on-screen display for output and input volume - vm-manager/ libvirt VM drawer: state, live stats, snapshots + desktop/ the drawer: sound, mail, VMs, appearance appearance/ wallpaper picker and colour scheme switcher - mail-overview/ unread mail per account, as a waybar count and a drawer window-switcher/ open windows as live previews in a grid, on ALT+TAB Each directory has its own README covering what it does and how to run it. ## Running one - qs -p ./volume-osd + qs -p ./desktop -To start it with the session, from Hyprland: +To start one with the session, from Hyprland: - exec-once = qs -p ~/Programming/GIT/quickshell/volume-osd + exec-once = qs -p ~/Programming/GIT/quickshell/desktop Quickshell uses private Qt APIs, so it must be rebuilt after every Qt update. `qs --version` should report the same Qt as the installed one. Some components ask the compositor for an effect they cannot draw themselves, -such as the blur behind `volume-osd`. Those rules live in the Hyprland config -rather than here, and each component's README gives the block to add. They are -optional: without the rule the component renders without that effect. +such as the blur behind the sound module's OSD. Those rules live in the +Hyprland config rather than here, and each component's README gives the block +to add. They are optional: without the rule the component renders without that +effect. ## License diff --git a/desktop/README.md b/desktop/README.md new file mode 100644 index 0000000..0350f9a --- /dev/null +++ b/desktop/README.md @@ -0,0 +1,133 @@ +# 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. + +The drawer opens as a grid of tiles, and clicking a tile either opens that +module's page or fires an action. Escape, a click outside, or a module's back +arrow returns to the grid; Escape again closes the drawer. It takes keyboard +focus while it is open, so those keys reach it. + +## Running it + + qs -p ./desktop + + qs -p ./desktop ipc call drawer toggle # the grid + qs -p ./desktop ipc call drawer open mail # straight to a page + +`open` always takes a page name, because quickshell 0.3.1 IPC requires every +declared argument to be present and a defaulted parameter registers as a type +the IPC layer rejects. The page-less grid entry is therefore the zero-argument +`toggle`. `ipc call drawer close` closes it. + +## The modules + + modules/sound/ output and input volume, the OSD, the player + modules/mail/ unread per account, threads, the watcher dot + modules/vm/ libvirt state, live stats, snapshots + modules/appearance/ a tile that opens the separate appearance shell + +Sound, mail and vm each carry their own README. Appearance is a tile onto the +separate appearance shell, whose README covers the picker it opens. + +## Writing a module + +A module is a `Module` (from `Module.qml` in this directory) declaring what the +drawer needs to show it: + +- `name`: identifies the module, both for grid ordering and for IPC, where it + is the page name `ipc call drawer open ` takes. +- `icon` and `label`: the glyph and the text on the tile. +- `tileContent`: a `Component` for the short state line under the label, or + null for a tile that says nothing beyond its name. +- `page`: a `Component` for the full-height page behind the tile, or null. +- `activate()`: what a tile with no page does when clicked. +- `alwaysActive`: whether the module's background service runs while the + drawer is closed, covered below. + +A module provides a tile, a page, both, or neither. The drawer does not care +which: it reads those properties and does the obvious thing. A page is wrapped +in `Page.qml` for the header and back arrow, and a tile with no page is +fire-and-forget, so its click calls `activate()` and closes the drawer. + +`modules/appearance/AppearanceModule.qml` is the smallest complete example: a +name, an icon, a label, and an `activate()` that launches the separate +appearance shell. It has no page and no service. Sound, mail and vm each add a +service and a page on top of that same shape. + +Module files live under `desktop/modules//` and reference root types +(`Module`, `Page`, `Button`, `Theme`), so each needs `import "../.."`. A plain +directory import does not reach the parent. The moved `TransportButton.qml` and +`Stat.qml` gained that import for the same reason. + +## alwaysActive + +Sound and mail are `alwaysActive: true`; vm is false. The property governs the +module's background service, not its page: pages are lazily loaded either way. + +Sound's OSD has to answer a volume keypress with no drawer open, which is the +whole point of it, so its PipeWire service runs for the session. Mail's unread +count outlives the drawer and its config watcher makes a newly added account +appear without a restart, so its service runs too. The vm module's 2s stats +poll exists only to paint a page nobody is looking at, so it starts and stops +with the page. + +That last line is not the whole story for vm: its lifecycle event stream runs +unconditionally, which is what keeps the VM list and the tile's dots current +while the page is closed. `alwaysActive` gates the poll, not everything the +module does. + +## 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 +`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 +click outside closes it. + +The top of the panel is an empty reserved area for the notification engine. It +is an empty `Item` claiming the space, not a placeholder graphic, so the grid +below it sits where it will sit once notifications arrive. The bottom is a +fixed, never-scrolled `Flow` grid: three columns at 180px minimum, wrapping +and adding rows up to a 3x3 ceiling for the modules that exist. + +## Hyprland and waybar + +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 +drawer sets `quickshell-desktop`, so it needs: + + hl.layer_rule({ + name = "blur-desktop", + match = { namespace = "^(quickshell-desktop)$" }, + blur = true, + xray = false, + ignore_alpha = 0.1, + }) + +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 +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`. + +## Theme + +`Theme.qml` is a symlink to `shared/Theme.qml`, as in every component here. diff --git a/desktop/modules/mail/README.md b/desktop/modules/mail/README.md index 34eaa53..022bdda 100644 --- a/desktop/modules/mail/README.md +++ b/desktop/modules/mail/README.md @@ -1,5 +1,12 @@ # mail-overview +> **Now the `mail` module of the `desktop/` shell.** It is no longer a +> standalone component: start the shell with `qs -p desktop` and open the page +> from the drawer, or deep-link with `qs -p desktop ipc call drawer open mail`. +> The `mail-overview` paths below are historical, the scripts now live under +> `desktop/modules/mail/`, and the waybar click uses `ipc call drawer open +> mail`. See `desktop/README.md`. + 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. diff --git a/desktop/modules/sound/README.md b/desktop/modules/sound/README.md index f0a8313..d1c50fc 100644 --- a/desktop/modules/sound/README.md +++ b/desktop/modules/sound/README.md @@ -1,5 +1,12 @@ # volume-osd +> **Now the `sound` module of the `desktop/` shell.** It is no longer a +> standalone component: start the shell with `qs -p desktop` and open the +> module's page from the drawer, or deep-link with +> `qs -p desktop ipc call drawer open sound`. The prose below predates the +> merge and its standalone `volume-osd` commands are historical. See +> `desktop/README.md`. + An on-screen display for volume, covering both output (speakers) and input (microphone). It appears at the bottom of the screen when the level or mute state changes, and fades out 1.5 seconds later. diff --git a/desktop/modules/vm/README.md b/desktop/modules/vm/README.md index bcf12ac..03ad1b5 100644 --- a/desktop/modules/vm/README.md +++ b/desktop/modules/vm/README.md @@ -1,5 +1,11 @@ # vm-manager +> **Now the `vm` module of the `desktop/` shell.** It is no longer a +> standalone component: start the shell with `qs -p desktop` and open the page +> from the drawer, or deep-link with `qs -p desktop ipc call drawer open vm`. +> The `vm-manager` paths and the `panel toggle` IPC below are historical. See +> `desktop/README.md`. + A drawer for libvirt virtual machines: state, live statistics, the actions that make sense in the current state, and the snapshot list. It replaces the menu half of `rofi-qemu.sh`, which could only ever show a list of strings. -- cgit v1.2.3