Skip to content

Latest commit

 

History

118 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GPL-3.0

grootshell.mp4

grootshell

A Quickshell desktop shell for Hyprland, written entirely in QML.

It was built for a headless NixOS box with no monitor, driven over Moonlight. Every animated frame is a frame encoded and pushed over a network, so motion is short, polling is demand-driven, and anything that redraws without being looked at was removed.

It works just as well on a machine you sit in front of; you simply get a shell that is stingier with the GPU than it needs to be.


Features

The bar is three floating pills over a thin frame rather than a solid band — workspaces on the left, a clock in the centre, tray and status on the right. Windows tile below them, so nothing is ever hidden behind a pill.

The island. The clock pill is the dashboard: clicking it expands the capsule into a panel that rises into the top border and hangs off it. Five tabs:

  • Dashboard — clock, month grid, agenda, and current weather at a glance
  • Media — album art cropped to a disc inside a live audio spectrum ring
  • Performance — CPU, GPU, memory, and both temperatures as dials
  • Wallpaper — a grid that applies as you move through it, regenerating the whole colour scheme with each pick
  • Weather — current conditions, the next twelve hours, and ten days

Wallpaper-derived theming. Picking a wallpaper runs it through matugen to produce a Material 3 palette, and the entire shell cross-fades to it — image and colours together, on the same frame. Light or dark is chosen from the image's own brightness, with a manual override. The same palette is written out for GTK, Qt (via qt6ct) and WezTerm, so applications follow the desktop.

Generated palettes are cached under ~/.cache/grootshell/themes, keyed by the image, its mtime, the mode and the templates. Wallpapers you have used before apply in about 40ms rather than the 0.1–1.6 seconds matugen takes to read them, which matters because the cross-fade waits for the palette. Editing an image or a template invalidates its entry; grootshell-ipc call theme regenerate ignores the cache entirely.

Calendar and agenda. Several iCal feeds merged into one agenda, with colour-coded calendars, day indicators on the month grid, and one-press joining of Zoom / Meet / Teams links found in an event.

A desktop switcher on SUPER+Tab showing every workspace drawn to scale, with live previews of the windows on each — including workspaces you cannot currently see.

Notifications that extrude from the right border, with actions, drag- and click-to-dismiss, and a centre that holds what you missed.

Side panels that reserve screen edge rather than covering it, so tiled windows resize around them and animate back when they close.

Also: an application launcher with command and calculator prefixes, a clipboard history browser, a translation panel, a wifi popout, a volume readout on the right edge that opens on hover, a keybind cheatsheet generated from the compositor's actual binds, and a game mode that strips every effect for streaming.


Requirements

Essential

Tool Why
quickshell 0.3.0+ The runtime. Qt 6.7+ for ClippingRectangle.
Hyprland Workspaces, window management and keybinds all go through it
Material Symbols Rounded Every icon in the shell. Without it you get boxes.

Fonts

Bundled into a closed fontconfig by the Nix package; install them yourself otherwise. Only the first is truly required.

  • Material Symbols Rounded — all iconography. Qt substitutes silently when a font is missing, so its absence shows up as boxes rather than an error; the shell logs a warning at startup naming any of these it cannot find.
  • Rubik — UI text
  • CaskaydiaCove Nerd Font Mono — monospace readouts
  • Noto Sans CJK — kanji numerals on empty workspaces (tofu without it)

Per feature

Each of these is probed at runtime, and its feature degrades quietly rather than erroring if it is missing.

Tool Feature
matugen Wallpaper-derived colour schemes
cava The media tab's spectrum ring
cliphist Clipboard history
nmcli (NetworkManager) The wifi popout
grim, slurp, swappy Screenshots
lm_sensors Temperatures on the performance tab
python3 + icalendar + recurring-ical-events The calendar agenda
systemd systemd-run, so launched apps outlive a shell restart
wl-clipboard, libnotify, procps, util-linux, gawk, glib Assorted

