Files
hyprcosmic/config/waybar/rules.css
T
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

190 lines
5.4 KiB
CSS

/* Geometry and layout for the HyprCosmic bar.
*
* No colour literals: every colour here is a name defined by palette.css and
* possibly re-pointed by bridge-hyde.css. That separation is what lets a theme
* change the palette without touching a single rule.
*
* The look is HyDE's: a strip carrying rounded pills rather than a flat row of
* text. HyDE gets its pills to float by making the bar itself transparent and
* letting the compositor blur the desktop behind them; cosmic-comp has no
* rule-driven blur, so the bar keeps a background here and the pills lift off
* it instead. See bridge-hyde.css for the two colours that implements.
*/
* {
/* JetBrainsMono Nerd Font first, and it has to be first.
*
* This previously named "FontAwesome 6 Free", which is not even the
* installed family's real name ("Font Awesome 6 Free") -- fontconfig's
* fuzzy matching resolved it anyway, to the Regular face, which carries
* only a small subset of the icon set. Every glyph outside that subset
* then came from whatever fallback fontconfig picked per character, so the
* bar's icons were only accidentally consistent.
*
* The Nerd Font carries the whole icon set and the text, so naming it
* first makes one font answer for both. Every codepoint in config.jsonc
* was checked against this family specifically. */
font-family: "JetBrainsMono Nerd Font", "Noto Sans", sans-serif;
font-size: 13px;
border: none;
border-radius: 0;
min-height: 0;
}
window#waybar {
background: @bar-bg;
color: @bar-fg;
}
/* Workspaces: pills, with the active one filled rather than underlined. */
#workspaces {
margin: 0 4px;
}
#workspaces button {
padding: 0 10px;
margin: 4px 2px;
border-radius: 10px;
color: @muted;
background: transparent;
}
#workspaces button.active {
color: @wb-act-fg;
background: @accent;
}
#workspaces button:hover {
color: @wb-hvr-fg;
background: @wb-hvr-bg;
}
#taskbar button {
padding: 0 6px;
margin: 4px 1px;
border-radius: 10px;
background: transparent;
}
#taskbar button.active {
background: @module-bg;
}
#taskbar button:hover {
background: @wb-hvr-bg;
}
/* Every status module gets the same pill. Listed one per line because waybar
* takes the CSS id from the module name with `/` turned into `-`, so these are
* not guessable from the config and are worth being able to read down. */
#custom-keybinds,
#window,
#mpris,
#clock,
#privacy,
#systemd-failed-units,
#idle_inhibitor,
#custom-swaync,
#bluetooth,
#pulseaudio,
#network,
#disk,
#temperature,
#custom-fan,
#cpu,
#memory,
#power-profiles-daemon,
#battery,
#tray,
#custom-power {
padding: 0 10px;
margin: 4px 2px;
border-radius: 10px;
background: @module-bg;
}
/* The only module here that is a button rather than a readout, so it is the
* only one that gains anything from a hover state. Colour rather than
* background, to match how the status colours below signal without moving
* anything. */
#custom-power:hover {
color: @critical;
}
/* The other button on the bar. Accent rather than critical, because this one is
* safe to press. */
#custom-keybinds:hover {
color: @accent;
}
#clock {
font-weight: bold;
}
/* mpris renders nothing when no player is running. Without this it would still
* draw an empty pill, so the padding goes away with the content. */
#mpris {
padding: 0;
background: transparent;
}
#mpris.playing,
#mpris.paused {
padding: 0 10px;
background: @module-bg;
}
/* Same reasoning: privacy is only present while something is capturing. */
#privacy {
padding: 0;
background: transparent;
}
#privacy-item {
padding: 0 8px;
margin: 4px 2px;
border-radius: 10px;
background: @module-bg;
color: @warning;
}
/* Same reasoning as mpris, but the class is not on the widget. waybar puts
* `empty` on the whole bar rather than on #window, so the selector has to
* descend into it -- waybar-hyprland-window(5) gives it as exactly this:
*
* window#waybar.empty #window When no windows are in the workspace
*
* The first `window` is the GTK window, the second is our module; they are
* unrelated names that collide, which is why this reads so oddly. */
window#waybar.empty #window {
padding: 0;
background: transparent;
}
/* States. These recolour the text inside the pill rather than the pill, so a
* warning cannot make a module unreadable against its own background. */
#battery.warning { color: @warning; }
#battery.critical { color: @critical; }
#temperature.critical { color: @critical; }
#network.disconnected { color: @critical; }
/* No state class needed: hide-on-ok removes the module entirely at zero, so it
* is on screen only when there is something to report and can be coloured
* unconditionally. */
#systemd-failed-units { color: @critical; }
/* Set by hyprcosmic-fan: `high` above 4000 rpm, `idle` when every fan has
* stopped. `normal` is left alone. */
#custom-fan.high { color: @warning; }
#custom-fan.idle { color: @muted; }
#pulseaudio.muted { color: @muted; }
#idle_inhibitor.activated { color: @accent; }
#custom-swaync.notification { color: @accent; }
#custom-swaync.dnd-none,
#custom-swaync.dnd-notification { color: @muted; }
/* No `#tray:empty` rule to hide the pill when nothing is in the tray: GTK's
* CSS has no :empty pseudo-class and rejects the whole stylesheet for it,
* which is fatal rather than cosmetic. waybar exits and the bar never appears.
*/