This commit is contained in:
EdenQwQ 2025-03-01 22:35:08 +08:00
commit 96895ec3aa
100 changed files with 6349 additions and 0 deletions

View file

@ -0,0 +1,231 @@
{
programs.niri.settings.animations = {
window-open = {
spring = {
damping-ratio = 0.7;
stiffness = 300;
epsilon = 0.001;
};
};
window-close = {
easing = {
curve = "ease-out-quad";
duration-ms = 500;
};
};
workspace-switch = {
spring = {
damping-ratio = 0.8;
stiffness = 600;
epsilon = 0.001;
};
};
horizontal-view-movement = {
spring = {
damping-ratio = 1.0;
stiffness = 600;
epsilon = 0.001;
};
};
window-movement = {
spring = {
damping-ratio = 1.0;
stiffness = 600;
epsilon = 0.001;
};
};
shaders = {
window-open =
# glsl
''
// Example: fill the current geometry with a solid vertical gradient and
// gradually make opaque.
vec4 solid_gradient(vec3 coords_geo, vec3 size_geo) {
vec4 color = vec4(0.0);
// Paint only the area inside the current geometry.
if (0.0 <= coords_geo.x && coords_geo.x <= 1.0
&& 0.0 <= coords_geo.y && coords_geo.y <= 1.0)
{
vec4 from = vec4(1.0, 0.0, 0.0, 1.0);
vec4 to = vec4(0.0, 1.0, 0.0, 1.0);
color = mix(from, to, coords_geo.y);
}
// Make it opaque.
color *= niri_clamped_progress;
return color;
}
// Example: gradually scale up and make opaque, equivalent to the default
// opening animation.
vec4 default_open(vec3 coords_geo, vec3 size_geo) {
// Scale up the window.
float scale = max(0.0, (niri_progress / 2.0 + 0.5));
coords_geo = vec3((coords_geo.xy - vec2(0.5)) / scale + vec2(0.5), 1.0);
// Get color from the window texture.
vec3 coords_tex = niri_geo_to_tex * coords_geo;
vec4 color = texture2D(niri_tex, coords_tex.st);
// Make the window opaque.
color *= niri_clamped_progress;
return color;
}
// Example: show the window as an expanding circle.
// Recommended setting: duration-ms 250
vec4 expanding_circle(vec3 coords_geo, vec3 size_geo) {
vec3 coords_tex = niri_geo_to_tex * coords_geo;
vec4 color = texture2D(niri_tex, coords_tex.st);
vec2 coords = (coords_geo.xy - vec2(0.5, 0.5)) * size_geo.xy * 2.0;
coords = coords / length(size_geo.xy);
float p = niri_clamped_progress;
if (p * p <= dot(coords, coords))
color = vec4(0.0);
return color * p;
}
// This is the function that you must define.
vec4 open_color(vec3 coords_geo, vec3 size_geo) {
// You can pick one of the example functions or write your own.
return default_open(coords_geo, size_geo);
}
'';
window-resize =
# glsl
''
// Example: fill the current geometry with a solid vertical gradient.
vec4 solid_gradient(vec3 coords_curr_geo, vec3 size_curr_geo) {
vec3 coords = coords_curr_geo;
vec4 color = vec4(0.0);
// Paint only the area inside the current geometry.
if (0.0 <= coords.x && coords.x <= 1.0
&& 0.0 <= coords.y && coords.y <= 1.0)
{
vec4 from = vec4(1.0, 0.0, 0.0, 1.0);
vec4 to = vec4(0.0, 1.0, 0.0, 1.0);
color = mix(from, to, coords.y);
}
return color;
}
// Example: crossfade between previous and next texture, stretched to the
// current geometry.
vec4 crossfade(vec3 coords_curr_geo, vec3 size_curr_geo) {
// Convert coordinates into the texture space for sampling.
vec3 coords_tex_prev = niri_geo_to_tex_prev * coords_curr_geo;
vec4 color_prev = texture2D(niri_tex_prev, coords_tex_prev.st);
// Convert coordinates into the texture space for sampling.
vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo;
vec4 color_next = texture2D(niri_tex_next, coords_tex_next.st);
vec4 color = mix(color_prev, color_next, niri_clamped_progress);
return color;
}
// Example: next texture, stretched to the current geometry.
vec4 stretch_next(vec3 coords_curr_geo, vec3 size_curr_geo) {
vec3 coords_tex_next = niri_geo_to_tex_next * coords_curr_geo;
vec4 color = texture2D(niri_tex_next, coords_tex_next.st);
return color;
}
// Example: next texture, stretched to the current geometry if smaller, and
// cropped if bigger.
vec4 stretch_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) {
vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo;
vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo;
vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo;
// We can crop if the current window size is smaller than the next window
// size. One way to tell is by comparing to 1.0 the X and Y scaling
// coefficients in the current-to-next transformation matrix.
bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0;
bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0;
vec3 coords = coords_stretch;
if (can_crop_by_x)
coords.x = coords_crop.x;
if (can_crop_by_y)
coords.y = coords_crop.y;
vec4 color = texture2D(niri_tex_next, coords.st);
// However, when we crop, we also want to crop out anything outside the
// current geometry. This is because the area of the shader is unspecified
// and usually bigger than the current geometry, so if we don't fill pixels
// outside with transparency, the texture will leak out.
//
// When stretching, this is not an issue because the area outside will
// correspond to client-side decoration shadows, which are already supposed
// to be outside.
if (can_crop_by_x && (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x))
color = vec4(0.0);
if (can_crop_by_y && (coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y))
color = vec4(0.0);
return color;
}
// Example: cropped next texture if it's bigger than the current geometry, and
// crossfade between previous and next texture otherwise.
vec4 crossfade_or_crop_next(vec3 coords_curr_geo, vec3 size_curr_geo) {
vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo;
vec3 coords_prev_geo = niri_curr_geo_to_prev_geo * coords_curr_geo;
vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo;
vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo;
vec3 coords_stretch_prev = niri_geo_to_tex_prev * coords_curr_geo;
// We can crop if the current window size is smaller than the next window
// size. One way to tell is by comparing to 1.0 the X and Y scaling
// coefficients in the current-to-next transformation matrix.
bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0;
bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0;
bool crop = can_crop_by_x && can_crop_by_y;
vec4 color;
if (crop) {
// However, when we crop, we also want to crop out anything outside the
// current geometry. This is because the area of the shader is unspecified
// and usually bigger than the current geometry, so if we don't fill pixels
// outside with transparency, the texture will leak out.
//
// When crossfading, this is not an issue because the area outside will
// correspond to client-side decoration shadows, which are already supposed
// to be outside.
if (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x ||
coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y) {
color = vec4(0.0);
} else {
color = texture2D(niri_tex_next, coords_crop.st);
}
} else {
// If we can't crop, then crossfade.
color = texture2D(niri_tex_next, coords_stretch.st);
vec4 color_prev = texture2D(niri_tex_prev, coords_stretch_prev.st);
color = mix(color_prev, color, niri_clamped_progress);
}
return color;
}
// This is the function that you must define.
vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) {
// You can pick one of the example functions or write your own.
return crossfade_or_crop_next(coords_curr_geo, size_curr_geo);
}
'';
};
};
}