No API keys anywhere. The weather comes from Open-Meteo, which needs none.

The two helpers the shell shells out to — the theme generator and the calendar fetcher — live in scripts/ here. The shell prefers a packaged grootshell-theme or grootshell-calendar on PATH when there is one, because the Nix wrappers bring their own dependencies, and otherwise runs the bundled script directly.


Installing it

With Nix (NixOS)

The flake ships a NixOS module, so this is the whole of it:

{
  inputs.grootshell.url = "github:BenjaminPrice/grootshell";

  # in your configuration.nix / a module
  imports = [ inputs.grootshell.nixosModules.default ];

  programs.grootshell = {
    enable = true;
    user = "you";
  };
}

That gives you the shell as a user service with the right PATH, the fonts and icon themes it draws with, the tools it shells out to, and the environment a keybind needs to find a running instance.

Then bind some keys in your own Hyprland config — every shell action is grootshell-ipc call <target> <function>:

bind = SUPER, space, exec, grootshell-ipc call launcher toggle
bind = SUPER, S,     exec, grootshell-ipc call island toggle
bind = SUPER, comma, exec, grootshell-ipc call settings toggle

Pass the same list to programs.grootshell.keybinds as [{ keys, description, category }] and the in-shell cheatsheet on SUPER+/ documents exactly what you bound. Extra fields are ignored, so one list can carry your dispatchers too.

Useful options:

Option What it does
devPath Run the QML from a writable checkout. Quickshell hot-reloads on save, so editing the shell stops needing a rebuild per change.
target The systemd user target to bind to. Use a compositor-specific one if you have it, so the shell cannot outlive the compositor.
calendarUrlFile Where the agenda's feed list lives, when it is a secret rather than a file in your config directory.
theming Install matugen and adw-gtk3. On by default; turn it off to keep your own colours.
fonts, clipboardHistory Both on by default.

What the module deliberately does not do: it does not touch your cursor theme, GTK or Qt settings files, or dconf keys, and it does not generate compositor keybinds. Those are opinions about a whole desktop rather than about this shell.

Without Nix

Nix is a convenience here, not a requirement. Point Quickshell at a checkout:

git clone https://github.com/BenjaminPrice/grootshell
qs -p grootshell

You are responsible for the fonts and the tools in the table above being on PATH; the shell names any font it cannot find, at startup, and every optional tool degrades quietly.

The helpers in scripts/ are found relative to shell.qml, so nothing needs installing:

scripts/generate-theme.sh ~/Pictures/Wallpapers/some.png   # colours, by hand
scripts/grootshell-ipc call launcher toggle                # for keybinds

Two files to know about:

  • ~/.config/grootshell/keybinds.json — the cheatsheet, if you are not using the NixOS module to generate it.
  • ~/.config/grootshell/calendars — one name|url per line, each being a calendar's "secret address in iCal format".

Binding keys

The shell binds nothing itself — your compositor does, and every panel is reachable over IPC:

grootshell-ipc call island toggle       # the dashboard
grootshell-ipc call launcher toggle
grootshell-ipc show                     # every target and function, live

KEYBINDS.md is the full version: all fifteen IPC targets, a set of default binds to paste into hyprland.conf.

Configuration

Everything is optional — ~/.config/grootshell/shell.json, watched and applied live. See config/Config.qml for the full surface with defaults.

{
  "appearance": { "fontScale": 1.0 },   // "sofa, not desk" — scales everything
  "bar":        { "workspaces": 5, "clockFormat": "ddd d MMM  HH:mm" },
  "border":     { "thickness": 0, "rounding": 25 },  // 0 = follow the compositor
  "wallpaper":  { "directory": "~/Pictures/Wallpapers" },
  "weather":    { "location": "Osaka", "units": "metric", "days": 10 }
}

Keys you have not set stay absent, and absent means "follow the shipped default" — so a default that improves upstream reaches you. Nothing writes this file wholesale for that reason; see services/Settings.qml.

