Commit Graph
5 Commits
Author SHA1 Message Date
gitops fc8c2a727d waybar: six more modules, and a keyboard reference the session had none of
The bar showed which windows existed but not which one had the keyboard,
and nothing at all about the GPU, the fans, the disk, or a unit that had
failed at boot. Six modules, two of which needed a script because waybar
has no module that fits.

hyprland/window, and no compositor change

Reading the active window needs `j/activewindow` on startup and
`activewindow>>` on .socket2.sock, and hypr_ipc already serves both. A
retitle of the focused window propagates too, which is the case that
usually needs a separate `windowtitle` event: this fork diffs a snapshot
on a 150 ms tick rather than emitting from call sites, so src/hypr_ipc/
sync.rs sees a (class, title) change and cannot miss it. There is already
a test for exactly that -- title_change_alone_emits_activewindow.

The rewrite rules trim the app suffix from titles written for a title bar
rather than a 60-column slot. All five now use the same [-<em dash>]
class. Vivaldi had a bare hyphen because it was written from the live
title -- j/clients reports "Inbox - ... - Gmail - Vivaldi" -- and the
others got the class in a later pass, which left the one rule that would
break if Vivaldi ever switched separators. COSMIC's own applications use
an em dash and not a hyphen, so a rule written with a hyphen matches
nothing there.

custom/fan, because neither of the obvious options can name this sensor

waybar has no fan module. Its temperature module cannot be borrowed: it
divides by 1000 to turn millidegrees into degrees, which renders 2700 rpm
as 2 C. Nor can hwmon-path-abs point at the fans. That option names a
parent directory and takes the one hwmonN inside it, which works for
k10temp and amdgpu because each has exactly one, but asus-nb-wmi has two:

    hwmon9   name=asus                  fan1_input, fan2_input
    hwmon10  name=asus_custom_fan_curve  the curve's set points

Which of the two waybar picked would be down to readdir order. So the
script resolves by content instead -- any hwmon with a fan*_input -- which
also makes it work on hardware that is not this laptop. It prints nothing
where there is no readable fan, and waybar draws an empty custom module as
nothing, so a VM loses the item rather than showing a dead 0 rpm.

hyprcosmic-keybinds, and why it does not read cosmic.conf

The obvious implementation reads cosmic.conf and would be wrong. This
machine's cosmic.conf declares six bindings. The session answers to 122.
The other 116 are COSMIC's defaults, which the fork does not restate
because it has no reason to -- Super+Q closes a window whether or not
anybody wrote it down. A reference showing six entries would not look
broken, it would look complete, while missing every window, workspace and
media key on the machine.

So it reads the three RON files the compositor reads: Shortcuts/v1/
defaults for the built-ins, the user's Shortcuts/v1/custom for what
cosmic-conf projected out of cosmic.conf, and Shortcuts/v1/system_actions
to turn System(Screenshot) into cosmic-screenshot. Without the third, a
third of the list would name internal actions rather than the programs
they launch, which is the half of the question that was being asked.

Parsing RON with sed deserves a defence. The principled home for this is a
`cosmic-conf binds` subcommand, which already has the schema and the bind
grammar -- and is a compiled change and a full package build. The files
are one binding per line and nothing downstream consumes this, so the cost
of being wrong is a mangled row in a help window, not a broken keybinding.
The script refuses to show a list it could not parse rather than showing a
short one.

The binding for it was chosen with the script: Super+K and Super+I are
both focus actions in COSMIC's defaults, and Super+Shift+/ is free.

install-assets: prune dot-directories from the audit

audit_config_tree refuses to run unless every file under config/ is
classified, and it started failing on six gitignored .omc/ state files
that tooling had dropped into config/waybar/. Failing on those trains you
to ignore the one message that catches a genuinely unclassified asset.
Dot files are still walked; only directories are pruned.

Verified before committing

The generated config parses as JSON with all 23 modules defined and none
defined but unplaced. The three stylesheets concatenate and parse clean
through GTK's own CssProvider, which is the parser waybar uses, and every
@colour they name is defined. Both scripts pass a syntax check, and
hyprcosmic-keybinds was run with rofi stubbed -- nothing appeared on the
desktop -- producing 118 rows, which is 116 defaults plus 6 ours less the
4 that override. System() actions resolved to real commands, chords sorted
Super first, and the modifier-only Super binding survived, which a parser
requiring a key field would have dropped.

