From 30c96c346aa7d82f3cbd4d8d277fb1de37c2aec5 Mon Sep 17 00:00:00 2001 From: chest Date: Sun, 1 Nov 2026 22:39:54 +0100 Subject: [PATCH] Theme Eclipse desktop with Miku Frost palette and glass surfaces --- host/Eclipse/modules/hyprland.nix | 17 ++++ modules/user/walker.nix | 36 ++++++- user/chest/modules/style.nix | 120 ++++++++++++++++++++++- user/chest/modules/waybar.nix | 152 +++++++++++++++++++++++++----- 4 files changed, 296 insertions(+), 29 deletions(-) diff --git a/host/Eclipse/modules/hyprland.nix b/host/Eclipse/modules/hyprland.nix index 253b952..f771b98 100644 --- a/host/Eclipse/modules/hyprland.nix +++ b/host/Eclipse/modules/hyprland.nix @@ -8,6 +8,23 @@ let }; in { wayland.windowManager.hyprland.settings = { + # Keep the animated wallpaper visible through the panel's translucent surface. + layer_rule = [ + { + match.namespace = "^waybar$"; + blur = true; + # Blur the glass, but exclude its low-alpha CSS shadow from the mask. + ignore_alpha = 0.2; + xray = true; + } + { + match.namespace = "^walker$"; + blur = true; + ignore_alpha = 0.08; + xray = true; + } + ]; + monitor = [ { output = "DP-3"; diff --git a/modules/user/walker.nix b/modules/user/walker.nix index bbead00..efa802c 100644 --- a/modules/user/walker.nix +++ b/modules/user/walker.nix @@ -1,4 +1,4 @@ -{ config, lib, ... }: let +{ config, lib, osConfig, ... }: let hyprLua = import ./hyprland/lua.nix { inherit lib; }; colors = config.lib.stylix.colors.withHashtag; accent = colors.${config.stylix.accent}; @@ -141,6 +141,40 @@ in { .preview-content.aptpackages { font-family: "${config.stylix.fonts.monospace.name}"; } + '' + lib.optionalString (config.home.username == "chest" && osConfig.networking.hostName == "Eclipse") '' + /* Keep the existing layout; let the compositor blur the wallpaper. */ + window { background: transparent; } + .box-wrapper { + background: alpha(@surface_color, 0.62); + border: 2px solid alpha(@accent_bg_color, 0.38); + padding: 19px; + box-shadow: none; + } + .input { + background: alpha(${colors.base02}, 0.42); + border-radius: 10px; + } + popover { + background: alpha(@surface_color, 0.86); + border: 2px solid alpha(@accent_bg_color, 0.38); + padding: 9px; + } + + /* Paint only the rounded item, never both its row and child. */ + child, row, + child:hover, row:hover, + child:selected, row:selected { + background: transparent; + border-radius: 10px; + } + child:hover .item-box, + row:hover .item-box { + background: alpha(@accent_bg_color, 0.12); + } + child:selected .item-box, + row:selected .item-box { + background: alpha(@accent_bg_color, 0.25); + } ''; }; }; diff --git a/user/chest/modules/style.nix b/user/chest/modules/style.nix index afc7b8a..13446a7 100644 --- a/user/chest/modules/style.nix +++ b/user/chest/modules/style.nix @@ -1,4 +1,29 @@ -{ pkgs, config, lib, ... }: let +{ pkgs, config, lib, osConfig, inputs, ... }: let + mikuFrost = osConfig.networking.hostName == "Eclipse"; + colors = config.lib.stylix.colors.withHashtag; + accent = colors.${config.stylix.accent}; + # Render the same Stylix templates, changing only native translucency and accent. + kvantumConfig = config.lib.stylix.colors { + template = builtins.readFile "${inputs.stylix}/modules/qt/kvconfig.mustache"; + extension = ".kvconfig"; + }; + kvantumSvg = config.lib.stylix.colors { + template = builtins.readFile "${inputs.stylix}/modules/qt/kvantum.svg.mustache"; + extension = ".svg"; + }; + frostKvantum = pkgs.runCommandLocal "miku-frost-kvantum" {} '' + directory="$out/share/Kvantum/Base16Kvantum" + mkdir -p "$directory" + cp ${kvantumConfig} "$directory/Base16Kvantum.kvconfig" + cp ${kvantumSvg} "$directory/Base16Kvantum.svg" + substituteInPlace "$directory/Base16Kvantum.kvconfig" \ + --replace-fail 'translucent_windows=false' 'translucent_windows=true' \ + --replace-fail 'blurring=false' 'blurring=true' \ + --replace-fail 'reduce_window_opacity=10' 'reduce_window_opacity=18' \ + --replace-fail 'highlight.color=${colors.base0E}' 'highlight.color=${accent}' + substituteInPlace "$directory/Base16Kvantum.svg" \ + --replace-fail '${colors.base0D}' '${accent}' + ''; miku-cursor = pkgs.stdenv.mkDerivation { name = "Miku cursor"; @@ -86,10 +111,46 @@ in { hyprland.enable = false; waybar.enable = false; kitty.variant256Colors = true; + gtk.extraCss = lib.mkIf mikuFrost '' + /* Native background alpha keeps text and controls fully opaque. */ + @define-color accent_color ${accent}; + @define-color accent_bg_color ${accent}; + @define-color theme_selected_bg_color ${accent}; + @define-color window_bg_color alpha(${colors.base01}, 0.78); + @define-color view_bg_color alpha(${colors.base00}, 0.48); + @define-color headerbar_bg_color alpha(${colors.base01}, 0.76); + @define-color headerbar_backdrop_color @window_bg_color; + @define-color sidebar_bg_color alpha(${colors.base01}, 0.72); + @define-color sidebar_backdrop_color @window_bg_color; + @define-color dialog_bg_color alpha(${colors.base01}, 0.84); + @define-color popover_bg_color alpha(${colors.base01}, 0.90); + @define-color theme_bg_color @window_bg_color; + @define-color theme_base_color @view_bg_color; + ''; }; - base16Scheme = "${pkgs.base16-schemes}/share/themes/eris.yaml"; - accent = "base0E"; + # Keep a file-backed scheme for consumers such as Neofetch. + base16Scheme = if mikuFrost then toString (pkgs.writeText "miku-frost.yaml" '' + scheme: "Miku Frost" + author: "chest" + base00: "101724" + base01: "192534" + base02: "253247" + base03: "47586B" + base04: "93A8BA" + base05: "E1EFF2" + base06: "F1F8F8" + base07: "F9FFFF" + base08: "EF829A" + base09: "F5AB82" + base0A: "EED69A" + base0B: "69DCC7" + base0C: "43D6CD" + base0D: "7BB9E8" + base0E: "B29DF4" + base0F: "E3A8D8" + '') else "${pkgs.base16-schemes}/share/themes/eris.yaml"; + accent = if mikuFrost then "base0C" else "base0E"; polarity = "dark"; image = pkgs.fetchurl { @@ -132,6 +193,59 @@ in { }; }; + qt.kvantum.themes = lib.mkIf mikuFrost (lib.mkForce [ frostKvantum ]); + + # Notifications share the island's surface and rounded, borderless silhouette. + services.mako.settings = lib.mkIf mikuFrost { + background-color = lib.mkForce "${colors.base01}D9"; + text-color = lib.mkForce colors.base05; + border-size = lib.mkForce 0; + border-radius = 22; + padding = "12"; + progress-color = lib.mkForce "over ${accent}33"; + "urgency=low".background-color = lib.mkForce "${colors.base01}D9"; + "urgency=critical" = { + background-color = lib.mkForce "${colors.base01}F0"; + text-color = lib.mkForce colors.base08; + }; + }; + + # Yazi uses its own true-colour theme rather than the terminal ANSI palette. + programs.yazi.theme = lib.mkIf mikuFrost { + filetype.rules = lib.mkBefore [ + { url = "*/"; fg = colors.base05; bold = false; } + ]; + icon.conds = lib.mkBefore [ + { "if" = "dir"; text = ""; fg = accent; } + ]; + mgr.border_style.fg = lib.mkForce colors.base03; + indicator = { + current = { fg = colors.base05; bg = lib.mkForce colors.base02; }; + preview = { fg = colors.base05; bg = lib.mkForce colors.base02; }; + }; + tabs = { + active = { fg = lib.mkForce colors.base00; bg = lib.mkForce accent; }; + inactive = { fg = lib.mkForce colors.base04; bg = lib.mkForce colors.base01; }; + }; + mode = { + normal_main = { fg = lib.mkForce colors.base00; bg = lib.mkForce accent; }; + normal_alt = { fg = lib.mkForce accent; bg = lib.mkForce colors.base01; }; + }; + input = { + border.fg = lib.mkForce accent; + selected.bg = lib.mkForce colors.base02; + }; + pick.border.fg = lib.mkForce accent; + completion.border.fg = lib.mkForce accent; + tasks.border.fg = lib.mkForce accent; + }; + + # Keep the original terminal glass, with colours inherited from Stylix. + programs.ghostty.themes.stylix = lib.mkIf mikuFrost { + background = lib.mkForce config.lib.stylix.colors.base00; + cursor-color = lib.mkForce config.lib.stylix.colors.base0C; + }; + dconf.settings = { "org/gnome/desktop/interface".color-scheme = "prefer-dark"; }; diff --git a/user/chest/modules/waybar.nix b/user/chest/modules/waybar.nix index ad5e99c..4f2ec3c 100644 --- a/user/chest/modules/waybar.nix +++ b/user/chest/modules/waybar.nix @@ -5,7 +5,9 @@ pkgs, lib, ... - }: { + }: let + mikuFrost = osConfig.networking.hostName == "Eclipse"; +in { home.packages = with pkgs; [ font-awesome nerd-fonts.symbols-only @@ -59,11 +61,20 @@ "pulseaudio" "custom/sep-left" "battery" + ] ++ lib.optional mikuFrost "custom/sep-battery" ++ [ "clock" ]; }; + "hyprland/workspaces" = lib.optionalAttrs mikuFrost { + tooltip = false; + }; + + tray = lib.optionalAttrs mikuFrost { + tooltip = false; + }; + cava = { bars = 9; bar_delimiter = 0; @@ -79,6 +90,7 @@ }; pulseaudio = { + tooltip = !mikuFrost; format = "{icon} {volume}%"; format-icons = { headphone = ""; @@ -94,18 +106,20 @@ }; mpris = { + tooltip = !mikuFrost; format = "{dynamic}"; dynamic-len = if osConfig.host.laptop then 16 else 32; dynamic-order = [ "title" "artist" "album" ]; }; battery = { + tooltip = !mikuFrost; interval = 5; states = { warning = 30; critical = 15; }; - format = "{icon} {capacity}% | "; + format = if mikuFrost then "{icon} {capacity}%" else "{icon} {capacity}% | "; format-icons = { default = ["󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹"]; charging = ["󰢟" "󰢜" "󰂆" "󰂇" "󰂈" "󰢝" "󰂉" "󰢞" "󰂊" "󰂋" "󰂅"]; @@ -117,7 +131,17 @@ tooltip = false; }; + # Follow battery visibility; the divider has no hover or click target. + "custom/sep-battery" = { + format = " {} "; + exec = "printf '|'"; + tooltip = false; + exec-if = "for battery in /sys/class/power_supply/*; do test \"$(cat \"$battery/type\" 2>/dev/null)\" = Battery && exit 0; done; exit 1"; + interval = 5; + }; + "keyboard-state" = { + tooltip = !mikuFrost; capslock = true; format = "{icon}"; format-icons = { @@ -127,6 +151,7 @@ }; "hyprland/language" = { + tooltip = !mikuFrost; format-en = "en"; format-ru = "ru"; }; @@ -146,6 +171,7 @@ }; network = { + tooltip = !mikuFrost; format = "{ifname}"; format-wifi = " {essid} ({signalStrength}%)"; format-ethernet = "{ifname}"; @@ -171,6 +197,7 @@ }; cpu = { + tooltip = !mikuFrost; interval = 2; format = "{usage}% "; min-length = 6; @@ -188,47 +215,97 @@ style = let colors = config.lib.stylix.colors.withHashtag; accent = colors.${config.stylix.accent}; + themed = frost: original: if mikuFrost then frost else original; scssFile = pkgs.writeText "waybar.scss" /*scss*/ '' window#waybar { background: transparent; color: ${colors.base05}; font-weight: bold; font-size: .85em; - & > * { padding: 0 0 6px 0; } + & > * { padding: ${themed "0 12px 14px" "0 0 6px 0"}; } } + ${lib.optionalString mikuFrost /*scss*/ '' + // Only the centered island paints a surface or a shadow. + window#waybar, + window#waybar > box { + background: transparent; + background-image: none; + border: none; + box-shadow: none; + } + ''} + .modules-center { - background: ${colors.base01}CC; - border-radius: 12px; + background: ${themed "rgba(${colors.base01}, 0.58)" "${colors.base01}CC"}; + border: none; + border-radius: ${themed "22px" "12px"}; padding: 10px; - box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.4); + box-shadow: ${themed "0 3px 8px rgba(0, 0, 0, 0.18)" "5px 5px 10px rgba(0, 0, 0, 0.4)"}; } #left, #right, #center { - background: ${colors.base02}; + background: ${themed "rgba(40, 59, 79, 0.68)" colors.base02}; padding: 0 2px; margin: 0 8px; - border-radius: 8px; + border-radius: ${themed "12px" "8px"}; } #left { margin-left: 0; } #right { margin-right: 0; } - #clock { padding-right: 10px; } + ${lib.optionalString mikuFrost /*scss*/ '' + // Keep hover surfaces inside the rounded groups. + #language, #keyboard-state, #keyboard-state label, + #cava, #mpris, #tray widget, #tray widget > image, + #network, #custom-mem, #cpu, #temperature, + #pulseaudio, #battery, #clock { + border-radius: 10px; + } - #cava { padding: 0 10px; } + #network, #custom-mem, #cpu, #temperature, + #pulseaudio, #battery, #clock { + padding-left: 6px; + padding-right: 6px; + } + + // Reserve the same width in active, inactive and hover states. + #workspaces button { + box-shadow: none; + text-shadow: none; + background-image: none; + } + + tooltip { + color: ${colors.base05}; + background: rgba(25, 37, 52, 0.94); + border: 2px solid rgba(67, 214, 205, 0.38); + border-radius: ${themed "12px" "8px"}; + } + tooltip label { padding: 6px; } + ''} + + #clock { padding-right: ${themed "6px" "10px"}; } + + #cava { padding: 0 10px; color: ${themed accent colors.base05}; } + + #custom-sep-left, + #custom-sep-battery { + color: ${themed "rgba(147, 168, 186, 0.38)" colors.base05}; + background: transparent; + } #language { padding: 8px; } #workspaces button { - color: ${colors.base05}; + color: ${themed colors.base04 colors.base05}; padding: 0 2px; border-radius: 999px; border: 1pt solid transparent; - min-width: 20px; + min-width: ${themed "25px" "20px"}; - &:hover { background: ${colors.base03}; } + &:hover { background: ${themed "rgba(67, 214, 205, 0.12)" colors.base03}; } &.active { background: ${accent}; @@ -236,27 +313,50 @@ min-width: 25px; &:hover { - border-color: ${accent}; - background: ${colors.base03}; + border-color: ${themed "transparent" accent}; + background: ${themed "rgba(67, 214, 205, 0.12)" colors.base03}; color: ${accent}; } } } #mpris { - border-radius: 8px; - padding: 4px; - &:hover { background: ${colors.base03}; } + border-radius: ${themed "10px" "8px"}; + // Do not leave a styled placeholder when the player has no text. + padding: ${themed "0" "4px"}; + border: none; + &:hover { background: ${themed "rgba(67, 214, 205, 0.12)" colors.base03}; } &.playing { - border: 2px solid ${accent}; - padding: 2px; + border: ${themed "none" "2px solid ${accent}"}; + padding: ${themed "0" "2px"}; + color: ${themed colors.base0E colors.base05}; } } + ${if mikuFrost then /*scss*/ '' + // Each SNI item is one event box containing one image. Paint it once. + #tray > widget { + border: none; + border-radius: 10px; + padding: 0; + background: transparent; + } + #tray > widget:hover { + background: rgba(67, 214, 205, 0.12); + } + #tray > widget > image { + padding: 8px; + margin: 0; + background: transparent; + } + #tray > widget.needs-attention { + background: rgba(245, 171, 130, 0.16); + } + '' else /*scss*/ '' #tray { widget { border: 1pt solid transparent; - &:hover { background: ${colors.base03}; } + &:hover { background: ${themed "rgba(67, 214, 205, 0.12)" colors.base03}; } & > image { padding: 8px; } } @@ -264,21 +364,23 @@ & > .needs-attention { border-color: ${colors.base09}; } } + ''} + #pulseaudio { - &:hover { background: ${colors.base03}; } + &:hover { background: ${themed "rgba(67, 214, 205, 0.12)" colors.base03}; } &.muted { background: ${colors.base08}; color: ${colors.base01}; &:hover { color: ${colors.base08}; - background: ${colors.base03}; + background: ${themed "rgba(67, 214, 205, 0.12)" colors.base03}; } } } #network { - &:hover { background: ${colors.base02}; } + &:hover { background: ${themed "rgba(67, 214, 205, 0.12)" colors.base02}; } &.disconnected { color: ${colors.base00}; background: ${colors.base08}; @@ -286,7 +388,7 @@ } #system .drawer-child > * { - margin-right: 4px + margin-right: 4px; } #keyboard-state label.locked {