From 7d07282234d2cee7530e9235043d7a6e8e5e308b Mon Sep 17 00:00:00 2001 From: Uyanide Date: Thu, 2 Oct 2025 05:44:34 +0200 Subject: [PATCH] add niri setup --- .scripts/change-colortheme | 11 +- .scripts/change-wallpaper | 35 +- .scripts/config-switch | 21 + README.md | 14 +- ghostty-niri/.gitignore | 1 + ghostty-niri/config | 21 + ghostty-niri/cursor-shaders/cursor-smear.glsl | 120 ++++++ ghostty/config | 2 +- hypr/hyprland/execs.conf | 3 + hypr/hyprland/rules.conf | 3 +- kitty-niri/Catppuccin-Mocha.conf | 80 ++++ kitty-niri/kitty.conf | 48 +++ kitty/Catppuccin-Macchiato.conf | 80 ---- niri/config.kdl | 393 ++++++++++++++++++ niri/config.kdl.template | 393 ++++++++++++++++++ nvim/lazyvim.json | 2 +- swaylock/config | 0 waybar-niri/config.jsonc | 213 ++++++++++ waybar-niri/mocha.css | 26 ++ waybar-niri/modules/.gitignore | 3 + waybar-niri/modules/mediaplayer.py | 221 ++++++++++ waybar-niri/modules/publicip.sh | 62 +++ waybar-niri/style.css | 217 ++++++++++ waybar-niri/style.css.template | 217 ++++++++++ waybar-niri/waybar.sh | 10 + wlogout/style.css | 2 +- wlogout/style.css.template | 2 +- 27 files changed, 2105 insertions(+), 95 deletions(-) create mode 100755 .scripts/config-switch create mode 100644 ghostty-niri/.gitignore create mode 100644 ghostty-niri/config create mode 100644 ghostty-niri/cursor-shaders/cursor-smear.glsl create mode 100644 kitty-niri/Catppuccin-Mocha.conf create mode 100755 kitty-niri/kitty.conf delete mode 100644 kitty/Catppuccin-Macchiato.conf create mode 100644 niri/config.kdl create mode 100644 niri/config.kdl.template create mode 100644 swaylock/config create mode 100644 waybar-niri/config.jsonc create mode 100644 waybar-niri/mocha.css create mode 100644 waybar-niri/modules/.gitignore create mode 100755 waybar-niri/modules/mediaplayer.py create mode 100755 waybar-niri/modules/publicip.sh create mode 100644 waybar-niri/style.css create mode 100644 waybar-niri/style.css.template create mode 100755 waybar-niri/waybar.sh diff --git a/.scripts/change-colortheme b/.scripts/change-colortheme index 00f25db..bcf45e4 100755 --- a/.scripts/change-colortheme +++ b/.scripts/change-colortheme @@ -32,6 +32,8 @@ edit $HOME/.config/wlogout/icons/*.svg - fuzzel: edit $HOME/.config/fuzzel/fuzzel.ini + +- niri: edit $HOME/.config/niri/config.kdl ''' import os @@ -208,6 +210,12 @@ def _change_fuzzel(palette: dict[str, str], flavor: str): replace_placeholders(fuzzel_dist, palette, flavor) +def _change_niri(palette: dict[str, str], flavor: str): + niri_template = Path.home().joinpath('.config', 'niri', 'config.kdl.template') + niri_dist = copy_template(niri_template, Path.home().joinpath('.config', 'niri')) + replace_placeholders(niri_dist, palette, flavor) + + apply_theme_funcs: dict[str, Callable[[dict[str, str], str], None]] = { 'kvantum': _change_kvantum, # 'nwg-look': _change_nwglook, @@ -220,7 +228,8 @@ apply_theme_funcs: dict[str, Callable[[dict[str, str], str], None]] = { 'mako': _change_mako, 'yazi': _change_yazi, 'wlogout': _change_wlogout, - 'fuzzel': _change_fuzzel + 'fuzzel': _change_fuzzel, + 'niri': _change_niri, } diff --git a/.scripts/change-wallpaper b/.scripts/change-wallpaper index 01413a4..3e194af 100755 --- a/.scripts/change-wallpaper +++ b/.scripts/change-wallpaper @@ -16,16 +16,43 @@ temp_img=$(mktemp --suffix=."$ext") || exit 1 cp "$image" "$temp_img" || exit 1 rm -f "$current_dir"/wallpaper.* cp -f "$temp_img" "$image_copied" || ( + notify-send "Error" "Could not copy image to $current_dir" rm -f "$temp_img" exit 1 ) rm -f "$temp_img" -hyprctl hyprpaper reload ,"$image_copied" || exit 1 -echo "preload = $image_copied" >"$HOME/.config/hypr/hyprpaper.conf" -echo "wallpaper = , $image_copied" >>"$HOME/.config/hypr/hyprpaper.conf" +if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then + hyprctl hyprpaper reload ,"$image_copied" || exit 1 + echo "preload = $image_copied" >"$HOME/.config/hypr/hyprpaper.conf" + echo "wallpaper = , $image_copied" >>"$HOME/.config/hypr/hyprpaper.conf" -notify-send "Wallpaper Changed" "$image" + notify-send "Wallpaper Changed" "$image" +elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then + killall swaybg + killall swww + + cache_dir="$HOME/.cache/swaybg" + mkdir -p "$cache_dir" || ( + notify-send "Error" "Could not create cache directory" + exit 1 + ) + blurred_image="$cache_dir/blurred.$ext" + + # blur + magick "$image_copied" -blur 0x16 "$blurred_image" || ( + notify-send "Error" "Could not create blurred image" + exit 1 + ) + + (setsid swaybg -i "$blurred_image" -m fill > /dev/null 2> /dev/null &)& + disown +Q + (setsid swww img "$image_copied" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null &)& + disown + + notify-send "Wallpaper Changed" "$image" +fi notify-send "Extracting colors from wallpaper" "This may take a few seconds..." change-colortheme -i "$image_copied" || exit 1 diff --git a/.scripts/config-switch b/.scripts/config-switch new file mode 100755 index 0000000..b2ba791 --- /dev/null +++ b/.scripts/config-switch @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +if [ -z "$1" ]; then + desktop="$XDG_CURRENT_DESKTOP" +else + desktop="$1" +fi + +for item in "waybar" "kitty" "ghostty"; do + [ -L "$HOME/.config/$item" ] || exit 1 + + rm "$HOME/.config/$item" + + path="$(dirname "$(readlink -f "$0")")" + + if [ "$desktop" = "niri" ]; then + ln -s "$path/../$item-niri" "$HOME/.config/$item" + else + ln -s "$path/../$item" "$HOME/.config/$item" + fi +done \ No newline at end of file diff --git a/README.md b/README.md index 4c454b5..9040532 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ ## Screenshots - desktop with a few widgets: - + - dynamic flavor based on Catppuccin Mocha: - + - the grub menu looks like: - + ## Setup Overview - **OS**: Archlinux -- **WM**: Hyprland +- **WM**: Niri & Hyprland (looks similar through screenshots) - **Bar**: Waybar - **Shell**: Fish - **Prompt**: Oh My Posh @@ -23,10 +23,14 @@ - **Desktop Widgets**: Eww - **Notification Daemon**: Mako -## Hyprland & some scripts +## Hyprland & friends Based on [end-4/dots-hyprland](https://github.com/end-4/dots-hyprland) but without ags amd tons of other stuff. +## Niri + +Ported some of the exsiting Hyprland configs, e.g. windows rules & keybindings, even using hyprlock as lock screen. + ## Eww - `main`, main dashboard, modified from [syndrizzle/hotfiles](https://github.com/syndrizzle/hotfiles/tree/bspwm) without notification center. diff --git a/ghostty-niri/.gitignore b/ghostty-niri/.gitignore new file mode 100644 index 0000000..4039784 --- /dev/null +++ b/ghostty-niri/.gitignore @@ -0,0 +1 @@ +shaders \ No newline at end of file diff --git a/ghostty-niri/config b/ghostty-niri/config new file mode 100644 index 0000000..7eda804 --- /dev/null +++ b/ghostty-niri/config @@ -0,0 +1,21 @@ +theme = Catppuccin Mocha + +background-opacity = 0.95 +background-blur = true + +window-padding-x = 10 +window-padding-y = 10 + +keybind = ctrl+shift+r=reload_config + +command = exec fish + +confirm-close-surface = false + +font-family = Maple Mono NF CN +font-size = 12 + +cursor-style = bar +adjust-cursor-thickness = 3 + +custom-shader = cursor-shaders/cursor-smear.glsl diff --git a/ghostty-niri/cursor-shaders/cursor-smear.glsl b/ghostty-niri/cursor-shaders/cursor-smear.glsl new file mode 100644 index 0000000..ca748e0 --- /dev/null +++ b/ghostty-niri/cursor-shaders/cursor-smear.glsl @@ -0,0 +1,120 @@ +// https://github.com/KroneCorylus/ghostty-shader-playground/blob/main/shaders/cursor_smear.glsl + +float getSdfRectangle(in vec2 p, in vec2 xy, in vec2 b) +{ + vec2 d = abs(p - xy) - b; + return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0); +} + +// Based on Inigo Quilez's 2D distance functions article: https://iquilezles.org/articles/distfunctions2d/ +// Potencially optimized by eliminating conditionals and loops to enhance performance and reduce branching + +float seg(in vec2 p, in vec2 a, in vec2 b, inout float s, float d) { + vec2 e = b - a; + vec2 w = p - a; + vec2 proj = a + e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0); + float segd = dot(p - proj, p - proj); + d = min(d, segd); + + float c0 = step(0.0, p.y - a.y); + float c1 = 1.0 - step(0.0, p.y - b.y); + float c2 = 1.0 - step(0.0, e.x * w.y - e.y * w.x); + float allCond = c0 * c1 * c2; + float noneCond = (1.0 - c0) * (1.0 - c1) * (1.0 - c2); + float flip = mix(1.0, -1.0, step(0.5, allCond + noneCond)); + s *= flip; + return d; +} + +float getSdfParallelogram(in vec2 p, in vec2 v0, in vec2 v1, in vec2 v2, in vec2 v3) { + float s = 1.0; + float d = dot(p - v0, p - v0); + + d = seg(p, v0, v3, s, d); + d = seg(p, v1, v0, s, d); + d = seg(p, v2, v1, s, d); + d = seg(p, v3, v2, s, d); + + return s * sqrt(d); +} + +vec2 normalize(vec2 value, float isPosition) { + return (value * 2.0 - (iResolution.xy * isPosition)) / iResolution.y; +} + +float antialising(float distance) { + return 1. - smoothstep(0., normalize(vec2(2., 2.), 0.).x, distance); +} + +float determineStartVertexFactor(vec2 a, vec2 b) { + // Conditions using step + float condition1 = step(b.x, a.x) * step(a.y, b.y); // a.x < b.x && a.y > b.y + float condition2 = step(a.x, b.x) * step(b.y, a.y); // a.x > b.x && a.y < b.y + + // If neither condition is met, return 1 (else case) + return 1.0 - max(condition1, condition2); +} + +vec2 getRectangleCenter(vec4 rectangle) { + return vec2(rectangle.x + (rectangle.z / 2.), rectangle.y - (rectangle.w / 2.)); +} +float ease(float x) { + return pow(1.0 - x, 3.0); +} +vec4 saturate(vec4 color, float factor) { + float gray = dot(color, vec4(0.299, 0.587, 0.114, 0.)); // luminance + return mix(vec4(gray), color, factor); +} + +vec4 TRAIL_COLOR = iCurrentCursorColor; +const float OPACITY = 0.6; +const float DURATION = 0.3; //IN SECONDS + +void mainImage(out vec4 fragColor, in vec2 fragCoord) +{ + + #if !defined(WEB) + fragColor = texture(iChannel0, fragCoord.xy / iResolution.xy); + #endif + // Normalization for fragCoord to a space of -1 to 1; + vec2 vu = normalize(fragCoord, 1.); + vec2 offsetFactor = vec2(-.5, 0.5); + + // Normalization for cursor position and size; + // cursor xy has the postion in a space of -1 to 1; + // zw has the width and height + vec4 currentCursor = vec4(normalize(iCurrentCursor.xy, 1.), normalize(iCurrentCursor.zw, 0.)); + vec4 previousCursor = vec4(normalize(iPreviousCursor.xy, 1.), normalize(iPreviousCursor.zw, 0.)); + + // When drawing a parellelogram between cursors for the trail i need to determine where to start at the top-left or top-right vertex of the cursor + float vertexFactor = determineStartVertexFactor(currentCursor.xy, previousCursor.xy); + float invertedVertexFactor = 1.0 - vertexFactor; + + // Set every vertex of my parellogram + vec2 v0 = vec2(currentCursor.x + currentCursor.z * vertexFactor, currentCursor.y - currentCursor.w); + vec2 v1 = vec2(currentCursor.x + currentCursor.z * invertedVertexFactor, currentCursor.y); + vec2 v2 = vec2(previousCursor.x + currentCursor.z * invertedVertexFactor, previousCursor.y); + vec2 v3 = vec2(previousCursor.x + currentCursor.z * vertexFactor, previousCursor.y - previousCursor.w); + + float sdfCurrentCursor = getSdfRectangle(vu, currentCursor.xy - (currentCursor.zw * offsetFactor), currentCursor.zw * 0.5); + float sdfTrail = getSdfParallelogram(vu, v0, v1, v2, v3); + + float progress = clamp((iTime - iTimeCursorChange) / DURATION, 0.0, 1.0); + float easedProgress = ease(progress); + // Distance between cursors determine the total length of the parallelogram; + vec2 centerCC = getRectangleCenter(currentCursor); + vec2 centerCP = getRectangleCenter(previousCursor); + float lineLength = distance(centerCC, centerCP); + + vec4 newColor = vec4(fragColor); + + vec4 trail = TRAIL_COLOR; + trail = saturate(trail, 2.5); + // Draw trail + newColor = mix(newColor, trail, antialising(sdfTrail)); + // Draw current cursor + newColor = mix(newColor, trail, antialising(sdfCurrentCursor)); + newColor = mix(newColor, fragColor, step(sdfCurrentCursor, 0.)); + // newColor = mix(fragColor, newColor, OPACITY); + fragColor = mix(fragColor, newColor, step(sdfCurrentCursor, easedProgress * lineLength)); +} \ No newline at end of file diff --git a/ghostty/config b/ghostty/config index f9c0f61..c3941d1 100644 --- a/ghostty/config +++ b/ghostty/config @@ -18,4 +18,4 @@ font-size = 12 cursor-style = bar adjust-cursor-thickness = 3 -# custom-shader = cursor-shaders/cursor-smear.glsl +custom-shader = cursor-shaders/cursor-smear.glsl diff --git a/hypr/hyprland/execs.conf b/hypr/hyprland/execs.conf index 2d9381e..e6884a4 100755 --- a/hypr/hyprland/execs.conf +++ b/hypr/hyprland/execs.conf @@ -1,3 +1,6 @@ +# Switch configs +exec-once = config-switch Hyprland + # Bar, wallpaper exec-once = waybar exec-once = hyprpaper diff --git a/hypr/hyprland/rules.conf b/hypr/hyprland/rules.conf index b458210..e06eb23 100755 --- a/hypr/hyprland/rules.conf +++ b/hypr/hyprland/rules.conf @@ -33,8 +33,9 @@ windowrulev2 = float, class:^(coin)$ windowrulev2 = noblur, class:^(coin)$ windowrulev2 = bordersize 0, class:^(coin)$ windowrulev2 = noshadow, class:^(coin) -windowrulev2 = float, class:^(wallpaper_chooser)$ +windowrulev2 = float, class:^(wallpaper-chooser)$ windowrulev2 = float, class:^(be.alexandervanhee.gradia)$ +windowrulev2 = float, title:^(图片查看器)$ # QQ # Picture-in-Picture windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ diff --git a/kitty-niri/Catppuccin-Mocha.conf b/kitty-niri/Catppuccin-Mocha.conf new file mode 100644 index 0000000..2533db7 --- /dev/null +++ b/kitty-niri/Catppuccin-Mocha.conf @@ -0,0 +1,80 @@ +# vim:ft=kitty + +## name: Catppuccin-Mocha +## author: Pocco81 (https://github.com/Pocco81) +## license: MIT +## upstream: https://github.com/catppuccin/kitty/blob/main/mocha.conf +## blurb: Soothing pastel theme for the high-spirited! + + + +# The basic colors +foreground #CDD6F4 +background #1E1E2E +selection_foreground #1E1E2E +selection_background #F5E0DC + +# Cursor colors +cursor #F5E0DC +cursor_text_color #1E1E2E + +# URL underline color when hovering with mouse +url_color #F5E0DC + +# Kitty window border colors +active_border_color #B4BEFE +inactive_border_color #6C7086 +bell_border_color #F9E2AF + +# OS Window titlebar colors +wayland_titlebar_color system +macos_titlebar_color system + +# Tab bar colors +active_tab_foreground #11111B +active_tab_background #CBA6F7 +inactive_tab_foreground #CDD6F4 +inactive_tab_background #181825 +tab_bar_background #11111B + +# Colors for marks (marked text in the terminal) +mark1_foreground #1E1E2E +mark1_background #B4BEFE +mark2_foreground #1E1E2E +mark2_background #CBA6F7 +mark3_foreground #1E1E2E +mark3_background #74C7EC + +# The 16 terminal colors + +# black +color0 #45475A +color8 #585B70 + +# red +color1 #F38BA8 +color9 #F38BA8 + +# green +color2 #A6E3A1 +color10 #A6E3A1 + +# yellow +color3 #F9E2AF +color11 #F9E2AF + +# blue +color4 #89B4FA +color12 #89B4FA + +# magenta +color5 #F5C2E7 +color13 #F5C2E7 + +# cyan +color6 #94E2D5 +color14 #94E2D5 + +# white +color7 #BAC2DE +color15 #A6ADC8 diff --git a/kitty-niri/kitty.conf b/kitty-niri/kitty.conf new file mode 100755 index 0000000..64d46c3 --- /dev/null +++ b/kitty-niri/kitty.conf @@ -0,0 +1,48 @@ +allow_remote_control yes +listen_on unix:/tmp/kitty +shell_integration enabled + +# kitty-scrollback.nvim Kitten alias +action_alias kitty_scrollback_nvim kitten $HOME/.local/share/nvim/lazy/kitty-scrollback.nvim/python/kitty_scrollback_nvim.py +# Browse scrollback buffer in nvim +map kitty_mod+h kitty_scrollback_nvim +# Browse output of the last shell command in nvim +map kitty_mod+g kitty_scrollback_nvim --config ksb_builtin_last_cmd_output +# Show clicked command output in nvim +mouse_map ctrl+shift+right press ungrabbed combine : mouse_select_command_output : kitty_scrollback_nvim --config ksb_builtin_last_visited_cmd_output + +# disable the stupid notification +confirm_os_window_close 0 + +# set shell to fish +shell fish + +# hide_window_decorations yes +window_padding_width 10 + +background_opacity 0.95 +background_blur 16 + +font_family Maple Mono NF CN +font_size 12 + +tab_bar_style powerline +tab_powerline_style round +tab_title_template {title}{' :{}:'.format(num_windows) if num_windows > 1 else ''} + +map ctrl+up previous_window +map ctrl+down next_window + +cursor_trail 1 +cursor_shape beam + +include Catppuccin-Mocha.conf + +map ctrl+plus change_font_size all +1 +map ctrl+kp_add change_font_size all +1 + +map ctrl+minus change_font_size all -1 +map ctrl+kp_subtract change_font_size all -1 + +map ctrl+0 change_font_size all 0 +map ctrl+kp_0 change_font_size all 0 \ No newline at end of file diff --git a/kitty/Catppuccin-Macchiato.conf b/kitty/Catppuccin-Macchiato.conf deleted file mode 100644 index a45b09f..0000000 --- a/kitty/Catppuccin-Macchiato.conf +++ /dev/null @@ -1,80 +0,0 @@ -# vim:ft=kitty - -## name: Catppuccin-Macchiato -## author: Pocco81 (https://github.com/Pocco81) -## license: MIT -## upstream: https://github.com/catppuccin/kitty/blob/main/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 - -# URL underline 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/niri/config.kdl b/niri/config.kdl new file mode 100644 index 0000000..3e040bb --- /dev/null +++ b/niri/config.kdl @@ -0,0 +1,393 @@ +/************************Input************************/ + +input { + keyboard { + xkb { + layout "de" + } + + numlock + } + + touchpad { + tap + natural-scroll + scroll-method "two-finger" + } + + mouse { + accel-speed 0.25 + } + + trackpoint { + off + } + + // Make the mouse warp to the center of newly focused windows. + warp-mouse-to-focus + + // Focus windows and outputs automatically when moving the mouse into them. + focus-follows-mouse max-scroll-amount="75%" +} + +/************************Output************************/ + +output "eDP-1" { + mode "2560x1600@240" + scale 1.25 + background-color "#1e1e2e" + backdrop-color "#1e1e2e" +} + +output "eDP-2" { + mode "2560x1600@240" + scale 1.25 + background-color "#1e1e2e" + backdrop-color "#1e1e2e" +} + +/************************Layout************************/ + +layout { + // gaps in & out + gaps 2 + + center-focused-column "never" + + preset-column-widths { + proportion 0.33333 + proportion 0.5 + proportion 0.66667 + } + + preset-window-heights { + proportion 0.5 + proportion 0.75 + proportion 1.0 + } + + default-column-width { proportion 0.66667; } + + focus-ring { + width 2 + active-color "#f5c2e7" + inactive-color "#1e1e2e" + } + + border { + off + } + + shadow { + on + + softness 30 + spread 5 + offset x=0 y=5 + color "#0007" + } + + background-color "#1e1e2e" +} + +// Disable the "Important Hotkeys" pop-up at startup. +hotkey-overlay { + skip-at-startup +} + +prefer-no-csd + +animations { + // off + // slowdown 3.0 +} + +layer-rule { + match namespace="^wallpaper$" + place-within-backdrop true +} + +/************************Autostart************************/ + +// Switch configs +spawn-sh-at-startup "config-switch niri" + +// Bar +spawn-at-startup "waybar" + +// Wallpaper +spawn-sh-at-startup "swaybg -i $(find $HOME/.cache/swaybg -type f) -m fill" +spawn-sh-at-startup "swww-daemon" + +// Not necessary maybe ... +spawn-at-startup "fcitx5" + +// Core +spawn-at-startup "blueman-applet" +spawn-at-startup "nm-applet" +spawn-sh-at-startup "gnome-keyring-daemon --start --components=secrets" +spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1" +spawn-at-startup "mako" + +// idle +spawn-sh-at-startup "swayidle -w timeout 300 'hyprlock &' timeout 600 'niri msg action power-off-monitors' before-sleep 'hyprlock &'" + +// Clipboard history +spawn-sh-at-startup "wl-paste --type text --watch cliphist store" +spawn-sh-at-startup "wl-paste --type image --watch cliphist store" + +// Logitech +spawn-sh-at-startup "solaar -w hide" + +// Application associations +spawn-at-startup "kbuildsycoca6" + +// Some other heavy apps +spawn-at-startup "sunshine" + +/************************Envs************************/ + +environment { + // Input Method + QT_IM_MODULE "fcitx" + XMODIFIERS "@im=fcitx" + SDL_IM_MODULE "fcitx" + GLFW_IM_MODULE "ibus" + INPUT_METHOD "fcitx" + + // Themes + QT_QPA_PLATFORM "wayland" + QT_QPA_PLATFORMTHEME "kde" + QT_STYLE_OVERRIDE "kvantum" + + // Nvidia + LIBVA_DRIVER_NAME "nvidia" + __GLX_VENDOR_LIBRARY_NAME "nvidia" + NVD_BACKEND "nvidia" + + // Others + XCURSOR_SIZE "24" + ELECTRON_OZONE_PLATFORM_HINT "wayland" +} + +/************************Rules************************/ + +// Picture-in-Picture +window-rule { + match title="^([Pp]icture[-\\s]?[Ii]n[-\\s]?[Pp]icture)(.*)$" + open-floating true +} + +// Dialog windows +window-rule { + match title="^(Open File)(.*)$" + match title="^(Select a File)(.*)$" + match title="^(Choose wallpaper)(.*)$" + match title="^(Open Folder)(.*)$" + match title="^(Save As)(.*)$" + match title="^(Library)(.*)$" + match title="^(File Upload)(.*)$" + open-floating true +} + +// FLoating terminal +window-rule { + match app-id="com.mitchellh.ghostty" + open-floating true +} + +// Other floating +window-rule { + match app-id="blueberry" + match app-id="blueman-manager" + match app-id="pavucontrol" + match app-id="org.pulseaudio.pavucontrol" + match app-id="Waydroid" + match app-id="org.kde.kcalc" + match app-id="org.kde.kalk" + match app-id="org.gnome.NautilusPreviewer" + match app-id="coin" + match app-id="wallpaper-chooser" + match app-id="be.alexandervanhee.gradia" + match title="^(图片查看器)(.*)$" // QQ + open-floating true +} + +window-rule { + geometry-corner-radius 14 + clip-to-geometry true +} + +window-rule { + match at-startup=true app-id="Spotify" + open-on-workspace "special" +} + + +/************************Others************************/ + +cursor { + xcursor-theme "Bibata-Modern-Ice" + xcursor-size 24 + hide-when-typing +} + +screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" + +// gestures { +// hot-corners { +// off +// } +// } + +/************************Keybindings************************/ + +binds { + // Apps + Mod+C { spawn-sh "code --password-store=gnome-libsecret"; } + Mod+E { spawn "dolphin"; } + Mod+W { spawn "zen"; } + Mod+X { spawn "gnome-text-editor"; } + Mod+B { spawn-sh "killall btop || ghostty -e btop"; } + Mod+T { spawn "kitty"; } + Mod+Return { spawn "kitty"; } + Mod+Shift+T { spawn "ghostty"; } + Mod+Shift+Return { spawn "ghostty"; } + Mod+Shift+W { spawn "wallpaper-chooser"; } + + // EWW + Mod+Space { spawn-sh "eww open main --toggle"; } + Mod+Shift+L { spawn-sh "lyrics-widgets"; } + + // Waybar + Mod+Shift+K { spawn-sh "waybar-toggle"; } + + // Rofi + Mod+D { spawn-sh "pkill rofi || rofi -show run"; } + Alt+Space { spawn-sh "pkill rofi || rofi -show drun"; } + + // Actions + Mod+V { spawn-sh "pkill rofi || cliphist list | rofi -dmenu -config ~/.config/rofi/dmenu.rasi -display-columns 2 -i | cliphist decode | wl-copy"; } + Mod+Period { spawn-sh "pkill rofi || rofi-emoji"; } + Ctrl+Alt+Delete { spawn-sh "pkill wlogout || wlogout -p layer-shell"; } + Print { screenshot-screen; } + Mod+Shift+S { screenshot; } + Mod+Ctrl+Shift+S { screenshot-window; } + Mod+Shift+C { spawn-sh "hyprpicker -a"; } + + // Session + Mod+L { spawn "hyprlock"; } + + // Media + XF86AudioRaiseVolume allow-when-locked=true { spawn-sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ && wp-vol"; } + XF86AudioLowerVolume allow-when-locked=true { spawn-sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%- && wp-vol"; } + XF86AudioMute allow-when-locked=true { spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; } + XF86AudioMicMute allow-when-locked=true { spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"; } + XF86AudioPlay allow-when-locked=true { spawn-sh "playerctl play-pause"; } + XF86AudioPause allow-when-locked=true { spawn-sh "playerctl play-pause"; } + XF86AudioNext allow-when-locked=true { spawn-sh "playerctl next"; } + XF86AudioPrev allow-when-locked=true { spawn-sh "playerctl previous"; } + + // Brightness + XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "+10%"; } + XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "10%-"; } + + // Window management + Mod+Tab repeat=false { toggle-overview; } + + Mod+Q repeat=false { close-window; } + + Mod+Left { focus-column-left; } + Mod+Down { focus-window-or-workspace-down; } + Mod+Up { focus-window-or-workspace-up; } + Mod+Right { focus-column-right; } + + Mod+Shift+Left { move-column-left; } + Mod+Shift+Down { move-window-down-or-to-workspace-down; } + Mod+Shift+Up { move-window-up-or-to-workspace-up; } + Mod+Shift+Right { move-column-right; } + + Mod+Home { focus-column-first; } + Mod+End { focus-column-last; } + Mod+Shift+Home { move-column-to-first; } + Mod+Shift+End { move-column-to-last; } + + Mod+Ctrl+Left { focus-monitor-left; } + Mod+Ctrl+Down { focus-monitor-down; } + Mod+Ctrl+Up { focus-monitor-up; } + Mod+Ctrl+Right { focus-monitor-right; } + + Mod+Shift+Ctrl+Left { move-window-to-monitor-left; } + Mod+Shift+Ctrl+Down { move-window-to-monitor-down; } + Mod+Shift+Ctrl+Up { move-window-to-monitor-up; } + Mod+Shift+Ctrl+Right { move-window-to-monitor-right; } + + Mod+Page_Down { focus-workspace-down; } + Mod+Page_Up { focus-workspace-up; } + Mod+Shift+Page_Down { move-window-to-workspace-down; } + Mod+Shift+Page_Up { move-window-to-workspace-up; } + + Mod+Ctrl+Shift+Page_Down { move-workspace-down; } + Mod+Ctrl+Shift+Page_Up { move-workspace-up; } + + Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; } + Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; } + Mod+Shift+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; } + Mod+Shift+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; } + + Mod+WheelScrollRight cooldown-ms=150 { focus-column-right; } + Mod+WheelScrollLeft cooldown-ms=150 { focus-column-left; } + Mod+Shift+WheelScrollRight { move-column-right; } + Mod+Shift+WheelScrollLeft { move-column-left; } + + Mod+1 { focus-workspace 1; } + Mod+2 { focus-workspace 2; } + Mod+3 { focus-workspace 3; } + Mod+4 { focus-workspace 4; } + Mod+5 { focus-workspace 5; } + Mod+6 { focus-workspace 6; } + Mod+7 { focus-workspace 7; } + Mod+8 { focus-workspace 8; } + Mod+9 { focus-workspace 9; } + Mod+Alt+1 { move-window-to-workspace 1; } + Mod+Alt+2 { move-window-to-workspace 2; } + Mod+Alt+3 { move-window-to-workspace 3; } + Mod+Alt+4 { move-window-to-workspace 4; } + Mod+Alt+5 { move-window-to-workspace 5; } + Mod+Alt+6 { move-window-to-workspace 6; } + Mod+Alt+7 { move-window-to-workspace 7; } + Mod+Alt+8 { move-window-to-workspace 8; } + Mod+Alt+9 { move-window-to-workspace 9; } + + Mod+Alt+Left { consume-or-expel-window-left; } + Mod+Alt+Right { consume-or-expel-window-right; } + + Mod+Shift+Comma { consume-window-into-column; } + Mod+Shift+Period { expel-window-from-column; } + + Mod+R { switch-preset-column-width; } + Mod+Shift+R { switch-preset-window-height; } + Mod+Ctrl+R { reset-window-height; } + Mod+F { maximize-column; } + Mod+Shift+F { fullscreen-window; } + Mod+Ctrl+F { expand-column-to-available-width; } + + Mod+Ctrl+C { center-column; } + + Mod+Minus { set-column-width "-10%"; } + Mod+Plus { set-column-width "+10%"; } + + Mod+Shift+Minus { set-window-height "-10%"; } + Mod+Shift+Plus { set-window-height "+10%"; } + + Mod+Alt+Space { toggle-window-floating; } + Alt+Tab { switch-focus-between-floating-and-tiling; } + + Mod+Ctrl+W { toggle-column-tabbed-display; } + + Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; } + + Mod+M allow-inhibiting=false { quit; } + + Mod+Shift+P { power-off-monitors; } +} diff --git a/niri/config.kdl.template b/niri/config.kdl.template new file mode 100644 index 0000000..3b9ca92 --- /dev/null +++ b/niri/config.kdl.template @@ -0,0 +1,393 @@ +/************************Input************************/ + +input { + keyboard { + xkb { + layout "de" + } + + numlock + } + + touchpad { + tap + natural-scroll + scroll-method "two-finger" + } + + mouse { + accel-speed 0.25 + } + + trackpoint { + off + } + + // Make the mouse warp to the center of newly focused windows. + warp-mouse-to-focus + + // Focus windows and outputs automatically when moving the mouse into them. + focus-follows-mouse max-scroll-amount="75%" +} + +/************************Output************************/ + +output "eDP-1" { + mode "2560x1600@240" + scale 1.25 + background-color "#1e1e2e" + backdrop-color "#1e1e2e" +} + +output "eDP-2" { + mode "2560x1600@240" + scale 1.25 + background-color "#1e1e2e" + backdrop-color "#1e1e2e" +} + +/************************Layout************************/ + +layout { + // gaps in & out + gaps 2 + + center-focused-column "never" + + preset-column-widths { + proportion 0.33333 + proportion 0.5 + proportion 0.66667 + } + + preset-window-heights { + proportion 0.5 + proportion 0.75 + proportion 1.0 + } + + default-column-width { proportion 0.66667; } + + focus-ring { + width 2 + active-color "#" + inactive-color "#1e1e2e" + } + + border { + off + } + + shadow { + on + + softness 30 + spread 5 + offset x=0 y=5 + color "#0007" + } + + background-color "#1e1e2e" +} + +// Disable the "Important Hotkeys" pop-up at startup. +hotkey-overlay { + skip-at-startup +} + +prefer-no-csd + +animations { + // off + // slowdown 3.0 +} + +layer-rule { + match namespace="^wallpaper$" + place-within-backdrop true +} + +/************************Autostart************************/ + +// Switch configs +spawn-sh-at-startup "config-switch niri" + +// Bar +spawn-at-startup "waybar" + +// Wallpaper +spawn-sh-at-startup "swaybg -i $(find $HOME/.cache/swaybg -type f) -m fill" +spawn-sh-at-startup "swww-daemon" + +// Not necessary maybe ... +spawn-at-startup "fcitx5" + +// Core +spawn-at-startup "blueman-applet" +spawn-at-startup "nm-applet" +spawn-sh-at-startup "gnome-keyring-daemon --start --components=secrets" +spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1" +spawn-at-startup "mako" + +// idle +spawn-sh-at-startup "swayidle -w timeout 300 'hyprlock &' timeout 600 'niri msg action power-off-monitors' before-sleep 'hyprlock &'" + +// Clipboard history +spawn-sh-at-startup "wl-paste --type text --watch cliphist store" +spawn-sh-at-startup "wl-paste --type image --watch cliphist store" + +// Logitech +spawn-sh-at-startup "solaar -w hide" + +// Application associations +spawn-at-startup "kbuildsycoca6" + +// Some other heavy apps +spawn-at-startup "sunshine" + +/************************Envs************************/ + +environment { + // Input Method + QT_IM_MODULE "fcitx" + XMODIFIERS "@im=fcitx" + SDL_IM_MODULE "fcitx" + GLFW_IM_MODULE "ibus" + INPUT_METHOD "fcitx" + + // Themes + QT_QPA_PLATFORM "wayland" + QT_QPA_PLATFORMTHEME "kde" + QT_STYLE_OVERRIDE "kvantum" + + // Nvidia + LIBVA_DRIVER_NAME "nvidia" + __GLX_VENDOR_LIBRARY_NAME "nvidia" + NVD_BACKEND "nvidia" + + // Others + XCURSOR_SIZE "24" + ELECTRON_OZONE_PLATFORM_HINT "wayland" +} + +/************************Rules************************/ + +// Picture-in-Picture +window-rule { + match title="^([Pp]icture[-\\s]?[Ii]n[-\\s]?[Pp]icture)(.*)$" + open-floating true +} + +// Dialog windows +window-rule { + match title="^(Open File)(.*)$" + match title="^(Select a File)(.*)$" + match title="^(Choose wallpaper)(.*)$" + match title="^(Open Folder)(.*)$" + match title="^(Save As)(.*)$" + match title="^(Library)(.*)$" + match title="^(File Upload)(.*)$" + open-floating true +} + +// FLoating terminal +window-rule { + match app-id="com.mitchellh.ghostty" + open-floating true +} + +// Other floating +window-rule { + match app-id="blueberry" + match app-id="blueman-manager" + match app-id="pavucontrol" + match app-id="org.pulseaudio.pavucontrol" + match app-id="Waydroid" + match app-id="org.kde.kcalc" + match app-id="org.kde.kalk" + match app-id="org.gnome.NautilusPreviewer" + match app-id="coin" + match app-id="wallpaper-chooser" + match app-id="be.alexandervanhee.gradia" + match title="^(图片查看器)(.*)$" // QQ + open-floating true +} + +window-rule { + geometry-corner-radius 14 + clip-to-geometry true +} + +window-rule { + match at-startup=true app-id="Spotify" + open-on-workspace "special" +} + + +/************************Others************************/ + +cursor { + xcursor-theme "Bibata-Modern-Ice" + xcursor-size 24 + hide-when-typing +} + +screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" + +// gestures { +// hot-corners { +// off +// } +// } + +/************************Keybindings************************/ + +binds { + // Apps + Mod+C { spawn-sh "code --password-store=gnome-libsecret"; } + Mod+E { spawn "dolphin"; } + Mod+W { spawn "zen"; } + Mod+X { spawn "gnome-text-editor"; } + Mod+B { spawn-sh "killall btop || ghostty -e btop"; } + Mod+T { spawn "kitty"; } + Mod+Return { spawn "kitty"; } + Mod+Shift+T { spawn "ghostty"; } + Mod+Shift+Return { spawn "ghostty"; } + Mod+Shift+W { spawn "wallpaper-chooser"; } + + // EWW + Mod+Space { spawn-sh "eww open main --toggle"; } + Mod+Shift+L { spawn-sh "lyrics-widgets"; } + + // Waybar + Mod+Shift+K { spawn-sh "waybar-toggle"; } + + // Rofi + Mod+D { spawn-sh "pkill rofi || rofi -show run"; } + Alt+Space { spawn-sh "pkill rofi || rofi -show drun"; } + + // Actions + Mod+V { spawn-sh "pkill rofi || cliphist list | rofi -dmenu -config ~/.config/rofi/dmenu.rasi -display-columns 2 -i | cliphist decode | wl-copy"; } + Mod+Period { spawn-sh "pkill rofi || rofi-emoji"; } + Ctrl+Alt+Delete { spawn-sh "pkill wlogout || wlogout -p layer-shell"; } + Print { screenshot-screen; } + Mod+Shift+S { screenshot; } + Mod+Ctrl+Shift+S { screenshot-window; } + Mod+Shift+C { spawn-sh "hyprpicker -a"; } + + // Session + Mod+L { spawn "hyprlock"; } + + // Media + XF86AudioRaiseVolume allow-when-locked=true { spawn-sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+ && wp-vol"; } + XF86AudioLowerVolume allow-when-locked=true { spawn-sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%- && wp-vol"; } + XF86AudioMute allow-when-locked=true { spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; } + XF86AudioMicMute allow-when-locked=true { spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"; } + XF86AudioPlay allow-when-locked=true { spawn-sh "playerctl play-pause"; } + XF86AudioPause allow-when-locked=true { spawn-sh "playerctl play-pause"; } + XF86AudioNext allow-when-locked=true { spawn-sh "playerctl next"; } + XF86AudioPrev allow-when-locked=true { spawn-sh "playerctl previous"; } + + // Brightness + XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "+10%"; } + XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "10%-"; } + + // Window management + Mod+Tab repeat=false { toggle-overview; } + + Mod+Q repeat=false { close-window; } + + Mod+Left { focus-column-left; } + Mod+Down { focus-window-or-workspace-down; } + Mod+Up { focus-window-or-workspace-up; } + Mod+Right { focus-column-right; } + + Mod+Shift+Left { move-column-left; } + Mod+Shift+Down { move-window-down-or-to-workspace-down; } + Mod+Shift+Up { move-window-up-or-to-workspace-up; } + Mod+Shift+Right { move-column-right; } + + Mod+Home { focus-column-first; } + Mod+End { focus-column-last; } + Mod+Shift+Home { move-column-to-first; } + Mod+Shift+End { move-column-to-last; } + + Mod+Ctrl+Left { focus-monitor-left; } + Mod+Ctrl+Down { focus-monitor-down; } + Mod+Ctrl+Up { focus-monitor-up; } + Mod+Ctrl+Right { focus-monitor-right; } + + Mod+Shift+Ctrl+Left { move-window-to-monitor-left; } + Mod+Shift+Ctrl+Down { move-window-to-monitor-down; } + Mod+Shift+Ctrl+Up { move-window-to-monitor-up; } + Mod+Shift+Ctrl+Right { move-window-to-monitor-right; } + + Mod+Page_Down { focus-workspace-down; } + Mod+Page_Up { focus-workspace-up; } + Mod+Shift+Page_Down { move-window-to-workspace-down; } + Mod+Shift+Page_Up { move-window-to-workspace-up; } + + Mod+Ctrl+Shift+Page_Down { move-workspace-down; } + Mod+Ctrl+Shift+Page_Up { move-workspace-up; } + + Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; } + Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; } + Mod+Shift+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; } + Mod+Shift+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; } + + Mod+WheelScrollRight cooldown-ms=150 { focus-column-right; } + Mod+WheelScrollLeft cooldown-ms=150 { focus-column-left; } + Mod+Shift+WheelScrollRight { move-column-right; } + Mod+Shift+WheelScrollLeft { move-column-left; } + + Mod+1 { focus-workspace 1; } + Mod+2 { focus-workspace 2; } + Mod+3 { focus-workspace 3; } + Mod+4 { focus-workspace 4; } + Mod+5 { focus-workspace 5; } + Mod+6 { focus-workspace 6; } + Mod+7 { focus-workspace 7; } + Mod+8 { focus-workspace 8; } + Mod+9 { focus-workspace 9; } + Mod+Alt+1 { move-window-to-workspace 1; } + Mod+Alt+2 { move-window-to-workspace 2; } + Mod+Alt+3 { move-window-to-workspace 3; } + Mod+Alt+4 { move-window-to-workspace 4; } + Mod+Alt+5 { move-window-to-workspace 5; } + Mod+Alt+6 { move-window-to-workspace 6; } + Mod+Alt+7 { move-window-to-workspace 7; } + Mod+Alt+8 { move-window-to-workspace 8; } + Mod+Alt+9 { move-window-to-workspace 9; } + + Mod+Alt+Left { consume-or-expel-window-left; } + Mod+Alt+Right { consume-or-expel-window-right; } + + Mod+Shift+Comma { consume-window-into-column; } + Mod+Shift+Period { expel-window-from-column; } + + Mod+R { switch-preset-column-width; } + Mod+Shift+R { switch-preset-window-height; } + Mod+Ctrl+R { reset-window-height; } + Mod+F { maximize-column; } + Mod+Shift+F { fullscreen-window; } + Mod+Ctrl+F { expand-column-to-available-width; } + + Mod+Ctrl+C { center-column; } + + Mod+Minus { set-column-width "-10%"; } + Mod+Plus { set-column-width "+10%"; } + + Mod+Shift+Minus { set-window-height "-10%"; } + Mod+Shift+Plus { set-window-height "+10%"; } + + Mod+Alt+Space { toggle-window-floating; } + Alt+Tab { switch-focus-between-floating-and-tiling; } + + Mod+Ctrl+W { toggle-column-tabbed-display; } + + Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; } + + Mod+M allow-inhibiting=false { quit; } + + Mod+Shift+P { power-off-monitors; } +} diff --git a/nvim/lazyvim.json b/nvim/lazyvim.json index 28d1e77..5d3659a 100755 --- a/nvim/lazyvim.json +++ b/nvim/lazyvim.json @@ -12,7 +12,7 @@ ], "install_version": 7, "news": { - "NEWS.md": "10960" + "NEWS.md": "11866" }, "version": 8 } \ No newline at end of file diff --git a/swaylock/config b/swaylock/config new file mode 100644 index 0000000..e69de29 diff --git a/waybar-niri/config.jsonc b/waybar-niri/config.jsonc new file mode 100644 index 0000000..095479b --- /dev/null +++ b/waybar-niri/config.jsonc @@ -0,0 +1,213 @@ +{ + // ------------------------------------------------------------------------- + // Global configuration + // ------------------------------------------------------------------------- + "layer": "bottom", + "position": "top", + "margin-left": 0, + "margin-bottom": 0, + "margin-right": 0, + "spacing": 2, // Gaps between modules (px) + "modules-left": [ + "custom/rofi", + "custom/separator", + "group/workspaceactions", + "custom/separator", + "niri/window", + "custom/mediaplayer" + ], + "modules-center": ["clock"], + "modules-right": ["group/monitors", "custom/separator", "group/tray-expander", "idle_inhibitor", "custom/power"], + // ------------------------------------------------------------------------- + // Modules + // ------------------------------------------------------------------------- + // Separators + "custom/separator": { + "format": "|" + }, + // Buttons + "custom/power": { + "format": "󰐥", + "tooltip": false, + "on-click": "wlogout", + "min-length": 2, + "max-length": 2 + }, + "custom/rofi": { + "format": "󰣇", + "tooltip": false, + // "on-click-right": "fuzzel -l 0 -p '>> ' | xargs -r sh -c", + // "on-click": "fuzzel", + // "on-click-middle": "pkill -9 fuzzel", + "on-click": "eww open main --toggle", + "on-click-right": "pkill rofi || rofi -show drun", + "min-length": 2, + "max-length": 2 + }, + "idle_inhibitor": { + "format": "{icon}", + "format-icons": { + "activated": "", + "deactivated": "" + }, + "min-length": 2, + "max-length": 2 + }, + // Time and Date + "clock": { + "format": "{:%H:%M | %e %b}", + "tooltip-format": "{:%Y %B}\n{calendar}", + "today-format": "{}" + }, + // System monitors + "group/monitors": { + "modules": ["network#speed", "custom/publicip", "temperature", "memory", "cpu", "battery", "backlight", "wireplumber"], + "orientation": "inherit" + }, + "network#speed": { + "interval": 1, + "format": "{ifname}", + "format-wifi": " {bandwidthDownBytes}  {bandwidthUpBytes} ", + "format-ethernet": " {bandwidthDownBytes}  {bandwidthUpBytes} ", + "format-disconnected": "󰌙", + "tooltip-format": "{ipaddr}", + "format-linked": "󰈁 {ifname} (No IP)", + "tooltip-format-wifi": "{essid} {signalStrength}%", + "tooltip-format-ethernet": "{ifname} 󰌘", + "tooltip-format-disconnected": "󰌙 Disconnected", + "min-length": 20 + }, + "custom/publicip": { + "interval": 30, + "return-type": "json", + "format": " {text}", + "tooltip-format": "{alt}", + "max-length": 6, + "min-length": 6, + "exec": "$HOME/.config/waybar/modules/publicip.sh", + "on-click": "rm -f $HOME/.config/waybar/modules/publicip.cache && sleep 0.1" + }, + "temperature": { + "interval": 5, + "thermal-zone": 6, + "hwmon-path": "/sys/class/hwmon/hwmon6/temp1_input", + "critical-threshold": 80, + // "format-critical": " {temperatureC}°C", + "format-critical": " {temperatureC}°C", + "format": "{icon} {temperatureC}°C", + "format-icons": ["", "", ""], + "max-length": 6, + "min-length": 6 + }, + "memory": { + "interval": 11, + // "format": " {used:0.2f} / {total:0.0f} GB", + "format": "󰍛 {percentage}%", + "on-click": "killall btop || ghostty -e btop", + "max-length": 6, + "min-length": 6 + }, + "cpu": { + "interval": 3, + //"format": " {}%", // Icon: microchip + "format": "󰘚 {usage}%", + "max-length": 6, + "min-length": 6, + "on-click": "killall btop || ghostty -e btop" + }, + "battery": { + "interval": 30, + "states": { + "good": 95, + "warning": 30, + "critical": 15 + }, + "format": "{icon} {capacity}%", + "format-charging": " {capacity}%", + "format-plugged": " {capacity}%", + "format-icons": ["", "", "", "", ""], + "max-length": 6, + "min-length": 6 + }, + "backlight": { + "device": "$DISPLAY_DEVICE", + "format": "{icon} {percent}%", + "format-alt": "{percent}% {icon}", + "format-alt-click": "click-right", + //"format-icons": ["", ""], + "format-icons": [""], + "on-scroll-down": "brightnessctl -d $HYPR_DISPLAY_DEVICE set 5%-", + "on-scroll-up": "brightnessctl -d $HYPR_DISPLAY_DEVICE set +5%", + "max-length": 6, + "min-length": 6 + }, + "wireplumber": { + "on-click": "pavucontrol", + //on-click: "${wpctl} set-mute @DEFAULT_AUDIO_SINK@ toggle"; + "on-scroll-down": "wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 0.04-", + "on-scroll-up": "wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 0.04+", + "format": "{icon} {volume}%", + "format-muted": "", + "format-source": "", + "format-source-muted": "", + //"format-muted": "", + //"format-icons": [ "" ] + "format-icons": { + "headphone": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", "", "", "", ""] + }, + "max-length": 6, + "min-length": 6 + }, + // Hyprland + "group/workspaceactions": { + "modules": ["niri/workspaces", "custom/workspacenew"], + "orientation": "inherit" + }, + "niri/workspaces": { + "all-outputs": true, + "format": "{index}", + "on-scroll-up": "hyprctl dispatch workspace e+1 1>/dev/null", + "on-scroll-down": "hyprctl dispatch workspace e-1 1>/dev/null", + "sort-by-number": true + }, + "niri/window": { + "format": "", + "separate-outputs": true, + "icon": true, + "on-click-middle": "hyprctl dispatch killactive", + "icon-size": 14 + }, + "custom/mediaplayer": { + "format": "{text}", + "return-type": "json", + "max-length": 100, + "escape": true, + "exec": "$HOME/.config/waybar/modules/mediaplayer.py 2> /dev/null", + "on-click": "playerctl play-pause", + "on-click-right": "lyrics-widgets", + "on-scroll-up": "playerctl next", + "on-scroll-down": "playerctl previous" + }, + "group/tray-expander": { + "orientation": "inherit", + "drawer": { + "transition-duration": 600, + "children-class": "tray-group-item" + }, + "modules": ["custom/expand-icon", "tray", "custom/separator"] + }, + "custom/expand-icon": { + "format": "", + "tooltip": false, + "min-length": 2, + "max-length": 2 + }, + "tray": { + "icon-size": 15, + "spacing": 5 + } +} diff --git a/waybar-niri/mocha.css b/waybar-niri/mocha.css new file mode 100644 index 0000000..0eb6a82 --- /dev/null +++ b/waybar-niri/mocha.css @@ -0,0 +1,26 @@ +@define-color rosewater #f5e0dc; +@define-color flamingo #f2cdcd; +@define-color pink #f5c2e7; +@define-color mauve #cba6f7; +@define-color red #f38ba8; +@define-color maroon #eba0ac; +@define-color peach #fab387; +@define-color yellow #f9e2af; +@define-color green #a6e3a1; +@define-color teal #94e2d5; +@define-color sky #89dceb; +@define-color sapphire #74c7ec; +@define-color blue #89b4fa; +@define-color lavender #b4befe; +@define-color text #cdd6f4; +@define-color subtext1 #bac2de; +@define-color subtext0 #a6adc8; +@define-color overlay2 #9399b2; +@define-color overlay1 #7f849c; +@define-color overlay0 #6c7086; +@define-color surface2 #585b70; +@define-color surface1 #45475a; +@define-color surface0 #313244; +@define-color base #1e1e2e; +@define-color mantle #181825; +@define-color crust #11111b; diff --git a/waybar-niri/modules/.gitignore b/waybar-niri/modules/.gitignore new file mode 100644 index 0000000..3a64743 --- /dev/null +++ b/waybar-niri/modules/.gitignore @@ -0,0 +1,3 @@ +publicip.conf +publicip.cache +publicip.log \ No newline at end of file diff --git a/waybar-niri/modules/mediaplayer.py b/waybar-niri/modules/mediaplayer.py new file mode 100755 index 0000000..8edbe7b --- /dev/null +++ b/waybar-niri/modules/mediaplayer.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python3 +from gi.repository.Playerctl import Player +from gi.repository import Playerctl, GLib +from typing import List +import os +import json +import signal +import sys +import logging +import argparse + +import gi + +gi.require_version("Playerctl", "2.0") + + +logger = logging.getLogger(__name__) + + +def signal_handler(sig, frame): + logger.info("Received signal to stop, exiting") + sys.stdout.write("\n") + sys.stdout.flush() + # loop.quit() + sys.exit(0) + + +class PlayerManager: + def __init__(self, selected_player=None, excluded_player=[]): + self.manager = Playerctl.PlayerManager() + self.loop = GLib.MainLoop() + self.manager.connect( + "name-appeared", lambda *args: self.on_player_appeared(*args) + ) + self.manager.connect( + "player-vanished", lambda *args: self.on_player_vanished(*args) + ) + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + signal.signal(signal.SIGPIPE, signal.SIG_DFL) + self.selected_player = selected_player + self.excluded_player = excluded_player.split(",") if excluded_player else [] + + self.init_players() + + def init_players(self): + for player in self.manager.props.player_names: + if player.name in self.excluded_player: + continue + if self.selected_player is not None and self.selected_player != player.name: + logger.debug(f"{player.name} is not the filtered player, skipping it") + continue + self.init_player(player) + + def run(self): + logger.info("Starting main loop") + self.loop.run() + + def init_player(self, player): + logger.info(f"Initialize new player: {player.name}") + player = Playerctl.Player.new_from_name(player) + player.connect("playback-status", self.on_playback_status_changed, None) + player.connect("metadata", self.on_metadata_changed, None) + self.manager.manage_player(player) + self.on_metadata_changed(player, player.props.metadata) + + def get_players(self) -> List[Player]: + return self.manager.props.players + + def write_output(self, text, player): + logger.debug(f"Writing output: {text}") + + output = { + "text": text, + "class": "custom-" + player.props.player_name, + "alt": player.props.player_name, + } + + sys.stdout.write(json.dumps(output) + "\n") + sys.stdout.flush() + + def clear_output(self): + sys.stdout.write("\n") + sys.stdout.flush() + + def on_playback_status_changed(self, player, status, _=None): + logger.debug( + f"Playback status changed for player {player.props.player_name}: {status}" + ) + self.on_metadata_changed(player, player.props.metadata) + + def get_first_playing_player(self): + players = self.get_players() + logger.debug(f"Getting first playing player from {len(players)} players") + if len(players) > 0: + # if any are playing, show the first one that is playing + # reverse order, so that the most recently added ones are preferred + for player in players[::-1]: + if player.props.status == "Playing": + return player + # if none are playing, show the first one + return players[0] + else: + logger.debug("No players found") + return None + + def show_most_important_player(self): + logger.debug("Showing most important player") + # show the currently playing player + # or else show the first paused player + # or else show nothing + current_player = self.get_first_playing_player() + if current_player is not None: + self.on_metadata_changed(current_player, current_player.props.metadata) + else: + self.clear_output() + + def on_metadata_changed(self, player, metadata, _=None): + logger.debug(f"Metadata changed for player {player.props.player_name}") + player_name = player.props.player_name + artist = player.get_artist() + title = player.get_title() + title = title.replace("&", "&") + + track_info = "" + if ( + player_name == "spotify" + and "mpris:trackid" in metadata.keys() + and ":ad:" in player.props.metadata["mpris:trackid"] + ): + track_info = "Advertisement" + elif artist is not None and title is not None: + track_info = f"{artist} - {title}" + else: + track_info = title + + if track_info: + if player.props.status == "Playing": + track_info = " " + track_info + else: + track_info = " " + track_info + # only print output if no other player is playing + current_playing = self.get_first_playing_player() + if ( + current_playing is None + or current_playing.props.player_name == player.props.player_name + ): + self.write_output(track_info, player) + else: + logger.debug( + f"Other player {current_playing.props.player_name} is playing, skipping" + ) + + def on_player_appeared(self, _, player): + logger.info(f"Player has appeared: {player.name}") + if player.name in self.excluded_player: + logger.debug( + "New player appeared, but it's in exclude player list, skipping" + ) + return + if player is not None and ( + self.selected_player is None or player.name == self.selected_player + ): + self.init_player(player) + else: + logger.debug( + "New player appeared, but it's not the selected player, skipping" + ) + + def on_player_vanished(self, _, player): + logger.info(f"Player {player.props.player_name} has vanished") + self.show_most_important_player() + + +def parse_arguments(): + parser = argparse.ArgumentParser() + + # Increase verbosity with every occurrence of -v + parser.add_argument("-v", "--verbose", action="count", default=0) + + parser.add_argument("-x", "--exclude", "- Comma-separated list of excluded player") + + # Define for which player we"re listening + parser.add_argument("--player") + + parser.add_argument("--enable-logging", action="store_true") + + return parser.parse_args() + + +def main(): + arguments = parse_arguments() + + # Initialize logging + if arguments.enable_logging: + logfile = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "media-player.log" + ) + logging.basicConfig( + filename=logfile, + level=logging.DEBUG, + format="%(asctime)s %(name)s %(levelname)s:%(lineno)d %(message)s", + ) + + # Logging is set by default to WARN and higher. + # With every occurrence of -v it's lowered by one + logger.setLevel(max((3 - arguments.verbose) * 10, 0)) + + logger.info("Creating player manager") + if arguments.player: + logger.info(f"Filtering for player: {arguments.player}") + if arguments.exclude: + logger.info(f"Exclude player {arguments.exclude}") + + player = PlayerManager(arguments.player, arguments.exclude) + player.run() + + +if __name__ == "__main__": + main() diff --git a/waybar-niri/modules/publicip.sh b/waybar-niri/modules/publicip.sh new file mode 100755 index 0000000..57f5485 --- /dev/null +++ b/waybar-niri/modules/publicip.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091,SC1090 + +# Entries in publicip.conf: +# IP_QUERY_URL: URL to query public IP of the system. +# return: JSON object with "ip" field. +# note: This URL will be queried with short intervals (60s for example), +# therefore it may not be a good idea to use a public API with +# a limited number of calls. +# IP_INFO_URL: URL to query IP location. +# placeholder: +# return: JSON object with "country_code" field. +# note: This URL will only be quetried when public IP changes or when "force" is given as parameter. + +path="$(dirname "$(readlink -f "$0")")" +cache_file="$path/publicip.cache" +config_file="$path/publicip.conf" +time_log="$path/publicip.log" + +[ -f "$config_file" ] || exit 1 +. "$config_file" +[ -z "$IP_QUERY_URL" ] && exit 1 +[ -z "$IP_INFO_URL" ] && exit 1 +[ "$1" == "force" ] && rm -f "$cache_file" +[ -f "$cache_file" ] && . "$cache_file" + +# Try to check network connectivity before querying +if ! ping -c 1 -W 2 1.1.1.1 >/dev/null 2>&1; then + # No network, return cached values if available + [ -z "$CACHED_IP" ] && CACHED_IP="N/A" + [ -z "$CACHED_CODE" ] && CACHED_CODE="N/A" + + echo "$(date +%Y-%m-%dT%H:%M:%S) - Waiting for network" >>"$time_log" + + jq -n --unbuffered --compact-output \ + --arg ip "$CACHED_IP" \ + --arg country "$CACHED_CODE" \ + '{alt: $ip, text: $country}' + exit 0 +fi + +ip_current=$(curl -s -L -4 "$IP_QUERY_URL" | jq -r '.ip') +[ -z "$ip_current" ] && exit 1 + +if [ "$ip_current" != "$CACHED_IP" ]; then + echo "$(date +%Y-%m-%dT%H:%M:%S) - IP changed: $CACHED_IP -> $ip_current" >>"$time_log" + CACHED_IP="$ip_current" + + ip_info_url=${IP_INFO_URL///$ip_current} + CACHED_CODE=$(curl -s -L "$ip_info_url" | jq -r '.country_code') + [ -z "$CACHED_CODE" ] && CACHED_CODE="N/A" + + echo "CACHED_IP=$CACHED_IP" >"$cache_file" + echo "CACHED_CODE=$CACHED_CODE" >>"$cache_file" + notify-send "New Public IP detected" "New IP: $ip_current\nCountry: $CACHED_CODE" +fi + +jq -n --unbuffered --compact-output \ + --arg ip "$CACHED_IP" \ + --arg country "$CACHED_CODE" \ + '{alt: $ip, text: $country}' + diff --git a/waybar-niri/style.css b/waybar-niri/style.css new file mode 100644 index 0000000..bf34a33 --- /dev/null +++ b/waybar-niri/style.css @@ -0,0 +1,217 @@ +@import 'mocha.css'; + +@define-color flavor #f5c2e7; +/* @define-color archlinux #1793d1; */ +@define-color archlinux @sapphire; + +/* Font(s) */ +* { + /* main font icons CJK fallback */ + font-family: 'Sour Gummy Light', 'Meslo LGM Nerd Font Mono', 'WenQuanYi Micro Hei', 'Noto Sans', sans-serif; + font-size: 16px; +} + +/* Reset all styles */ +* { + border: none; + border-radius: 14px; + min-height: 0; + margin: 2px 1px 2px 1px; + padding: 0; + transition-property: background-color; + transition-duration: 0.5s; +} + +/* The whole bar */ +#waybar { + background: linear-gradient(to bottom, alpha(@base, 0.8), alpha(@base, 0)); + /* background: transparent; */ + color: @text; + border-radius: 0px; + margin: 0px; +} + +tooltip { + background: @base; + border: 2px solid @overlay0; +} + +#workspaceactions, +#window, +#clock, +#monitors, +#custom-mediaplayer, +#custom-power-menu, +#tray, +#custom-rofi, +#idle_inhibitor, +#custom-power { + padding: 0px 10px; +} + +#custom-separator { + padding: 0px 5px; + font-size: 20px; +} + +#custom-rofi, +#idle_inhibitor, +#custom-power, +#custom-expand-icon { + padding: 0px 6px; + font-size: 18px; +} + +#custom-workspacenew, +#workspaces button { + padding: 0px; + margin: 3px 3px; + border-radius: 8px; + color: @flavor; + background-color: transparent; + transition: all 0.3s ease-in-out; +} + +#workspaces button:hover { + background-color: alpha(@flavor, 0.3); +} + +#workspaces button.active { + color: @base; + background: @flavor; +} + +#workspaces button.urgent { + color: @base; + background-color: @red; +} + +#workspaceactions { + padding-left: 1px; + padding-right: 1px; +} + +#workspaces { + padding: 0px; + margin: 0px; +} + +#window { + transition: none; /* Disable background transition */ +} + +window#waybar.empty #window { + background-color: transparent; + padding: 0; + margin: 0; +} + +#custom-mediaplayer { + color: @flavor; +} + +#network.speed { + color: @flavor; +} + +#custom-publicip { + color: @peach; +} + +#temperature { + color: @yellow; +} + +#memory { + color: @green; +} + +#cpu { + color: @teal; +} + +#battery { + color: @sapphire; +} + +#battery.charging, +#battery.full, +#battery.plugged { + color: @green; +} + +#backlight { + color: @blue; +} + +#wireplumber { + color: @lavender; +} + +#custom-power { + color: @maroon; +} + +#custom-power:hover { + background-color: alpha(@maroon, 0.3); +} + +#custom-rofi { + color: @archlinux; +} + +#custom-rofi:hover { + background-color: alpha(@archlinux, 0.3); +} + +#custom-expand-icon { + color: @green; +} + +#idle_inhibitor { + color: @yellow; +} + +#idle_inhibitor:hover { + background-color: alpha(@yellow, 0.3); +} + +#idle_inhibitor.activated { + color: @peach; +} + +#clock { + color: @flavor; +} + +@keyframes blink { + to { + background-color: alpha(@red, 0.5); + } +} + +#battery.critical:not(.charging) { + color: @red; + animation-name: blink; + animation-duration: 1s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +#network.disconnected { + color: @red; +} + +#temperature.critical { + background-color: #eb4d4b; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; +} diff --git a/waybar-niri/style.css.template b/waybar-niri/style.css.template new file mode 100644 index 0000000..5fb3644 --- /dev/null +++ b/waybar-niri/style.css.template @@ -0,0 +1,217 @@ +@import 'mocha.css'; + +@define-color flavor #; +/* @define-color archlinux #1793d1; */ +@define-color archlinux @sapphire; + +/* Font(s) */ +* { + /* main font icons CJK fallback */ + font-family: 'Sour Gummy Light', 'Meslo LGM Nerd Font Mono', 'WenQuanYi Micro Hei', 'Noto Sans', sans-serif; + font-size: 16px; +} + +/* Reset all styles */ +* { + border: none; + border-radius: 14px; + min-height: 0; + margin: 2px 1px 2px 1px; + padding: 0; + transition-property: background-color; + transition-duration: 0.5s; +} + +/* The whole bar */ +#waybar { + background: linear-gradient(to bottom, alpha(@base, 0.8), alpha(@base, 0)); + /* background: transparent; */ + color: @text; + border-radius: 0px; + margin: 0px; +} + +tooltip { + background: @base; + border: 2px solid @overlay0; +} + +#workspaceactions, +#window, +#clock, +#monitors, +#custom-mediaplayer, +#custom-power-menu, +#tray, +#custom-rofi, +#idle_inhibitor, +#custom-power { + padding: 0px 10px; +} + +#custom-separator { + padding: 0px 5px; + font-size: 20px; +} + +#custom-rofi, +#idle_inhibitor, +#custom-power, +#custom-expand-icon { + padding: 0px 6px; + font-size: 18px; +} + +#custom-workspacenew, +#workspaces button { + padding: 0px; + margin: 3px 3px; + border-radius: 8px; + color: @flavor; + background-color: transparent; + transition: all 0.3s ease-in-out; +} + +#workspaces button:hover { + background-color: alpha(@flavor, 0.3); +} + +#workspaces button.active { + color: @base; + background: @flavor; +} + +#workspaces button.urgent { + color: @base; + background-color: @red; +} + +#workspaceactions { + padding-left: 1px; + padding-right: 1px; +} + +#workspaces { + padding: 0px; + margin: 0px; +} + +#window { + transition: none; /* Disable background transition */ +} + +window#waybar.empty #window { + background-color: transparent; + padding: 0; + margin: 0; +} + +#custom-mediaplayer { + color: @flavor; +} + +#network.speed { + color: @flavor; +} + +#custom-publicip { + color: @peach; +} + +#temperature { + color: @yellow; +} + +#memory { + color: @green; +} + +#cpu { + color: @teal; +} + +#battery { + color: @sapphire; +} + +#battery.charging, +#battery.full, +#battery.plugged { + color: @green; +} + +#backlight { + color: @blue; +} + +#wireplumber { + color: @lavender; +} + +#custom-power { + color: @maroon; +} + +#custom-power:hover { + background-color: alpha(@maroon, 0.3); +} + +#custom-rofi { + color: @archlinux; +} + +#custom-rofi:hover { + background-color: alpha(@archlinux, 0.3); +} + +#custom-expand-icon { + color: @green; +} + +#idle_inhibitor { + color: @yellow; +} + +#idle_inhibitor:hover { + background-color: alpha(@yellow, 0.3); +} + +#idle_inhibitor.activated { + color: @peach; +} + +#clock { + color: @flavor; +} + +@keyframes blink { + to { + background-color: alpha(@red, 0.5); + } +} + +#battery.critical:not(.charging) { + color: @red; + animation-name: blink; + animation-duration: 1s; + animation-timing-function: linear; + animation-iteration-count: infinite; + animation-direction: alternate; +} + +#network.disconnected { + color: @red; +} + +#temperature.critical { + background-color: #eb4d4b; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + background-color: #eb4d4b; +} diff --git a/waybar-niri/waybar.sh b/waybar-niri/waybar.sh new file mode 100755 index 0000000..d03b9eb --- /dev/null +++ b/waybar-niri/waybar.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh + +# Terminate already running bar instances +killall -q waybar + +# Wait until the processes have been shut down +while pgrep -x waybar >/dev/null; do sleep 1; done + +# Launch main +waybar & diff --git a/wlogout/style.css b/wlogout/style.css index 0e8f9ec..408f348 100644 --- a/wlogout/style.css +++ b/wlogout/style.css @@ -30,7 +30,7 @@ button:focus, button:active, button:hover { background-size: 20%; - background-color: alpha(#1e1e2e, 1); + background-color: alpha(#1e1e2e, 0.7); animation: gradient_f 20s ease-in infinite; transition: all 0.3s cubic-bezier(0.55, 0, 0.28, 1.682); } diff --git a/wlogout/style.css.template b/wlogout/style.css.template index 1784646..1384e56 100644 --- a/wlogout/style.css.template +++ b/wlogout/style.css.template @@ -30,7 +30,7 @@ button:focus, button:active, button:hover { background-size: 20%; - background-color: alpha(#1e1e2e, 1); + background-color: alpha(#1e1e2e, 0.7); animation: gradient_f 20s ease-in infinite; transition: all 0.3s cubic-bezier(0.55, 0, 0.28, 1.682); }