Every sensor was read before being wired to a module rather than after:
amdgpu edge 55 C, cpu_fan 2700 and gpu_fan 2500 rpm, zero failed units in
both scopes, / at 3 percent.

Not verified: how any of it looks. Rendering needs waybar restarted
against a session that is not at the lock screen.
2026-08-11 07:46:29 +07:00
gitops 08028e6f2c Add a power menu, so a session can be left without rebooting
The hyprcosmic profile disables cosmic-panel, and COSMIC's power applet lives
in that panel. Nothing replaced it, so the session had no logout, reboot or
shutdown anywhere in it: the only way out was `systemctl reboot` typed into a
terminal, which also meant every login-time change cost a reboot to test.

hyprcosmic-powermenu is a rofi menu offering lock, suspend, log out, reboot and
shut down. It is reached two ways -- $mainMod SHIFT E and a button at the right
end of waybar -- and both run the same script, so a click cannot bypass the
confirmation a keypress gets.

Logging out calls com.system76.CosmicSession.Exit, which is the method the
panel applet used and the only one that stops the session's clients in order
rather than pulling the compositor out from under them. Reboot and poweroff go
straight to systemd; polkit already authorises both for an active local session
without a prompt, verified with pkcheck, so no pkexec is involved. Lock uses
loginctl, which cosmic-greeter is listening for.

Confirmation is asked only for the three that end the session. Lock and suspend
undo themselves with a keypress, so a prompt there is pure friction; the other
three throw away everything unsaved and are one keystroke away at all times.
"No" is listed first so it is the row already selected.

The menu entries are plain words rather than Nerd Font glyphs. The bar icon is
the only glyph involved, and it comes through generate-config.py, whose whole
purpose is that no Private Use Area character is ever typed by hand -- U+F011,
confirmed present in the installed JetBrainsMono Nerd Font.

rules.css names every module id explicitly, so #custom-power had to be added
there too or the button would have rendered with no pill behind it.

The script sits under config/bin/ rather than a new top-level directory so that
install-assets.sh's audit still covers it: that check refuses to run unless
every file under config/ is classified, which is what stops a new file from
being silently left uninstalled.
2026-08-10 15:35:45 +07:00
gitops b89e4be10a Generate the waybar config, and fill the bar out
The bar had six modules and one dead click: pulseaudio's on-click ran
pavucontrol, which is not installed. It now opens `cosmic-settings sound`,
with middle-click as a mute toggle. Added: mpris, bluetooth, temperature,
idle_inhibitor, privacy, a swaync notification button, and power-profiles-
daemon -- and HyDE's pill styling, so each module is its own rounded chip
rather than text in a row.

No backlight module. This machine's panel has no sysfs backlight interface, so
it would render as a permanent error.

The rest of this is about the icons, which have now been got wrong enough
times to deserve a mechanism.

Private Use Area characters do not survive being typed. Writing the previous
version I put a comment at the top of the file saying every glyph was an
escape, then typed literal glyphs into the same file; the codepoint dump found
`format-bluetooth` had picked up a stray U+F293 and another field had two
glyphs where I had written one. Nothing errors -- waybar is perfectly happy to
render a label that is one space.

So the config is generated, not written. config.jsonc.in is pure ASCII with
@@TOKEN@@ placeholders, generate-config.py holds the name-to-codepoint table
and emits `\uXXXX` escapes, and it asserts its own output `.isascii()` before
writing. A hand-typed glyph now cannot reach the file. The delimiter is
doubled because single `@NAME@` collided with wpctl's `@DEFAULT_AUDIO_SINK@`,
which the generator caught as an unknown token rather than mangling.

Every codepoint was checked against the installed font with `fc-list
":charset=..."`. HyDE's own values do not all survive that: its muted glyph
U+FA80 is an old Material Design Icons codepoint that Nerd Fonts v3 moved, so
copying upstream verbatim would have shipped tofu. Three were replaced.

The font stack named "FontAwesome 6 Free" first, which resolves to the Regular
face and carries a fraction of the icon set -- the icons that did appear were
coming from accidental per-character fontconfig fallback. JetBrainsMono Nerd
Font goes first now.

