Dotfiles/desktopenvs/hyprdrive/astro-menu
Amir Alexander Abdelbaki 7ebff0d2a4 feat(hyprdrive): configurable/live-polled workspace layout picker in astro-menu
config.json gains a "layouts" key to narrow which hypr/layouts-discovered
layouts astro-menu offers (and reorder them), plus per-layout option
overrides (trim directions, hide fit_method/stepper), without touching the
.lua layout sources.

Also: the taskbar's workspace/layout panel now polls every second while the
menu is open, since switching workspaces doesn't notify (or get blocked by)
the menu and the panel previously only refreshed on open.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 15:07:02 +02:00
..
backend feat(hyprdrive): materialize animations, dmenu mode, ship-systems astro rework 2026-07-18 17:17:48 +02:00
lib fix(astro-menu): clip card bloom so corners stop poking out of the rounded border 2026-07-22 23:13:29 +02:00
modules feat(hyprdrive): materialize animations, dmenu mode, ship-systems astro rework 2026-07-18 17:17:48 +02:00
services feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
style feat(hyprdrive): materialize animations, dmenu mode, ship-systems astro rework 2026-07-18 17:17:48 +02:00
ui feat(hyprdrive): configurable/live-polled workspace layout picker in astro-menu 2026-07-24 15:07:02 +02:00
.gitignore feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
README.md feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
appservices.py feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
config.py feat(hyprdrive): configurable/live-polled workspace layout picker in astro-menu 2026-07-24 15:07:02 +02:00
main.py feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
module_base.py feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
paths.py feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
registry.py feat(hyprdrive): materialize animations, dmenu mode, ship-systems astro rework 2026-07-18 17:17:48 +02:00
settings.py feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
theme.py feat(hyprdrive): add hyprdrive DE with full Cosmonaut Shell suite 2026-07-17 15:28:07 +02:00
window.py feat(hyprdrive): configurable/live-polled workspace layout picker in astro-menu 2026-07-24 15:07:02 +02:00

README.md

astro-menu

A touch-friendly GTK4 popup control centre for the hyprlua desktop, replacing nwg-dock and nwg-drawer. Triggered from the EWW top bar (or Super+D).

Layout: a 2×2 quad grid of feature modules over a full-width application drawer. Any quad can expand to overlay the other three; the drawer can expand to the bottom of the screen. A single margined root box letterboxes the menu identically in every state.

Stack

  • Frontend: Python + PyGObject + GTK4, as a wlr-layer-shell surface (gtk4-layer-shell). Chosen over Lua because lgi/Astal-Lua only support GTK3.
  • Location map: a static OSM image stitched from tiles (backend/staticmap.py, Pillow). libshumate does not paint tiles in this environment (the official shumate-demo shows the same blank map), though tile downloads work — hence the static fallback.
  • Services: Astal GObject libraries via introspection — AstalNetwork, AstalBluetooth, AstalApps — plus our own IP-geolocation singleton.
  • Backends: Python/Bash scripts in backend/ (JSON on stdout), run async via Gio.Subprocess so nothing blocks the UI.
  • Theme: style/_colors.css (@define-color, generated from ~/Dotfiles/colors.conf by apply-theme.sh) + style/style.css.

Running

main.py is single-instance. The autostart launches a hidden resident daemon; verbs are forwarded over D-Bus:

scripts/astro-menu-start.sh          # resident daemon (hidden); sets LD_PRELOAD
scripts/menu-toggle.sh               # --toggle
scripts/menu-toggle.sh appdrawer     # open with the app drawer expanded

astro-menu-start.sh must LD_PRELOAD libgtk4-layer-shell (it loads after libwayland under PyGObject otherwise).

Adding a module

  1. Create modules/<name>.py exposing a top-level SPEC = ModuleSpec(...) whose build(ctx) returns a ModuleInstance(compact=…, expanded=…, …).
    • compact shows in the 2×2 cell; a distinct expanded widget enables the expand button (they must be separate instances — GTK widgets have one parent).
    • Declare per-feature toggles via features=[Feature("id", "Label", default)]; read them with ctx.feature("id"). A disabled quad never calls build().
    • Shared state (network, bluetooth, location) is on ctx.services.
  2. Append its SPEC to ALL_SPECS in registry.py. Nothing else changes.

Files

main.py          app + single-instance IPC        window.py    layer-shell + letterbox
registry.py      module list (extension seam)      settings.py  JSON toggles/order
module_base.py   ModuleSpec / ModuleInstance / ModuleContext
appservices.py   shared Astal services + location
ui/              quadcard, quadgrid, appdrawer
modules/         location, weather, bluetooth, network
services/        location (geolocation singleton)
lib/             ansi (SGR→TextTag), proc (async subprocess)
backend/         geolocate.py, weather.sh, network.sh
style/           _colors.css, style.css