View file

@ -0,0 +1,158 @@
{
pkgs,
lib,
user,
...
}:
let
niri-autostart = pkgs.writeShellApplication {
name = "niri-autostart";
runtimeInputs = with pkgs; [
niri-blur-wallpaper
swww
clash-meta
wlsunset
];
extraShellCheckFlags = [ ];
bashOptions = [ ];
text =
# bash
''
swww kill
swww-daemon &
clash-meta -d ~/.config/clash &
wlsunset -s 00:00 -S 00:00 -t 5000 -T 5001 &
niri-blur-wallpaper &
'';
};
niri-blur-wallpaper = pkgs.writers.writePython3Bin "niri-blur-wallpaper" { doCheck = false; } ''
import os
import subprocess
import json
wallpapers_path = "/home/${user}/Pictures/Wallpapers/generated/"
wallpapers_cache_path = "/home/${user}/.cache/swww/"
events_of_interest = [
"Workspace changed",
"Workspace focused",
"Window opened",
"Window closed",
]
def get_niri_msg_output(msg):
output = subprocess.check_output(["niri", "msg", "-j", msg])
output = json.loads(output)
return output
def get_current_wallpaper(monitor):
with open(os.path.join(wallpapers_cache_path, monitor)) as f:
wallpaper = str(f.read())
return wallpaper
def set_wallpaper(monitor, wallpaper):
subprocess.run(
[
"swww",
"img",
"--transition-type",
"fade",
"--transition-duration",
"0.5",
"-o",
monitor,
wallpaper,
]
)
def get_wallpaper_name(wallpaper_path):
current_wallpaper_is_blurred = "blurred" in wallpaper_path
if current_wallpaper_is_blurred:
wallpaper_name = "-".join(wallpaper_path.split("-")[1:-1])
else:
wallpaper_name = "-".join(wallpaper_path.split("-")[1:])
wallpaper_name = ".".join(wallpaper_name.split(".")[:-1])
return wallpaper_name
def set_wallpaper_if_needed(active_workspace, init):
active_workspace_monitor = active_workspace["output"]
current_wallpaper = get_current_wallpaper(active_workspace_monitor)
current_wallpaper_is_live = current_wallpaper.endswith(".gif")
if current_wallpaper_is_live:
return
wallpaper_name = get_wallpaper_name(current_wallpaper)
active_workspace_is_empty = active_workspace["active_window_id"] is None
if active_workspace_is_empty:
wallpaper = os.path.join(wallpapers_path, f"{wallpaper_name}.jpg")
else:
wallpaper = os.path.join(wallpapers_path, f"{wallpaper_name}-blurred.jpg")
real_wallpaper = os.path.realpath(wallpaper)
if current_wallpaper == real_wallpaper and not init:
return
set_wallpaper(active_workspace_monitor, wallpaper)
def change_wallpaper(init=False):
workspaces = get_niri_msg_output("workspaces")
active_workspaces = [
workspace for workspace in workspaces if workspace["is_active"]
]
for active_workspace in active_workspaces:
set_wallpaper_if_needed(active_workspace, init)
def main():
change_wallpaper(init=True)
event_stream = subprocess.Popen(
["niri", "msg", "event-stream"], stdout=subprocess.PIPE
)
if not event_stream.stdout:
return
for line in iter(event_stream.stdout.readline, ""):
if any(event in line.decode() for event in events_of_interest):
change_wallpaper()
if __name__ == "__main__":
main()
'';
in
{
systemd.user.services.niri-blur-wallpaper = {
Unit = {
Description = "Niri Blur Wallpaper";
BindsTo = [ "graphical-session.target" ];
Before = [ "graphical-session.target" ];
Wants = [ "graphical-session-pre.target" ];
After = [ "graphical-session-pre.target" ];
};
Install = {
WantedBy = [ "graphical-session.target" ];
Wants = [ "xdg-desktop-autostart.target" ];
After = [ "xdg-desktop-autostart.target" ];
};
Service = {
Type = "simple";
ExecStart = "${niri-blur-wallpaper}/bin/niri-blur-wallpaper";
Restart = "on-failure";
RestartSec = "5s";
};
};
home.activation.restart-niri-blur-wallpaper =
lib.hm.dag.entryAfter [ "reload-waybar" ]
# bash
''run --quiet ${pkgs.systemd}/bin/systemctl --user restart niri-blur-wallpaper'';
programs.niri.settings.spawn-at-startup = [
{ command = [ "${niri-autostart}/bin/niri-autostart" ]; }
{
command = [
"fcitx5"
"-d"
];
}
];
}