One CSS note, because the failure mode is not obvious: GTK has no `:empty`
pseudo-class, and an unknown pseudo-class does not skip the rule, it rejects
the whole stylesheet and waybar exits 1. `#tray:empty` took the bar down.

install-assets.sh grows a third category. The template and the generator live
under config/ but must not be installed -- a file full of placeholders sitting
next to the real config is a coin toss for whoever opens one first -- and the
audit refuses to run until every file is classified, which is exactly what it
is for.
2026-08-10 12:58:48 +07:00
gitops e46514cbab Install HyDE themes end to end
Three things stood between `assets.rs` and a themed desktop.

`import-theme` never called it. The module was written, tested and unreachable;
`--assets` now wires it up, with `--source`, `--overwrite` and `--dry-run`, and
finds the theme repo's Source/ directory by searching upward rather than
assuming HyDE's exact nesting depth.

The archive guard rejected every real icon theme. Refusing any `..` in a link
target is right for an entry path but wrong for a symlink: icon themes are
built out of relative links into sibling directories, and Tela ships thousands
of `../devices/network-wireless.svg`. What matters is whether the target
resolves inside the destination, which `stays_within_root` now decides
lexically -- no canonicalize, since the tree does not exist at plan time and
following real links during validation would be a TOCTOU window. Absolute
targets and links that climb past the root are still refused; the existing
escape tests still pass.

`apply` silently ignored `source`. It parsed and resolved inline while `watch`
went through `compile`, and `flatten` drops `Item::Source` -- so an include
that worked under `watch` vanished under `apply`. `apply` now uses `compile`
too. This matters immediately: the generated theme lives in its own
theme.conf, sourced from cosmic.conf, so re-importing a theme cannot clobber
the keybindings.

The waybar stylesheet claimed a theme could be dropped in ahead of it to
recolour the bar. It could not -- HyDE names its colours main-bg/wb-act-bg and
the rules referenced bar-bg/accent. Split into palette + theme + bridge +
rules, imported in that order, so the claim is now true. Verified by loading
the result through GTK's own CSS parser: with Tokyo Night installed main-bg
resolves to #24283b and wb-act-bg to #bb9af7; with an empty theme.css the
defaults stand. Both parse without error.

Two deliberate departures, both commented where they are made: the theme's
near-transparent bar-bg is composited at 0.85 because cosmic-comp has no blur
to put behind it, and theme.css is copied next to style.css rather than
imported from HyDE's own path, because a missing @import is fatal in GTK and
would break the bar on any machine without a theme.
2026-08-10 09:11:08 +07:00
gitops 214a77ea18 Add the waybar config and autostart file the hyprcosmic profile needs
The profile disables cosmic-panel, so without these a HyprCosmic session is a
compositor with no bar, no clock and no tray. Waybar's shipped default is not
a substitute: /etc/xdg/waybar/config.jsonc is built entirely from sway/*
modules and renders nothing under cosmic-comp.

Module choices are pinned to what cosmic-comp actually advertises on the
Wayland registry, checked against a live session rather than assumed:

  ext/workspaces  ext_workspace_manager_v1, present in stock cosmic-comp.
  wlr/taskbar     zwlr_foreign_toplevel_management_v1, absent from stock
                  cosmic-comp and supplied by this fork's Patch A. Under a
                  stock compositor the module stays empty and the bar
                  otherwise works, so the config is not fork-only.
  tray            cosmic-panel normally hosts the StatusNotifierWatcher via
                  cosmic-applet-status-area; with the panel disabled waybar
                  hosts it instead.

The stylesheet keeps every colour behind an @define-color so that a HyDE
theme's waybar.theme -- which is only a list of such declarations, the bespoke
CSS being HyDE's own rather than any theme's -- can recolour the bar by being
sourced ahead of it.

Configs install to /usr/share/hyprcosmic/waybar so the autostart file needs no
per-user paths, which matters because the profile parser is deliberately not a
shell and cannot expand ~ or $HOME.

Verified: config.jsonc parses as JSONC and every compositor module placed on
the bar has a matching config block.
2026-08-10 08:36:11 +07:00