Compare commits

...

37 Commits

Author SHA1 Message Date
Uyanide 311a89c0e6 update 2026-05-05 09:22:05 +02:00
Uyanide 874311fcb0 update 2026-05-04 23:02:34 +02:00
Uyanide e648c1f1a6 stop building the wheels! 2026-05-04 23:02:18 +02:00
Uyanide f789df0f02 update 2026-05-03 12:37:05 +02:00
Uyanide 417d48f0f5 update 2026-05-03 00:11:07 +02:00
Uyanide a60f923e59 update 2026-04-29 00:16:38 +02:00
Uyanide fff6a726d8 yazi: update keybinds 2026-04-27 18:47:20 +02:00
Uyanide ac91f71a92 niri: disable xray for floating windows 2026-04-25 22:50:47 +02:00
Uyanide 5fefee5e2c qs: add caps event handler for niri ipc 2026-04-25 22:38:21 +02:00
Uyanide 6f90fe5d28 gtk settings 2026-04-25 21:58:04 +02:00
Uyanide b043039705 ya pkg upgrade 2026-04-25 21:58:04 +02:00
Uyanide 9453784fb8 regard ghostty shaders as submodule 2026-04-25 21:52:52 +02:00
Uyanide 410021ae9e update 2026-04-25 21:52:52 +02:00
Uyanide 6a70cb85a3 update 2026-04-25 21:52:52 +02:00
Uyanide 24b90c6721 update 2026-04-25 21:52:52 +02:00
Uyanide 26e7b292d7 add ghostty shaders 2026-04-25 21:52:52 +02:00
Uyanide 6c1bc55687 Refactor README structure and content
Reorganize sections and update details for clarity.
2026-04-25 21:52:51 +02:00
Uyanide de839f7faf update 2026-04-25 20:41:43 +02:00
Uyanide 12d3fdcaa5 update 2026-04-25 20:41:31 +02:00
Uyanide 88c522e9d1 update 2026-04-25 12:19:51 +02:00
Uyanide e3c273516e update
Co-authored-by: Copilot <copilot@github.com>
2026-04-24 21:41:21 +02:00
Uyanide c09865abbc qt: fix media notify 2026-04-21 22:26:19 +02:00
Uyanide 8e2029a768 fix: quickshell-kill 2026-04-21 13:43:54 +02:00
Uyanide 1d9a7cfecf update 2026-04-21 10:48:42 +02:00
Uyanide 73c5a74d0f isCapslockOn ? "CAPS LOCK ON" : "caps lock off" 2026-04-20 19:02:20 +02:00
Uyanide 294788a314 led_monitor -> led-monitor 2026-04-20 18:59:18 +02:00
Uyanide 454262a803 qs: add capslock monitor 2026-04-20 18:55:59 +02:00
Uyanide 266ea92f4a qs: add TempNotification 2026-04-20 17:49:13 +02:00
Uyanide 8562298d83 update 2026-04-20 12:30:30 +02:00
Uyanide 315f9442ae Update README.md
Updated the asset link for Niri & Quickshell in README.
2026-04-19 18:53:52 +02:00
Uyanide 1f52782226 update 2026-04-14 22:57:32 +02:00
Uyanide 7f41488da6 update 2026-04-13 18:25:22 +02:00
Uyanide 561be8b27a update 2026-04-08 21:06:19 +02:00
Uyanide fb6289e032 update 2026-04-08 21:03:43 +02:00
Uyanide e022516712 qs: update 2026-04-08 09:02:15 +02:00
Uyanide 18ac682f98 update 2026-04-04 06:25:34 +02:00
Uyanide 93e8a13765 back to ghostty 2026-04-01 17:03:26 +02:00
107 changed files with 2352 additions and 1272 deletions
+3
View File
@@ -4,3 +4,6 @@
[submodule "assets/yazi-catppuccin"]
path = assets/yazi-catppuccin
url = https://github.com/catppuccin/yazi.git
[submodule "config/ghostty/.config/ghostty/shaders"]
path = config/ghostty/.config/ghostty/shaders
url = https://github.com/0xhckr/ghostty-shaders.git
+43 -23
View File
@@ -3,19 +3,9 @@
## How it looks like...
<details>
<summary>Hyprland & Waybar & Eww</summary>
<figure>
<img src="https://github.com/Uyanide/backgrounds/blob/master/screenshots/desktop.jpg?raw=true"/>
</figure>
</details>
<details>
<summary>Niri & Quickshell</summary>
https://github.com/user-attachments/assets/1fd0f3be-e83f-4d1c-9e4f-16cc77e3981b
https://github.com/user-attachments/assets/2550607a-48ea-4662-98ba-d26722b26b1b
<figure>
<img src="https://github.com/Uyanide/backgrounds/blob/master/screenshots/desktop-alt.webp?raw=true"/>
@@ -47,10 +37,10 @@ https://github.com/user-attachments/assets/1fd0f3be-e83f-4d1c-9e4f-16cc77e3981b
- Bar: ~~Waybar~~ | **Quickshell**
- Shell: (bash & fish) | **Zsh**
- Prompt: Oh My Posh | **Starship**
- Terminal: **Kitty** & (**WezTerm** | Ghostty)
- Terminal: **Kitty** | WezTerm | Ghostty
- Power Menu: **Wlogout** & Quickshell
- Colorscheme: **Catppuccin Mocha**
- App Launcher: **Rofi** | ~~Fuzzel~~
- App Launcher: ~~Rofi~~ | ~~Fuzzel~~ | **vicinae**
- Desktop Widgets: ~~Eww~~ | **Quickshell**
- Wallpaper Daemon: ~~Awww~~ | **Quickshell**
- Notification Daemon: ~~Mako~~ | **Quickshell**
@@ -66,22 +56,12 @@ Not based on, but heavily depends on many modules from (an old version of) [noct
This setup is currently only adapted for Niri.
## Eww
- `main`, main dashboard, modified from [syndrizzle/hotfiles](https://github.com/syndrizzle/hotfiles/tree/bspwm) but without notification center.
- `lyrics`, scrolling lyrics player, depends on [a small utility](https://github.com/Uyanide/Spotify_Lyrics) from myself <small>(which also happens to be my frist Golang program :D)</small>.
- `lyrics-single`, similar to `lyrics`, but only with a single line and can be easily embeded into the status bar.
## Wallpaper & Colortheme
- [WallReel](https://github.com/Uyanide/WallReel): an Image Carousel implemented with QtQuick to browse and set wallpapers from.
- [change-colortheme](./config/scripts/.local/scripts/change-colortheme): script that extract colors from the current wallpaper and generate a catppuccin color scheme accordingly.
- [backgrounds](https://github.com/Uyanide/backgrounds) collection for personal use (mostly waifus).
## Rofi
Based on [codeopshq/dotfiles](https://github.com/codeopshq/dotfiles), also serves as the clipboard history browser and emoji picker.
## Grub theme
Based on [vinceliuice/Elegant-grub2-themes](https://github.com/vinceliuice/Elegant-grub2-themes) with an [illustration from 紺屋](https://www.pixiv.net/artworks/119683453).
@@ -89,3 +69,43 @@ Based on [vinceliuice/Elegant-grub2-themes](https://github.com/vinceliuice/Elega
## Fonts
See [fontconfig.md](https://github.com/Uyanide/dotfiles/blob/main/memo/fontconfig.md).
---
<details>
<summary>Previous setup</summary>
## How it looks like...
<figure>
<img src="https://github.com/Uyanide/backgrounds/blob/master/screenshots/desktop.jpg?raw=true"/>
</figure>
## Hyprland & friends
Based on an old version of [end-4/dots-hyprland](https://github.com/end-4/dots-hyprland) but without ags, quickshell, eww and tons of other stuff.
## Eww
- `main`, main dashboard, modified from [syndrizzle/hotfiles](https://github.com/syndrizzle/hotfiles/tree/bspwm) but without notification center.
- `lyrics`, scrolling lyrics player, depends on [a small utility](https://github.com/Uyanide/Spotify_Lyrics) from myself <small>(which also happens to be my frist Golang program :D)</small>.
- `lyrics-single`, similar to `lyrics`, but only with a single line and can be easily embeded into the status bar.
## Swww
The wallpaper will be automatically blurred when there is a window in focus, which is implemented in the [wallpaper-daemon](https://github.com/Uyanide/dotfiles/blob/main/.scripts/wallpaper-daemon) script.
This feature is only enabled in Niri. Swww also manages wallpapers of the Hyprland setup, yet only in the regular way.
## Wallpaper & Colortheme
The most suitable primary color (or so-called flavor) will be chosen from the [Catppuccin Mocha](https://catppuccin.com/palette/) palette and applied to various apps automatically after changing wallpaper. And also:
- [wallpaper-chooser](https://github.com/Uyanide/Wallpaper_Chooser) to select wallpaper, which implements an Image Carousel with Qt Widgets.
- [backgrounds collection](https://github.com/Uyanide/backgrounds) for personal use.
## Rofi
Based on [codeopshq/dotfiles](https://github.com/codeopshq/dotfiles), also serves as the clipboard history browser and emoji picker.
</details>
+3 -26
View File
@@ -17,7 +17,6 @@ TUI_PKGS = [
*BASE_PKGS,
"fastfetch", # sys info,
"helix", # editor
"nvim", # editor
"shell", # fish & .bash_profile & shell prompt
"yazi", # terminal file manager
]
@@ -55,7 +54,7 @@ NIRI_PKGS = [
"hypr", # for hyprlock & hypridle
"niri", # wm config
"quickshell", # widgets & status bar & notifications & ...
"rofi", # application launcher
"vicinae", # application launcher
"wlogout", # logout menu
]
@@ -67,12 +66,6 @@ PKGS = {
"niri": NIRI_PKGS,
}
SESSION_NAME = {
"hyprland": "Hyprland",
"niri": "niri",
"default": "default",
}
PKGS_PATH = Path(__file__).resolve().parent.resolve() / "config"
DEST_PATH = Path.home().expanduser()
@@ -103,13 +96,8 @@ def stow(pkg: str):
def unstow(pkg: str):
subprocess.run(
["stow", "-v", "-d", str(PKGS_PATH), "-t", str(DEST_PATH), "-D", pkg], check=True,
)
def switch(session: str):
subprocess.run(
[str(Path("~/.local/scripts/config-switch").expanduser()), session], check=True
["stow", "-v", "-d", str(PKGS_PATH), "-t", str(DEST_PATH), "-D", pkg],
check=True,
)
@@ -147,17 +135,6 @@ def main():
if is_unstow:
return # No need to switch session if we're just unstowing
if args.package in SESSION_NAME:
session = SESSION_NAME[args.package]
else:
session = SESSION_NAME["default"]
try:
switch(session)
_log("INFO", f"Switched to session profile '{session}'.")
except subprocess.CalledProcessError as e:
_log("ERROR", f"Failed to switch session profile '{session}': {e}")
if __name__ == "__main__":
main()
@@ -1 +0,0 @@
shaders
+4 -1
View File
@@ -1,11 +1,14 @@
theme = Catppuccin Mocha
background-opacity = 0.75
background-opacity = 0.9
background-blur = true
window-padding-x = 10
window-padding-y = 10
window-width = 100
window-height = 36
keybind = ctrl+shift+r=reload_config
keybind = ctrl+shift+h=write_screen_file:copy
+35 -27
View File
@@ -2,48 +2,56 @@ theme = "catppuccin_mocha"
[editor]
default-yank-register = "+"
shell = ["bash", "-c"]
line-number = "relative"
indent-guides.render = true
shell = ["bash", "-c"]
line-number = "relative"
indent-guides.render = true
trim-trailing-whitespace = true
[editor.cursor-shape]
insert = "bar"
normal = "block"
select = "underline"
[editor.whitespace.render]
space = "none"
tab = "all"
nbsp = "none"
nnbsp = "none"
newline = "none"
[keys.normal]
# "esc" = "collapse_selection"
"-" = "collapse_selection"
"-" = "collapse_selection"
"A-minus" = "flip_selections"
"#" = "switch_to_lowercase"
"A-#" = "switch_to_uppercase"
"#" = "switch_to_lowercase"
"A-#" = "switch_to_uppercase"
"+" = "select_register"
# Previously "["
[keys.normal."ö"]
"d" = "goto_prev_diag"
"D" = "goto_first_diag"
"f" = "goto_prev_function"
"t" = "goto_prev_class"
"a" = "goto_prev_parameter"
"c" = "goto_prev_comment"
"T" = "goto_prev_test"
"p" = "goto_prev_paragraph"
"g" = "goto_prev_change"
"G" = "goto_first_change"
"d" = "goto_prev_diag"
"D" = "goto_first_diag"
"f" = "goto_prev_function"
"t" = "goto_prev_class"
"a" = "goto_prev_parameter"
"c" = "goto_prev_comment"
"T" = "goto_prev_test"
"p" = "goto_prev_paragraph"
"g" = "goto_prev_change"
"G" = "goto_first_change"
"space" = "add_newline_above"
# Previously "]"
[keys.normal."ä"]
"d" = "goto_next_diag"
"D" = "goto_last_diag"
"f" = "goto_next_function"
"t" = "goto_next_class"
"a" = "goto_next_parameter"
"c" = "goto_next_comment"
"T" = "goto_next_test"
"p" = "goto_next_paragraph"
"g" = "goto_next_change"
"G" = "goto_last_change"
"d" = "goto_next_diag"
"D" = "goto_last_diag"
"f" = "goto_next_function"
"t" = "goto_next_class"
"a" = "goto_next_parameter"
"c" = "goto_next_comment"
"T" = "goto_next_test"
"p" = "goto_next_paragraph"
"g" = "goto_next_change"
"G" = "goto_last_change"
"space" = "add_newline_below"
# [keys.insert."j"]
@@ -1,13 +1,7 @@
[[language]]
name = "c"
auto-format = true
indent = { tab-width = 4, unit = " " }
[[language]]
name = "cpp"
auto-format = true
indent = { tab-width = 4, unit = " " }
[[language]]
name = "toml"
auto-format = true
@@ -1,48 +0,0 @@
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 zsh
shell /bin/zsh
# hide_window_decorations yes
window_padding_width 10
background_opacity 0.75
background_blur 16
font_family monospace
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
@@ -1,80 +0,0 @@
# 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
+7
View File
@@ -0,0 +1,7 @@
include kitty.conf
remember_window_size false
initial_window_width 960
initial_window_height 720
window_padding_width 5
@@ -3,13 +3,13 @@ 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
# 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
# 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
# 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
# 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
@@ -20,7 +20,7 @@ shell /bin/zsh
# hide_window_decorations yes
window_padding_width 10
background_opacity 0.95
background_opacity 0.9
background_blur 16
font_family monospace
+20 -17
View File
@@ -12,16 +12,18 @@ binds {
// Apps
Mod+C repeat=false { spawn "code"; }
Mod+E repeat=false { spawn "dolphin" "--new-window"; }
Mod+Shift+E repeat=false { spawn "nautilus" "--new-window"; }
Mod+W repeat=false { spawn-sh "zen || zen-browser"; }
Mod+B repeat=false { spawn-sh "pkill -x -n btop || wezterm -e btop"; }
Mod+Shift+T repeat=false { spawn "wezterm"; }
Mod+Shift+Return repeat=false { spawn "wezterm"; }
Mod+B repeat=false { spawn-sh "pkill -x -n btop || kitty-floating -e btop"; }
Mod+Shift+T repeat=false { spawn "kitty-floating"; }
Mod+Shift+Return repeat=false { spawn "kitty-floating"; }
Mod+T repeat=false { spawn "kitty"; }
Mod+Return repeat=false { spawn "kitty"; }
Mod+Shift+W repeat=false { spawn "wallreel"; }
Mod+O repeat=false { spawn-sh "pkill -x -n pwvucontrol || pwvucontrol"; }
Ctrl+Alt+Delete repeat=false { spawn-sh "pkill -x wlogout || wlogout"; }
Mod+P repeat=false { spawn-sh "wl-mirror $(niri msg --json focused-output | jq -r .name)"; }
Mod+H repeat=false { spawn "hexecute"; }
// Quickshell
@@ -32,20 +34,20 @@ binds {
Mod+I repeat=false { spawn "qs" "ipc" "call" "idleInhibitor" "toggle"; }
Mod+Alt+R repeat=false { spawn "qs" "ipc" "call" "recording" "startOrStop"; }
Mod+Alt+G repeat=false { spawn "qs" "ipc" "call" "recording" "saveReplay"; }
Mod+Shift+E repeat=false { spawn "qs" "ipc" "call" "sunset" "toggle"; }
Mod+S repeat=false { spawn "qs" "ipc" "call" "sunset" "toggle"; }
Mod+X repeat=false { spawn "qs" "ipc" "call" "notes" "openRecent"; }
Mod+Shift+X repeat=false { spawn "qs" "ipc" "call" "notes" "create"; }
// Rofi
Mod+D repeat=false { spawn-sh "pkill -x rofi || rofi -show run"; }
Alt+Space repeat=false { spawn-sh "pkill -x rofi || rofi -show drun"; }
// Launcher
Alt+Space repeat=false { spawn "vicinae" "toggle"; }
Mod+V repeat=false { spawn "vicinae" "vicinae://launch/clipboard/history?toggle=true"; }
Mod+Period repeat=false { spawn "vicinae" "vicinae://launch/core/search-emojis?toggle=true"; }
Mod+D repeat=false { spawn "vicinae" "vicinae://launch/system/run?toggle=true"; }
// Actions
Mod+V repeat=false { spawn "wezterm" "start" "--" "fzfclip-wrap"; }
Mod+Period repeat=false { spawn-sh "pkill -x rofi || rofi-emoji"; }
Print repeat=false { screenshot-screen; }
Mod+Shift+S repeat=false { screenshot; }
Mod+Ctrl+Shift+S repeat=false { screenshot-window; }
Print repeat=false { screenshot-screen show-pointer=false; }
Mod+Shift+S repeat=false { screenshot show-pointer=false; }
Mod+Ctrl+Shift+S repeat=false { screenshot-window show-pointer=false; }
Mod+Shift+C repeat=false { spawn "hyprpicker" "-a"; }
// Media
@@ -63,10 +65,11 @@ binds {
XF86MonBrightnessDown allow-when-locked=true { spawn "qs" "ipc" "call" "brightness" "down"; }
// Window management
Mod+Tab repeat=false { toggle-overview; }
Mod+Tab repeat=false { toggle-overview; }
Mod+Q repeat=false { close-window; }
Alt+F4 repeat=false { close-window; } // can't imagine this does not come as default
Mod+Q repeat=false { close-window; }
Mod+Shift+Q repeat=false { spawn "niri-force-kill-window"; }
Alt+F4 repeat=false { close-window; } // can't imagine this does not come as default
Mod+Left { focus-column-left; }
Mod+Down { focus-window-or-workspace-down; }
@@ -159,8 +162,8 @@ binds {
Mod+Escape allow-inhibiting=false repeat=false { toggle-keyboard-shortcuts-inhibit; }
// Session
Mod+Shift+Q allow-inhibiting=false repeat=false { quit; }
Mod+Shift+P allow-inhibiting=false repeat=false { spawn-sh "hyprlock & niri msg action power-off-monitors"; }
Mod+K allow-inhibiting=false repeat=false { quit; }
Mod+Shift+P allow-inhibiting=false repeat=false { spawn-sh "loginctl lock-session; niri msg action power-off-monitors"; }
Mod+L allow-inhibiting=false repeat=false { spawn "loginctl" "lock-session"; }
}
+2 -17
View File
@@ -1,21 +1,8 @@
// Switch configs
spawn-at-startup "config-switch" "niri"
// Not necessary maybe ...
spawn-at-startup "fcitx5"
// Core
spawn-at-startup "nm-applet"
spawn-at-startup "gnome-keyring-daemon" "--start" "--components=secrets"
spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1"
// Clipboard history
spawn-at-startup "wl-paste" "--type" "text" "--watch" "cliphist" "store"
spawn-at-startup "wl-paste" "--type" "image" "--watch" "cliphist" "store"
// wlsunset
// spawn-at-startup "sunset" // managed by quickshell
// Logitech
spawn-at-startup "solaar" "-w" "hide"
@@ -30,7 +17,5 @@ spawn-at-startup "hypridle"
// QuickShell
spawn-at-startup "quickshell"
// According to (https://ghostty.org/docs/linux/systemd#starting-ghostty-at-login)
// spawn-sh-at-startup "systemctl start --user app-com.mitchellh.ghostty.service"
//
// spawn-at-startup "flatpak" "run" "com.gopeed.Gopeed" "--hidden"
// Vicinae
spawn-at-startup "vicinae" "server"
+1
View File
@@ -2,6 +2,7 @@ screenshot-path "~/Pictures/Screenshots/niri_screenshot_%Y-%m-%d_%H-%M-%S.png"
debug {
render-drm-device "/dev/dri/renderD129"
honor-xdg-activation-with-invalid-serial
}
// gestures {
+10 -1
View File
@@ -19,6 +19,8 @@ window-rule {
// FLoating terminal
window-rule {
match app-id="org.wezfurlong.wezterm"
match app-id="com.mitchellh.ghostty"
match app-id="kitty-floating"
open-floating true
default-column-width { proportion 0.5; }
}
@@ -60,9 +62,16 @@ window-rule {
open-floating true
}
// QQ
window-rule {
match app-id="QQ" title="资料卡"
match app-id="QQ" title="天气"
open-focused false
}
// Block from recording
window-rule {
match app-id="thunderbird"
match app-id="org.mozilla.Thunderbird"
block-out-from "screen-capture"
}
+28 -7
View File
@@ -64,10 +64,15 @@ prefer-no-csd
animations {
// off
// slowdown 3.0
workspace-switch {
duration-ms 300
curve "ease-out-cubic"
duration-ms 300
curve "ease-out-cubic"
}
overview-open-close {
duration-ms 300
curve "ease-out-cubic"
}
}
@@ -77,10 +82,20 @@ layer-rule {
}
cursor {
xcursor-theme "Bibata-Modern-Ice"
xcursor-size 24
hide-when-typing
layer-rule {
match layer="top"
match layer="overlay"
background-effect {
xray false
}
}
window-rule {
match is-floating=true
background-effect {
xray false
}
}
// I love rounded corners
@@ -89,6 +104,12 @@ window-rule {
clip-to-geometry true
}
cursor {
xcursor-theme "Bibata-Modern-Ice"
xcursor-size 24
hide-when-typing
}
recent-windows {
highlight {
active-color "#89b4fa"
@@ -1 +1,4 @@
file:///home/kolkas/Videos Videos
file:///home/kolkas/Repositories Repositories
file:///home/kolkas/Pictures Pictures
file:///home/kolkas/Desktop
+2 -1
View File
@@ -12,8 +12,9 @@ gtk-enable-input-feedback-sounds=0
gtk-font-name=Sarasa UI SC, 10
gtk-icon-theme-name=Papirus
gtk-menu-images=true
gtk-modules=colorreload-gtk-module
gtk-modules=colorreload-gtk-module:appmenu-gtk-module
gtk-primary-button-warps-slider=true
gtk-shell-shows-menubar=1
gtk-sound-theme-name=ocean
gtk-theme-name=catppuccin-mocha-blue-standard+default
gtk-toolbar-icon-size=GTK_ICON_SIZE_LARGE_TOOLBAR
@@ -14,6 +14,7 @@ Rectangle {
id: root
property ShellScreen screen
property var activeTrayItem: null
implicitWidth: trayFlow.implicitWidth + 20
implicitHeight: parent.height
@@ -90,10 +91,15 @@ Rectangle {
modelData.secondaryActivate && modelData.secondaryActivate()
} else if (mouse.button === Qt.RightButton) {
// Close the menu if it was visible
if (trayPanel && trayPanel.visible) {
// Right-click the same icon toggles menu off.
if (root.activeTrayItem === modelData) {
trayPanel.close()
return
}
// Switch directly to another tray item's menu.
trayPanel.close()
return
}
if (modelData.hasMenu && modelData.menu && trayMenu.item) {
@@ -105,15 +111,13 @@ Rectangle {
menuX = (width / 2) - (trayMenu.item.width / 2)
menuY = root.height
trayMenu.item.menu = modelData.menu
root.activeTrayItem = modelData
trayMenu.item.showAt(parent, menuX, menuY)
} else {
Logger.d("Tray", "No menu available for", modelData.id, "or trayMenu not set")
}
}
}
onEntered: {
trayPanel.close()
}
}
}
}
@@ -136,6 +140,7 @@ Rectangle {
function close() {
visible = false
root.activeTrayItem = null
if (trayMenu.item) {
trayMenu.item.hideMenu()
}
@@ -14,7 +14,6 @@ PopupWindow {
property real anchorX
property real anchorY
property bool isSubMenu: false
property bool isHovered: rootMouseArea.containsMouse
property ShellScreen screen
readonly property int menuWidth: 180
@@ -67,11 +66,15 @@ PopupWindow {
}
}
// Full-sized, transparent MouseArea to track the mouse.
MouseArea {
id: rootMouseArea
anchors.fill: parent
hoverEnabled: true
function closeSiblingSubMenus(currentEntry) {
for (var i = 0; i < columnLayout.children.length; i++) {
const sibling = columnLayout.children[i]
if (sibling !== currentEntry && sibling?.subMenu) {
sibling.subMenu.hideMenu()
sibling.subMenu.destroy()
sibling.subMenu = null
}
}
}
Item {
@@ -176,66 +179,46 @@ PopupWindow {
anchors.fill: parent
hoverEnabled: true
enabled: (modelData?.enabled ?? true) && !(modelData?.isSeparator ?? false) && root.visible
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: {
if (modelData && !modelData.isSeparator && !modelData.hasChildren) {
modelData.triggered()
root.hideMenu()
}
}
onEntered: {
if (!root.visible)
onClicked: mouse => {
if (!modelData || modelData.isSeparator) {
return
// Close all sibling submenus
for (var i = 0; i < columnLayout.children.length; i++) {
const sibling = columnLayout.children[i]
if (sibling !== entry && sibling?.subMenu) {
sibling.subMenu.hideMenu()
sibling.subMenu.destroy()
sibling.subMenu = null
}
}
// Create submenu if needed
if (modelData?.hasChildren) {
if (modelData.hasChildren) {
if (entry.subMenu) {
entry.subMenu.hideMenu()
entry.subMenu.destroy()
entry.subMenu = null
return
}
// Need a slight overlap so that menu don't close when moving the mouse to a submenu
const submenuWidth = menuWidth // Assuming a similar width as the parent
const overlap = 4 // A small overlap to bridge the mouse path
root.closeSiblingSubMenus(entry)
// Position with overlap
const anchorX = -submenuWidth + overlap
const submenuWidth = menuWidth
const overlap = 12
const subAnchorX = -submenuWidth + overlap
// Create submenu
entry.subMenu = Qt.createComponent("TrayMenu.qml").createObject(root, {
"menu": modelData,
"anchorItem": entry,
"anchorX": anchorX,
"anchorX": subAnchorX,
"anchorY": 0,
"isSubMenu": true,
"screen": root.screen
})
if (entry.subMenu) {
entry.subMenu.showAt(entry, anchorX, 0)
entry.subMenu.showAt(entry, subAnchorX, 0)
}
return
}
}
onExited: {
Qt.callLater(() => {
if (entry.subMenu && !entry.subMenu.isHovered) {
entry.subMenu.hideMenu()
entry.subMenu.destroy()
entry.subMenu = null
}
})
if (mouse.button === Qt.LeftButton || mouse.button === Qt.RightButton) {
modelData.triggered()
root.hideMenu()
}
}
}
}
@@ -6,8 +6,6 @@ import qs.Modules.Bar.Services
import qs.Services
UProgressExpand {
// Quickshell.execDetached(["wezterm", "start", "--", "btop"]);
iconName: "cpu"
fillColor: Colors.mCyan
critical: SystemStatService.cpuUsage > 90
@@ -11,15 +11,23 @@ Item {
property color fillColor: Colors.mRed
property color _actualColor: Colors.mRed
property bool _expand: mouseArea.containsMouse
property string displayText: Niri.castOutputs.length > 0 ? Niri.castOutputs.join(", ") : "Casting"
visible: RecordService.isRecording
visible: Niri.isCasting
implicitHeight: Math.max(symbolIcon.implicitHeight, textLabel.implicitHeight)
implicitWidth: height + expander.implicitWidth
Connections {
target: Niri
onCastOutputsListChanged: {
root.displayText = Niri.castOutputs.length > 0 ? Niri.castOutputs.join(", ") : "Casting";
}
}
SequentialAnimation {
id: blinkAnimation
running: RecordService.isRecording
running: root.visible
loops: Animation.Infinite
ColorAnimation {
@@ -70,7 +78,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 5
text: RecordService.recordingDisplay || "Recording"
text: root.displayText
color: root.fillColor
}
@@ -92,12 +100,6 @@ Item {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: (mouse) => {
if (mouse.button === Qt.LeftButton)
RecordService.startOrStop();
}
}
Behavior on _actualColor {
@@ -1,28 +1,102 @@
import QtQuick
import QtQuick.Layouts
import Quickshell.Io
import qs.Components
import qs.Constants
import qs.Services
Text {
text: TimeService.time + " | " + TimeService.dateString
font.pointSize: Style.fontSizeM
font.family: Fonts.primary
color: Colors.mPrimary
Item {
id: root
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onClicked: {
action.running = !action.running;
readonly property int switchDistance: Style.barHeight
readonly property int animationDuration: Style.animationNormal
implicitWidth: Math.max(timeLayer.implicitWidth, notiLayer.implicitWidth)
implicitHeight: Math.max(timeLayer.implicitHeight, notiLayer.implicitHeight)
clip: true
UText {
id: timeLayer
readonly property real restY: (root.height - implicitHeight) / 2
anchors.horizontalCenter: parent.horizontalCenter
y: TempNotificationService.active ? restY + root.switchDistance : restY
opacity: TempNotificationService.active ? 0 : 1
text: TimeService.time + " | " + TimeService.dateString
color: Colors.mPrimary
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
cursorShape: Qt.PointingHandCursor
onClicked: {
action.running = !action.running;
}
}
Process {
id: action
running: false
command: ["vicinae", "toggle"]
}
Behavior on y {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.OutCubic
}
}
Behavior on opacity {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.OutCubic
}
}
}
Process {
id: action
RowLayout {
id: notiLayer
readonly property real restY: (root.height - implicitHeight) / 2
anchors.horizontalCenter: parent.horizontalCenter
y: TempNotificationService.active ? restY : restY - root.switchDistance
opacity: TempNotificationService.active ? 1 : 0
spacing: Style.marginXS
UIcon {
visible: TempNotificationService.iconName !== ""
iconName: TempNotificationService.iconName
color: Colors.mPrimary
}
UText {
text: TempNotificationService.message
color: Colors.mPrimary
}
Behavior on y {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.OutCubic
}
}
Behavior on opacity {
NumberAnimation {
duration: root.animationDuration
easing.type: Easing.OutCubic
}
}
running: false
command: ["rofi", "-show", "drun"]
}
}
@@ -18,7 +18,7 @@ Singleton {
id: process
running: false
command: ["wezterm", "start", "--", "btop"]
command: ["kitty-floating", "-e", "btop"]
}
}
@@ -633,4 +633,42 @@ Singleton {
}
Pipewire.preferredDefaultAudioSource = newSource;
}
onVolumeChanged: {
if (root.consumeOutputOSDSuppression()) {
return;
}
if (root.muted) {
TempNotificationService.showWithIcon("volume-mute", "Muted");
return;
}
TempNotificationService.showWithIcon(root.getOutputIcon(), Math.round(root.volume * 100) + "%");
}
onInputVolumeChanged: {
if (root.consumeInputOSDSuppression()) {
return;
}
if (root.inputMuted) {
TempNotificationService.showWithIcon("volume-mute", "Muted");
return;
}
TempNotificationService.showWithIcon(root.getInputIcon(), Math.round(root.inputVolume * 100) + "%");
}
onMutedChanged: {
if (root.muted) {
TempNotificationService.showWithIcon("volume-mute", "Muted");
} else {
TempNotificationService.showWithIcon(root.getOutputIcon(), Math.round(root.volume * 100) + "%");
}
}
onInputMutedChanged: {
if (root.inputMuted) {
TempNotificationService.showWithIcon("microphone-mute", "Input Muted");
} else {
TempNotificationService.showWithIcon("microphone", "Input Unmuted");
}
}
}
@@ -1,6 +1,7 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Services
import qs.Utils
pragma Singleton
@@ -161,6 +162,9 @@ Singleton {
Component.onDestruction: {
stopInhibition();
}
onIsInhibitedChanged: {
TempNotificationService.showWithIcon("mug-filled", isInhibited ? "Inhibition active: " + reason : "Inhibition stopped");
}
// Process for maintaining the inhibition (subprocess fallback only)
Process {
@@ -0,0 +1,39 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Services
import qs.Utils
pragma Singleton
Singleton {
id: root
property bool isCapslockOn: false
onIsCapslockOnChanged: {
if (root.isCapslockOn) {
TempNotificationService.showWithIcon("letter-case-toggle", "CAPS LOCK ON");
} else {
TempNotificationService.showWithIcon("letter-case", "caps lock off");
}
}
Process {
id: capslockMonitorProcess
running: true
command: ["led-monitor", "-l", "capslock"]
stdout: SplitParser {
splitMarker: "\n"
onRead: (line) => {
if (line.trim() === "1")
root.isCapslockOn = true;
else if (line.trim() === "0")
root.isCapslockOn = false;
}
}
}
}
@@ -153,4 +153,16 @@ Item {
target: "notes"
}
IpcHandler {
function showMsg(message: string, duration: int) {
TempNotificationService.show(message, duration);
}
function showWithIcon(iconName: string, message: string, duration: int) {
TempNotificationService.showWithIcon(iconName, message, duration);
}
target: "tempNotification"
}
}
@@ -11,7 +11,7 @@ Singleton {
property bool dirsLoaded: false
property bool initialized: dirsLoaded && ImageCacheService.initialized && ShellState.isLoaded && SettingsService.isLoaded
Component.onCompleted: {
function mkdirs() {
let mkdirs = "";
for (const dir of [Paths.cacheDir, Paths.configDir, Paths.recordingDir, Paths.notesDir]) {
mkdirs += `mkdir -p "${dir}" && `;
@@ -22,6 +22,11 @@ Singleton {
process.running = true;
}
Component.onCompleted: {
ImageCacheService.init();
root.mkdirs();
}
Process {
id: process
@@ -193,7 +193,10 @@ Singleton {
}
}
onInternalPositionChanged: updateIndex()
onLyricsOffsetChanged: updateIndex()
onLyricsOffsetChanged: () => {
TempNotificationService.showWithIcon("hourglass-empty", root.lyricsOffset + "ms");
updateIndex()
}
Connections {
function onCurrentPlayerChanged() {
@@ -251,7 +254,7 @@ Singleton {
}
const player = this.queuedPlayer.toLowerCase();
this.queuedPlayer = "";
this.command = ["lrx", "--player", player, "fetch"];
this.command = ["lrx", "--player", player, "fetch", "--normalize"];
this.running = true;
}
@@ -271,6 +271,10 @@ Singleton {
}
}
function notifyTrackChange() {
TempNotificationService.showWithIcon("music", (trackArtist ? trackArtist + " - " : "") + trackTitle, 3000);
}
Component.onCompleted: {
updateCurrentPlayer();
}
@@ -280,6 +284,8 @@ Singleton {
currentPosition = 0;
}
onTrackTitleChanged: Qt.callLater(notifyTrackChange)
onTrackArtistChanged: Qt.callLater(notifyTrackChange)
// Update progress bar every second while playing
Timer {
@@ -35,11 +35,16 @@ Singleton {
})
property var workspaceCache: ({
})
property var castCache: ({
})
property var castOutputs: []
property bool isCasting: false
signal workspaceChanged()
signal activeWindowChanged()
signal windowListChanged()
signal outputsChanged()
signal castOutputsListChanged()
function initialize() {
niriEventStream.connected = true;
@@ -434,6 +439,79 @@ Singleton {
}
}
function _syncCasts() {
isCasting = Object.keys(castCache).length > 0;
castOutputs = [];
for (const castId in castCache) {
const cast = castCache[castId];
if (cast.output) {
if (!castOutputs.includes(cast.output))
castOutputs.push(cast.output);
}
}
castOutputsListChanged();
}
function _handleCastsChanged(eventData) {
try {
const casts = eventData.casts || [];
castCache = {
};
castOutputs = [];
for (const cast of casts) {
const castData = {
"id": cast.stream_id,
"stream_id": cast.stream_id,
"session_id": cast.session_id,
"kind": cast.kind,
"output": cast.target?.Output?.name,
"pid": cast.pid
};
castCache[castData.id] = castData;
}
_syncCasts();
} catch (e) {
Logger.e("NiriService", "Error handling CastsChanged:", e);
}
}
function _handleCastStopped(eventData) {
try {
const castId = eventData.stream_id;
delete castCache[castId];
_syncCasts();
} catch (e) {
Logger.e("NiriService", "Error handling CastStopped:", e);
}
}
function _handleCastStartedOrChanged(eventData) {
try {
const cast = eventData.cast;
if (!cast)
return ;
if (cast.is_active === true) {
// If the cast is active, we can treat it as a new or updated cast
const castData = {
"id": cast.stream_id,
"stream_id": cast.stream_id,
"session_id": cast.session_id,
"kind": cast.kind,
"output": cast.target?.Output?.name,
"pid": cast.pid
};
castCache[castData.id] = castData;
} else {
// If the cast is not active, we should remove it from the cache
const castId = cast.stream_id;
delete castCache[castId];
}
_syncCasts();
} catch (e) {
Logger.e("NiriService", "Error handling CastStartedOrChanged:", e);
}
}
function switchToWorkspace(workspace) {
try {
Quickshell.execDetached(["niri", "msg", "action", "focus-workspace", workspace.idx.toString()]);
@@ -578,6 +656,12 @@ Singleton {
_queryDisplayScales();
else if (event.ScreenshotCaptured)
_handleScreenshotCaptured(event.ScreenshotCaptured);
else if (event.CastsChanged)
_handleCastsChanged(event.CastsChanged);
else if (event.CastStopped)
_handleCastStopped(event.CastStopped);
else if (event.CastStartedOrChanged)
_handleCastStartedOrChanged(event.CastStartedOrChanged);
} catch (e) {
Logger.e("NiriService", "Error parsing event stream:", e, data);
}
@@ -39,7 +39,7 @@ Singleton {
function openNote(path) {
recentNotePath = path;
Quickshell.execDetached(["wezterm", "start", "--", "sh", "-c", `exec nvim "${path}"`]);
Quickshell.execDetached(["kitty-floating", "-e", "helix", path]);
}
function openRecent() {
@@ -46,8 +46,10 @@ Singleton {
if (!sunsetProcess.running) {
temperature = 0;
Logger.i("Sunset", "Stopped sunset process");
TempNotificationService.showWithIcon("sunset-2-filled", "Sunset disabled");
} else {
Logger.i("Sunset", "Started sunset process");
TempNotificationService.showWithIcon("sunset-2-filled", "Sunset enabled");
}
}
@@ -0,0 +1,38 @@
pragma Singleton
import QtQuick
import Quickshell
Singleton {
id: root
readonly property int defaultDuration: 2000
property bool active: false
property string message: ""
property string iconName: ""
function show(message: string, duration: int) {
root._showInternal("", message, duration);
}
function showWithIcon(iconName: string, message: string, duration: int) {
root._showInternal(iconName, message, duration);
}
function _showInternal(iconName: string, message: string, duration: int) {
root.iconName = iconName;
root.message = message;
root.active = true;
resetTimer.interval = duration > 0 ? duration : root.defaultDuration;
resetTimer.restart();
}
Timer {
id: resetTimer
repeat: false
onTriggered: root.active = false
}
}
@@ -10,10 +10,6 @@ import qs.Services
ShellRoot {
id: root
Component.onCompleted: {
ImageCacheService.init();
}
Loader {
id: loader
@@ -24,6 +20,7 @@ ShellRoot {
SunsetService;
NotesService;
WallpaperCycle;
CapslockService;
}
IPCService {
@@ -40,13 +40,9 @@ CONFIG_DIR = Path("~/.config").expanduser()
# An application may have multiple scripts (e.g. due to config-switch)
SCRIPTS = {
"eww": [CONFIG_DIR / "eww" / "apply-color"],
"fastfetch": [CONFIG_DIR / "fastfetch" / "apply-color"],
"fuzzel": [CONFIG_DIR / "fuzzel" / "apply-color"],
"hypr": [CONFIG_DIR / "hypr" / "apply-color"],
"kvantum": [CONFIG_DIR / "Kvantum" / "apply-color"],
"nwg-look": [CONFIG_DIR / "nwg-look" / "apply-color"],
"mako": [CONFIG_DIR / "mako" / "apply-color"],
"niri": [CONFIG_DIR / "niri" / "apply-color"],
"oh-my-posh": [
CONFIG_DIR / "fish" / "apply-color-omp"
@@ -55,12 +51,7 @@ SCRIPTS = {
CONFIG_DIR / "fish" / "apply-color-starship"
], # borrowing fish's directory
"quickshell": [CONFIG_DIR / "quickshell" / "apply-color"],
"rofi": [CONFIG_DIR / "rofi" / "apply-color"],
"waybar": [CONFIG_DIR / "waybar" / "apply-color"],
"wlogout": [
CONFIG_DIR / ".alt" / "wlogout-default" / "apply-color",
CONFIG_DIR / ".alt" / "wlogout-niri" / "apply-color",
],
"wlogout": [CONFIG_DIR / "wlogout" / "apply-color"],
"yazi": [CONFIG_DIR / "yazi" / "apply-color"],
}
# or simply `find [-L] <CONFIG_DIR> -type f -name 'apply-color*'` to get all available scripts,
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
kitty --app-id kitty-floating --config "$HOME/.config/kitty/floating.conf" "$@"
+175
View File
@@ -0,0 +1,175 @@
#!/usr/bin/env python3
import sys
import glob
import argparse
import select
import os
import struct
EVENT_FORMAT = '@llHHi'
EVENT_SIZE = struct.calcsize(EVENT_FORMAT)
EV_LED = 0x11
LED_CODES = {
'numlock': 0x00,
'capslock': 0x01,
'scrolllock': 0x02
}
def get_led_state(led_type: str) -> int:
pattern = f"/sys/class/leds/*::{led_type}/brightness"
paths = glob.glob(pattern)
if not paths:
return 0
for path in paths:
try:
with open(path, 'r') as f:
if int(f.read().strip()) > 0:
return 1
except (IOError, ValueError, OSError):
continue
return 0
def has_led_capability(event_path: str) -> bool:
try:
basename = os.path.basename(event_path)
cap_path = f"/sys/class/input/{basename}/device/capabilities/led"
if os.path.exists(cap_path):
with open(cap_path, 'r') as f:
return f.read().strip() != "0"
except OSError:
pass
return False
class DeviceMonitor:
def __init__(self, target_led: str):
self.target_led = target_led
self.target_led_code = LED_CODES[target_led]
self.poller = select.poll()
self.active_fds = {}
self.last_state = get_led_state(self.target_led)
self.emit_state(self.last_state)
def emit_state(self, state: int) -> None:
try:
sys.stdout.write(f"{state}\n")
sys.stdout.flush()
except BrokenPipeError:
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.exit(0)
def scan_devices(self) -> None:
current_fds = set(self.active_fds.keys())
paths = glob.glob('/dev/input/event*')
found_fds = set()
for path in paths:
if not has_led_capability(path):
continue
already_open = False
for fd, opened_path in self.active_fds.items():
if opened_path == path:
found_fds.add(fd)
already_open = True
break
if not already_open:
try:
fd = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
self.poller.register(fd, select.POLLIN)
self.active_fds[fd] = path
found_fds.add(fd)
except PermissionError:
sys.stderr.write(f"Error: Permission denied when opening {path}.\n")
sys.exit(1)
except OSError:
continue
for fd in current_fds - found_fds:
self.remove_device(fd)
def remove_device(self, fd: int) -> None:
if fd in self.active_fds:
try:
self.poller.unregister(fd)
os.close(fd)
del self.active_fds[fd]
except Exception:
pass
def handle_device_events(self, events: list) -> None:
fds_to_remove = []
for fd, event in events:
if event & select.POLLIN:
try:
while True:
data = os.read(fd, 4096)
if not data:
fds_to_remove.append(fd)
break
events_count = len(data) // EVENT_SIZE
for i in range(events_count):
chunk = data[i * EVENT_SIZE : (i + 1) * EVENT_SIZE]
_, _, ev_type, ev_code, ev_value = struct.unpack(EVENT_FORMAT, chunk)
if ev_type == EV_LED and ev_code == self.target_led_code:
if ev_value != self.last_state:
self.emit_state(ev_value)
self.last_state = ev_value
except BlockingIOError:
pass
except OSError:
fds_to_remove.append(fd)
for fd in fds_to_remove:
self.remove_device(fd)
def run(self) -> None:
self.scan_devices()
try:
while True:
events = self.poller.poll(1000)
if not events:
self.scan_devices()
current_state = get_led_state(self.target_led)
if current_state != self.last_state:
self.emit_state(current_state)
self.last_state = current_state
else:
self.handle_device_events(events)
except KeyboardInterrupt:
sys.exit(0)
finally:
for fd in list(self.active_fds.keys()):
try:
os.close(fd)
except Exception:
pass
def main():
parser = argparse.ArgumentParser(description="Zero-polling keyboard LED monitor.")
parser.add_argument(
'-l', '--led',
type=str,
default='capslock',
choices=['capslock', 'numlock', 'scrolllock'],
help="Target LED to monitor"
)
args = parser.parse_args()
monitor = DeviceMonitor(args.led)
monitor.run()
if __name__ == "__main__":
main()
+182
View File
@@ -0,0 +1,182 @@
#!/usr/bin/env bash
# https://github.com/SHORiN-KiWATA/shorin-contrib
#
# niri-force-kill-window
# 通过鼠标点击强制结束 (SIGKILL) 任意窗口。
# 完美支持 Wayland 原生与 XWayland 代理窗口的独立精准击杀。
# 具备多语言自适应 (i18n) 与智能依赖提示功能。
#
# 实现原理:
# 1. 通过 niri msg pick-window 点选窗口
# 2. 利用窗口的 PID通过进程名称判断它是 xwayland 还是 wayland
# 3. 获取真实的底层 PIDWayland 直接取用XWayland 利用 xprop 获取焦点窗口的真实 PID
# 4. 向上追溯该真实 PID找到所属应用的根进程 (App Root),并利用防火墙逻辑避开系统进程
# 5. 从根进程向下遍历收集所有子孙进程,利用 kill -9 执行“九头蛇绞杀”,防止多进程软件死灰复燃
# ==========================================
# 0. 多语言 (Locale) 自适应支持
# ==========================================
if [[ "${LANG}" == zh_* ]]; then
STR_ERR_DEP_TITLE="依赖缺失"
STR_ERR_DEP_MSG="缺少必需命令: %s\n请尝试安装包: %s"
STR_ERR_INFO_TITLE="获取信息失败"
STR_ERR_INFO_MSG="无法获取窗口或进程信息。"
STR_ERR_KILL_TITLE="结束失败"
STR_ERR_KILL_MSG="无法提取目标真实 PID。"
STR_SUCC_TITLE="强制杀死 (%s)"
STR_SUCC_MSG="应用: %s\n连根拔起: 成功摧毁 %s 个相关进程。"
STR_UNKNOWN_APP="未知应用"
else
STR_ERR_DEP_TITLE="Missing Dependency"
STR_ERR_DEP_MSG="Command not found: %s\nPlease install package: %s"
STR_ERR_INFO_TITLE="Info Error"
STR_ERR_INFO_MSG="Could not determine window or process information."
STR_ERR_KILL_TITLE="Kill Failed"
STR_ERR_KILL_MSG="Could not extract target real PID."
STR_SUCC_TITLE="Headshot! (%s)"
STR_SUCC_MSG="App: %s\nHydra Kill: %s processes terminated."
STR_UNKNOWN_APP="Unknown App"
fi
# ==========================================
# 1. 环境与依赖检查
# ==========================================
check_dependency() {
local cmd="$1"
local pkg="$2"
if ! command -v "$cmd" &> /dev/null; then
local msg
printf -v msg "$STR_ERR_DEP_MSG" "$cmd" "$pkg"
# 如果 notify-send 存在,则发送桌面通知;否则输出到终端标准错误
if command -v notify-send &> /dev/null; then
notify-send "$STR_ERR_DEP_TITLE" "$msg" -u critical -i dialog-error
else
echo -e "[${STR_ERR_DEP_TITLE}]\n${msg}" >&2
fi
exit 1
fi
}
# 检查三大核心依赖并提示对应软件包
check_dependency "niri" "niri"
check_dependency "notify-send" "libnotify"
check_dependency "xprop" "xorg-xprop"
# ==========================================
# 2. 辅助函数:发送通知与音效 (非阻塞)
# ==========================================
notify_and_play() {
local title="$1"
local msg="$2"
notify-send "$title" "$msg" -a "Window Killer" -i application-exit
# pw-play 不是强依赖,如果有则播放音效,放入后台运行防止阻塞
if command -v pw-play &> /dev/null; then
pw-play /usr/share/sounds/freedesktop/stereo/dialog-error.oga >/dev/null 2>&1 &
fi
}
# ==========================================
# 3. 抓取目标窗口信息
# ==========================================
# 执行命令并捕获退出码。如果用户按 Esc 取消,返回非零退出码,静默退出
if ! output=$(niri msg pick-window 2>/dev/null); then
exit 0
fi
# 如果没有任何输出,直接退出
if [[ -z "$output" ]]; then
exit 0
fi
# 提取 Niri 视角下的 PID 和 App ID
pid=$(grep -oP 'PID:\s*\K\d+' <<< "$output")
app_id=$(grep -oP 'App ID:\s*"\K[^"]+' <<< "$output")
app_name="${app_id:-$STR_UNKNOWN_APP}"
# 如果正则没有提取到 PID未命中合法窗口静默退出
if [[ -z "$pid" ]]; then
exit 0
fi
# 如果确实抓到了 PID但此时进程已不存在发出异常通知
if [[ ! -f "/proc/$pid/comm" ]]; then
notify-send "$STR_ERR_INFO_TITLE" "$STR_ERR_INFO_MSG" -a "Window Killer" -i dialog-error
exit 1
fi
# ==========================================
# 4. 判定协议类型并获取真实 PID
# ==========================================
process_name=$(cat "/proc/$pid/comm")
process_name_lower="${process_name,,}"
if [[ "$process_name_lower" == *"xwayland"* ]]; then
proto_str="XWayland"
# 给内核与 X11 服务端预留 50 毫秒的时间传递和同步焦点
sleep 0.05
# 顺着焦点,询问 X11 当前活动的窗口 ID
active_wid=$(xprop -root -notype _NET_ACTIVE_WINDOW 2>/dev/null | grep -o '0x[0-9a-fA-F]\+')
# 顺藤摸瓜:提取这个 X11 窗口绑定的真实底层 Linux PID
real_pid=$(xprop -id "$active_wid" -notype _NET_WM_PID 2>/dev/null | grep -oP '\d+')
else
proto_str="Wayland"
real_pid="$pid"
fi
if [[ -z "$real_pid" ]]; then
notify-send "$STR_ERR_KILL_TITLE" "$STR_ERR_KILL_MSG" -a "Window Killer" -i dialog-error
exit 1
fi
# ==========================================
# 5. 九头蛇绞杀逻辑 (Hydra Kill)
# ==========================================
# 向上追溯,寻找进程家族的老祖宗 (App Root)
app_root=$real_pid
current=$real_pid
while true; do
ppid=$(ps -o ppid= -p "$current" 2>/dev/null | tr -d ' ')
# 如果找不到父进程,或者父进程是系统最高层 PID 1停止追溯
if [[ -z "$ppid" || "$ppid" == "1" ]]; then
break
fi
pname=$(ps -o comm= -p "$ppid" 2>/dev/null)
# 【核心防火墙】:遇到桌面环境、终端、系统核心服务,立刻停止溯源!
if [[ "$pname" =~ ^(systemd|niri|bash|zsh|fish|tmux|screen|xwayland.*|sshd|login|init|sway|hyprland)$ ]]; then
break
fi
app_root=$ppid
current=$ppid
done
# 向下递归,收集家族所有子孙 PID
get_descendants() {
local p=$1
echo "$p"
# pgrep -P 获取直接子进程
for c in $(pgrep -P "$p" 2>/dev/null); do
get_descendants "$c"
done
}
family_pids=$(get_descendants "$app_root")
pid_count=$(echo "$family_pids" | wc -w)
# 执行联合绞杀:把所有收集到的 PID 一次性全部强制终止
kill -9 $family_pids 2>/dev/null
# ==========================================
# 6. 发送战果通知与音效
# ==========================================
printf -v final_title "$STR_SUCC_TITLE" "$proto_str"
printf -v final_msg "$STR_SUCC_MSG" "$app_name" "$pid_count"
notify_and_play "$final_title" "$final_msg"
@@ -10,8 +10,7 @@ pids=$(pgrep -x quickshell)
children=()
for pid in $pids; do
# children=$(pgrep -P "$pid" 2>/dev/null)
children+=($!)
mapfile -t -O "${#children[@]}" children < <(pgrep -P "$pid" 2>/dev/null)
kill "$pid" || true
done
+5 -7
View File
@@ -45,22 +45,20 @@ prepend_path "$HOME/.local/scripts"
prepend_path "$HOME/.local/share/fnm"
export PATH
# fnm
if type fnm &>/dev/null; then
eval "$(fnm env --shell bash)"
fi
# export UY_ENABLE_GPG_AGENT_SSH=1 in .profile to enable GPG agent for SSH
if type gpgconf &>/dev/null && type gpg-connect-agent &>/dev/null &&
[ -x "$HOME/.local/scripts/gpg-init" ] &&
[ "${UY_ENABLE_GPG_AGENT_SSH:-0}" = "1" ]; then
[ "${UY_ENABLE_GPG_AGENT_SSH:-0}" = "1" ]; then
# GPG agent for SSH
eval "$($HOME/.local/scripts/gpg-init 2>/dev/null)" &>/dev/null
fi
if type ssh-add &>/dev/null && type ssh-agent &>/dev/null &&
{ [ -z "$SSH_AUTH_SOCK" ] ||
[ "$(ssh-add -l &>/dev/null; echo $?)" -eq 2 ]; } &&
[ "$(
ssh-add -l &>/dev/null
echo $?
)" -eq 2 ]; } &&
[ -x "$HOME/.local/scripts/ssh-init" ]; then
unset SSH_AUTH_SOCK
# SSH with cross-session ssh-agent
+15 -11
View File
@@ -2,6 +2,16 @@
[[ $- == *i* ]] || return
if type fish &>/dev/null; then
alias f="exec fish"
if [[ ${UY_ENABLE_FISH_AUTO_LOGIN:-0} == 1 ]] &&
[[ $(ps --no-header --pid=$PPID --format=comm) != "fish" && -z ${BASH_EXECUTION_STRING} && ${SHLVL} == 1 ]]; then
shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION=''
exec fish $LOGIN_OPTION
fi
fi
HISTCONTROL=ignoreboth
HISTSIZE=16384
HISTFILESIZE=32768
@@ -39,6 +49,10 @@ if type gpg &>/dev/null; then
export GPG_TTY
fi
if type fnm &>/dev/null; then
eval "$(fnm env --shell bash --use-on-cd)"
fi
if [[ ${UY_USING_SSH_AGENT:-0} == 1 ]]; then
function sshs() {
if ! ssh-add -l &>/dev/null; then
@@ -52,16 +66,6 @@ if [[ ${UY_USING_SSH_AGENT:-0} == 1 ]]; then
}
fi
if type fish &>/dev/null; then
alias f="exec fish"
if [[ ${UY_ENABLE_FISH_AUTO_LOGIN:-0} == 1 ]] &&
[[ $(ps --no-header --pid=$PPID --format=comm) != "fish" && -z ${BASH_EXECUTION_STRING} && ${SHLVL} == 1 ]]; then
shopt -q login_shell && LOGIN_OPTION='--login' || LOGIN_OPTION=''
exec fish $LOGIN_OPTION
fi
fi
if type starship &>/dev/null; then
eval $(starship init bash)
eval "$(starship init bash)"
fi
@@ -26,3 +26,8 @@ set -x -g GPG_TTY (tty)
# done
set -U __done_min_cmd_duration 10000
set -U __done_notification_urgency_level low
# fnm
if type -q fnm
fnm env --shell fish --use-on-cd | source
end
@@ -1,7 +1,7 @@
# ssh with encrypted private keys
# $ssh_keys should be set in advance or left empty to use the default keys
if set -q ENABLE_GPG_AGENT_SSH; and test $ENABLE_GPG_AGENT_SSH != "0";\
if set -q UY_ENABLE_GPG_AGENT_SSH; and test $UY_ENABLE_GPG_AGENT_SSH != "0";\
and type -q gpg-init; and type -q gpgconf
true # do nothing
@@ -1,4 +1,10 @@
fpath=(~/.zfunc $fpath)
if [ -d "$HOME/.zfunc" ]; then
fpath=($HOME/.zfunc $fpath)
fi
if [ -d "$HOME/.zsh/completions" ]; then
fpath=($HOME/.zsh/completions $fpath)
fi
# Cache compinit: only regenerate dump once daily
autoload -Uz compinit
@@ -29,3 +29,22 @@ fi
# Catppuccin Mocha — autosuggestions color
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#6c7086"
# Vscode shell integration
if (( $+commands[code] )) && [[ "$TERM_PROGRAM" == "vscode" ]]; then
source "$(code --locate-shell-integration-path zsh)"
fi
# fnm
if (( $+commands[fnm] )); then
eval "$(fnm env --shell zsh --use-on-cd)"
fi
# bat
if (( $+commands[bat] )); then
export BAT_THEME="Catppuccin Mocha"
export BAT_STYLE="default,-numbers"
fi
+25 -6
View File
@@ -54,12 +54,12 @@ if (( $+commands[fzf] )); then
if (( $+commands[yay] )); then
# fyq: fuzzy yay local query
alias fyq="yay -Qq | fzf --preview 'yay -Qi {}'"
alias fyq="yay -Qq | fzf --preview 'yay -Qi {}' --preview-window='right,70%,wrap'"
# fyi: fuzzy yay install
fyi() {
local pkg
pkg=$(yay -Sl | awk '{print $1"/"$2}' | fzf -m --preview 'yay -Si {}' --header "[install package]" "$@")
pkg=$(yay -Sl | awk '{print $1"/"$2}' | fzf -m --preview 'yay -Si {}' --preview-window='right,70%,wrap' --header "[install package]" "$@")
# yay supports "repo/package" format
[[ -n "$pkg" ]] && yay -S ${=pkg}
}
@@ -67,7 +67,7 @@ if (( $+commands[fzf] )); then
# fyr: fuzzy yay remove
fyr() {
local pkg
pkg=$(yay -Qq | fzf -m --preview 'yay -Qi {}' --header "[remove package]" "$@")
pkg=$(yay -Qq | fzf -m --preview 'yay -Qi {}' --preview-window='right,70%,wrap' --header "[remove package]" "$@")
[[ -n "$pkg" ]] && yay -Rn ${=pkg}
}
fi
@@ -180,17 +180,20 @@ if (( $+commands[git] )); then
print -r -- "$repo"
}
gc() {
gcl() {
local repo
repo=$(uy_git_repo_from_clipboard) || return 1
git clone "$repo"
}
pingo() {
cd "$HOME/Repositories/PGdP" || return 1
builtin cd "$HOME/Repositories/Uni" || return 1
local repo
repo=$(uy_git_repo_from_clipboard) || return 1
local dir_name="${repo:t:r}"
local course="${${dir_name%%[^[:lower:]]*}:u}"
mkdir -p "$course"
builtin cd "$course" || return 1
if [[ ! -d "$dir_name" ]]; then
git clone "$repo" || return 1
fi
@@ -201,12 +204,28 @@ if (( $+commands[git] )); then
disown
else
echo "Opening method missing or invalid"
cd "$dir_name"
builtin cd "$dir_name"
fi
}
fi
fi
# jj
if (( $+commands[jj] )); then
jjc() {
jj describe -m "$*"
jj new
}
jjp() {
local branch pos
branch=${1:-master}
pos=${2:-@-}
jj bookmark move "$branch" --to "$pos"
jj git push
}
fi
# Global aliases (can be used as part of a command)
# wl-paste
@@ -1,2 +0,0 @@
*
!.gitignore
+2 -6
View File
@@ -44,18 +44,14 @@ fi
# Paths
[[ -f "$HOME/.cargo/env" ]] && source "$HOME/.cargo/env"
(( $+commands[opam] )) && eval "$(opam env)"
prepend_path "$HOME/.cargo/bin"
prepend_path "$HOME/go/bin"
prepend_path "$HOME/.local/bin"
prepend_path "$HOME/.local/scripts"
prepend_path "$HOME/.local/share/fnm"
export PATH
# fnm
if (( $+commands[fnm] )); then
eval "$(fnm env --shell zsh)"
fi
# GPG agent for SSH
if (( $+commands[gpgconf] )) && (( $+commands[gpg-connect-agent] )) &&
-12
View File
@@ -1,21 +1,9 @@
# lrx completions
fpath=(/home/kolkas/.zsh/completions $fpath)
#!/hint/zsh
[[ $- != *i* ]] && return
for _f in "${XDG_CONFIG_HOME:-$HOME/.config}"/zsh/conf.d/*.zsh(N); do
source "$_f"
done
for _f in "${XDG_CONFIG_HOME:-$HOME/.config}"/zsh/funcs.d/*.zsh(N); do
source "$_f"
done
for _f in "${XDG_CONFIG_HOME:-$HOME/.config}"/zsh/funcs.d/_*(N); do
source "$_f"
done
unset _f
@@ -0,0 +1,23 @@
// This configuration is merged with the default vicinae configuration file, which you can obtain by running the `vicinae config default` command.
// Every item defined in this file takes precedence over the values defined in the default config or any other imported file.
//
// You can make manual edits to this file, however you should keep in mind that this file may be written to by vicinae when a configuration change is made through the GUI.
// When that happens, any custom comments or formatting will be lost.
//
// If you want to maintain a configuration file with your own comments and formatting, you should create a separate file and add it to the 'imports' array.
//
// Learn more about configuration at https://docs.vicinae.com/config
{
"$schema": "https://vicinae.com/schemas/config.json",
"font": {
"normal": {
"family": "LXGW WenKai"
}
},
"theme": {
"dark": {
"name": "catppuccin-mocha"
}
}
}
@@ -1,24 +0,0 @@
#!/bin/bash
[ -f "$HOME/.local/snippets/apply-color-helper" ] || {
echo "Missing helper script: $HOME/.local/snippets/apply-color-helper"
exit 1
}
. "$HOME/.local/snippets/apply-color-helper"
for file in "$path"/icons/*.svg; do
[ -f "$file" ] || continue
sed -i -E "s/(fill=\"#)([0-9A-Fa-f]{6})(\")/\1${colorHex}\3/" "$file" || {
log_error "Failed to edit ${file}"
exit 1
}
done
file="$path"/style.css
sed -i -E "s/(border-color:\s*#)([0-9A-Fa-f]{6})(;)/\1${colorHex}\3/" "$file" || {
log_error "Failed to edit ${file}"
exit 1
}
log_success "wlogout"
@@ -1,36 +0,0 @@
{
"label": "lock",
"action": "loginctl lock-session",
"text": "Lock",
"keybind": "l"
}
{
"label": "hibernate",
"action": "systemctl hibernate",
"text": "Hibernate",
"keybind": "h"
}
{
"label": "logout",
"action": "hyprctl dispatch exit",
"text": "Logout",
"keybind": "e"
}
{
"label": "shutdown",
"action": "systemctl poweroff",
"text": "Shutdown",
"keybind": "s"
}
{
"label": "suspend",
"action": "sleep 0.1 && systemctl suspend",
"text": "Suspend",
"keybind": "u"
}
{
"label": "reboot",
"action": "systemctl reboot",
"text": "Reboot",
"keybind": "r"
}
@@ -1,120 +0,0 @@
* {
background-image: none;
font-size: 24px;
font-family: 'Sour Gummy Light';
}
window {
background-color: rgba(30, 30, 46, 0.5);
}
button {
color: #cdd6f4;
border-radius: 0;
outline-style: none;
background-color: alpha(#1e1e2e, 0.8);
border: none;
border-width: 0px;
border-radius: 0px;
border-color: #89b4fa;
box-shadow: none;
text-shadow: none;
text-decoration-color: #cdd6f4;
background-repeat: no-repeat;
background-position: center;
background-size: 20%;
animation: gradient_f 20s ease-in infinite;
}
button:focus,
button:active,
button:hover {
background-size: 20%;
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);
}
#lock {
background-image: image(url('./icons/lock.svg'));
border-radius: 32px 0 0 0;
}
#logout {
background-image: image(url('./icons/logout.svg'));
}
#suspend {
background-image: image(url('./icons/suspend.svg'));
border-radius: 0 32px 0 0;
}
#hibernate {
background-image: image(url('./icons/hibernate.svg'));
border-radius: 0 0 0 32px;
}
#shutdown {
background-image: image(url('./icons/shutdown.svg'));
}
#reboot {
background-image: image(url('./icons/reboot.svg'));
border-radius: 0 0 32px 0;
}
#lock:hover {
border-radius: 30px 0 0 0;
margin: -30px 0 0 -30px;
}
#logout:hover {
margin: -30px 0 0 0;
}
#suspend:hover {
border-radius: 0 30px 0 0;
margin: -30px -30px 0 0;
}
#hibernate:hover {
border-radius: 0 0 0 30px;
margin: 0 0 -30 -30px;
}
#shutdown:hover {
margin: 0 0 -30px 0;
}
#reboot:hover {
border-radius: 0 0 30px 0;
margin: 0 -30px -30px 0;
}
#lock:focus {
border-radius: 60px 0 0 0;
margin: -60px 0 0 -60px;
}
#logout:focus {
margin: -60px 0 0 0;
}
#suspend:focus {
border-radius: 0 60px 0 0;
margin: -60px -60px 0 0;
}
#hibernate:focus {
border-radius: 0 0 0 60px;
margin: 0 0 -60 -60px;
}
#shutdown:focus {
margin: 0 0 -60px 0;
}
#reboot:focus {
border-radius: 0 0 60px 0;
margin: 0 -60px -60px 0;
}
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#89b4fa" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><g><path d="M500,10C229.4,10,10,229.4,10,500s219.4,490,490,490s490-219.4,490-490S770.6,10,500,10z M500,885.1c-212.7,0-385.1-172.4-385.1-385.1S287.3,114.9,500,114.9S885.1,287.3,885.1,500S712.7,885.1,500,885.1z M576.5,308.7v382.4c0,42.2-34.2,76.5-76.5,76.5c-42.3,0-76.5-34.2-76.5-76.5V308.7c0-42.2,34.2-76.5,76.5-76.5C542.2,232.3,576.5,266.5,576.5,308.7z"/></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g><g></g></g>
</svg>

Before

Width:  |  Height:  |  Size: 969 B

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#89b4fa" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><g><path d="M321.8,455.5h356.4V321.8c0-49.2-17.4-91.2-52.2-126c-34.8-34.8-76.8-52.2-126-52.2c-49.2,0-91.2,17.4-126,52.2c-34.8,34.8-52.2,76.8-52.2,126L321.8,455.5L321.8,455.5z M900.9,522.3v400.9c0,18.6-6.5,34.3-19.5,47.3c-13,13-28.8,19.5-47.3,19.5H165.9c-18.6,0-34.3-6.5-47.3-19.5s-19.5-28.8-19.5-47.3V522.3c0-18.6,6.5-34.3,19.5-47.3c13-13,28.8-19.5,47.3-19.5h22.3V321.8c0-85.4,30.6-158.7,91.9-219.9C341.3,40.6,414.6,10,500,10c85.4,0,158.7,30.6,219.9,91.9c61.3,61.3,91.9,134.6,91.9,219.9v133.6h22.3c18.6,0,34.3,6.5,47.3,19.5C894.4,487.9,900.9,503.7,900.9,522.3L900.9,522.3z"/></g></g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#89b4fa" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><path d="M622.5,990H50.8C26.3,990,10,973.7,10,949.2V50.8C10,26.3,26.3,10,50.8,10h571.7c24.5,0,40.8,16.3,40.8,40.8v285.8c0,24.5-16.3,40.8-40.8,40.8s-40.8-16.3-40.8-40.8v-245h-490v816.7h490v-245c0-24.5,16.3-40.8,40.8-40.8s40.8,16.3,40.8,40.8v285.8C663.3,973.7,647,990,622.5,990z"/><path d="M949.2,540.8H336.7c-24.5,0-40.8-16.3-40.8-40.8c0-24.5,16.3-40.8,40.8-40.8h612.5c24.5,0,40.8,16.3,40.8,40.8C990,524.5,973.7,540.8,949.2,540.8z"/><path d="M949.2,540.8c-12.3,0-20.4-4.1-28.6-12.3L757.3,365.3c-16.3-16.3-16.3-40.8,0-57.2c16.3-16.3,40.8-16.3,57.2,0l163.3,163.3c16.3,16.3,16.3,40.8,0,57.2C969.6,536.8,961.4,540.8,949.2,540.8z"/><path d="M785.8,704.2c-12.3,0-20.4-4.1-28.6-12.3c-16.3-16.3-16.3-40.8,0-57.2l163.3-163.3c16.3-16.3,40.8-16.3,57.2,0c16.3,16.3,16.3,40.8,0,57.2L814.4,691.9C806.3,700.1,798.1,704.2,785.8,704.2z"/></g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#89b4fa" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><path d="M134.6,285.6C64.9,420.7,60.1,590,137.1,723.4L42,668.5l-32,55.4c93.1,52.1,133.6,75.9,184,106.2c28.5-51.5,52.8-94.4,107.4-186.1L246,612l-53.4,92.5C65.4,502.7,167.2,200.3,398.8,126.2C638,29.3,929,223.5,931.5,481.5c19.6,236.7-208.9,443.6-439.3,416.2l-29.5,51c277.7,54.4,556.5-201.7,524.7-483.1C976.1,170.8,637.1-41.2,367.1,77.5C262.8,114.2,183.1,191.5,134.6,285.6z"/></g>
</svg>

Before

Width:  |  Height:  |  Size: 877 B

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#89b4fa" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><path d="M764,152.1c30.9,22,58.3,46.8,82.4,74.6c24,27.8,44.6,57.8,61.8,90.1c17.2,32.3,30.2,66.4,39.1,102.4c8.9,36,13.4,72.6,13.4,109.6c0,63.8-12.2,123.7-36.5,179.6c-24.4,55.9-57.3,104.7-98.8,146.2c-41.5,41.5-90.2,74.5-146.2,98.8C623.2,977.8,563.3,990,499.5,990c-63.1,0-122.7-12.2-178.6-36.5c-55.9-24.4-104.8-57.3-146.7-98.8c-41.9-41.5-74.8-90.2-98.8-146.2c-24-55.9-36-115.8-36-179.6c0-36.4,4.3-72.1,12.9-107.1c8.6-35,20.8-68.3,36.5-99.9c15.8-31.6,35.3-61.1,58.7-88.5c23.3-27.5,49.4-52.2,78.2-74.1c15.1-11,31.4-15.1,48.9-12.4c17.5,2.7,31.7,11.3,42.7,25.7c11,14.4,15.1,30.5,12.4,48.4c-2.7,17.8-11.3,32.3-25.7,43.2c-43.2,31.6-76.4,70.3-99.3,116.3c-23,46-34.5,95.4-34.5,148.2c0,45.3,8.6,88,25.7,128.2c17.2,40.1,40.7,75.1,70.5,105c29.9,29.9,64.9,53.5,105,71c40.1,17.5,82.9,26.3,128.2,26.3c45.3,0,88-8.7,128.2-26.3c40.1-17.5,75.1-41.2,105-71s53.5-64.9,71-105c17.5-40.1,26.3-82.9,26.3-128.2c0-53.5-12.4-104.1-37.1-151.8c-24.7-47.7-59.4-87-104-117.9c-15.1-10.3-24.2-24.4-27.3-42.2c-3.1-17.8,0.5-34.3,10.8-49.4c10.3-14.4,24.4-23.2,42.2-26.2C732.5,138.2,748.9,141.8,764,152.1L764,152.1z M499.5,531.9c-17.8,0-33.1-6.3-45.8-19c-12.7-12.7-19-28-19-45.8V75.9c0-17.8,6.3-33.3,19-46.3c12.7-13,28-19.6,45.8-19.6c18.5,0,34.1,6.5,46.8,19.6c12.7,13,19,28.5,19,46.3v391.2c0,17.8-6.3,33.1-19,45.8C533.6,525.6,518,531.9,499.5,531.9L499.5,531.9z"/></g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg fill="#89b4fa" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><path d="M500,990c-66.1,0-130.3-13-190.7-38.5c-58.4-24.7-110.8-60-155.7-105s-80.3-97.4-105-155.7C23,630.3,10,566.1,10,500c0-66.1,13-130.3,38.5-190.7c24.7-58.4,60-110.8,105-155.7c45-45,97.4-80.3,155.7-105C369.7,23,433.9,10,500,10c66.1,0,130.3,13,190.7,38.5c58.4,24.7,110.8,60,155.7,105c45,45,80.3,97.4,105,155.7C977,369.7,990,433.9,990,500c0,66.1-13,130.3-38.5,190.7c-24.7,58.4-60,110.8-105,155.7s-97.4,80.3-155.7,105C630.3,977,566.1,990,500,990z M500,79.6c-112.3,0-217.9,43.7-297.3,123.1C123.3,282.1,79.6,387.7,79.6,500s43.7,217.9,123.1,297.3c79.4,79.4,185,123.1,297.3,123.1c112.3,0,217.9-43.7,297.3-123.1c79.4-79.4,123.1-185,123.1-297.3s-43.7-217.9-123.1-297.3C717.9,123.3,612.3,79.6,500,79.6z"/><path d="M322.5,290.6h108v412h-108V290.6z"/><path d="M561.6,290.6h107.9v412H561.6V290.6z"/></g>
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 969 B

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 877 B

After

Width:  |  Height:  |  Size: 877 B

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -1,6 +1,6 @@
{
"label": "lock",
"action": "hyprlock &",
"action": "loginctl lock-session",
"text": "Lock",
"keybind": "l"
}
@@ -24,7 +24,7 @@
}
{
"label": "suspend",
"action": "sleep 0.1 && systemctl suspend",
"action": "systemctl suspend",
"text": "Suspend",
"keybind": "u"
}
@@ -33,4 +33,4 @@
"action": "systemctl reboot",
"text": "Reboot",
"keybind": "r"
}
}
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,11 +1,11 @@
[[plugin.deps]]
use = "yazi-rs/plugins:git"
rev = "1962818"
hash = "26db011a778f261d730d4f5f8bf24b3f"
rev = "ac82af3"
hash = "6849444b7c2df08eace83f3f86fb55a3"
[[plugin.deps]]
use = "yazi-rs/plugins:smart-enter"
rev = "1962818"
rev = "ac82af3"
hash = "187cc58ba7ac3befd49c342129e6f1b6"
[[plugin.deps]]
@@ -20,8 +20,8 @@ hash = "771af2becc575a3f43d0542de823969d"
[[plugin.deps]]
use = "llanosrocas/yaziline"
rev = "d9cc2cb"
hash = "b6073aadf2f9a1d5389a6d389f33f69c"
rev = "cc0314c"
hash = "b937c5c8e2d9fa314d4532489176814e"
[[plugin.deps]]
use = "Rolv-Apneseth/starship"
@@ -25,14 +25,16 @@ And register it as fetchers in your `~/.config/yazi/yazi.toml`:
```toml
[[plugin.prepend_fetchers]]
id = "git"
url = "*"
run = "git"
id = "git" # Remove if Yazi > v26.1.22
url = "*"
run = "git"
group = "git"
[[plugin.prepend_fetchers]]
id = "git"
url = "*/"
run = "git"
id = "git" # Remove if Yazi > v26.1.22
url = "*/"
run = "git"
group = "git"
```
## Advanced
@@ -30,17 +30,15 @@ local function setup(_, options)
color = options.color or nil,
secondary_color = options.secondary_color or nil,
default_files_color = options.default_files_color
or th.which.separator_style:fg()
or "darkgray",
or th.which.separator_style:fg()
or "darkgray",
selected_files_color = options.selected_files_color
or th.mgr.count_selected:bg()
or "white",
yanked_files_color = options.selected_files_color
or th.mgr.count_copied:bg()
or "green",
cut_files_color = options.cut_files_color
or th.mgr.count_cut:bg()
or "red",
or th.mgr.count_selected:bg()
or "white",
yanked_files_color = options.yanked_files_color
or th.mgr.count_copied:bg()
or "green",
cut_files_color = options.cut_files_color or th.mgr.count_cut:bg() or "red",
}
local current_separator_style = config.separator_styles
@@ -55,10 +53,10 @@ local function setup(_, options)
local style = self:style()
return ui.Line({
ui.Span(current_separator_style.separator_head)
:fg(config.color or style.main:bg()),
:fg(config.color or style.main:bg()),
ui.Span(" " .. mode .. " ")
:fg(th.which.mask:bg())
:bg(config.color or style.main:bg()),
:fg(th.which.mask:bg())
:bg(config.color or style.main:bg()),
})
end
@@ -67,9 +65,14 @@ local function setup(_, options)
local size = h and (h:size() or h.cha.len) or 0
local style = self:style()
return ui.Span(current_separator_style.separator_close .. " " .. ya.readable_size(size) .. " ")
:fg(config.color or style.main:bg())
:bg(config.secondary_color or th.which.separator_style:fg())
return ui.Span(
current_separator_style.separator_close
.. " "
.. ya.readable_size(size)
.. " "
)
:fg(config.color or style.main:bg())
:bg(config.secondary_color or th.which.separator_style:fg())
end
function Status:utf8_sub(str, start_char, end_char)
@@ -90,8 +93,8 @@ local function setup(_, options)
if utf8.len(base_name) > max_length then
base_name = self:utf8_sub(base_name, 1, config.filename_truncate_length)
.. config.filename_truncate_separator
.. self:utf8_sub(base_name, -config.filename_truncate_length)
.. config.filename_truncate_separator
.. self:utf8_sub(base_name, -config.filename_truncate_length)
end
return base_name .. extension
@@ -103,19 +106,18 @@ local function setup(_, options)
if not h then
return ui.Line({
ui.Span(current_separator_style.separator_close .. " ")
:fg(config.secondary_color or th.which.separator_style:fg()),
ui.Span("Empty dir")
:fg(config.color or style.main:bg()),
:fg(config.secondary_color or th.which.separator_style:fg()),
ui.Span("Empty dir"):fg(config.color or style.main:bg()),
})
end
local truncated_name = self:truncate_name(h.name, config.filename_max_length)
local truncated_name =
self:truncate_name(h.name, config.filename_max_length)
return ui.Line({
ui.Span(current_separator_style.separator_close .. " ")
:fg(config.secondary_color or th.which.separator_style:fg()),
ui.Span(truncated_name)
:fg(config.color or style.main:bg()),
:fg(config.secondary_color or th.which.separator_style:fg()),
ui.Span(truncated_name):fg(config.color or style.main:bg()),
})
end
@@ -124,28 +126,22 @@ local function setup(_, options)
local files_selected = #cx.active.selected
local files_cut = cx.yanked.is_cut
local selected_fg = files_selected > 0
and config.selected_files_color
or config.default_files_color
local selected_fg = files_selected > 0 and config.selected_files_color
or config.default_files_color
local yanked_fg = files_yanked > 0
and
(files_cut
and config.cut_files_color
or config.yanked_files_color
)
or config.default_files_color
and (files_cut and config.cut_files_color or config.yanked_files_color)
or config.default_files_color
local yanked_text = files_yanked > 0
and config.yank_symbol .. " " .. files_yanked
or config.yank_symbol .. " 0"
or config.yank_symbol .. " 0"
return ui.Line({
ui.Span(" " .. current_separator_style.separator_close_thin .. " ")
:fg(th.which.separator_style:fg()),
:fg(th.which.separator_style:fg()),
ui.Span(config.select_symbol .. " " .. files_selected .. " ")
:fg(selected_fg),
ui.Span(yanked_text .. " ")
:fg(yanked_fg),
:fg(selected_fg),
ui.Span(yanked_text .. " "):fg(yanked_fg),
})
end
@@ -159,8 +155,12 @@ local function setup(_, options)
local cha = hovered.cha
local time = (cha.mtime or 0) // 1
return ui.Span(os.date("%Y-%m-%d %H:%M", time) .. " " .. current_separator_style.separator_open_thin .. " ")
:fg(th.which.separator_style:fg())
return ui.Span(
os.date("%Y-%m-%d %H:%M", time)
.. " "
.. current_separator_style.separator_open_thin
.. " "
):fg(th.which.separator_style:fg())
end
function Status:percent()
@@ -182,13 +182,13 @@ local function setup(_, options)
local style = self:style()
return ui.Line({
ui.Span(" " .. current_separator_style.separator_open)
:fg(config.secondary_color or th.which.separator_style:fg()),
:fg(config.secondary_color or th.which.separator_style:fg()),
ui.Span(percent)
:fg(config.color or style.main:bg())
:bg(config.secondary_color or th.which.separator_style:fg()),
:fg(config.color or style.main:bg())
:bg(config.secondary_color or th.which.separator_style:fg()),
ui.Span(current_separator_style.separator_open)
:fg(config.color or style.main:bg())
:bg(config.secondary_color or th.which.separator_style:fg()),
:fg(config.color or style.main:bg())
:bg(config.secondary_color or th.which.separator_style:fg()),
})
end
@@ -198,10 +198,13 @@ local function setup(_, options)
local style = self:style()
return ui.Line({
ui.Span(string.format(" %2d/%-2d ", math.min(cursor + 1, length), length))
:fg(th.which.mask:bg())
:bg(config.color or style.main:bg()),
ui.Span(current_separator_style.separator_tail):fg(config.color or style.main:bg()),
ui.Span(
string.format(" %2d/%-2d ", math.min(cursor + 1, length), length)
)
:fg(th.which.mask:bg())
:bg(config.color or style.main:bg()),
ui.Span(current_separator_style.separator_tail)
:fg(config.color or style.main:bg()),
})
end
+15 -15
View File
@@ -10,11 +10,11 @@ find_position = { fg = "#f5c2e7", bg = "reset", italic = true }
marker_copied = { fg = "#a6e3a1", bg = "#a6e3a1" }
marker_cut = { fg = "#f38ba8", bg = "#f38ba8" }
marker_marked = { fg = "#94e2d5", bg = "#94e2d5" }
marker_selected = { fg = "#f5c2e7", bg = "#f5c2e7" }
marker_selected = { fg = "#89b4fa", bg = "#89b4fa" }
count_copied = { fg = "#1e1e2e", bg = "#a6e3a1" }
count_cut = { fg = "#1e1e2e", bg = "#f38ba8" }
count_selected = { fg = "#1e1e2e", bg = "#f5c2e7" }
count_selected = { fg = "#1e1e2e", bg = "#89b4fa" }
border_symbol = "│"
border_style = { fg = "#7f849c" }
@@ -26,8 +26,8 @@ active = { fg = "#1e1e2e", bg = "#cdd6f4", bold = true }
inactive = { fg = "#cdd6f4", bg = "#45475a" }
[mode]
normal_main = { fg = "#1e1e2e", bg = "#f5c2e7", bold = true }
normal_alt = { fg = "#f5c2e7", bg = "#313244"}
normal_main = { fg = "#1e1e2e", bg = "#89b4fa", bold = true }
normal_alt = { fg = "#89b4fa", bg = "#313244"}
select_main = { fg = "#1e1e2e", bg = "#a6e3a1", bold = true }
select_alt = { fg = "#a6e3a1", bg = "#313244"}
@@ -37,7 +37,7 @@ unset_alt = { fg = "#f2cdcd", bg = "#313244"}
[indicator]
parent = { fg = "#1e1e2e", bg = "#cdd6f4" }
current = { fg = "#1e1e2e", bg = "#f5c2e7" }
current = { fg = "#1e1e2e", bg = "#89b4fa" }
preview = { fg = "#1e1e2e", bg = "#cdd6f4" }
[status]
@@ -55,29 +55,29 @@ perm_exec = { fg = "#a6e3a1" }
perm_sep = { fg = "#7f849c" }
[input]
border = { fg = "#f5c2e7" }
border = { fg = "#89b4fa" }
title = {}
value = {}
selected = { reversed = true }
[pick]
border = { fg = "#f5c2e7" }
border = { fg = "#89b4fa" }
active = { fg = "#f5c2e7" }
inactive = {}
[confirm]
border = { fg = "#f5c2e7" }
title = { fg = "#f5c2e7" }
border = { fg = "#89b4fa" }
title = { fg = "#89b4fa" }
body = {}
list = {}
btn_yes = { reversed = true }
btn_no = {}
[cmp]
border = { fg = "#f5c2e7" }
border = { fg = "#89b4fa" }
[tasks]
border = { fg = "#f5c2e7" }
border = { fg = "#89b4fa" }
title = {}
hovered = { fg = "#f5c2e7", bold = true }
@@ -126,13 +126,13 @@ rules = [
{ url = "*/", is = "dummy", bg = "#f38ba8" },
# Fallback
{ url = "*/", fg = "#f5c2e7" },
{ url = "*/", fg = "#89b4fa" },
]
[spot]
border = { fg = "#f5c2e7" }
title = { fg = "#f5c2e7" }
tbl_cell = { fg = "#f5c2e7", reversed = true }
border = { fg = "#89b4fa" }
title = { fg = "#89b4fa" }
tbl_cell = { fg = "#89b4fa", reversed = true }
tbl_col = { bold = true }
[icon]
+1 -1
View File
@@ -92,4 +92,4 @@
:monitor 0
:geometry (geometry :x 1108 :y -45)
(lyrics-single)
)
)
@@ -18,7 +18,7 @@ alt() {
fi
}
for item in "kitty" "wlogout"; do
for item in "kitty" "wlogout" "ghostty"; do
if [[ ! -L $HOME/.config/$item ]] && [[ -e $HOME/.config/$item ]]; then
echo "Error: $HOME/.config/$item exists and is not a symlink." >&2
exit 1

Some files were not shown because too many files have changed in this diff Show More