View file

@ -0,0 +1,117 @@
{ config, user, ... }:
{
programs.niri.settings.binds = with config.lib.niri.actions; {
"Mod+Return".action = spawn "kitty";
"Mod+X".action = spawn "/home/${user}/scripts/tofi/powermenu";
"Mod+Shift+C".action = spawn "/home/${user}/scripts/tofi/colorscheme";
"Mod+P".action = spawn "sh" "-c" "$(tofi-run)";
"Mod+Shift+P".action = spawn "tofi-drun" "--drun-launch=true";
"Mod+Shift+W".action = spawn "/home/${user}/scripts/change-wal-niri";
"Mod+B".action = spawn "pkill" "-USR1" ".waybar-wrapped";
"Mod+Q".action = close-window;
"Mod+E".action = expand-column-to-available-width;
"Mod+ALt+C".action = spawn "wl-color-picker";
"Mod+N".action =
spawn "wayneko" "--layer" "top" "--follow-pointer" "true" "--survive-close" "--background-colour"
"0x${config.lib.stylix.colors.base0E}dd"
"--outline-colour"
"0x${config.lib.stylix.colors.base01}dd";
"Mod+Shift+Slash".action = show-hotkey-overlay;
"Mod+T".action = toggle-column-tabbed-display;
"XF86AudioMute".action = spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle";
"XF86AudioMicMute".action = spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle";
"XF86AudioRaiseVolume".action = spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+";
"XF86AudioLowerVolume".action = spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-";
"Mod+Left".action = focus-column-left;
"Mod+Down".action = focus-window-down;
"Mod+Up".action = focus-window-up;
"Mod+Right".action = focus-column-right;
"Mod+H".action = focus-column-or-monitor-left;
"Mod+J".action = focus-window-or-workspace-down;
"Mod+K".action = focus-window-or-workspace-up;
"Mod+L".action = focus-column-or-monitor-right;
"Mod+Shift+Left".action = move-column-left;
"Mod+Shift+Down".action = move-window-down;
"Mod+Shift+Up".action = move-window-up;
"Mod+Shift+Right".action = move-column-right;
"Mod+Shift+H".action = move-column-left-or-to-monitor-left;
"Mod+Shift+J".action = move-window-down-or-to-workspace-down;
"Mod+Shift+K".action = move-window-up-or-to-workspace-up;
"Mod+Shift+L".action = move-column-right-or-to-monitor-right;
"Mod+Ctrl+Left".action = focus-monitor-left;
"Mod+Ctrl+Down".action = focus-monitor-down;
"Mod+Ctrl+Up".action = focus-monitor-up;
"Mod+Ctrl+Right".action = focus-monitor-right;
"Mod+Ctrl+H".action = focus-monitor-left;
"Mod+Ctrl+J".action = focus-monitor-down;
"Mod+Ctrl+K".action = focus-monitor-up;
"Mod+Ctrl+L".action = focus-monitor-right;
"Mod+Shift+Ctrl+Left".action = move-window-to-monitor-left;
"Mod+Shift+Ctrl+Down".action = move-window-to-monitor-down;
"Mod+Shift+Ctrl+Up".action = move-window-to-monitor-up;
"Mod+Shift+Ctrl+Right".action = move-window-to-monitor-right;
"Mod+Shift+Ctrl+H".action = move-window-to-monitor-left;
"Mod+Shift+Ctrl+J".action = move-window-to-monitor-down;
"Mod+Shift+Ctrl+K".action = move-window-to-monitor-up;
"Mod+Shift+Ctrl+L".action = move-window-to-monitor-right;
"Mod+Shift+Space".action = toggle-window-floating;
"Mod+Space".action = switch-focus-between-floating-and-tiling;
"Mod+TouchpadScrollDown" = {
action = focus-workspace-down;
cooldown-ms = 150;
};
"Mod+TouchpadScrollUp" = {
action = focus-workspace-up;
cooldown-ms = 150;
};
"Mod+1".action = focus-workspace 1;
"Mod+2".action = focus-workspace 2;
"Mod+3".action = focus-workspace 3;
"Mod+4".action = focus-workspace 4;
"Mod+5".action = focus-workspace 5;
"Mod+6".action = focus-workspace 6;
"Mod+7".action = focus-workspace 7;
"Mod+8".action = focus-workspace 8;
"Mod+9".action = focus-workspace 9;
"Mod+Ctrl+1".action = move-window-to-workspace 1;
"Mod+Ctrl+2".action = move-window-to-workspace 2;
"Mod+Ctrl+3".action = move-window-to-workspace 3;
"Mod+Ctrl+4".action = move-window-to-workspace 4;
"Mod+Ctrl+5".action = move-window-to-workspace 5;
"Mod+Ctrl+6".action = move-window-to-workspace 6;
"Mod+Ctrl+7".action = move-window-to-workspace 7;
"Mod+Ctrl+8".action = move-window-to-workspace 8;
"Mod+Ctrl+9".action = move-window-to-workspace 9;
"Mod+Comma".action = consume-window-into-column;
"Mod+Period".action = expel-window-from-column;
"Mod+BracketLeft".action = consume-or-expel-window-left;
"Mod+BracketRight".action = consume-or-expel-window-left;
"Mod+R".action = switch-preset-column-width;
"Mod+Shift+R".action = reset-window-height;
"Mod+F".action = maximize-column;
"Mod+Shift+F".action = fullscreen-window;
"Mod+C".action = center-column;
"Mod+Minus".action = set-column-width "-10%";
"Mod+Equal".action = set-column-width "+10%";
"Mod+Shift+Minus".action = set-window-height "-10%";
"Mod+Shift+Equal".action = set-window-height "+10%";
"Print".action = screenshot;
"Ctrl+Print".action = screenshot-screen;
"Alt+Print".action = screenshot-window;
"Mod+Shift+E".action = quit;
};
}

View file

@ -0,0 +1,169 @@
{
config,
pkgs,
...
}:
{
imports = [
./binds.nix
./animations.nix
./waybar.nix
./autostart.nix
./override-config.nix
];
programs.niri = {
# home.file.".config/niri/config.kdl".source = ./config.kdl;
enable = true;
package = pkgs.niri-unstable;
settings =
with config.lib.stylix.colors.withHashtag;
let
shadowConfig = {
enable = true;
spread = 0;
softness = 10;
color = "#000000dd";
};
in
{
hotkey-overlay.skip-at-startup = true;
prefer-no-csd = true;
input = {
focus-follows-mouse.enable = true;
touchpad.natural-scroll = false;
keyboard.xkb.options = "caps:escape";
};
outputs = builtins.mapAttrs (name: value: {
inherit (value) scale mode position;
transform.rotation = value.rotation;
background-color = base01;
}) config.monitors;
window-rules = [
{
geometry-corner-radius = {
bottom-left = 10.0;
bottom-right = 10.0;
top-left = 10.0;
top-right = 10.0;
};
clip-to-geometry = true;
draw-border-with-background = false;
}
{
matches = [
{ app-id = "yad"; }
];
open-floating = true;
}
{
matches = [
{ app-id = "firefox"; }
{ app-id = "org.qutebrowser.qutebrowser"; }
{ app-id = "kitty"; }
{ app-id = "evince"; }
{ app-id = "zathura"; }
];
default-column-width = {
proportion = 1.0;
};
}
{
matches = [
{ is-focused = true; }
];
opacity = 0.95;
}
{
matches = [
{ is-focused = false; }
];
opacity = 0.85;
}
];
layer-rules = [
{
matches = [ { namespace = "launcher"; } ];
geometry-corner-radius = {
bottom-left = 15.0;
bottom-right = 15.0;
top-left = 15.0;
top-right = 15.0;
};
shadow = shadowConfig;
}
];
gestures = {
dnd-edge-view-scroll = {
trigger-width = 60;
delay-ms = 100;
max-speed = 1500;
};
};
workspaces = {
"1" = {
open-on-output = config.mainMonitorName;
name = "coding";
};
"2" = {
open-on-output = config.mainMonitorName;
name = "browsing";
};
"3" = {
open-on-output = builtins.head config.otherMonitorsNames;
name = "reading";
};
"4" = {
open-on-output = config.mainMonitorName;
name = "music";
};
};
layout = {
gaps = 12;
border = {
enable = true;
width = 4;
active = {
gradient = {
from = base07;
to = base0E;
angle = 45;
in' = "oklab";
# relative-to = "workspace-view";
};
};
# inactive.color = "#585b70";
inactive.color = base02;
};
focus-ring.enable = false;
struts = {
left = 2;
right = 2;
top = 0;
bottom = 2;
};
insert-hint = {
enable = true;
display = {
gradient = {
# from = "#f9e2af";
from = base0A;
# to = "#eba0ac";
to = base09;
angle = 45;
};
};
};
shadow = shadowConfig;
tab-indicator = {
hide-when-single-tab = true;
gap = 5;
width = 6;
length.total-proportion = 0.5;
position = "right";
gaps-between-tabs = 2;
};
};
};
};
}

View file

@ -0,0 +1,48 @@
{ config, ... }:
let
# tabIndicatorConfig = # kdl
# ''
# tab-indicator {
# hide-when-single-tab
# gap 5
# width 6
# length total-proportion=0.5
# position "right"
# gaps-between-tabs 2
# }
# '';
# shadowConfig =
# # kdl
# ''
# shadow {
# on
# spread 0
# softness 10
# color "#000000dd"
# }
# '';
# tabBinds = # kdl
# ''
# Mod+T { toggle-column-tabbed-display; }
# '';
extraConfig =
# kdl
'''';
finalNiriConfig =
# builtins.replaceStrings
# [ "layout {" ]
# [
# ("layout {\n" + shadowConfig + "\n" + tabIndicatorConfig)
# ]
# (config.programs.niri.finalConfig + "\n" + extraConfig)
# |>
# builtins.replaceStrings
# [ "binds {" ]
# [
# ("binds {\n" + tabBinds)
# ];
config.programs.niri.finalConfig + "\n" + extraConfig;
in
{
home.file.".config/niri/config-override.kdl".text = finalNiriConfig;
}

View file

@ -0,0 +1,420 @@
{ config, ... }:
let
moduleConfiguration =
# jsonc
''
// Modules configuration
"niri/workspaces": {
"format": "{icon}",
"format-icons": {
// "coding": " ",
// "browsing": " ",
// "reading": "󰈙",
// "default": "󱄅 ",
// "default": " ",
// "active": " "
"default": ""
},
},
"niri/window": {
"format": "{}",
"separate-outputs": true,
"icon": true,
"icon-size": 18
},
"memory": {
"interval": 30,
"format": "<span foreground='#${config.lib.stylix.colors.base0E}'> </span> {used:0.1f}G/{total:0.1f}G",
"on-click": "kitty --class=htop,htop -e htop"
},
"backlight": {
"device": "intel_backlight",
"on-scroll-up": "light -A 1",
"on-scroll-down": "light -U 1",
"format": "<span size='13000' foreground='#${config.lib.stylix.colors.base0D}'>{icon} </span> {percent}%",
"format-icons": [
"",
""
]
},
"tray": {
"icon-size": 16,
"spacing": 10
},
"clock": {
"format": "<span foreground='#${config.lib.stylix.colors.base0E}'> </span> {:%a %d %H:%M}",
"tooltip-format": "<big>{:%Y %B}</big>\n<tt><small>{calendar}</small></tt>",
"on-click": "kitty --class=clock,clock --title=clock -o remember_window_size=no -o initial_window_width=600 -o initial_window_height=200 -e tty-clock -s -c -C 5"
},
"battery": {
"states": {
"warning": 30,
"critical": 15,
},
"format": "<span size='13000' foreground='#${config.lib.stylix.colors.base0E}'>{icon} </span>{capacity}%",
"format-warning": "<span size='13000' foreground='#${config.lib.stylix.colors.base0E}'>{icon} </span>{capacity}%",
"format-critical": "<span size='13000' foreground='#${config.lib.stylix.colors.base08}'>{icon} </span>{capacity}%",
"format-charging": "<span size='13000' foreground='#${config.lib.stylix.colors.base0E}'> </span>{capacity}%",
"format-plugged": "<span size='13000' foreground='#${config.lib.stylix.colors.base0E}'> </span>{capacity}%",
"format-alt": "<span size='13000' foreground='#${config.lib.stylix.colors.base0E}'>{icon} </span>{time}",
"format-full": "<span size='13000' foreground='#${config.lib.stylix.colors.base0E}'> </span>{capacity}%",
"format-icons": [
"",
"",
"",
"",
""
],
"tooltip-format": "{time}",
"interval": 5
},
"network": {
"format-wifi": "<span size='13000' foreground='#${config.lib.stylix.colors.base06}'>󰖩 </span>{essid}",
"format-ethernet": "<span size='13000' foreground='#${config.lib.stylix.colors.base06}'>󰤭</span> Disconnected",
"format-linked": "{ifname} (No IP) 󱚵",
"format-disconnected": "<span size='13000' foreground='#${config.lib.stylix.colors.base06}'> </span>Disconnected",
"tooltip-format-wifi": "Signal Strenght: {signalStrength}%",
"on-click": "kitty --class nmtui,nmtui --title=nmtui -o remember_window_size=no -o initial_window_width=400 -o initial_window_height=400 -e doas nmtui"
},
"pulseaudio": {
"on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
"on-scroll-up": "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.01+",
"on-scroll-down": "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.01-",
"format": "<span size='13000' foreground='#${config.lib.stylix.colors.base0A}'>{icon} </span>{volume}%",
"format-muted": "<span size='13000' foreground='#${config.lib.stylix.colors.base0A}'> </span>Muted",
"format-icons": {
"headphone": "󱡏",
"hands-free": "",
"headset": "󱡏",
"phone": "",
"portable": "",
"car": "",
"default": [
"󰕿",
"󰖀",
"󰕾",
"󰕾"
]
}
},
"group/meters": {
"orientation": "inherit",
"drawer": {
"transition-duration": 500,
"transition-left-to-right": false,
},
"modules": [
"battery",
"memory",
"network",
"pulseaudio",
"backlight"
]
}
'';
trayBackgroundColor = if config.stylix.polarity == "dark" then "@base00" else "@base05";
colors = {
inherit (config.lib.stylix.colors.withHashtag)
base00
base01
base04
base05
base06
base07
base08
base0A
base0D
base0E
base0F
;
};
in
{
imports = [ ../../../../lib/monitors.nix ];
programs.waybar = {
enable = true;
systemd = {
enable = true;
target = config.wayland.systemd.target;
};
};
home.file = {
".config/waybar/config.jsonc".text =
let
otherMonitorsConfig =
map (
name:
# json
''
{
"position": "top",
"layer": "top",
"output": "${name}",
"modules-left": [
"niri/workspaces",
"niri/window"
],
"modules-right": [
"tray",
"group/meters"
],
${moduleConfiguration}
},
'') config.otherMonitorsNames
|> builtins.concatStringsSep "\n";
in
# json
''
[
${otherMonitorsConfig}
{
"position": "top",
"layer": "top",
"output": "${config.mainMonitorName}",
"modules-left": [
"niri/workspaces",
"tray",
"niri/window"
],
"modules-center": [
"clock",
"memory"
],
"modules-right": [
"network",
"pulseaudio",
"backlight",
"battery",
],
${moduleConfiguration}
}
]
'';
".config/waybar/colors.css".text =
# css
(builtins.mapAttrs (name: value: "@define-color ${name} ${value};") colors)
|> builtins.attrValues
|> builtins.concatStringsSep "\n";
".config/waybar/tray.css".text =
# css
''
#tray {
background: shade(alpha(${trayBackgroundColor}, 0.9), 1);
}
'';
".config/waybar/style.css".text =
# css
''
@import "animation.css";
@import "colors.css";
@import "tray.css";
* {
/* all: unset; */
font-size: 14px;
/* font-family: "Comic Mono Nerd Font"; */
font-family: "Hug Me Tight", "Xiaolai SC";
min-height: 0;
}
window#waybar {
/* border-radius: 15px; */
/* color: @base04; */
background: transparent;
}
tooltip {
background: @base01;
border-radius: 5px;
border-width: 2px;
border-style: solid;
border-color: @base07;
}
#network,
#clock,
#battery,
#pulseaudio,
#workspaces,
#backlight,
#memory,
#tray,
#window {
padding: 4px 10px;
background: shade(alpha(@base00, 0.9), 1);
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.377);
color: @base05;
margin-top: 10px;
margin-bottom: 5px;
margin-left: 5px;
margin-right: 5px;
box-shadow: 1px 2px 2px #101010;
border-radius: 10px;
}
#workspaces {
margin-left: 15px;
font-size: 0px;
padding: 6px 3px;
border-radius: 20px;
}
#workspaces button {
font-size: 0px;
background-color: @base07;
padding: 0px 1px;
margin: 0px 4px;
border-radius: 20px;
transition: all 0.25s cubic-bezier(0.55, -0.68, 0.48, 1.682);
}
#workspaces button.active {
font-size: 1px;
background-color: @base0E;
border-radius: 20px;
min-width: 30px;
background-size: 400% 400%;
}
#workspaces button.empty {
font-size: 1px;
background-color: @base04;
}
#window {
color: @base00;
background: radial-gradient(circle, @base05 0%, @base07 100%);
background-size: 400% 400%;
animation: gradient_f 40s ease-in-out infinite;
transition: all 0.3s cubic-bezier(0.55, -0.68, 0.48, 1.682);
}
window#waybar.empty #window {
background: none;
background-color: transparent;
box-shadow: none;
}
#battery {
margin-right: 15px;
background: @base0F;
/* background: linear-gradient(118deg, @base07 0%, @base0F 25%, @base07 50%, @base0F 75%, @base07 100%); */
/* background: linear-gradient(118deg, @base07 5%, @base0F 5%, @base0F 20%, @base07 20%, @base07 40%, @base0F 40%, @base0F 60%, @base07 60%, @base07 80%, @base0F 80%, @base0F 95%, @base07 95%); */
background: linear-gradient(
118deg,
@base0B 5%,
@base0F 5%,
@base0F 20%,
@base0B 20%,
@base0B 40%,
@base0F 40%,
@base0F 60%,
@base0B 60%,
@base0B 80%,
@base0F 80%,
@base0F 95%,
@base0B 95%
);
background-size: 200% 300%;
animation: gradient_f_nh 4s linear infinite;
color: @base01;
}
#battery.charging,
#battery.plugged {
background: linear-gradient(
118deg,
@base0E 5%,
@base0D 5%,
@base0D 20%,
@base0E 20%,
@base0E 40%,
@base0D 40%,
@base0D 60%,
@base0E 60%,
@base0E 80%,
@base0D 80%,
@base0D 95%,
@base0E 95%
);
background-size: 200% 300%;
animation: gradient_rv 4s linear infinite;
}
#battery.full {
background: linear-gradient(
118deg,
@base0E 5%,
@base0D 5%,
@base0D 20%,
@base0E 20%,
@base0E 40%,
@base0D 40%,
@base0D 60%,
@base0E 60%,
@base0E 80%,
@base0D 80%,
@base0D 95%,
@base0E 95%
);
background-size: 200% 300%;
animation: gradient_rv 20s linear infinite;
}
#tray > .passive {
-gtk-icon-effect: dim;
}
#tray > .needs-attention {
-gtk-icon-effect: highlight;
}
'';
".config/waybar/animation.css".text =
# css
''
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 30%;
}
100% {
background-position: 0% 50%;
}
}
@keyframes gradient_f {
0% {
background-position: 0% 200%;
}
50% {
background-position: 200% 0%;
}
100% {
background-position: 400% 200%;
}
}
@keyframes gradient_f_nh {
0% {
background-position: 0% 200%;
}
100% {
background-position: 200% 200%;
}
}
@keyframes gradient_rv {
0% {
background-position: 200% 200%;
}
100% {
background-position: 0% 200%;
}
}
'';
};
}