Runtime state — the current wallpaper, the light/dark override — lives separately in $XDG_STATE_HOME/quickshell/by-shell/grootshell/state.json, deliberately: the config file is read and never written, so a later change to a default is not silently frozen by something the shell wrote.

Calendars

The agenda reads iCal feeds, one per line, name|url:

Work|https://calendar.google.com/calendar/ical/…/basic.ics
Family|https://calendar.google.com/calendar/ical/…/basic.ics

In Google Calendar that URL is Settings → your calendar → Integrate calendar → "Secret address in iCal format". Any iCal feed works; Google is just the common case. Drop the name| and the feed's own name is used. One feed failing does not lose the others — the error is reported against that calendar alone.

Read-only, and no OAuth: no consent flow, no refresh token, no client registration to keep alive. The trade is that the URL is the credential — anyone holding it can read the calendar, with no account and no revocation short of regenerating the link. Treat it accordingly.

Without Nix, put those lines in ~/.config/grootshell/calendars.

With Nix, that means it must not be an option value — those land in the world-readable Nix store. Put it through sops and point the module at the decrypted path:

sops.secrets."calendar/ical-urls".owner = "alice";
programs.grootshell.calendarUrlFile =
  config.sops.secrets."calendar/ical-urls".path;

calendarUrlFile overrides the config-directory location, which is why the secret can live somewhere unguessable under /run.

Colours are assigned shell-side, keyed by the name you gave the feed — a colour is not sensitive, and changing one should not mean decrypting a file:

{ "services": { "calendarColours": { "Work": "#7aa2f7", "Family": "#9ece6a" } } }

Needs python3 with icalendar and recurring-ical-events; the Nix package brings both.


Layout

shell.qml            entry point: windows, IPC surface, input mask
config/              Config singleton, design tokens, colour scheme
services/            system state — audio, network, metrics, players, weather
components/          shared widgets
modules/
  background/        wallpaper surface
  border/            the frame everything else insets into
  bar/               the pill bar
  island/            the clock pill that becomes the dashboard
  launcher/          slides up from the bottom
  notifications/     toasts and the centre, docked into the border
  osd/               volume, right edge
  network/           wifi popout
  clipboard/         cliphist history
  switcher/          window and desktop switchers
  translate/         translation panel
  keybinds/          the cheatsheet
nix/                 package definition and the NixOS module
templates/           matugen templates: the shell, GTK, Qt, WezTerm
scripts/             theme generator, calendar fetcher, ipc wrapper, qml-audit

Developing

nix flake check      # parses every QML file, then audits it

scripts/qml-audit.py catches mistakes that parse cleanly and fail at load.

Run it before pushing. It is faster than finding out from a crash loop.

Credits

GPL-3.0-only. An original work, but it owes ideas — and in places structure — to other Quickshell configurations, all GPL-3.0-only themselves.

caelestia-dots/shell — the overall composition: a shell-drawn border everything else insets into, panels that grow out of that border rather than float above it, the centre island that tabs between dashboard, media and performance, and notifications docked into the frame. Caelestia's versions lean on a large C++/Qt plugin; these are pure QML.

AvengeMedia/DankMaterialShell — the wallpaper picker as a dashboard tab that applies as you browse, easing the whole palette between schemes instead of snapping, and album art cropped to a disc inside an audio spectrum ring.

Axenide/Ambxst — the bar as separate floating pills over a uniformly thin border, rather than one solid band.

pctrade/end4-pC and enhaoswen/Tide-island — the desktop switcher: workspaces drawn to scale with live window previews, including the ones not currently on screen.

end-4/dots-hyprland — a side panel as a home for tools rather than status, which is what the translate panel is.

LUCKYS1NGHH/ChillPill-Shell — the cliphist integration.

Built on Quickshell (LGPL-3.0), which does the actual heavy lifting, with colour schemes from matugen and forecasts from Open-Meteo.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages