diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-11 17:20:50 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-11 17:20:50 +0200 |
| commit | 7b55910a786c7652ac80a576399ebd9f81a8ac72 (patch) | |
| tree | 4adaedb0f0ac5003ecc0e19093a2cb5871b18deb | |
| parent | c7cb9dad4e782ee0ff99773c3607ec3b7792c723 (diff) | |
| download | unified-desktop-theme-7b55910a786c7652ac80a576399ebd9f81a8ac72.tar.gz unified-desktop-theme-7b55910a786c7652ac80a576399ebd9f81a8ac72.zip | |
feat(palette): generate every themed config from one scheme
Colours were written down five times: rofi's palette.rasi, the waybar theme,
the kitty theme, the conky and dunst templates, and a MACCHIATO dict inside
udt-accent. Changing scheme meant finding all five, and they had already
drifted: the active waybar theme was Macchiato while theme.css next to it was
Mocha, and nothing imported the latter.
Now a scheme is two files. palette/<scheme>.conf holds the colours under the
scheme's own names, and palette/roles-<scheme>.conf says what each is for.
palette/roles.conf is one `scheme =` line selecting the pair. bin/udt-palette
renders that into each consumer's syntax and install.sh runs it first.
The role map is per-scheme rather than shared because the palettes are
genuinely different shapes: Nord has no `base`, Dracula no `surface0`, and
forcing Catppuccin's names onto them would have meant either renaming their
colours or inventing values. A role names a palette colour with two optional
modifiers, `name/75` for alpha and `name*50` for brightness. The second exists
because GTK's shade() has no palette name to point at.
Ships macchiato, tokyo-night, nord and dracula. --selftest resolves every role
in every scheme and asserts they define the same set, so a scheme that cannot
satisfy a consumer fails at install rather than when someone switches to it.
Verified against the live configs: all 46 waybar colours resolve identically to
the previously active theme including its alpha() and shade() derivations, the
kitty theme is colour-for-colour identical, and the rofi palette has the same
26 values. Generated files are gitignored; switching scheme and switching back
reproduces the originals.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015gbjA2bmswN8jyPDKzrvqe
| -rw-r--r-- | .gitignore | 9 | ||||
| -rw-r--r-- | AGENTS.md | 33 | ||||
| -rw-r--r-- | README.md | 15 | ||||
| -rwxr-xr-x | bin/udt-accent | 49 | ||||
| -rwxr-xr-x | bin/udt-palette | 416 | ||||
| -rwxr-xr-x | install.sh | 25 | ||||
| -rw-r--r-- | palette/dracula.conf | 29 | ||||
| -rw-r--r-- | palette/macchiato.conf | 40 | ||||
| -rw-r--r-- | palette/nord.conf | 41 | ||||
| -rw-r--r-- | palette/roles-dracula.conf | 101 | ||||
| -rw-r--r-- | palette/roles-macchiato.conf | 115 | ||||
| -rw-r--r-- | palette/roles-nord.conf | 101 | ||||
| -rw-r--r-- | palette/roles-tokyo-night.conf | 99 | ||||
| -rw-r--r-- | palette/roles.conf | 14 | ||||
| -rw-r--r-- | palette/tokyo-night.conf | 43 | ||||
| -rw-r--r-- | rofi/udt/palette.rasi | 41 | ||||
| -rw-r--r-- | templates/conky.conf.in | 102 | ||||
| -rw-r--r-- | templates/dunstrc.in | 75 | ||||
| -rw-r--r-- | templates/terminal/kitty-macchiato.conf | 84 | ||||
| -rw-r--r-- | templates/waybar/catppuccin-macchiato.css | 63 |
20 files changed, 1269 insertions, 226 deletions
@@ -1,3 +1,12 @@ HANDOFF.md __pycache__/ *.pyc + +# Generated by udt-palette from palette/<scheme>.conf. Regenerated by +# install.sh, so tracking them would turn every scheme switch into a diff. +/rofi/udt/palette.rasi +/templates/dunstrc +/templates/conky.conf +/templates/waybar/theme.css +/templates/terminal/kitty-theme.conf +/bin/udt_colors.py @@ -13,6 +13,35 @@ the working config cannot drift from what is committed. Everything outside `~/.config/rofi/udt/` and `~/bin/` was edited in place; `docs/MIGRATION.md` is the record of what changed and where the backups are. +## One palette, many consumers + +Colours are written down once. `palette/<scheme>.conf` holds them under the +scheme's own names, `palette/roles-<scheme>.conf` says what each is for, and +`palette/roles.conf` is a single `scheme = ` line selecting the pair. +`bin/udt-palette` renders that into every consumer's own syntax; `install.sh` +runs it first, before anything is linked. + +Four schemes ship: `macchiato`, `tokyo-night`, `nord`, `dracula`. Switching is +one line plus `./install.sh`. + +Generated files are gitignored, because tracking them would turn every scheme +switch into a diff. Editing one is pointless: the next install overwrites it. +The generated set is `rofi/udt/palette.rasi`, `templates/dunstrc`, +`templates/conky.conf`, `templates/waybar/theme.css`, +`templates/terminal/kitty-theme.conf` and `bin/udt_colors.py`. The last is +imported by `udt-accent`, which is why its accent table follows the scheme. + +Roles are a flat namespace, so a name collides across sections: conky's outline +is `body_outline` because waybar already has `outline`. A role value is a +palette name with two optional modifiers, `name/75` for 75% alpha and +`name*50` for half brightness. The second exists only because GTK's +`shade(@main-bg, 0.5)` has no palette name to point at: half-brightness crust +is darker than the darkest colour any of these schemes ships. + +**Adding a scheme means adding two files, never editing a consumer.** Every +scheme must define the same role set; `udt-palette --selftest` fails if one +drifts, which is what stops a switch from breaking a config nobody looked at. + ## The accent One colour moves: `@accent`. `bin/udt-accent` reads the current wallpaper, @@ -111,7 +140,9 @@ them by exit code: rofi exits non-zero for ordinary reasons such as Escape. - **Themes that reference an image cannot be committed with a real path.** `launcher.rasi` carries an `@WPAPER@` placeholder that `install.sh` rewrites. rofi does not expand `~` inside a `url()`. -- After editing anything under `rofi/udt/`, run `./install.sh`. +- After editing anything under `rofi/udt/` or `palette/`, run `./install.sh`. +- `bin/udt-palette --selftest` resolves every role in every scheme. Run it + after touching a palette, a role map, or the generator. - `bin/udt-accent --selftest` covers the snapping logic. Run it after touching the colour maths. @@ -33,6 +33,21 @@ the pywal cache. GPLv2 only. See `LICENSE`. +## Changing the colour scheme + +Edit one line in `palette/roles.conf`: + + scheme = macchiato + +and run `./install.sh`. Ships with `macchiato`, `tokyo-night`, `nord` and +`dracula`; rofi, dunst, conky, waybar, kitty, Firefox and quickshell all +follow. + +A scheme is two files: `palette/<name>.conf` for the colours under the +scheme's own names, and `palette/roles-<name>.conf` saying what each is for. +Nothing else needs touching, and `bin/udt-palette --selftest` checks that a +scheme can satisfy every role before you switch to it. + ## Development Approach This project is developed using AI-assisted tools. Code is generated with the help of AI based on human-provided specifications, design decisions, and iterative feedback. diff --git a/bin/udt-accent b/bin/udt-accent index 7949597..1a222b5 100755 --- a/bin/udt-accent +++ b/bin/udt-accent @@ -21,25 +21,12 @@ import sys import tempfile from pathlib import Path -# The candidate accents. rosewater and flamingo are excluded as near-neutral -# tints that capture saturated inputs, and maroon and sapphire as near-duplicate -# hues of red and sky. lavender sits close to mauve but is kept: it is the -# desktop-wide fallback, used by Qt, GTK and anything else that needs one fixed -# accent, so it has to be a value snap() can also return. -ACCENTS = { - "pink": "#f5bde6", - "mauve": "#c6a0f6", - "lavender": "#b7bdf8", - "red": "#ed8796", - "peach": "#f5a97f", - "yellow": "#eed49f", - "green": "#a6da95", - "teal": "#8bd5ca", - "sky": "#91d7e3", - "blue": "#8aadf4", -} - -FALLBACK = "lavender" +# The colour tables are generated: udt-palette renders them from +# palette/<scheme>.conf and palette/roles-<scheme>.conf, so the accents this +# snaps to follow whatever scheme is selected. Regenerate with ./install.sh. +sys.path.insert(0, str(Path(__file__).resolve().parent)) +from udt_colors import ACCENTS, FALLBACK, PALETTE # noqa: E402 + MIN_CHROMA = 10.0 OUTPUT = Path.home() / ".cache" / "wal" / "udt-accent.rasi" @@ -47,24 +34,12 @@ BORDER_OUTPUT = Path.home() / ".cache" / "wal" / "udt-border.lua" DUNSTRC = Path.home() / ".cache" / "wal" / "dunstrc" QML_OUTPUT = Path.home() / ".cache" / "wal" / "udt-palette.qml" -# The installed palette, which is a symlink back into this repo. Read rather -# than duplicated: palette.rasi is the one place the structural colours are -# written down, and a second copy here would drift the first time one changed. +# The installed rofi palette, itself generated by udt-palette. Read rather than +# duplicated so the QML singleton carries exactly the colours rofi uses, under +# the same names. PALETTE_RASI = Path.home() / ".config" / "rofi" / "udt" / "palette.rasi" COLORS_JSON = Path.home() / ".cache" / "wal" / "colors.json" -# Catppuccin Macchiato, in pywal's slot order. Firefox is themed from this -# via pywalfox, which reads colors.json and nothing else. -MACCHIATO = { - "background": "#24273a", "foreground": "#cad3f5", "cursor": "#f4dbd6", - "colors": [ - "#494d64", "#ed8796", "#a6da95", "#eed49f", - "#8aadf4", "#f5bde6", "#8bd5ca", "#b8c0e0", - "#5b6078", "#ed8796", "#a6da95", "#eed49f", - "#8aadf4", "#f5bde6", "#8bd5ca", "#a5adcb", - ], -} - def write_atomic(path, content): """Write a file atomically, so no reader ever sees it half-written. @@ -256,7 +231,7 @@ def write_colors_json(name, image): palette stays fixed Macchiato while only the accent moves. """ hexval = ACCENTS[name] - colors = list(MACCHIATO["colors"]) + colors = list(PALETTE["colors"]) # Slots 4 and 12 are pywalfox's link/highlight colour. colors[4] = colors[12] = hexval @@ -267,8 +242,8 @@ def write_colors_json(name, image): "wallpaper": os.path.realpath(image), "alpha": "100", "special": { - "background": MACCHIATO["background"], - "foreground": MACCHIATO["foreground"], + "background": PALETTE["background"], + "foreground": PALETTE["foreground"], "cursor": hexval, }, "colors": {f"color{i}": c for i, c in enumerate(colors)}, diff --git a/bin/udt-palette b/bin/udt-palette new file mode 100755 index 0000000..b2951c6 --- /dev/null +++ b/bin/udt-palette @@ -0,0 +1,416 @@ +#!/usr/bin/env python3 +# udt-palette: generate every themed config from one palette and one role map. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +"""Render the palette into each consumer's own syntax. + +Usage: udt-palette [--roles <file>] [--out <dir>] + udt-palette --selftest + +The point of this script is that a colour is written down once. palette/<scheme> +.conf holds the colours under the scheme's own names, palette/roles.conf says +what each is for, and everything else here is generated. Editing a generated +file is pointless: the next install.sh overwrites it. +""" + +import re +import sys +from pathlib import Path + +REPO = Path(__file__).resolve().parent.parent +PALETTE_DIR = REPO / "palette" + + +def parse_conf(path): + """Parse `key = value` lines into a dict, preserving order. + + [section] headers are read but not nested: roles are a flat namespace, and + the sections exist to group the file for a human reader. A duplicate key is + an error rather than a silent last-wins, because two roles quietly fighting + is exactly the drift this file exists to prevent. + """ + out = {} + for lineno, raw in enumerate(path.read_text().splitlines(), 1): + # A comment is a whole line starting with #, or a trailing "# " after + # a value. Splitting on a bare "#" would eat every colour, since the + # values are hex literals that start with one. + line = raw.strip() + if line.startswith("#"): + continue + line = re.split(r"\s#\s", line, maxsplit=1)[0].strip() + if not line or line.startswith("["): + continue + if "=" not in line: + raise ValueError(f"{path}:{lineno}: expected 'key = value', got {raw!r}") + key, value = (p.strip() for p in line.split("=", 1)) + if key in out: + raise ValueError(f"{path}:{lineno}: duplicate key {key!r}") + out[key] = value + return out + + +def resolve(roles, palette, roles_path): + """Turn every role into (r, g, b, alpha). Unknown colour names are fatal. + + A role names a palette colour, optionally with an alpha percentage: + `text/75`. Failing here is the whole safety story: a typo or a name the new + scheme does not carry stops the build, rather than emitting a config with a + black hole where a colour should be. + """ + resolved = {} + for role, spec in roles.items(): + if role in ("scheme", "snap"): + continue + # `name/75` is 75% alpha; `name*50` is the colour at 50% brightness. + # shade exists because GTK's shade() has no palette name to point at: + # half-brightness crust is darker than the darkest colour a scheme + # ships, so it can only be computed. + rest, _, alpha_s = spec.partition("/") + name, _, shade_s = rest.partition("*") + name = name.strip() + if name not in palette: + available = ", ".join(sorted(palette)) + raise ValueError( + f"{roles_path}: role {role!r} wants colour {name!r}, which the " + f"{roles['scheme']} palette does not define.\nAvailable: {available}") + alpha = 100 + if alpha_s: + alpha = int(alpha_s.strip()) + if not 0 <= alpha <= 100: + raise ValueError(f"{roles_path}: role {role!r} alpha {alpha} out of 0-100") + hexval = palette[name] + r, g, b = (int(hexval[i:i + 2], 16) for i in (1, 3, 5)) + if shade_s: + factor = int(shade_s.strip()) / 100 + if not 0 <= factor <= 2: + raise ValueError(f"{roles_path}: role {role!r} shade out of 0-200") + r, g, b = (min(255, round(c * factor)) for c in (r, g, b)) + resolved[role] = (r, g, b, alpha) + return resolved + + +def hex6(c): + r, g, b, _ = c + return f"#{r:02x}{g:02x}{b:02x}" + + +def hex8(c): + r, g, b, a = c + return f"#{r:02x}{g:02x}{b:02x}{round(a * 255 / 100):02x}" + + +def rgba(c): + r, g, b, a = c + return f"rgb({r},{g},{b})" if a == 100 else f"rgba({r},{g},{b},{a / 100:.2f})" + + +BANNER = "Generated by udt-palette from palette/{scheme}.conf. Do not edit." + + +def gen_rofi(res, scheme, palette): + """rofi: the structural palette, under the scheme's own colour names. + + Emits palette names rather than roles because the rofi themes were written + against Catppuccin names and still use them. The role layer reaches rofi + through accent.rasi, which udt-accent generates per wallpaper. + """ + rows = "\n".join(f" {k + ':':11}{v}ff;" for k, v in sorted(palette.items())) + return ( + f"/*\n * {BANNER.format(scheme=scheme)}\n *\n" + " * Structural colors only. The accent lives in accent.rasi.\n */\n\n" + f"* {{\n{rows}\n}}\n" + ) + + +def gen_waybar(res, scheme, palette): + """waybar: one file holding the palette and the role layer. + + Both halves together because style.css imports a single themes/<scheme>.css, + and the stylesheets reference names from each: @lavender and @surface1 from + the palette, @cpu and @hover-bg from the roles. + + Role names keep their existing hyphenated spelling (main-bg, not main_bg): + the stylesheets reference them and are not generated. + """ + palette_rows = "\n".join(f"@define-color {k:<12}{v};" + for k, v in sorted(palette.items())) + + def emit(role, css_name=None): + c = res[role] + return f"@define-color {(css_name or role.replace('_', '-')):<12}{rgba(c)};" + + ui = ["main_br", "main_bg", "main_fg", "hover_bg", "hover_fg", "outline"] + modules = ["workspaces", "temperature", "memory", "cpu", "time", "date", + "tray", "volume", "backlight", "battery"] + states = ["warning", "critical", "charging"] + + return "\n".join([ + f"/* {BANNER.format(scheme=scheme)} */", + "", + palette_rows, + "", + "/* br - border, bg - background, fg - foreground */", + "", + "/* main colors */", + f"@define-color accent {rgba(res['accent'])};", + *(emit(r) for r in ui), + "", + "/* module colors */", + *(emit(r) for r in modules), + "", + "/* state colors */", + *(emit(r) for r in states), + "", + ]) + + +def gen_kitty(res, scheme, palette): + """kitty: the 16 ANSI slots plus chrome. + + kitty has no include-with-override, so this is the whole colour section of + the theme file rather than a fragment. + """ + ansi = ["black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"] + lines = [f"# {BANNER.format(scheme=scheme)}", ""] + for key, role in [("foreground", "fg"), ("background", "bg"), + ("selection_foreground", "selection_fg"), + ("selection_background", "selection_bg"), + ("cursor", "cursor"), ("cursor_text_color", "bg"), + ("url_color", "url"), + ("active_border_color", "border_active"), + ("inactive_border_color", "border_inactive"), + ("active_tab_foreground", "tab_active_fg"), + ("active_tab_background", "tab_active_bg"), + ("inactive_tab_foreground", "tab_inactive_fg"), + ("inactive_tab_background", "tab_inactive_bg"), + ("tab_bar_background", "tab_bar_bg"), + ("scrollbar_handle_color", "scrollbar_handle"), + ("scrollbar_track_color", "scrollbar_track"), + ("bell_border_color", "bell_border"), + ("mark1_foreground", "bg"), ("mark1_background", "mark1"), + ("mark2_foreground", "bg"), ("mark2_background", "mark2"), + ("mark3_foreground", "bg"), ("mark3_background", "mark3")]: + lines.append(f"{key:<24}{hex6(res[role])}") + lines.append("") + for i, name in enumerate(ansi): + lines.append(f"color{i:<3}{' ' * 16}{hex6(res[name])}") + for i, name in enumerate(ansi): + lines.append(f"color{i + 8:<3}{' ' * 16}{hex6(res['bright_' + name])}") + return "\n".join(lines) + "\n" + + +def gen_dunst(res, scheme, palette, template): + """dunst: substitute colours into the shipped template. + + Kept as a template rather than fully generated: dunstrc is mostly geometry + and behaviour that has nothing to do with colour. @ACCENT@ is left alone, + because udt-accent substitutes it per wallpaper after pywal renders it. + """ + out = template.replace("@SCHEME@", scheme) + for role in ("bg", "bg_alt", "fg", "fg_dim", "border", "critical"): + out = out.replace(f"@{role.upper()}@", hex6(res[role])) + return out + + +def gen_conky(res, scheme, palette, template): + """conky: substitute into the shipped template. + + conky.text is laid out with absolute ${goto} offsets tuned to label widths, + so it is never regenerated, only its colour block is substituted. + """ + out = template.replace("@SCHEME@", scheme) + for role in ("heading", "label", "rule", "value", "highlight", "ok", + "body", "body_outline", "body_shade"): + out = out.replace(f"@{role.upper()}@", hex6(res[role])) + return out + + +def gen_accent_py(res, scheme, palette, snap_names): + """The accent table and Macchiato dict udt-accent carries for Firefox. + + Written as a Python fragment that udt-accent imports, so the accent snapping + and the pywalfox palette both follow the scheme instead of hardcoding one. + """ + # The snap candidates are declared per scheme, not derived: which hues make + # good accents is a judgement about the palette (drop near-neutrals, drop + # near-duplicate hues) that the colour values alone do not carry. + candidates = {n: palette[n] for n in snap_names} + rows = "\n".join(f' {n!r}: {v!r},' for n, v in candidates.items()) + + ansi = ["black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"] + normal = ", ".join(f'"{hex6(res[n])}"' for n in ansi) + bright = ", ".join(f'"{hex6(res["bright_" + n])}"' for n in ansi) + + return ( + f"# {BANNER.format(scheme=scheme)}\n" + '"""Generated colour tables. See palette/roles.conf."""\n\n' + f"SCHEME = {scheme!r}\n\n" + "# The candidate accents udt-accent snaps a wallpaper to.\n" + f"ACCENTS = {{\n{rows}\n}}\n\n" + f"FALLBACK = {accent_name(res, palette, candidates)!r}\n\n" + "# Firefox, via pywalfox, which reads colors.json and nothing else.\n" + "PALETTE = {\n" + f' "background": "{hex6(res["bg"])}",\n' + f' "foreground": "{hex6(res["fg"])}",\n' + f' "cursor": "{hex6(res["cursor"])}",\n' + f" \"colors\": [{normal},\n" + f" {bright}],\n" + "}\n" + ) + + +def accent_name(res, palette, candidates): + """Which candidate udt-accent falls back to when a wallpaper is too grey. + + The accent role's own colour, so the fallback matches what every consumer + that does not track the wallpaper is already sitting on. + """ + target = hex6(res["accent"]) + for name, hexval in candidates.items(): + if hexval == target: + return name + return next(iter(candidates)) + + +def schemes(): + """Every scheme that ships both a palette and a role map.""" + return sorted(p.stem for p in PALETTE_DIR.glob("*.conf") + if p.stem != "roles" and not p.stem.startswith("roles-")) + + +def load(selector_path, scheme=None): + """Read the selected scheme's palette and role map. + + The selector names a scheme; the scheme names two files. Keeping the role + map per-scheme is what lets a palette use its own colour names: Nord has no + `base` and Dracula no `surface0`, so one shared map could not satisfy both. + """ + if scheme is None: + scheme = parse_conf(selector_path).get("scheme") + if not scheme: + raise ValueError(f"{selector_path}: no 'scheme' line") + + palette_path = PALETTE_DIR / f"{scheme}.conf" + roles_path = PALETTE_DIR / f"roles-{scheme}.conf" + for needed in (palette_path, roles_path): + if not needed.exists(): + raise ValueError( + f"scheme {scheme!r} is missing {needed.name}. " + f"Available: {', '.join(schemes())}") + + roles = parse_conf(roles_path) + palette = parse_conf(palette_path) + for name, value in palette.items(): + if not re.fullmatch(r"#[0-9a-fA-F]{6}", value): + raise ValueError(f"{palette_path}: {name} = {value!r} is not #rrggbb") + snap_names = roles.get("snap", "").split() + if not snap_names: + raise ValueError(f"{roles_path}: no [accents] snap list") + for name in snap_names: + if name not in palette: + raise ValueError( + f"{roles_path}: snap candidate {name!r} is not in the {scheme} palette") + + roles["scheme"] = scheme + return scheme, palette, resolve(roles, palette, roles_path), snap_names + + +# What gets written where. Templates are read from the repo, everything else is +# generated whole. +TARGETS = [ + ("rofi/udt/palette.rasi", gen_rofi, None), + ("templates/waybar/theme.css", gen_waybar, None), + ("templates/terminal/kitty-theme.conf", gen_kitty, None), + ("templates/dunstrc", gen_dunst, "templates/dunstrc.in"), + ("templates/conky.conf", gen_conky, "templates/conky.conf.in"), + ("bin/udt_colors.py", gen_accent_py, None), +] + + +def generate(roles_path, out_dir): + scheme, palette, res, snap_names = load(roles_path) + written = [] + for target, fn, template in TARGETS: + dest = out_dir / target + if template: + src = REPO / template + if not src.exists(): + raise ValueError(f"missing template {src}") + content = fn(res, scheme, palette, src.read_text()) + elif fn is gen_accent_py: + content = fn(res, scheme, palette, snap_names) + else: + content = fn(res, scheme, palette) + dest.parent.mkdir(parents=True, exist_ok=True) + dest.write_text(content) + written.append(target) + return scheme, written + + +def selftest(): + # Every shipped scheme must satisfy every role, or switching to it breaks + # at install time. load() raises on any role the palette cannot supply. + names = schemes() + assert names, "no palettes found" + + seen = {} + for scheme in names: + _, _, res, snap = load(None, scheme=scheme) + assert res["bg"] != res["fg"], f"{scheme}: bg and fg are the same colour" + assert len(snap) >= 5, f"{scheme}: only {len(snap)} snap candidates" + seen[scheme] = set(res) + print(f" {scheme}: {len(res)} roles, {len(snap)} accents") + + # Every scheme must define the SAME roles: a generator asks for a role by + # name, so one scheme missing it would fail only once that scheme was + # selected, which is exactly the late failure this check exists to prevent. + reference = seen[names[0]] + for scheme, roles in seen.items(): + missing = reference - roles + extra = roles - reference + assert not missing, f"{scheme} is missing roles: {sorted(missing)}" + assert not extra, f"{scheme} has roles no other scheme has: {sorted(extra)}" + + # Alpha survives the round trip into each syntax. + assert hex8((202, 211, 245, 75)) == "#cad3f5bf", hex8((202, 211, 245, 75)) + assert hex8((202, 211, 245, 100)) == "#cad3f5ff" + assert rgba((202, 211, 245, 100)) == "rgb(202,211,245)" + assert rgba((202, 211, 245, 75)) == "rgba(202,211,245,0.75)" + + # A role naming a colour the palette lacks must fail, not emit a hole. + try: + resolve({"scheme": "x", "bad": "nosuchcolour"}, {"base": "#000000"}, "probe") + except ValueError as exc: + assert "nosuchcolour" in str(exc) + else: + raise AssertionError("unknown colour did not raise") + + print("selftest OK") + + +def main(argv): + if "--selftest" in argv: + selftest() + return 0 + + roles_path = PALETTE_DIR / "roles.conf" + out_dir = REPO + if "--roles" in argv: + roles_path = Path(argv[argv.index("--roles") + 1]).expanduser() + if "--out" in argv: + out_dir = Path(argv[argv.index("--out") + 1]).expanduser() + + try: + scheme, written = generate(roles_path, out_dir) + except ValueError as exc: + print(f"udt-palette: {exc}", file=sys.stderr) + return 1 + + print(f"{scheme}: {len(written)} files") + for w in written: + print(f" {w}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) @@ -9,6 +9,12 @@ target="$HOME/.config/rofi/udt" mkdir -p "$target" "$HOME/bin" "$HOME/.cache/wal" +# Generate every themed config from the selected scheme first: the rofi palette +# symlinked below is one of its outputs, so this has to run before anything is +# linked. It fails loudly if a role names a colour the scheme does not define, +# which is the whole point of keeping the colours in one place. +"$repo/bin/udt-palette" + # Theme files are symlinked individually; accent.rasi is NOT, it points at the # generated file in the wal cache instead. for f in palette.rasi common.rasi menu.rasi list.rasi grid.rasi powermenu.rasi confirm.rasi; do @@ -30,6 +36,25 @@ fi ln -sfn "$HOME/.cache/wal/udt-accent.rasi" "$target/accent.rasi" ln -sfn "$repo/bin/udt-accent" "$HOME/bin/udt-accent" +ln -sfn "$repo/bin/udt-palette" "$HOME/bin/udt-palette" + +# The generated configs for everything that is not rofi. These are copied +# rather than symlinked: each app owns its config file and mixes theme keys +# with settings that are none of this repo's business. +scheme="$(sed -n 's/^scheme *= *//p' "$repo/palette/roles.conf")" + +# waybar reads whichever themes/<name>.css style.css imports, so write the file +# under the scheme's own name and repoint the import. The older per-flavour +# files in themes/ are left alone; they are simply no longer imported. +install -Dm644 "$repo/templates/waybar/theme.css" \ + "$HOME/.config/waybar/themes/$scheme.css" +if [ -f "$HOME/.config/waybar/style.css" ]; then + sed -i "s|@import \"themes/.*\.css\";|@import \"themes/$scheme.css\";|" \ + "$HOME/.config/waybar/style.css" +fi + +install -Dm644 "$repo/templates/terminal/kitty-theme.conf" \ + "$HOME/.config/kitty/current-theme.conf" echo "installed:" ls -l "$target" "$HOME/bin/udt-accent" diff --git a/palette/dracula.conf b/palette/dracula.conf new file mode 100644 index 0000000..c1b41fb --- /dev/null +++ b/palette/dracula.conf @@ -0,0 +1,29 @@ +# Dracula. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# Source: https://draculatheme.com/contribute +# +# Dracula ships six accents and three greys, fewer than Catppuccin's ten and +# nine. The two extra surfaces below are not upstream: they are interpolated +# between background and current_line so roles that want a third depth have +# one. Everything else is the published palette. + +background = #282a36 +current_line = #44475a +selection = #44475a +foreground = #f8f8f2 +comment = #6272a4 + +cyan = #8be9fd +green = #50fa7b +orange = #ffb86c +pink = #ff79c6 +purple = #bd93f9 +red = #ff5555 +yellow = #f1fa8c + +# Interpolated, not upstream. See the note above. +surface_low = #21222c +surface_mid = #343746 +surface_high = #565872 diff --git a/palette/macchiato.conf b/palette/macchiato.conf new file mode 100644 index 0000000..8ba796f --- /dev/null +++ b/palette/macchiato.conf @@ -0,0 +1,40 @@ +# Catppuccin Macchiato. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# Source: https://github.com/catppuccin/palette +# +# Colour definitions only, in the scheme's own names. Nothing here says what a +# colour is used for; roles.conf does that. Adding a scheme means adding a file +# like this one, not editing any consumer. + +rosewater = #f4dbd6 +flamingo = #f0c6c6 +pink = #f5bde6 +mauve = #c6a0f6 +red = #ed8796 +maroon = #ee99a0 +peach = #f5a97f +yellow = #eed49f +green = #a6da95 +teal = #8bd5ca +sky = #91d7e3 +sapphire = #7dc4e4 +blue = #8aadf4 +lavender = #b7bdf8 + +text = #cad3f5 +subtext1 = #b8c0e0 +subtext0 = #a5adcb + +overlay2 = #939ab7 +overlay1 = #8087a2 +overlay0 = #6e738d + +surface2 = #5b6078 +surface1 = #494d64 +surface0 = #363a4f + +base = #24273a +mantle = #1e2030 +crust = #181926 diff --git a/palette/nord.conf b/palette/nord.conf new file mode 100644 index 0000000..a3454b7 --- /dev/null +++ b/palette/nord.conf @@ -0,0 +1,41 @@ +# Nord. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# Source: https://www.nordtheme.com/docs/colors-and-palettes +# +# Nord names its colours nord0..nord15 and nothing else, so that is what this +# file uses. The aurora and frost groupings below are Nord's own; the readable +# aliases are a convenience for roles.conf and are the same colours. + +nord0 = #2e3440 +nord1 = #3b4252 +nord2 = #434c5e +nord3 = #4c566a + +nord4 = #d8dee9 +nord5 = #e5e9f0 +nord6 = #eceff4 + +nord7 = #8fbcbb +nord8 = #88c0d0 +nord9 = #81a1c1 +nord10 = #5e81ac + +nord11 = #bf616a +nord12 = #d08770 +nord13 = #ebcb8b +nord14 = #a3be8c +nord15 = #b48ead + +# Aliases, same values. Nord has no sixteenth hue, so the darker frost blue +# stands in where a scheme with more accents would offer one. +frost_teal = #8fbcbb +frost_cyan = #88c0d0 +frost_blue = #81a1c1 +frost_deep = #5e81ac +aurora_red = #bf616a +aurora_orange = #d08770 +aurora_yellow = #ebcb8b +aurora_green = #a3be8c +aurora_purple = #b48ead diff --git a/palette/roles-dracula.conf b/palette/roles-dracula.conf new file mode 100644 index 0000000..40aa2a6 --- /dev/null +++ b/palette/roles-dracula.conf @@ -0,0 +1,101 @@ +# Role assignments for Dracula. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# Dracula ships three greys where Catppuccin has nine, so this leans on the +# interpolated surfaces in palette/dracula.conf. It has no distinct bright +# variants, so bright_* name the same colours as normal. + +[ui] +bg = background +bg_alt = surface_low +bg_deep = surface_low +surface = surface_mid +surface_alt = current_line +border = current_line + +fg = foreground +fg_dim = comment +fg_faint = surface_high + +accent = purple + +[state] +warning = yellow +critical = red +success = green +info = cyan + +[waybar] +main_br = comment +main_bg = surface_low +main_fg = foreground +hover_bg = background +hover_fg = foreground/75 +outline = surface_low*50 + +workspaces = surface_low +temperature = surface_low +memory = background +cpu = surface_mid +time = surface_mid +date = background +tray = surface_low +volume = surface_low +backlight = background +battery = surface_mid +charging = green + +[conky] +heading = purple +label = comment +rule = current_line +value = cyan +highlight = pink +ok = green +body = foreground +body_outline = current_line +body_shade = surface_low + +[terminal] +cursor = foreground +selection_bg = selection +selection_fg = foreground +url = cyan +tab_active_bg = purple +tab_active_fg = background +tab_inactive_bg = surface_low +tab_inactive_fg = foreground +border_active = purple +border_inactive = comment + +black = current_line +red = red +green = green +yellow = yellow +blue = purple +magenta = pink +cyan = cyan +white = foreground + +bright_black = surface_high +bright_red = red +bright_green = green +bright_yellow = orange +bright_blue = purple +bright_magenta = pink +bright_cyan = cyan +bright_white = foreground + +# kitty extras: marks, scrollbar, bell, tab bar ground. +mark1 = purple +mark2 = pink +mark3 = cyan +scrollbar_handle = comment +scrollbar_track = current_line +bell_border = yellow +tab_bar_bg = surface_low + +[accents] +# Dracula ships six accents. +snap = pink purple red orange yellow green cyan diff --git a/palette/roles-macchiato.conf b/palette/roles-macchiato.conf new file mode 100644 index 0000000..0a34f03 --- /dev/null +++ b/palette/roles-macchiato.conf @@ -0,0 +1,115 @@ +# Role assignments for Catppuccin Macchiato. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# Every role in here names a colour from palette/macchiato.conf. See +# palette/roles.conf for what the roles mean and how to select a scheme. + +[ui] +# Surfaces, darkest to lightest. Most consumers only need the first three. +bg = base +bg_alt = mantle +bg_deep = crust +surface = surface0 +surface_alt = surface1 +border = surface1 + +# Text, brightest to dimmest. +fg = text +fg_dim = subtext0 +fg_faint = overlay0 + +# The one colour that tracks the wallpaper. udt-accent overrides this per +# wallpaper for rofi, dunst, Hyprland and Firefox; the value here is the +# fallback, and what the consumers that do not track it sit on permanently. +accent = lavender + +[state] +# Notification urgency, waybar module states, conky warnings. +warning = yellow +critical = red +success = green +info = teal + +[waybar] +# Bar chrome. These were already role names in ~/.config/waybar/theme.css. +main_br = subtext0 +main_bg = crust +main_fg = text +hover_bg = base +hover_fg = text/75 +outline = crust*50 + +# Per-module backgrounds. Alternating depths, not semantic: the bar reads as +# stripes, so neighbouring modules differ. +workspaces = mantle +temperature = mantle +memory = base +cpu = surface0 +time = surface0 +date = base +tray = mantle +volume = mantle +backlight = base +battery = surface0 +charging = green + +[conky] +# conky indexes colours by number. The names say what each number draws. +heading = lavender +label = subtext0 +rule = surface1 +value = teal +highlight = mauve +ok = green +body = text +body_outline = surface1 +body_shade = mantle + +[terminal] +# The 16 ANSI slots, plus kitty's own chrome. normal and bright are separate +# because a scheme may offer distinct pairs; Catppuccin and Dracula do not, so +# these name the same colours twice. +cursor = rosewater +selection_bg = rosewater +selection_fg = base +url = rosewater +tab_active_bg = mauve +tab_active_fg = crust +tab_inactive_bg = mantle +tab_inactive_fg = text +border_active = lavender +border_inactive = overlay0 + +black = surface1 +red = red +green = green +yellow = yellow +blue = blue +magenta = pink +cyan = teal +white = subtext1 + +bright_black = surface2 +bright_red = red +bright_green = green +bright_yellow = yellow +bright_blue = blue +bright_magenta = pink +bright_cyan = teal +bright_white = subtext0 + +# kitty extras: marks, scrollbar, bell, tab bar ground. +mark1 = lavender +mark2 = mauve +mark3 = sapphire +scrollbar_handle = overlay2 +scrollbar_track = surface1 +bell_border = yellow +tab_bar_bg = crust + +[accents] +# The hues udt-accent snaps a wallpaper to. Near-neutral tints (rosewater, +# flamingo) and near-duplicate hues (maroon, sapphire) are left out so the +# snapping has one candidate per region of the wheel. +snap = pink mauve lavender red peach yellow green teal sky blue diff --git a/palette/roles-nord.conf b/palette/roles-nord.conf new file mode 100644 index 0000000..b04e9c8 --- /dev/null +++ b/palette/roles-nord.conf @@ -0,0 +1,101 @@ +# Role assignments for Nord. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# Nord's Polar Night (nord0..nord3) is the only dark group, so bg_deep reuses +# nord0 rather than inventing a darker shade. Nord has no pink or magenta, so +# the aurora purple fills both. + +[ui] +bg = nord0 +bg_alt = nord1 +bg_deep = nord0 +surface = nord1 +surface_alt = nord2 +border = nord3 + +fg = nord4 +fg_dim = nord4 +fg_faint = nord3 + +accent = frost_cyan + +[state] +warning = aurora_yellow +critical = aurora_red +success = aurora_green +info = frost_teal + +[waybar] +main_br = nord4 +main_bg = nord0 +main_fg = nord6 +hover_bg = nord1 +hover_fg = nord6/75 +outline = nord0*50 + +workspaces = nord1 +temperature = nord1 +memory = nord0 +cpu = nord2 +time = nord2 +date = nord0 +tray = nord1 +volume = nord1 +backlight = nord0 +battery = nord2 +charging = aurora_green + +[conky] +heading = frost_cyan +label = nord4 +rule = nord3 +value = frost_teal +highlight = aurora_purple +ok = aurora_green +body = nord6 +body_outline = nord3 +body_shade = nord1 + +[terminal] +cursor = nord4 +selection_bg = nord2 +selection_fg = nord6 +url = frost_cyan +tab_active_bg = aurora_purple +tab_active_fg = nord0 +tab_inactive_bg = nord1 +tab_inactive_fg = nord4 +border_active = frost_cyan +border_inactive = nord3 + +black = nord1 +red = aurora_red +green = aurora_green +yellow = aurora_yellow +blue = frost_blue +magenta = aurora_purple +cyan = frost_teal +white = nord5 + +bright_black = nord3 +bright_red = aurora_red +bright_green = aurora_green +bright_yellow = aurora_orange +bright_blue = frost_cyan +bright_magenta = aurora_purple +bright_cyan = frost_teal +bright_white = nord6 + +# kitty extras: marks, scrollbar, bell, tab bar ground. +mark1 = frost_cyan +mark2 = aurora_purple +mark3 = frost_teal +scrollbar_handle = nord3 +scrollbar_track = nord2 +bell_border = aurora_yellow +tab_bar_bg = nord0 + +[accents] +# Nord has nine usable hues, one fewer than Catppuccin. +snap = aurora_purple frost_blue aurora_red aurora_orange aurora_yellow aurora_green frost_teal frost_cyan frost_deep diff --git a/palette/roles-tokyo-night.conf b/palette/roles-tokyo-night.conf new file mode 100644 index 0000000..fb54926 --- /dev/null +++ b/palette/roles-tokyo-night.conf @@ -0,0 +1,99 @@ +# Role assignments for Tokyo Night. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# Tokyo Night carries fewer surface depths than Catppuccin, so bg_highlight +# does double duty as both surfaces. Its comment colour is the dim foreground. + +[ui] +bg = bg +bg_alt = bg_dark +bg_deep = bg_float +surface = bg_highlight +surface_alt = bg_visual +border = fg_gutter + +fg = fg +fg_dim = fg_dark +fg_faint = comment + +accent = blue + +[state] +warning = yellow +critical = red +success = green +info = cyan + +[waybar] +main_br = fg_dark +main_bg = bg_dark +main_fg = fg +hover_bg = bg +hover_fg = fg/75 +outline = bg_dark*50 + +workspaces = bg_dark +temperature = bg_dark +memory = bg +cpu = bg_highlight +time = bg_highlight +date = bg +tray = bg_dark +volume = bg_dark +backlight = bg +battery = bg_highlight +charging = green + +[conky] +heading = blue +label = fg_dark +rule = fg_gutter +value = cyan +highlight = magenta +ok = green +body = fg +body_outline = fg_gutter +body_shade = bg_dark + +[terminal] +cursor = orange +selection_bg = bg_visual +selection_fg = fg +url = cyan +tab_active_bg = magenta +tab_active_fg = bg_dark +tab_inactive_bg = bg_dark +tab_inactive_fg = fg +border_active = blue +border_inactive = comment + +black = terminal_black +red = red +green = green +yellow = yellow +blue = blue +magenta = magenta +cyan = cyan +white = fg_dark + +bright_black = comment +bright_red = red1 +bright_green = green1 +bright_yellow = orange +bright_blue = blue5 +bright_magenta = purple +bright_cyan = blue1 +bright_white = fg + +# kitty extras: marks, scrollbar, bell, tab bar ground. +mark1 = blue +mark2 = magenta +mark3 = cyan +scrollbar_handle = comment +scrollbar_track = fg_gutter +bell_border = yellow +tab_bar_bg = bg_dark + +[accents] +snap = magenta purple blue red orange yellow green teal cyan blue1 diff --git a/palette/roles.conf b/palette/roles.conf new file mode 100644 index 0000000..3b789b8 --- /dev/null +++ b/palette/roles.conf @@ -0,0 +1,14 @@ +# Scheme selection. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# This is the one line to change to retheme the desktop. It picks a pair of +# files: palette/<scheme>.conf, which holds the colours under the scheme's own +# names, and palette/roles-<scheme>.conf, which says what each colour is for. +# +# Run ./install.sh afterwards. Every themed config is generated from that pair, +# so nothing else needs editing. +# +# Available: macchiato, tokyo-night, nord, dracula + +scheme = macchiato diff --git a/palette/tokyo-night.conf b/palette/tokyo-night.conf new file mode 100644 index 0000000..eb1b0b6 --- /dev/null +++ b/palette/tokyo-night.conf @@ -0,0 +1,43 @@ +# Tokyo Night. +# Copyright (C) 2026 Danilo M. <danix@danix.xyz> +# Licensed under the GNU General Public License v2 only. +# +# Source: https://github.com/folke/tokyonight.nvim (the "night" variant) +# +# Tokyo Night names colours by hue rather than by role, and carries three +# background depths plus a distinct float/sidebar shade. The names below are +# upstream's own. + +bg = #1a1b26 +bg_dark = #16161e +bg_highlight = #292e42 +bg_float = #16161e +bg_visual = #283457 + +fg = #c0caf5 +fg_dark = #a9b1d6 +fg_gutter = #3b4261 + +comment = #565f89 +terminal_black = #414868 + +blue = #7aa2f7 +blue0 = #3d59a5 +blue1 = #2ac3de +blue2 = #0db9d7 +blue5 = #89ddff +blue6 = #b4f9f8 +blue7 = #394b70 + +cyan = #7dcfff +magenta = #bb9af7 +magenta2 = #ff007c +purple = #9d7cd8 +orange = #ff9e64 +yellow = #e0af68 +green = #9ece6a +green1 = #73daca +green2 = #41a6b5 +teal = #1abc9c +red = #f7768e +red1 = #db4b4b diff --git a/rofi/udt/palette.rasi b/rofi/udt/palette.rasi deleted file mode 100644 index 4a95fea..0000000 --- a/rofi/udt/palette.rasi +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Catppuccin Macchiato palette for rofi. - * Copyright (C) 2026 Danilo M. <danix@danix.xyz> - * Licensed under the GNU General Public License v2 only. - * - * Source: https://github.com/catppuccin/palette - * Structural colors only. The accent lives in accent.rasi and is generated. - */ - -* { - base: #24273aff; - mantle: #1e2030ff; - crust: #181926ff; - - text: #cad3f5ff; - subtext1: #b8c0e0ff; - subtext0: #a5adcbff; - - overlay2: #939ab7ff; - overlay1: #8087a2ff; - overlay0: #6e738dff; - - surface2: #5b6078ff; - surface1: #494d64ff; - surface0: #363a4fff; - - rosewater: #f4dbd6ff; - flamingo: #f0c6c6ff; - pink: #f5bde6ff; - mauve: #c6a0f6ff; - red: #ed8796ff; - maroon: #ee99a0ff; - peach: #f5a97fff; - yellow: #eed49fff; - green: #a6da95ff; - teal: #8bd5caff; - sky: #91d7e3ff; - sapphire: #7dc4e4ff; - blue: #8aadf4ff; - lavender: #b7bdf8ff; -} diff --git a/templates/conky.conf.in b/templates/conky.conf.in new file mode 100644 index 0000000..92ad989 --- /dev/null +++ b/templates/conky.conf.in @@ -0,0 +1,102 @@ +conky.config={ + out_to_x = false, + out_to_wayland = true, + alignment='tr', + mpd_host = '172.16.34.5', + mpd_port = 6600, + background=true, + color1='@HEADING@', + color2='@LABEL@', + color3='@RULE@', + color4='@VALUE@', + color5='@HIGHLIGHT@', + color6='@OK@', + pad_percents=0, + cpu_avg_samples=4, + default_color='@BODY@', + default_outline_color='@BODY_OUTLINE@', + default_shade_color='@BODY_SHADE@', + default_bar_height=10, + default_bar_width=500, + double_buffer=true, + draw_borders=false, + draw_graph_borders=false, + draw_outline=false, + draw_shades=false, + font='Inconsolata Nerd Font:style=Regular:size=10', + use_spacer=none, + format_human_readable=true, + short_units=yes, + gap_x=1, + gap_y=0, + minimum_height=1030, + minimum_width=600, + net_avg_samples=2, + no_buffers=true, + override_utf8_locale=true, + own_window=true, + own_window_type='desktop', + own_window_argb_value=225, + own_window_class='Conky', + text_buffer_size=2048, + total_run_times=0, + update_interval=3, + uppercase=false, + use_xft=true, + xftalpha=0, +} + +conky.text=[[ +${color1} ${goto 20}Queen Anne's Revenge ${goto 250}${color5} ${cpugovernor cpu0}${alignr}${color}${cpu cpu0}% +${alignr}${color4}TCTL: ${color}${execi 5 cat $(grep -l '^k10temp$' /sys/class/hwmon/hwmon*/name | head -1 | xargs dirname)/temp1_input | awk '{printf "%.0f", $1/1000}'}°c ${color3}| ${color4}TCCD1: ${color}${execi 5 sensors k10temp-pci-00c3 | grep 'Tccd1' | awk '{print $2}' | tr -d '+°C'}°c +${alignr}${color4}NVME: ${color}${execi 5 cat $(grep -l '^nvme$' /sys/class/hwmon/hwmon*/name | head -1 | xargs dirname)/temp1_input | awk '{printf "%.0f", $1/1000}'}°c +${goto 50}${cpugraph cpu0 74,500 5e7b7b d8deeb -t} +${goto 30}${color}\#1 ${cpubar cpu1 5,100} ${goto 165}\#5 ${cpubar cpu5 5,100} ${goto 300}\#9 ${cpubar cpu9 5,100} ${goto 440}\#13 ${cpubar cpu13 5,100} +${goto 30}\#2 ${cpubar cpu2 5,100} ${goto 165}\#6 ${cpubar cpu6 5,100} ${goto 300}\#10 ${cpubar cpu10 5,100} ${goto 440}\#14 ${cpubar cpu14 5,100} +${goto 30}\#3 ${cpubar cpu3 5,100} ${goto 165}\#7 ${cpubar cpu7 5,100} ${goto 300}\#11 ${cpubar cpu11 5,100} ${goto 440}\#15 ${cpubar cpu15 5,100} +${goto 30}\#4 ${cpubar cpu4 5,100} ${goto 165}\#8 ${cpubar cpu8 5,100} ${goto 300}\#12 ${cpubar cpu12 5,100} ${goto 440}\#16 ${cpubar cpu16 5,100} +${color3}${hr} + +${color1} ${goto 20}GPU ${color4}Arc B580${color}${alignr}${color4}pkg: ${color1}${execi 5 cat $(grep -l xe /sys/class/hwmon/hwmon*/name | head -1 | xargs dirname)/temp2_input | awk '{printf "%.0f", $1/1000}'}°c ${color4}vram: ${color}${execi 5 cat $(grep -l xe /sys/class/hwmon/hwmon*/name | head -1 | xargs dirname)/temp3_input | awk '{printf "%.0f", $1/1000}'}°c +${alignr}${color4}fan: ${color}${execi 5 cat $(grep -l xe /sys/class/hwmon/hwmon*/name | head -1 | xargs dirname)/fan1_input} RPM +${color1} ${goto 20}MoBo ${color4}GigaByte X870 Eagle WiFi 7${color}${alignr}${color}${execi 5 cat $(grep -l '^gigabyte_wmi$' /sys/class/hwmon/hwmon*/name | head -1 | xargs dirname)/temp2_input | awk '{printf "%.0f", $1/1000}'}°c / ${execi 5 cat $(grep -l '^gigabyte_wmi$' /sys/class/hwmon/hwmon*/name | head -1 | xargs dirname)/temp3_input | awk '{printf "%.0f", $1/1000}'}°c +${color3}${hr} + +${color1} ${goto 20}Disk +${color4}${goto 50}FREE% | FREE / TOTAL ${color} +${goto 18}${color5} ${color}${fs_free_perc /}% free ${color4}|${color} ${fs_free /} ${color4}/${color} ${fs_size /}${alignr}${fs_bar 10,250 /} +${goto 18}${color6} ${color}${fs_free_perc /home}% free ${color4}|${color} ${fs_free /home} ${color4}/${color} ${fs_size /home}${alignr}${fs_bar 10,250 /home} +${if_mounted /data}${goto 18}${color1} ${color}${fs_free_perc /data}% free ${color4}|${color} ${fs_free /data} ${color4}/${color} ${fs_size /data}${alignr}${fs_bar 10,250 /data}${endif} +${if_mounted /mnt/nfs/Slackware}${goto 18}${color1} ${color}${fs_free_perc /mnt/nfs/Slackware}% free ${color4}|${color} ${fs_free /mnt/nfs/Slackware} ${color4}/${color} ${fs_size /mnt/nfs/Slackware}${alignr}${fs_bar 10,250 /mnt/nfs/Slackware}${endif} +${diskiograph_read 50,245}${alignr}${diskiograph_write 50,245} +${color1}${goto 120}${diskio_read}/s${goto 360}${diskio_write}/s +${color4}${goto 119}read ${goto 360} write${color} +${color1} ${goto 20}Cache ${color5}${execpi 900 du -sh $HOME/.cache 2>/dev/null | cut -f1}${color} +${color4}${goto 60}Folder Name${alignr 60}Size${color} +${execpi 900 du -sh $HOME/.cache/* 2>/dev/null | sort -hr | head -4 | awk '{n=substr($2, index($2, ".cache/")+7); if (length(n)>26) n=substr(n,1,25) "…"; printf "${color6}${goto 60}%s${color1}${alignr 60}%s\n", n, $1}'} +${color3}${hr} + +${color1}${font Inconsolata Nerd Font:style=Regular:pixelsize=13} ${goto 20}Network + ${goto 20}${addr br0}${alignr} ${color6}${execi 300 curl ipinfo.io/ip}${color} +${downspeedgraph br0 50,245}${alignr}${upspeedgraph br0 50,245} +${if_match "${addr br0}"!="No Address"}${goto 20}${color1} ${downspeed br0}k/s${goto 350} ${upspeed br0}k/s${endif} +${color4}${goto 20} download ${goto 360} upload${color} +${color3}${hr} + +${color4} ${color}${goto 20}${exec cat /etc/slackware-version}${alignr} ${uptime} + ${goto 20}${kernel}${alignr} ${exec ls /var/log/packages|wc -l} + ${memperc}% / ${memmax}${alignr}${membar 10,250}${color} +${color3}${hr} + +${color4}${goto 60}Process${alignr 60}PID %CPU %MEM${color} +${color6}${goto 60}${top name 1}${color1}${alignr 60}${top pid 1} ${top cpu 1} ${top mem 1} +${color6}${goto 60}${top name 2}${color1}${alignr 60}${top pid 2} ${top cpu 2} ${top mem 2} +${color6}${goto 60}${top name 3}${color1}${alignr 60}${top pid 3} ${top cpu 3} ${top mem 3} +${color6}${goto 60}${top name 4}${color1}${alignr 60}${top pid 4} ${top cpu 4} ${top mem 4} +${color6}${goto 60}${top name 5}${color1}${alignr 60}${top pid 5} ${top cpu 5} ${top mem 5} +${color3}${hr} + +${color1} ${goto 20}${execi 2 playerctl status 2>/dev/null} · ${color4}${execi 2 playerctl metadata --format "{{artist}}"}${color} +${goto 20}${execi 2 playerctl metadata --format "{{title}} ({{duration(position)}} / {{duration(mpris:length)}})" 2>/dev/null} +${goto 50}${execibar 2 5,500 bash -c "pos=\$(playerctl position 2>/dev/null); len=\$(playerctl metadata mpris:length 2>/dev/null); echo \"scale=6; (\$pos / (\$len / 1000000)) * 100\" | bc | awk '{print int(\$1)}'"} +]] diff --git a/templates/dunstrc.in b/templates/dunstrc.in new file mode 100644 index 0000000..f3a096e --- /dev/null +++ b/templates/dunstrc.in @@ -0,0 +1,75 @@ +# See dunst(5) for all configuration options +[global] + monitor = 1 + follow = none + width = 300 + height = (0, 300) + origin = top-right + offset = (10, 50) + scale = 0 + notification_limit = 0 + progress_bar = true + progress_bar_height = 10 + progress_bar_frame_width = 1 + progress_bar_min_width = 150 + progress_bar_max_width = 300 + indicate_hidden = yes + transparency = 15 + separator_height = 2 + padding = 8 + horizontal_padding = 8 + text_icon_padding = 0 + frame_width = 3 + frame_color = "@ACCENT@" + gap_size = 0 + separator_color = frame + sort = yes + font = Noto Sans 11 + line_height = 0 + markup = full + format = "<b>%a</b>\n<i>%s</i>%p %n\n%b" + alignment = left + vertical_alignment = center + show_age_threshold = 60 + ellipsize = middle + ignore_newline = no + stack_duplicates = true + hide_duplicate_count = false + show_indicators = yes + enable_recursive_icon_lookup = true + icon_theme = MB-Blueberry-Suru-GLOW + icon_position = left + icon_corner_radius = 5 + min_icon_size = 32 + max_icon_size = 128 + icon_path = ~/.local/share/icons/MB-Blueberry-Suru-GLOW/status/48/:/usr/share/icons/MB-Blueberry-Suru-GLOW/devices/48/ + sticky_history = yes + history_length = 20 + dmenu = /usr/bin/rofi -theme ~/.config/rofi/udt/list.rasi -dmenu -p dunst + browser = /usr/bin/xdg-open + always_run_script = true + title = Dunst + class = Dunst + corner_radius = 10 + ignore_dbusclose = false + mouse_left_click = context, close_current + mouse_middle_click = do_action, close_current + mouse_right_click = close_all +# to have a consistent behaviour across releases. +[experimental] + per_monitor_dpi = false +[urgency_low] + background = "@BG_ALT@" + foreground = "@FG_DIM@" + frame_color = "@BORDER@" + timeout = 10 +[urgency_normal] + background = "@BG@" + foreground = "@FG@" + frame_color = "@ACCENT@" + timeout = 10 +[urgency_critical] + background = "@BG@" + foreground = "@FG@" + frame_color = "@CRITICAL@" + timeout = 0 diff --git a/templates/terminal/kitty-macchiato.conf b/templates/terminal/kitty-macchiato.conf deleted file mode 100644 index 92fa1c0..0000000 --- a/templates/terminal/kitty-macchiato.conf +++ /dev/null @@ -1,84 +0,0 @@ -# vim:ft=kitty - -## name: Catppuccin Kitty Macchiato -## author: Catppuccin Org -## license: MIT -## upstream: https://github.com/catppuccin/kitty/blob/main/themes/macchiato.conf -## blurb: Soothing pastel theme for the high-spirited! - - - -# The basic colors -foreground #cad3f5 -background #24273a -selection_foreground #24273a -selection_background #f4dbd6 - -# Cursor colors -cursor #f4dbd6 -cursor_text_color #24273a - -# Scrollbar colors -scrollbar_handle_color #939ab7 -scrollbar_track_color #494d64 - -# URL color when hovering with mouse -url_color #f4dbd6 - -# Kitty window border colors -active_border_color #b7bdf8 -inactive_border_color #6e738d -bell_border_color #eed49f - -# OS Window titlebar colors -wayland_titlebar_color system -macos_titlebar_color system - -# Tab bar colors -active_tab_foreground #181926 -active_tab_background #c6a0f6 -inactive_tab_foreground #cad3f5 -inactive_tab_background #1e2030 -tab_bar_background #181926 - -# Colors for marks (marked text in the terminal) -mark1_foreground #24273a -mark1_background #b7bdf8 -mark2_foreground #24273a -mark2_background #c6a0f6 -mark3_foreground #24273a -mark3_background #7dc4e4 - -# The 16 terminal colors - -# black -color0 #494d64 -color8 #5b6078 - -# red -color1 #ed8796 -color9 #ed8796 - -# green -color2 #a6da95 -color10 #a6da95 - -# yellow -color3 #eed49f -color11 #eed49f - -# blue -color4 #8aadf4 -color12 #8aadf4 - -# magenta -color5 #f5bde6 -color13 #f5bde6 - -# cyan -color6 #8bd5ca -color14 #8bd5ca - -# white -color7 #b8c0e0 -color15 #a5adcb diff --git a/templates/waybar/catppuccin-macchiato.css b/templates/waybar/catppuccin-macchiato.css deleted file mode 100644 index d98b1cc..0000000 --- a/templates/waybar/catppuccin-macchiato.css +++ /dev/null @@ -1,63 +0,0 @@ -/* catppuccin macchiato */ - -@define-color rosewater #f4dbd6; -@define-color flamingo #f0c6c6; -@define-color pink #f5bde6; -@define-color mauve #c6a0f6; -@define-color red #ed8796; -@define-color maroon #ee99a0; -@define-color peach #f5a97f; -@define-color yellow #eed49f; -@define-color green #a6da95; -@define-color teal #8bd5ca; -@define-color sky #91d7e3; -@define-color sapphire #7dc4e4; -@define-color blue #8aadf4; -@define-color lavender #b7bdf8; -@define-color text #cad3f5; -@define-color subtext1 #b8c0e0; -@define-color subtext0 #a5adcb; -@define-color overlay2 #939ab7; -@define-color overlay1 #8087a2; -@define-color overlay0 #6e738d; -@define-color surface2 #5b6078; -@define-color surface1 #494d64; -@define-color surface0 #363a4f; -@define-color base #24273a; -@define-color mantle #1e2030; -@define-color crust #181926; - -/* - br - border - bg - background - fg - foreground -*/ - -/* main colors */ - -@define-color accent @lavender; -@define-color main-br @subtext0; -@define-color main-bg @crust; -@define-color main-fg @text; -@define-color hover-bg @base; -@define-color hover-fg alpha(@main-fg, 0.75); -@define-color outline shade(@main-bg, 0.5); - -/* module colors */ - -@define-color workspaces @mantle; -@define-color temperature @mantle; -@define-color memory @base; -@define-color cpu @surface0; -@define-color time @surface0; -@define-color date @base; -@define-color tray @mantle; -@define-color volume @mantle; -@define-color backlight @base; -@define-color battery @surface0; - -/* state colors */ - -@define-color warning @yellow; -@define-color critical @red; -@define-color charging @green; |
