Compare commits

...

34 Commits

Author SHA1 Message Date
673a30e1df 👐 foo: too lazy to come up with a helpful commit message :) 2025-12-16 21:48:57 +01:00
809de7f5a3 👐 foo: too lazy to come up with a helpful commit message :) 2025-12-16 21:45:53 +01:00
8fc4fd63ff script: add playlive 2025-12-16 00:46:54 +01:00
d5ed8744f6 fix emojis not displaying 2025-12-15 23:01:24 +01:00
915dc9faf0 Update README.md 2025-12-07 07:07:11 +01:00
a1696ee49b optimize scripts 2025-12-07 02:52:32 +01:00
f23fe5506d fish: same as last 2025-12-06 18:14:48 +01:00
ed5688306b fish: fix sourcing errors on ssh servers 2025-12-06 18:13:11 +01:00
b4e8dcd75a fonts 2025-12-05 00:10:27 +01:00
3e56a80ee2 minor 2025-12-04 22:02:06 +01:00
916bd6b61e some ghostty related fix 2025-12-03 20:07:20 +01:00
4a525e2822 too lazy to come up with a helpful commit message :) 2025-12-01 17:27:27 +01:00
8063f91d8a change-wallpaper: update hash & cache logic 2025-12-01 15:30:48 +01:00
bfda6f4ac2 quickshell: meaningless refactor 2025-12-01 14:17:49 +01:00
8f9df4c730 fix: import envs in ghostty 2025-12-01 01:14:16 +01:00
34a2f71c6d too lazy to come up with a helpful commit message :) 2025-11-30 19:29:30 +01:00
9f469589b4 update niri 2025-11-30 14:21:09 +01:00
a6608b1d81 waiting... 2025-11-29 21:37:04 +01:00
3d0c6f8de0 wallpaper: better (and more spaghetti) logic with cache 2025-11-28 12:22:44 +01:00
915f19f142 fix: ghostty-capture: double check if the correct filepath is given 2025-11-28 10:53:51 +01:00
94bb499764 fix: also copy output of grim to clipboard 2025-11-28 10:36:46 +01:00
1bf6921992 niri: swap roles for ghostty and kitty & ghostty: ghostty-capture 2025-11-28 03:44:11 +01:00
4c69672211 niri outputs 2025-11-27 22:25:45 +01:00
fd35954c9d too lazy to come up with a helpful commit message :) 2025-11-26 19:46:03 +01:00
920b4451d9 fix: finally a working screenshot script 2025-11-26 15:22:47 +01:00
1c39877f14 too lazy to come up with a helpful commit message :) 2025-11-25 02:06:31 +01:00
0c11bfbf80 yazi: update & niri: minor adjustments 2025-11-23 21:23:58 +01:00
d4b4904b0e fish: acp (add commit push) & niri 2025-11-23 04:52:49 +01:00
7b6b31204d niri: update 2025-11-22 14:23:53 +01:00
02cace931d yazi 2025-11-20 14:42:02 +01:00
a78a899f02 makes no sense 2025-11-20 02:49:45 +01:00
7ce1babeed :) 2025-11-19 22:11:12 +01:00
f5a9a20a1f fix: correct command for adding remote in git-remote.md 2025-11-18 23:10:28 +01:00
3175db4900 add a memo about git 2025-11-18 23:03:50 +01:00
66 changed files with 1605 additions and 1914 deletions

View File

@@ -1,4 +1,4 @@
<img src="https://raw.githubusercontent.com/Uyanide/dotfiles/refs/heads/main/assets/works-on-my-machine.png" alt="Works on my machine(s)" width="200" />
<img src="https://raw.githubusercontent.com/Uyanide/dotfiles/refs/heads/main/assets/works-on-my-machines.png" alt="Works on my machine(s)" width="200" />
## How it looks like...
@@ -73,11 +73,11 @@ This setup is currently only adapted for Niri.
- `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
## Awww (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.
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/config/scripts/.local/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.
This feature is only enabled in Niri. Awww also manages wallpapers of the Hyprland setup, yet only in the regular way.
## Wallpaper & Colortheme
@@ -92,7 +92,7 @@ Based on [codeopshq/dotfiles](https://github.com/codeopshq/dotfiles), also serve
## 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).
Based on [vinceliuice/Elegant-grub2-themes](https://github.com/vinceliuice/Elegant-grub2-themes) with an [illustration from 紺屋](https://www.pixiv.net/artworks/119683453).
## MPV
@@ -100,11 +100,4 @@ Based on [noelsimbolon/mpv-config](https://github.com/noelsimbolon/mpv-config.gi
## Fonts
including:
- Maple Mono NF CN
- MesloLGM Nerd Font (& Mono)
- WenQuanYi Micro Hei
- Sour Gummy
- Noto Sans
- ...
See [fontconfig.md](https://github.com/Uyanide/dotfiles/blob/main/memo/fontconfig.md).

View File

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -8,6 +8,10 @@ window-padding-y = 10
keybind = ctrl+shift+r=reload_config
keybind = ctrl+shift+h=write_screen_file:copy
keybind = ctrl+shift+j=text:ghostty-capture\n
keybind = ctrl+enter=unbind
command = exec fish
confirm-close-surface = false

View File

@@ -8,6 +8,10 @@ window-padding-y = 10
keybind = ctrl+shift+r=reload_config
keybind = ctrl+shift+h=write_screen_file:copy
keybind = ctrl+shift+j=text:ghostty-capture\n
keybind = ctrl+enter=unbind
command = exec fish
confirm-close-surface = false
@@ -19,3 +23,5 @@ cursor-style = bar
adjust-cursor-thickness = 3
custom-shader = cursor-shaders/cursor-smear.glsl
quit-after-last-window-closed = false

View File

@@ -169,7 +169,7 @@ bind = Super+Shift, Page_Up, movetoworkspace, -1 # [hidden]
bind = Super+Alt, S, movetoworkspacesilent, special:s
bind = Super, P, pin
bind = Alt, Tab, cyclenext
bind = Super, Tab, hyprexpo:expo, toggle # can be: toggle, select, off/disable or on/enable
# bind = Super, Tab, hyprexpo:expo, toggle # can be: toggle, select, off/disable or on/enable
bind = Super+Ctrl, T, exec, workspace-new # Create new workspace
bind = Super, M, exit

View File

@@ -36,8 +36,8 @@ map ctrl+down next_window
cursor_trail 1
cursor_shape beam
remember_window_size no
initial_window_width 1021
# remember_window_size no
# initial_window_width 1021
include Catppuccin-Mocha.conf

View File

@@ -6,9 +6,9 @@
}
. "$HOME/.local/snippets/apply-color-helper"
file="$path"/config.kdl
file="$path"/config/styles.kdl
sed -i -E "s/^(\s*active-color\s+\"#)([0-9A-Fa-f]{6})(\")/\1${colorHex}\3/" "$file" || {
sed -i -E "s/^(\s*active-color\s+\"#)([0-9A-Fa-f]{6})(\")/\1${colorHex}\3/g" "$file" || {
log_error "Failed to edit ${file}"
exit 1
}

View File

@@ -1,455 +1,8 @@
/************************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="99%"
}
/************************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 0
center-focused-column "never"
preset-column-widths {
proportion 0.3
proportion 0.5
proportion 0.7
}
preset-window-heights {
proportion 0.5
proportion 0.75
proportion 1.0
}
default-column-width { proportion 0.7; }
focus-ring {
width 2
active-color "#89b4fa"
inactive-color "#1e1e2e"
}
border {
off
}
shadow {
on
softness 30
spread 5
offset x=0 y=5
color "#0007"
}
struts {
top 2
right 2
bottom 3
left 2
}
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="^swww-daemonbackdrop$"
place-within-backdrop true
}
/************************Autostart************************/
// Switch configs
spawn-sh-at-startup "config-switch niri"
// Wallpaper
spawn-at-startup "wallpaper-daemon"
// Not necessary maybe ...
spawn-at-startup "fcitx5"
// Core
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"
// Clipboard history
spawn-sh-at-startup "wl-paste --type text --watch cliphist store"
spawn-sh-at-startup "wl-paste --type image --watch cliphist store"
// wlsunset
// spawn-at-startup "sunset"
// Logitech
spawn-sh-at-startup "solaar -w hide"
// Some other heavy apps
spawn-at-startup "sunshine"
// spawn-at-startup "spotify"
// spawn-at-startup "thunderbird"
// Idle
spawn-at-startup "hypridle"
// QuickShell
spawn-at-startup "quickshell"
/************************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
default-column-width { proportion 0.5; }
}
// Normal terminal
// window-rule {
// match app-id="kitty"
// default-column-width { proportion 0.3; }
// }
// Scrcpy
window-rule {
match app-id="scrcpy"
default-column-width { proportion 0.3; }
}
// Editor
window-rule {
match app-id="org.gnome.TextEditor"
default-column-width { proportion 0.3; }
}
// Other floating
window-rule {
match app-id="blueberry"
match app-id="blueman-manager"
match app-id="org.pulseaudio.pavucontrol"
match app-id="com.saivert.pwvucontrol"
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-carousel"
match app-id="be.alexandervanhee.gradia"
match title="^(图片查看器)(.*)$" // QQ
open-floating true
}
// Block from recording
window-rule {
match app-id="thunderbird"
block-out-from "screen-capture"
}
// workspace "first"
// workspace "second"
// Startup in "second" workspace
window-rule {
// match at-startup=true app-id="Spotify"
// match at-startup=true app-id="thunderbird"
open-on-workspace "second"
}
window-rule {
match app-id="at.yrlf.wl_mirror"
// default-column-width { proportion 1.0; }
open-floating true
// open-maximized-to-edges true
// open-on-output "HDMI-A-1"
}
// I love round corners
window-rule {
geometry-corner-radius 14
clip-to-geometry true
}
/************************Others************************/
cursor {
xcursor-theme "Bibata-Modern-Ice"
xcursor-size 24
hide-when-typing
}
debug {
render-drm-device "/dev/dri/card0"
}
screenshot-path "~/Pictures/Screenshots/.niri_screenshot.png"
// gestures {
// hot-corners {
// off
// }
// }
/************************Keybindings************************/
binds {
// Apps
Mod+C { spawn-sh "code --password-store=gnome-libsecret"; }
Mod+E { spawn-sh "dolphin --new-window"; }
Mod+W { spawn-sh "zen || zen-browser"; }
Mod+X { spawn "gnome-text-editor" "--new-window"; }
Mod+B { spawn-sh "pkill -x -n 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-sh "wallpaper-carousel"; }
Mod+O { spawn-sh "pkill -x -n pwvucontrol || pwvucontrol"; }
// Quickshell
Mod+Space { spawn-sh "qs ipc call panels toggleControlCenter"; }
Mod+Shift+D { spawn-sh "qs ipc call panels toggleCalendar"; }
Mod+Shift+L { spawn-sh "qs ipc call lyrics toggleBarLyrics"; }
Mod+Shift+K { spawn-sh "quickshell-kill || quickshell"; }
Mod+I { spawn-sh "qs ipc call idleInhibitor toggleInhibitor"; }
Mod+Alt+R { spawn-sh "qs ipc call recording startOrStopRecording"; }
Mod+Shift+E { spawn-sh "qs ipc call sunset toggleSunset"; }
// Rofi
Mod+D { spawn-sh "pkill -x rofi || rofi -show run"; }
Alt+Space { spawn-sh "pkill -x rofi || rofi -show drun"; }
// Actions
Mod+V { spawn-sh "pkill -x rofi || rofi-cliphist"; }
Mod+Period { spawn-sh "pkill -x rofi || rofi-emoji"; }
Ctrl+Alt+Delete { spawn-sh "pkill -x wlogout || wlogout -p layer-shell"; }
Print { spawn-sh "screenshot-script full"; }
Mod+Shift+S { spawn-sh "screenshot-script area"; }
Mod+Ctrl+Shift+S { spawn-sh "screenshot-script window"; }
Mod+Shift+C { spawn-sh "hyprpicker -a"; }
// Session
Mod+L { spawn-sh "loginctl lock-session"; }
// Media
XF86AudioRaiseVolume allow-when-locked=true { spawn-sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"; }
XF86AudioLowerVolume allow-when-locked=true { spawn-sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%-"; }
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 "set-brightness" "+10%"; }
XF86MonBrightnessDown allow-when-locked=true { spawn "set-brightness" "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+Shift+M { toggle-column-tabbed-display; }
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+Y { 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 { spawn-sh "(hyprlock &) && niri msg action power-off-monitors"; }
Mod+P repeat=false { spawn-sh "wl-mirror $(niri msg --json focused-output | jq -r .name)"; }
}
include "config/input.kdl"
include "config/monitors.kdl"
include "config/styles.kdl"
include "config/execs.kdl"
include "config/envs.kdl"
include "config/rules.kdl"
include "config/binds.kdl"
include "config/misc.kdl"

View File

@@ -0,0 +1,164 @@
recent-windows {
binds {
Alt+Tab { next-window; }
Alt+Shift+Tab { previous-window; }
// dead_circumflex: ^
Alt+dead_circumflex { next-window filter="app-id"; }
Alt+Shift+dead_circumflex { previous-window filter="app-id"; }
}
}
binds {
// Apps
Mod+C { spawn "code"; }
Mod+E { spawn "dolphin" "--new-window"; }
Mod+W { spawn-sh "zen || zen-browser"; }
Mod+X { spawn "gnome-text-editor" "--new-window"; }
Mod+B { spawn-sh "pkill -x -n btop || kitty -e btop"; }
Mod+T { spawn "ghostty" "+new-window"; }
Mod+Return { spawn "ghostty" "+new-window"; }
Mod+Shift+T { spawn "kitty"; }
Mod+Shift+Return { spawn "kitty"; }
Mod+Shift+W { spawn "wallpaper-carousel"; }
Mod+O { spawn-sh "pkill -x -n pwvucontrol || pwvucontrol"; }
// Quickshell
Mod+Space { spawn "qs" "ipc" "call" "panels" "toggleControlCenter"; }
Mod+Shift+D { spawn "qs" "ipc" "call" "panels" "toggleCalendar"; }
Mod+Shift+L { spawn "qs" "ipc" "call" "lyrics" "toggleBarLyrics"; }
Mod+Shift+K { spawn-sh "quickshell-kill || quickshell"; }
Mod+I { spawn "qs" "ipc" "call" "idleInhibitor" "toggleInhibitor"; }
Mod+Alt+R { spawn "qs" "ipc" "call" "recording" "startOrStopRecording"; }
Mod+Shift+E { spawn "qs" "ipc" "call" "sunset" "toggleSunset"; }
// Rofi
Mod+D { spawn-sh "pkill -x rofi || rofi -show run"; }
Alt+Space { spawn-sh "pkill -x rofi || rofi -show drun"; }
// Actions
Mod+V { spawn-sh "pkill -x rofi || rofi-cliphist"; }
Mod+Period { spawn-sh "pkill -x rofi || rofi-emoji"; }
Ctrl+Alt+Delete { spawn-sh "pkill -x wlogout || wlogout -p layer-shell"; }
Print { spawn "niri" "msg" "action" "screenshot-screen"; }
Mod+Shift+S { spawn "niri" "msg" "action" "screenshot"; }
Mod+Ctrl+Shift+S { spawn "niri" "msg" "action" "screenshot-window"; }
Mod+Shift+C { spawn "hyprpicker" "-a"; }
// Session
Mod+L { spawn "loginctl" "lock-session"; }
// Media
XF86AudioRaiseVolume allow-when-locked=true { spawn-sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%+"; }
XF86AudioLowerVolume allow-when-locked=true { spawn-sh "wpctl set-volume -l 1 @DEFAULT_AUDIO_SINK@ 5%-"; }
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 "set-brightness" "+10%"; }
XF86MonBrightnessDown allow-when-locked=true { spawn "set-brightness" "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+Shift+M { toggle-column-tabbed-display; }
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+M { maximize-window-to-edges; }
Mod+Y { 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+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
Mod+Shift+Q allow-inhibiting=false { quit; }
Mod+Shift+P { spawn-sh "(hyprlock &) && niri msg action power-off-monitors"; }
Mod+P repeat=false { spawn-sh "wl-mirror $(niri msg --json focused-output | jq -r .name)"; }
}

View File

@@ -0,0 +1,22 @@
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"
}

View File

@@ -0,0 +1,39 @@
// Switch configs
spawn-at-startup "config-switch" "niri"
// Wallpaper
spawn-at-startup "wallpaper-daemon"
// 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"
// Logitech
spawn-at-startup "solaar" "-w" "hide"
// Some other heavy apps
spawn-at-startup "sunshine"
// spawn-at-startup "spotify"
// spawn-at-startup "thunderbird"
// Idle
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"
// No, do not do this, otherwise the envs defined in envs.kdl won't be applied to ghostty.
// Unless you have something like "config/shell/.config/fish/prev.d/niri-env.fish"

View File

@@ -0,0 +1,29 @@
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="100%"
}

View File

@@ -0,0 +1,11 @@
screenshot-path "~/Pictures/Screenshots/niri_screenshot_%Y-%m-%d_%H-%M-%S.png"
debug {
render-drm-device "/dev/dri/card0"
}
// gestures {
// hot-corners {
// off
// }
// }

View File

@@ -0,0 +1,22 @@
output "eDP-1" {
// off
mode "2560x1600@240"
scale 1.25
background-color "#1e1e2e"
backdrop-color "#1e1e2e"
}
output "eDP-2" {
mode "2560x1600@60"
scale 1.25
background-color "#1e1e2e"
backdrop-color "#1e1e2e"
}
output "DP-1" {
mode "2560x1440@179.845"
scale 1.0
background-color "#1e1e2e"
backdrop-color "#1e1e2e"
// transform "90"
}

View File

@@ -0,0 +1,89 @@
// 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="kitty"
open-floating true
default-column-width { proportion 0.5; }
}
// Normal terminal
window-rule {
match app-id="com.mitchellh.ghostty"
default-column-width { proportion 0.3; }
}
// Scrcpy
window-rule {
match app-id="scrcpy"
default-column-width { proportion 0.3; }
}
// Editor
window-rule {
match app-id="org.gnome.TextEditor"
default-column-width { proportion 0.3; }
}
// Other floating
window-rule {
match app-id="blueberry"
match app-id="blueman-manager"
match app-id="org.pulseaudio.pavucontrol"
match app-id="com.saivert.pwvucontrol"
match app-id="Waydroid"
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-carousel"
match app-id="be.alexandervanhee.gradia"
match title="^(图片查看器)(.*)$" // QQ
open-floating true
}
// Block from recording
window-rule {
match app-id="thunderbird"
block-out-from "screen-capture"
}
// workspace "first"
// workspace "second"
// Startup in "second" workspace
window-rule {
// match at-startup=true app-id="Spotify"
// match at-startup=true app-id="thunderbird"
open-on-workspace "second"
}
window-rule {
match app-id="at.yrlf.wl_mirror"
// default-column-width { proportion 1.0; }
open-floating true
// open-maximized-to-edges true
// open-on-output "HDMI-A-1"
}

View File

@@ -0,0 +1,86 @@
layout {
gaps 0
center-focused-column "never"
preset-column-widths {
proportion 0.3
proportion 0.5
proportion 0.7
}
preset-window-heights {
proportion 0.5
proportion 0.75
proportion 1.0
}
default-column-width { proportion 0.7; }
focus-ring {
width 2
active-color "#89b4fa"
inactive-color "#1e1e2e"
}
border {
off
}
shadow {
on
softness 30
spread 5
offset x=0 y=5
color "#0007"
}
struts {
top 2
right 2
bottom 3
left 2
}
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="^swww-daemonbackdrop$"
place-within-backdrop true
}
cursor {
xcursor-theme "Bibata-Modern-Ice"
xcursor-size 24
hide-when-typing
}
// I love round corners
window-rule {
geometry-corner-radius 14
clip-to-geometry true
}
recent-windows {
highlight {
active-color "#89b4fa"
urgent-color "#f38ba8"
corner-radius 14
}
}

View File

@@ -131,7 +131,6 @@ Variants {
}
CavaBar {
count: 6
}
Item {

View File

@@ -2,27 +2,17 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Constants
import qs.Services
import qs.Utils
import qs.Modules.Bar.Misc
Item {
id: root
property int count: 6
property int barWidth: 5
property int barSpacing: 3
property bool forceEnable: false
implicitWidth: root.barWidth * root.count + root.barSpacing * (root.count - 1)
implicitWidth: root.barWidth * CavaBarService.count + root.barSpacing * (CavaBarService.count - 1)
implicitHeight: parent.height - 10
Cava {
id: cavaProcess
count: root.count
forceEnable: root.forceEnable
}
RowLayout {
anchors.fill: parent
spacing: root.barSpacing
@@ -32,7 +22,7 @@ Item {
}
Repeater {
model: cavaProcess.values
model: CavaBarService.values
Rectangle {
width: root.barWidth
@@ -64,7 +54,7 @@ Item {
else if (mouse.button === Qt.RightButton)
SettingsService.showLyricsBar = !SettingsService.showLyricsBar;
else if (mouse.button === Qt.MiddleButton)
root.forceEnable = !root.forceEnable;
CavaBarService.forceEnable = !CavaBarService.forceEnable;
}
onWheel: function(wheel) {
if (wheel.angleDelta.y > 0)

View File

@@ -16,7 +16,7 @@ MonitorItem {
action.signal(15);
return ;
}
action.exec(["ghostty", "-e", "btop"]);
action.exec(["kitty", "-e", "btop"]);
}
Process {

View File

@@ -16,7 +16,7 @@ MonitorItem {
action.signal(15);
return ;
}
action.exec(["ghostty", "-e", "btop"]);
action.exec(["kitty", "-e", "btop"]);
}
Process {

View File

@@ -19,7 +19,7 @@ MonitorItem {
action.signal(15);
return ;
}
action.exec(["ghostty", "-e", "btop"]);
action.exec(["kitty", "-e", "btop"]);
}
onRightClicked: {
_showPercent = !_showPercent;

View File

@@ -183,7 +183,7 @@ Item {
return Colors.primary;
if (model.isActive)
return Colors.primary.lighter(130);
return Colors.overlay2;
if (model.isUrgent)
return Theme.error;

View File

@@ -0,0 +1,20 @@
import QtQuick
import Quickshell
import qs.Utils
pragma Singleton
Singleton {
id: root
property int count: 6
property int forceEnable: 6
property alias values: cavaProcess.values
Cava {
id: cavaProcess
count: root.count
forceEnable: root.forceEnable
}
}

View File

@@ -67,7 +67,7 @@ Singleton {
}
function showLyricsText() {
action.command = ["sh", "-c", "ghostty -e sh -c 'spotify-lyrics fetch | less'"];
action.command = ["sh", "-c", "ghostty -e sh -c 'spotify-lyrics fetch 2>/dev/null | less'"];
action.startDetached();
}

View File

@@ -15,6 +15,7 @@ Singleton {
property bool inOverview: false
property string focusedWindowTitle: ""
property string focusedWindowAppId: ""
property var onScreenshotCaptured: null
function updateFocusedWindowTitle() {
if (windows && windows[focusedWindowId]) {
@@ -84,7 +85,8 @@ Singleton {
const event = JSON.parse(data.trim());
if (event.WorkspacesChanged) {
workspaceProcess.running = true;
} else if (event.WindowsChanged) {
}
if (event.WindowsChanged) {
try {
const windowsData = event.WindowsChanged.windows;
const windowsMap = {};
@@ -104,9 +106,11 @@ Singleton {
} catch (e) {
Logger.error("Niri", "Error parsing windows event:", e);
}
} else if (event.WorkspaceActivated) {
}
if (event.WorkspaceActivated) {
workspaceProcess.running = true;
} else if (event.WindowFocusChanged) {
}
if (event.WindowFocusChanged) {
try {
const focusedId = event.WindowFocusChanged.id;
if (focusedId) {
@@ -123,13 +127,15 @@ Singleton {
} catch (e) {
Logger.error("Niri", "Error parsing window focus event:", e);
}
} else if (event.OverviewOpenedOrClosed) {
}
if (event.OverviewOpenedOrClosed) {
try {
root.inOverview = event.OverviewOpenedOrClosed.is_open === true;
} catch (e) {
Logger.error("Niri", "Error parsing overview state:", e);
}
} else if (event.WindowOpenedOrChanged) {
}
if (event.WindowOpenedOrChanged) {
try {
const targetWin = event.WindowOpenedOrChanged.window;
const id = targetWin.id;
@@ -165,9 +171,10 @@ Singleton {
} catch (e) {
Logger.error("Niri", "Error parsing window opened/changed event:", e);
}
} else if (event.windowClosed) {
}
if (event.WindowClosed) {
try {
const closedId = event.windowClosed.id;
const closedId = event.WindowClosed.id;
if (closedId && (root.windows && root.windows[closedId])) {
delete root.windows[closedId];
if (root.focusedWindowId === closedId) {
@@ -179,6 +186,17 @@ Singleton {
Logger.error("Niri", "Error parsing window closed event:", e);
}
}
if (event.ScreenshotCaptured) {
try {
const path = event.ScreenshotCaptured.path || "";
if (!path) return;
if (root.onScreenshotCaptured && typeof root.onScreenshotCaptured === "function") {
root.onScreenshotCaptured(path);
}
} catch (e) {
Logger.error("Niri", "Error parsing screenshot captured event:", e);
}
}
} catch (e) {
Logger.error("Niri", "Error parsing event stream:", e, data);
}

View File

@@ -0,0 +1,17 @@
import QtQuick
import Quickshell
import Quickshell.Io
pragma Singleton
Singleton {
id: root
function onScreenshotCaptured(path) {
if (!path || typeof path !== "string")
return ;
console.log("Screenshot captured at path:", path);
Quickshell.execDetached(["screenshot-script", "edit", path]);
}
}

View File

@@ -18,6 +18,7 @@ ShellRoot {
sourceComponent: Item {
Component.onCompleted: {
SunsetService;
Niri.onScreenshotCaptured = Screenshot.onScreenshotCaptured;
}
Notification {

View File

@@ -9,8 +9,10 @@
import os
import sys
from pathlib import Path
import argparse
import subprocess
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
PALETTES = {
"catppuccin-mocha": {
@@ -50,77 +52,121 @@ SCRIPTS = {
"wlogout": [CONFIG_DIR / ".alt" / "wlogout-default" / "apply-color", CONFIG_DIR / ".alt" / "wlogout-niri" / "apply-color"],
"yazi": [CONFIG_DIR / "yazi" / "apply-color"],
}
# or simply `find ${CONFIG_DIR} -type f -iname 'apply-color*'` to get all available scripts,
# but I need the exact application names anyway, so hardcoding does make some sense
# or simply `find -L ${CONFIG_DIR} -type f -iname 'apply-color*'` to get all available scripts,
# but I do need the exact application names anyway, so hardcoding does make some sense
def hex2rgb(hex_color: str) -> tuple[int, int, int]:
"""#rrggbb to (r, g, b)"""
return tuple(int(hex_color[i:i + 2], 16) for i in (0, 2, 4)) # type: ignore
def clamp(x, minimum, maximum) -> float:
"""Clamp x to the range [minimum, maximum]"""
return max(minimum, min(x, maximum))
def rgb2hsv(rr: int, gg: int, bb: int) -> tuple[float, float, float]:
"""(r, g, b) 0-255 to (h, s, v)"""
r, g, b = rr/255.0, gg/255.0, bb/255.0
r = clamp(r, 0.0, 1.0)
g = clamp(g, 0.0, 1.0)
b = clamp(b, 0.0, 1.0)
mx = max(r, g, b)
mn = min(r, g, b)
df = mx-mn
h = 0.0
if mx == mn:
h = 0.0
elif mx == r:
h = (60 * ((g-b)/df) + 360) % 360
elif mx == g:
h = (60 * ((b-r)/df) + 120) % 360
elif mx == b:
h = (60 * ((r-g)/df) + 240) % 360
if mx == 0:
s = 0.0
else:
s = (df/mx)*100
v = mx*100
return h, s, v
def extract_color(image_path: str) -> str:
"""Extract a dominant color from the image and return it as a #rrggbb string."""
# Only import when needed
from colorthief import ColorThief
return "#{:02x}{:02x}{:02x}".format(*ColorThief(image_path).get_color(quality=10))
ct = ColorThief(image_path)
# Get first 5 dominant colors
palette = ct.get_palette(color_count=5, quality=10)
best_color = None
max_score = -1.0
for color in palette:
h, s, v = rgb2hsv(*color)
# Filter out undesirable colors
# Too dark
if v < 20:
continue
# Too light
if v > 95 and s < 5:
continue
# Saturation first, then value
score = s * 2.0 + v
if score > max_score:
max_score = score
best_color = color
# Fallback to the most dominant color
if best_color is None:
best_color = ct.get_color(quality=10)
return "#{:02x}{:02x}{:02x}".format(*best_color)
def match_color(color: str, palette: dict[str, str]) -> str:
""" Matches a given color (rrggbb hex) to the closest color in the palette."""
# HUE distance of the given and returned color must no<t exceed this value
HUE_THRESHOLD = 60.0 # degrees
"""Match the given #rrggbb color to the closest flavor in the palette."""
color = color.lower().strip().removeprefix('#')
target_rgb = hex2rgb(color)
target_h, target_s, target_v = rgb2hsv(*target_rgb)
# weigh by CCIR 601 luminosity
fr, fg, fb = 0.299 / 255 / 255, 0.587 / 255 / 255, 0.114 / 255 / 255
lfr, lfg, lfb = 0.299 / 255, 0.587 / 255, 0.114 / 255
# Warn if not representative (nearly grayscale)
if target_s < 5:
print(f"Warning: Extracted color {color} is nearly grayscale. Matching might be inaccurate.")
def color_distance(c1: str, c2: str) -> float:
r1, g1, b1 = hex2rgb(c1)
r2, g2, b2 = hex2rgb(c2)
diff_l = (lfr * (r1 - r2) + lfg * (g1 - g2) + lfb * (b1 - b2))
diff_r = fr * (r1 - r2) ** 2
diff_g = fg * (g1 - g2) ** 2
diff_b = fb * (b1 - b2) ** 2
return (diff_r + diff_g + diff_b) * 0.75 + diff_l ** 2
def get_weighted_distance(hex_val: str) -> float:
p_rgb = hex2rgb(hex_val)
p_h, p_s, p_v = rgb2hsv(*p_rgb)
def color_distance_hue(c1: str, c2: str) -> float:
def rgb2hue(r, g, b) -> float:
r, g, b = r / 255.0, g / 255.0, b / 255.0
mx = max(r, g, b)
mn = min(r, g, b)
diff = mx - mn
# RGB distance with weighting
rmean = (target_rgb[0] + p_rgb[0]) / 2
dr = target_rgb[0] - p_rgb[0]
dg = target_rgb[1] - p_rgb[1]
db = target_rgb[2] - p_rgb[2]
rgb_distance = ((2 + rmean / 256) * dr**2 + 4 * dg**2 + (2 + (255 - rmean) / 256) * db**2) ** 0.5
if diff == 0:
return 0.0
# Hue difference (with wrapping)
hue_diff = abs(target_h - p_h)
if hue_diff > 180:
hue_diff = 360 - hue_diff
if mx == r:
hue = (g - b) / diff + (6 if g < b else 0)
elif mx == g:
hue = (b - r) / diff + 2
else:
hue = (r - g) / diff + 4
# Increase hue weight when saturation is high
hue_weight = 2.0 if target_s > 20 else 0.5
return hue * 60
r1, g1, b1 = hex2rgb(c1)
r2, g2, b2 = hex2rgb(c2)
return abs(rgb2hue(r1, g1, b1) - rgb2hue(r2, g2, b2))
return rgb_distance + (hue_diff * hue_weight * 3)
closest_color = min(palette.keys(), key=lambda k: color_distance(color, palette[k]))
print(f"Matched color {color} to {closest_color}")
# if the hue distance is too large, rematch
if color_distance_hue(color, palette[closest_color]) > HUE_THRESHOLD:
print(f"Color {color} is too far from {closest_color}, rematching'")
else:
return closest_color
closest_color = min(palette.keys(), key=lambda k: color_distance_hue(color, palette[k]))
print(f"Rematched color {color} to {closest_color}")
return closest_color
closest_flavor = min(palette.keys(), key=lambda k: get_weighted_distance(palette[k]))
print(f"Matched color #{color} to {closest_flavor} (#{palette[closest_flavor]})")
return closest_flavor
def pick_flavor(palette: dict[str, str]) -> str:
def pick_flavor_interactive(palette: dict[str, str]) -> str:
"""Prompt the user to pick a flavor interactively."""
def is_interactive() -> bool:
return sys.stdin.isatty() and sys.stdout.isatty()
@@ -153,6 +199,27 @@ def pick_flavor(palette: dict[str, str]) -> str:
sys.exit(1)
def run_script(script_path: Path, args: list[str]):
"""Helper to run a single script safely."""
script_str = str(script_path)
if not script_path.exists():
print(f"Warning: Script not found: {script_str}")
return
if not os.access(script_path, os.X_OK):
print(f"Warning: Script not executable: {script_str}")
return
try:
cmd = [script_str] + args
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"Error running {script_path}:\n{result.stderr.strip()}")
else:
print(f"✓ {script_path}")
except Exception as e:
print(f"Exception running {script_path}: {e}")
def main():
parser = argparse.ArgumentParser(description="Change color theme for various applications.")
parser.add_argument('-i', '--image', type=str, help="Path to the image")
@@ -186,7 +253,7 @@ def main():
flavor = match_color(color, palette)
print(f"Matched color: {flavor}")
else:
flavor = pick_flavor(palette)
flavor = pick_flavor_interactive(palette)
return flavor
def parse_apps() -> tuple[set[str], set[str]]:
@@ -222,15 +289,18 @@ def main():
print(f"Applying flavor '{flavor}' for {len(apps)} applications.")
for app in apps:
for script in SCRIPTS[app]:
print(f"Running {script}:")
ret = os.system(f'"{script}" {palette_name} {flavor} {palette[flavor]}')
print(f"{script} exited with code {ret}")
print("")
script_args = [palette_name, flavor, palette[flavor]]
tasks = []
os.system(
f'notify-send -a "change-colortheme" "Colortheme Changed" "Palette: {palette_name};\nFlavor: {flavor};\nApplied to {len(apps)} applications."')
with ThreadPoolExecutor(max_workers=8) as executor:
for app in apps:
for script in SCRIPTS[app]:
tasks.append(executor.submit(run_script, script, script_args))
subprocess.run([
"notify-send", "-a", "change-colortheme", "Colortheme Changed",
f"Palette: {palette_name}\nFlavor: {flavor}"
])
if __name__ == "__main__":

View File

@@ -35,6 +35,29 @@ fi
[ -z "$image" ] && exit 1
[ ! -f "$image" ] && exit 1
# Obtain screen resolution
screen_width=$2
screen_height=$3
[ -z "$screen_width" ] && {
if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then
screen_width=$(hyprctl -j monitors | jq '.[0].resolution.x')
elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
screen_width=$(niri msg focused-output | grep 'Current mode' | awk '{print $3}' | cut -d'x' -f1)
fi
}
[ -z "$screen_height" ] && {
if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then
screen_height=$(hyprctl -j monitors | jq '.[0].resolution.y')
elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
screen_height=$(niri msg focused-output | grep 'Current mode' | awk '{print $3}' | cut -d'x' -f2)
fi
}
[ -z "$screen_width" ] && screen_width=2560
[ -z "$screen_height" ] && screen_height=1440
# $HOME/.config/wallpaper-chooser/config.json:
# ```json
@@ -44,45 +67,65 @@ fi
# }
# ```
# So in order to let the most recently used wallpapers appear first:
touch "$image"
touch "$image" 2>/dev/null || true # ignore errors
# Copy image to local wallpaper directory
ext=${image##*.}
wallpaper_ext="png"
random_name=$(tr -dc 'a-zA-Z0-9' </dev/urandom | head -c 16)
current_dir="$HOME/.local/share/wallpaper/current"
image_copied="$current_dir/wallpaper-${random_name}.${ext}"
wallpaper_image="$current_dir/wallpaper-${random_name}.${wallpaper_ext}"
mkdir -p "$current_dir" || {
echo "Could not create directory $current_dir"
exit 1
}
temp_img=$(mktemp --suffix=."$ext") || exit 1
temp_img=$(mktemp --suffix=."$wallpaper_ext") || exit 1
trap 'rm -f "$temp_img"' EXIT
cp "$image" "$temp_img" || exit 1
rm -f "${current_dir:?}"/wallpaper-*
cp -f "$temp_img" "$image_copied" || {
echo "Could not copy image to $current_dir"
magick "$image" -resize "${screen_width}x${screen_height}^" -gravity center -extent "${screen_width}x${screen_height}" "$temp_img" || {
echo "Could not resize and crop image"
exit 1
}
cp "$temp_img" "$wallpaper_image" || exit 1
hash="$(md5sum "$image" | awk '{print $1}')-${screen_width}x${screen_height}"
# Clean up old wallpapers
find "$current_dir" -type f -name "wallpaper-*" ! -name "$(basename "$wallpaper_image")" -delete
# Generate blurred wallpaper
blur_dir="$HOME/.local/share/wallpaper/blurred"
mkdir -p "$blur_dir" || {
blur_cache_dir="$HOME/.local/share/wallpaper/blurred-cache"
mkdir -p "$blur_dir" "$blur_cache_dir" || {
echo "Could not create cache directory"
exit 1
}
rm -f "${blur_dir:?}"/blurred-*
blurred_image="$blur_dir/blurred-${random_name}.$ext"
blurred_image="$blur_dir/blurred-${random_name}.${wallpaper_ext}"
blurred_cache_image="$blur_cache_dir/${hash}.${wallpaper_ext}"
## Time consuming task (magick -blur) in background
(
# notify-send -a "change-wallpaper" "Generating Blurred Wallpaper" "This may take a few seconds..."
sigma=$(magick identify -format "%w %h" "$image_copied" | awk -v f=0.01 '{
### Check if cached blurred image exists
if [ -f "$blurred_cache_image" ]; then
# sleep 1 # Some ugly workaround
if ! cp -f "$blurred_cache_image" "$blurred_image"; then
echo "Could not copy cached blurred image"
# exit 1 # Non-critical error
else
find "$blur_dir" -type f -name "blurred-*" ! -name "$(basename "$blurred_image")" -delete
if [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
swww img -n backdrop "$blurred_image" --transition-type fade --transition-duration 2 >/dev/null 2>/dev/null
fi
notify-send -a "change-wallpaper" "Blurred Wallpaper From Cache" "$blurred_image" -i "$blurred_image"
exit 0
fi
fi
sigma=$(magick identify -format "%w %h" "$wallpaper_image" | awk -v f=0.01 '{
m=($1>$2)?$1:$2;
s=m*f;
if(s<2) s=2;
@@ -91,19 +134,27 @@ blurred_image="$blur_dir/blurred-${random_name}.$ext"
}')
### use a temporary file to avoid incomplete file being used
temp_blurred=$(mktemp --suffix=."$ext") || exit 1
temp_blurred=$(mktemp --suffix=."$wallpaper_ext") || exit 1
trap 'rm -f "${temp_blurred}"' EXIT
magick "$image_copied" -blur 0x"$sigma" "$temp_blurred" || {
magick "$wallpaper_image" -blur 0x"$sigma" "$temp_blurred" || {
echo "Could not create blurred image"
exit 1
}
mv -f "$temp_blurred" "$blurred_image" || {
echo "Could not move blurred image to cache directory"
exit 1
}
find "$blur_dir" -type f -name "blurred-*" ! -name "$(basename "$blurred_image")" -delete
cp -f "$blurred_image" "$blurred_cache_image" || {
echo "Could not cache blurred image"
# exit 1 # Non-critical error
}
if [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
swww img -n backdrop "$blurred_image" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null
swww img -n backdrop "$blurred_image" --transition-type fade --transition-duration 2 >/dev/null 2>/dev/null
fi
notify-send -a "change-wallpaper" "Blurred Wallpaper Generated" "$blurred_image" -i "$blurred_image"
@@ -112,17 +163,18 @@ blurred_image="$blur_dir/blurred-${random_name}.$ext"
# Apply wallpaper
if [ "$XDG_CURRENT_DESKTOP" = "Hyprland" ]; then
swww img -n background "$image_copied" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null
swww img -n background "$wallpaper_image" --transition-type fade --transition-duration 2 >/dev/null 2>/dev/null
notify-send -a "change-wallpaper" "Wallpaper Changed" "$image" -i "$image_copied"
notify-send -a "change-wallpaper" "Wallpaper Changed" "$image" -i "$wallpaper_image"
change-colortheme -i "$image_copied" || exit 1
change-colortheme -i "$wallpaper_image" || exit 1
elif [ "$XDG_CURRENT_DESKTOP" = "niri" ]; then
swww img -n background "$image_copied" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null
### Handled in wallpaper-daemon
# swww img -n background "$wallpaper_image" --transition-type fade --transition-duration 2 > /dev/null 2> /dev/null
notify-send -a "change-wallpaper" "Wallpaper Changed" "$image" -i "$image_copied"
notify-send -a "change-wallpaper" "Wallpaper Changed" "$image" -i "$wallpaper_image"
change-colortheme -i "$image_copied" || exit 1
change-colortheme -i "$wallpaper_image" || exit 1
else
echo "Unsupported desktop environment: $XDG_CURRENT_DESKTOP"
exit 1

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# Description:
# Use a sequence of keybinds to open the scrollback(or screen) buffer in editor (default to vim)
# without switching contexts.
# (It will be much easier if write_<screen|scrollback>_file:open is correctly implemented)
#
# Requirements:
# - ghostty (of course)
# - wl-clipboard
#
# Example configuration in ~/.config/ghostty/config:
# # ctrl+shift+h>j to open the screen buffer in editor
# keybind = ctrl+shift+h=write_screen_file:copy
# keybind = ctrl+shift+j=text:ghostty-capture\n
# Or without wl-paste:
# # ctrl+shift+h>j > Enter to open the screen buffer in editor
# keybind = ctrl+shift+j=text:ghostty-capture\x20
# keybind = ctrl+shift+h=write_screen_file:paste
if [ -z "$1" ] && ! command -v wl-paste &> /dev/null; then
echo "Error: wl-paste not found." >&2
exit 1
fi
file=${1:-$(wl-paste --no-newline)}
[ -z "$file" ] && {
echo "No file provided or found in clipboard." >&2
exit 1
}
[ -f "$file" ] || {
echo "File does not exist: $file" >&2
exit 1
}
case "$file" in
/tmp/*/*.txt) ;;
*)
echo "Possibily not a Ghostty generated temp file: $file" >&2
exit 1
;;
esac
if [[ "$EDITOR" == *"code"* ]]; then
$EDITOR --wait "$file"
else
${EDITOR:-vim} "$file"
fi
rm -f "$file"
rmdir "$(dirname "$file")" 2>/dev/null

View File

@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# Description:
# Play the video embedded in a live photo file (or so-called Motion Photo).
# The literal tags differ between manufacturers, so this script looks for
# the common 'ftyp' box that indicates the start of an MP4 video stream
# instead of "MotionPhotoVideo" or "EmbeddedVideo" or something.
#
# Requirements:
# - mpv or vlc media player installed.
#
# Usage:
# - playlive <file>
# - case `config/scripts/.local/share/applications/playlive.desktop` is installed,
# right-click on a live photo file in file manager and choose "Open With..." ->
# "Play Live Photo".
set -euo pipefail
if [ -z "${1:-}" ]; then
echo "Usage: $0 <file>" >&2
exit 1
fi
play_cmd=()
if command -v mpv >/dev/null 2>&1; then
play_cmd=(mpv --title="Live Photo View" --keep-open=no --loop-file=no --loop-playlist=no --idle=no)
elif command -v vlc >/dev/null 2>&1; then
play_cmd=(vlc --play-and-exit)
else
echo "Error: No suitable media player found." >&2
exit 1
fi
file="$1"
if [ ! -f "$file" ]; then
echo "Error: File '$file' not found." >&2
exit 1
fi
tmp_video=$(mktemp --suffix=.mp4)
trap 'rm -f "$tmp_video"' EXIT
offset=$(grep -aobP "ftyp(mp42|isom)" "$file" | tail -n 1 | cut -d: -f1 || true)
if [ -z "$offset" ]; then
echo "Error: No valid video stream found in '$file'." >&2
exit 1
fi
mp4_offset=$((offset - 3))
tail -c "+$mp4_offset" "$file" > "$tmp_video"
"${play_cmd[@]}" "$tmp_video"

View File

@@ -7,5 +7,7 @@ for child in $(pgrep -P "$pid" 2>/dev/null); do
kill "$child"
done
sleep 0.3
kill "$pid"

View File

@@ -6,13 +6,14 @@
#
# Requirements:
# - hyprshot (for Hyprland)
# - - (niri has screenshot functionality built-in)
# - grim + slurp (for Niri)
# - gradia (for editing)
# - glib bindings for python
import argparse
import subprocess
import time
import fcntl
from os import environ
from datetime import datetime
from enum import Enum
@@ -31,12 +32,13 @@ class ScreenshotType(Enum):
FULL = "full"
AREA = "area"
WINDOW = "window"
EDIT = "edit"
SCREENSHOT_DIR = Path.home() / "Pictures" / "Screenshots"
def wait_until_file_exists(filepath: Path, timeout: int = 5):
def wait_until_file_exists(filepath: Path, timeout: int = 1):
"""Wait until a file exists or timeout."""
start_time = time.time()
while not filepath.exists():
@@ -47,41 +49,60 @@ def wait_until_file_exists(filepath: Path, timeout: int = 5):
def take_screenshot(filepath: Path, typeStr: str):
type = ScreenshotType(typeStr)
currentDesktop = environ.get("XDG_CURRENT_DESKTOP", "")
if "Hyprland" in currentDesktop:
cmd = {
ScreenshotType.FULL: f"hyprshot -z -m output -m active -o {SCREENSHOT_DIR} -f ", # since I only have one monitor
ScreenshotType.AREA: f"hyprshot -z -m region -o {SCREENSHOT_DIR} -f ",
ScreenshotType.WINDOW: f"hyprshot -z -m window -o {SCREENSHOT_DIR} -f ",
}
process = subprocess.run(f"{cmd[type]}{filepath.name}", shell=True)
if process.returncode != 0:
raise RuntimeError("Failed to take screenshot: hyprshot command failed.")
wait_until_file_exists(filepath)
elif "niri" in currentDesktop:
cmd = {
ScreenshotType.FULL: "niri msg action screenshot-screen",
ScreenshotType.AREA: "niri msg action screenshot",
ScreenshotType.WINDOW: "niri msg action screenshot-window",
}
niriScreenshotPath = SCREENSHOT_DIR / ".niri_screenshot.png"
if niriScreenshotPath.exists():
niriScreenshotPath.unlink()
# if os.system(cmd[type]):
process = subprocess.run(cmd[type], shell=True)
if process.returncode != 0:
raise RuntimeError("Failed to take screenshot: niri built-in screenshot command failed.")
wait_until_file_exists(niriScreenshotPath)
if niriScreenshotPath.exists():
# niriScreenshotPath.rename(filepath)
copy2(niriScreenshotPath, filepath)
lockFD = open("/tmp/screenshot-script.lock", "w")
try:
fcntl.flock(lockFD, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
lockFD.close()
raise RuntimeError("Another screenshot is currently being taken.")
try:
type = ScreenshotType(typeStr)
currentDesktop = environ.get("XDG_CURRENT_DESKTOP", "")
if "Hyprland" in currentDesktop:
cmd = {
# since I only have one monitor
ScreenshotType.FULL: f"hyprshot -z -m output -m active -o {SCREENSHOT_DIR} -f ",
ScreenshotType.AREA: f"hyprshot -z -m region -o {SCREENSHOT_DIR} -f ",
ScreenshotType.WINDOW: f"hyprshot -z -m window -o {SCREENSHOT_DIR} -f ",
}
process = subprocess.run(f"{cmd[type]}{filepath.name}", shell=True)
if process.returncode != 0:
raise RuntimeError("Failed to take screenshot: hyprshot command failed.")
if not wait_until_file_exists(filepath):
raise RuntimeError("Failed to take screenshot: output file not found after hyprshot command.")
elif "niri" in currentDesktop:
niriScreenshotPath = SCREENSHOT_DIR / ".niri_screenshot.png"
cmd = {
# niri's built-in screenshot commands are asynchronous, which does not wait for the user to select the area.
# and the selection ui is drawn inside of niri without its state exposed to external programs.
# so we use grim + slurp for area mode and niri's built-in commands for others.
ScreenshotType.FULL: "niri msg action screenshot-screen",
ScreenshotType.AREA: f" grim -g \"$(slurp)\" -t png {niriScreenshotPath} && cat {niriScreenshotPath} | wl-copy",
ScreenshotType.WINDOW: "niri msg action screenshot-window",
}
if niriScreenshotPath.exists():
niriScreenshotPath.unlink()
process = subprocess.run(cmd[type], shell=True)
if process.returncode != 0:
print(process.returncode)
raise RuntimeError("Failed to take screenshot: niri screenshot command failed.")
if wait_until_file_exists(niriScreenshotPath):
# niriScreenshotPath.rename(filepath)
copy2(niriScreenshotPath, filepath)
else:
raise RuntimeError("Failed to take screenshot: output file not found after niri command.")
if not wait_until_file_exists(filepath):
raise RuntimeError("Failed to take screenshot: output file not found after copying.")
else:
raise RuntimeError("Failed to take screenshot: screenshot file nto found after niri command.")
wait_until_file_exists(filepath)
else:
# print("Unsupported desktop environment.")
raise RuntimeError("Unsupported desktop environment.")
# print("Unsupported desktop environment.")
raise RuntimeError("Unsupported desktop environment.")
finally:
fcntl.flock(lockFD, fcntl.LOCK_UN)
lockFD.close()
def edit_screenshot(filepath: Path):
@@ -105,19 +126,29 @@ if __name__ == "__main__":
choices=[t.value for t in ScreenshotType],
help="Type of screenshot to take.",
)
parser.add_argument(
"path",
nargs="?",
default="",
help="Path of the given screenshot file (for edit type only).",
)
args = parser.parse_args()
# file path
SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True)
filename = gen_file_name()
filepath = SCREENSHOT_DIR / filename
filepath: Path = Path()
if not args.type == ScreenshotType.EDIT.value:
# file path
SCREENSHOT_DIR.mkdir(parents=True, exist_ok=True)
filename = gen_file_name()
filepath = SCREENSHOT_DIR / filename
# take screenshot
take_screenshot(filepath, args.type)
# check if successful
if not filepath.exists():
raise RuntimeError("Failed to take screenshot: screenshot file not found.")
# take screenshot
take_screenshot(filepath, args.type)
else:
if not args.path:
raise RuntimeError("Path argument is required for edit type.")
filepath = Path(args.path).expanduser()
if not filepath.exists():
raise RuntimeError(f"File does not exist: {filepath}")
# create loop instance
loop = GLib.MainLoop()
@@ -140,9 +171,9 @@ if __name__ == "__main__":
loop.quit()
n = Notify.Notification.new(
"Screenshot Taken",
# Mako doesn't have action buttons displayed with notification cards,
"Click to edit",
str(filepath),
)
n.add_action(
# so default action is used, which will be triggered on simply clicking the notification card

View File

@@ -34,7 +34,7 @@ def getNiriSocket():
def _log(msg: str):
print(msg)
# print(msg)
# logFIle = Path("/tmp/niri-autoblur.log")
# try:
@@ -75,7 +75,7 @@ def swwwLoadImg(namespace: str, wallpaper: Path):
def swwwStartDaemon(namespace: str):
# Check if daemon is already running
cmd = ["pgrep", "-f", f"swww daemon -n {namespace}"], "-u", str(getuid())
cmd = ["pgrep", "-f", f"swww-daemon -n {namespace}", "-u", str(getuid())]
try:
output = subprocess.check_output(cmd, text=True)
pids = output.strip().splitlines()
@@ -104,13 +104,15 @@ class AutoBlur:
_blurredDir: Path
_isBlurred = threading.Event()
_thread: threading.Thread | None = None
_lastWallpaer: Path | None = None
_lastWallpaper: Path | None = None
_isFirst = True
_applyLock: threading.Lock
def __init__(self, normalDir, blurredDir, interval=0.2):
self._interval = interval
self._normalDir = normalDir
self._blurredDir = blurredDir
self._applyLock = threading.Lock()
# Niri will send "WindowsChanged" event on connect, so no need to init here
# init state
@@ -204,15 +206,16 @@ class AutoBlur:
sleep(self._interval)
def _apply(self, wallpaper: Path) -> bool:
if wallpaper == self._lastWallpaer:
with self._applyLock:
if wallpaper == self._lastWallpaper:
return True
if not swwwLoadImg("background", wallpaper):
return False
self._lastWallpaper = wallpaper
return True
if not swwwLoadImg("background", wallpaper):
return False
self._lastWallpaer = wallpaper
return True
autoBlurInst = AutoBlur(NORMAL_WALLPAPER_DIR, BLURRED_WALLPAPER_DIR)
@@ -339,14 +342,22 @@ if __name__ == "__main__":
swwwLoadImg("background", normal)
# Connect to Niri socket
_log(f"[Main] connecting to Niri socket")
_log("[Main] connecting to Niri socket")
niri_socket = getNiriSocket()
if not niri_socket:
_log("[Main] NIRI_SOCKET environment variable is not set.")
exit(1)
while True:
try:
if not connectNiri(niri_socket, handleEvent):
_log("[Main] Connection lost or failed.")
except Exception as e:
_log(f"[Main] Exception in connection loop: {e}")
if not connectNiri(niri_socket, handleEvent):
exit(1)
_log("[Main] Retrying in 3 seconds...")
sleep(3)
niri_socket = getNiriSocket() or niri_socket
elif desktop == "Hyprland":
_log("[Main] running in Hyprland")
_log("[Main] starting swww daemon")

View File

@@ -0,0 +1,9 @@
[Desktop Entry]
Type=Application
Name=Play Live Photo
Comment=Play embedded video from Live Photos
Exec=/home/kolkas/.local/scripts/playlive %f
Icon=multimedia-video-player
Terminal=false
Categories=AudioVideo;Video;Utility;
MimeType=image/jpeg;image/heic;image/heif;

View File

@@ -2,9 +2,14 @@
# Description:
# Select which GPU to use for rendering for Hyprland and Niri.
#
# envs exported:
# HYPR_AQ_DRM_DEVICES - Colon-separated list of DRM device paths for Hyprland's aq_drm
# BRIGHTNESSCTL_DEVICE - Device identifier for brightnessctl
# AMD -> Nvidia -> Intel
prefer_order=(amd nvidia intel)
# Constants
niri_config_file="$HOME/.config/niri/config/misc.kdl"
prefer_order=(amd nvidia intel) # AMD -> Nvidia -> Intel
# Get vendor and path of each GPU
default_dri_path="$(find /dev/dri/card* 2>/dev/null | head -n 1)"
@@ -59,14 +64,20 @@ for who in "${prefer_order[@]}"; do
done
# Update niri config
for file in "$HOME/.config/niri/config.kdl" "$HOME/.config/niri/config.kdl.template"; do
[[ -f "$file" ]] || continue
function update_niri_config() {
local config_file="$1"
local device_path="$2"
if grep -qE '^\s*render-drm-device\s+"[^"]+"' "$file"; then
current="$(grep -E '^\s*render-drm-device\s+"[^"]+"' "$file" | sed -E 's/^\s*render-drm-device\s+"([^"]+)".*/\1/')"
[[ "$current" == "$primary_device" ]] && continue
sed -i -E "s|^(\s*render-drm-device\s+)\"[^\"]+\"|\1\"$primary_device\"|" "$file"
[[ -f "$config_file" ]] || return
if grep -qE '^\s*render-drm-device\s+"[^"]+"' "$config_file"; then
local current
current="$(grep -E '^\s*render-drm-device\s+"[^"]+"' "$config_file" | sed -E 's/^\s*render-drm-device\s+"([^"]+)".*/\1/')"
[[ "$current" == "$device_path" ]] && return
sed -i -E "s|^(\s*render-drm-device\s+)\"[^\"]+\"|\1\"$device_path\"|" "$config_file"
else
printf '\ndebug {\nrender-drm-device "%s"\n}\n' "$primary_device" >> "$file"
printf '\ndebug {\nrender-drm-device "%s"\n}\n' "$device_path" >> "$config_file"
fi
done
}
update_niri_config "$niri_config_file" "$primary_device"

View File

@@ -11,5 +11,9 @@ command -v nvim >/dev/null 2>&1 && {
export VISUAL=nvim
}
command -v f >/dev/null 2>&1 || {
alias f="exec fish"
}
[ -f "$HOME/.profile" ] && . "$HOME/.profile"
[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc"

View File

@@ -1,6 +1,6 @@
*
!.gitignore
!fetch.fish
!fetch.fish.template
!sshs.fish
!alias.fish
!alias.fish
!ghostty.fish

View File

@@ -85,8 +85,32 @@ alias ....='cd ../../..'
# grep
alias grep="grep --color=auto"
# others
if type -q tty-clock
alias clock="tty-clock -c -C 4"
end
if type -q git
function gcp
if test (count $argv) -eq 0
git commit -a -m "👐 foo: too lazy to come up with a helpful commit message :)" || return 1
else
git commit -a -m "$argv" || return 1
end
git push
end
if type -q wl-paste
alias gc="git clone \$(wl-paste)"
if type -q idea
function pingo
cd "$HOME/Repositories/PGdP" || return 1
set -l repo (wl-paste)
git clone $repo || return 1
set -l repo_name (basename $repo .git)
nohup idea $repo_name >/dev/null 2>&1 & disown
end
end
end
end

View File

@@ -1,18 +1,18 @@
if not set -q fetch_logo_type
set -g fetch_logo_type "auto"
set -g fetch_logo_type auto
end
if not set -q fetch_color
set -g fetch_color "#89b4fa"
end
if test "$fetch_logo_type" = "symbols"
if test "$fetch_logo_type" = symbols
set -g fetch_args "--logo-type raw --logo-width 42 --logo \"$HOME/.config/fastfetch/logo_ros/42x.symbols\" --color \"$fetch_color\""
set -g fetch_args_brief "--logo-type raw --logo-width 28 --logo \"$HOME/.config/fastfetch/logo_ros/28x.symbols\" --color \"$fetch_color\""
else if test "$fetch_logo_type" = "logo"
else if test "$fetch_logo_type" = logo
set -g fetch_args "--logo-type builtin"
set -g fetch_args_brief "--logo-type small"
else if test "$fetch_logo_type" = "sixel"
else if test "$fetch_logo_type" = sixel
set -g fetch_args "--logo-type raw --logo-width 42 --logo \"$HOME/.config/fastfetch/logo_ros/42x.sixel\" --color \"$fetch_color\""
set -g fetch_args_brief "--logo-type raw --logo-width 28 --logo \"$HOME/.config/fastfetch/logo_ros/28x.sixel\" --color \"$fetch_color\""
else # "kitty" or "auto" and others
@@ -24,15 +24,15 @@ if type -q fastfetch
alias ff="fastfetch -c $HOME/.config/fastfetch/config.jsonc $fetch_args"
if test -f "$HOME/.config/fastfetch/brief.jsonc"
alias ff-brief="fastfetch -c $HOME/.config/fastfetch/brief.jsonc $fetch_args_brief"
alias ffb="fastfetch -c $HOME/.config/fastfetch/brief.jsonc $fetch_args_brief"
else
alias ff-brief=ff
alias ffb=ff
end
end
# add 'set -g no_fetch' somewhere other than post.d to disable fetching
if not set -q no_fetch
if type -q ff-brief
ff-brief
if type -q ffb
ffb
end
end

View File

@@ -3,3 +3,4 @@
!prompt.fish
!theme.fish
!env.fish
!niri-env.fish

View File

@@ -0,0 +1,22 @@
if test -n "$XDG_CURRENT_DESKTOP"; and test "$XDG_CURRENT_DESKTOP" = "niri"
set -l env_config "$HOME/.config/niri/config/envs.kdl"
if test -f "$env_config"
while read -la line
# Remove comments
set line (string replace -r '//.*' '' -- "$line")
# Trim whitespace
set line (string trim -- "$line")
# Skip "environment" block lines
if test -z "$line"; or string match -q "environment*" -- "$line"; or test "$line" = "}"
continue
end
# Match lines like: VARIABLE "value" where VARIABLE is alphanumeric/underscore
set -l matches (string match -r '^([0-9A-Za-z_]+)\s+"(.*)"$' -- "$line")
# If have a matches, set the environment variable
if test (count $matches) -eq 3
set -xg $matches[2] $matches[3]
end
end < "$env_config"
end
end

View File

@@ -1,20 +1,16 @@
{
"wallpaper": {
"dirs": [
"~/Pictures/backgrounds",
"/media/Beta/壁纸/库"
],
"excludes": [
"~/Pictures/backgrounds/nao-stars-crop-adjust-flop.jpg",
"~/Pictures/backgrounds/miku-gate.jpg",
"~/Pictures/backgrounds/README.md"
]
},
"action": {
"confirm": "change-wallpaper \"%1\""
},
"sort": {
"type": "date",
"reverse": true
}
"wallpaper": {
"dirs": ["~/Pictures/backgrounds", "/media/Beta/壁纸/库"],
"excludes": ["~/Pictures/backgrounds/nao-stars-crop-adjust-flop.jpg", "~/Pictures/backgrounds/miku-gate.jpg"]
},
"action": {
"confirm": "change-wallpaper \"%1\" 2560 1600"
},
"style": {
"no_loading_screen": false
},
"sort": {
"type": "date",
"reverse": true
}
}

View File

@@ -7,7 +7,7 @@
/* 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-family: 'Sour Gummy Light', 'Meslo LGM Nerd Font Mono', 'Sarasa UI SC', 'Noto Sans', sans-serif;
font-size: 16px;
}

View File

@@ -1,5 +0,0 @@
--password-store=gnome-libsecret
--enable-features=UseOzonePlatform
--ozone-platform=wayland
--use-gl=desktop
--enable-wayland-ime

View File

@@ -1,10 +1,10 @@
require("yaziline"):setup({
separator_style = "angly",
select_symbol = "",
yank_symbol = "󰆐",
filename_max_length = 24, -- trim when filename > 24
filename_trim_length = 6, -- trim 6 chars from both ends
})
-- require("yaziline"):setup({
-- separator_style = "angly",
-- select_symbol = "",
-- yank_symbol = "󰆐",
-- filename_max_length = 24, -- trim when filename > 24
-- filename_trim_length = 6, -- trim 6 chars from both ends
-- })
-- require("starship"):setup {
-- config_file = "~/.config/yazi/starship.toml",
-- }

View File

@@ -82,7 +82,7 @@ keymap = [
# { on = "<C-r>", run = "select_all --state=none", desc = "Inverse selection of all files" },
# Find
{ on = "<C-p>", run = "plugin fzf", desc = "Jump to a directory or reveal a file using fzf" },
{ on = "<C-f>", run = "plugin fzf", desc = "Jump to a directory or reveal a file using fzf" },
{ on = [
"z",
"o",
@@ -105,7 +105,7 @@ keymap = [
{ on = ".", run = "hidden toggle", desc = "Toggle the visibility of hidden files" },
# Open
{ on = "r", run = "open --interactive", desc = "Open selected files interactively" },
{ on = "L", run = "open --interactive", desc = "Open selected files interactively" },
# Create
{ on = "T", run = "create", desc = "Create a file (ends with / for directories)" },
@@ -361,7 +361,9 @@ keymap = [
keymap = [
# Navigation
{ on = "u", run = "arrow -1", desc = "Move up" },
{ on = "<Up>", run = "arrow -1", desc = "Move up" },
{ on = "e", run = "arrow 1", desc = "Move down" },
{ on = "<Down>", run = "arrow 1", desc = "Move down" },
# Close
{ on = "<Esc>", run = "close", desc = "Close spotter" },

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 - sxyazi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Himanshu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,37 +0,0 @@
<div align="center">
<img src="https://github.com/sxyazi/yazi/blob/main/assets/logo.png?raw=true" alt="Yazi logo" width="20%">
</div>
<h3 align="center">
Onedark Flavor for <a href="https://github.com/sxyazi/yazi">Yazi</a>
</h3>
## 👀 Preview
<img src="preview.png" width="600" />
## 🎨 Installation
```bash
# Linux/macOS
git clone https://github.com/BennyOe/onedark.yazi.git ~/.config/yazi/flavors/onedark.yazi
# Windows
git clone https://github.com/BennyOe/onedark.yazi.git %AppData%\yazi\config\flavors\onedark.yazi
```
## ⚙️ Usage
Add the these lines to your `theme.toml` configuration file to use it:
```toml
[flavor]
dark = "onedark"
```
## 📜 License
The flavor is MIT-licensed, and the included tmTheme is also MIT-licensed.
Check the [LICENSE](LICENSE) and [LICENSE-tmtheme](LICENSE-tmtheme) file for more details.

View File

@@ -1,151 +0,0 @@
# : Manager {{{
[manager]
cwd = { fg = "#61AFEF" } # Blue
# Hovered
hovered = { bg = "#282C34" } # Darkened background
preview_hovered = { underline = true }
# Find
find_keyword = { fg = "#E06C75", italic = true, underline = true } # Red
find_position = { fg = "#E5C07B", italic = true } # Orange
# Marker
marker_copied = { fg = "#ABB2BF", bg = "#98C379" } # Light gray on Green
marker_cut = { fg = "#ABB2BF", bg = "#E06C75" } # Light gray on Red
marker_marked = { fg = "#ABB2BF", bg = "#56B6C2" } # Light gray on cyan
marker_selected = { fg = "#ABB2BF", bg = "#E5C07B" } # Light gray on Orange
# Tab
tab_active = { bg = "#282C34", fg = "#61AFEF" } # Darkened background, Blue text
tab_inactive = {}
tab_width = 1
# Count
count_copied = { fg = "#98C379", bg = "#282C34" } # Green on Darkened background
count_cut = { fg = "#E06C75", bg = "#282C34" } # Red on Darkened background
count_selected = { fg = "#98C379", bg = "#282C34" } # Green on Darkened background
# Border
border_symbol = "│"
border_style = { fg = "#282C34" } # Darkened background
# : }}}
# : Status {{{
[status]
separator_open = ""
separator_close = ""
separator_style = { fg = "#61AFEF", bg = "#282C34" } # Blue on Darkened background
# Mode
mode_normal = { fg = "#282C34", bg = "#61AFEF", bold = true } # Dark gray on Blue
mode_select = { fg = "#282C34", bg = "#61AFEF", bold = true } # Dark gray on Blue
mode_unset = { fg = "#282C34", bg = "#61AFEF", bold = true } # Dark gray on Blue
# Progress
progress_label = { fg = "#ABB2BF", bold = true } # Light gray
progress_normal = { fg = "#98C379", bg = "#282C34" } # Green on Darkened background
progress_error = { fg = "#E06C75", bg = "#282C34" } # Red on Darkened background
# Permissions
permissions_t = { fg = "#98C379" } # Green
permissions_r = { fg = "#E06C75" } # Red
permissions_w = { fg = "#E5C07B" } # Orange
permissions_x = { fg = "#98C379" } # Green
permissions_s = { fg = "#ABB2BF" } # Light gray
# : }}}
# : Select {{{
[select]
border = { fg = "#98C379" } # Green
active = { fg = "#E5C07B", bold = true } # Orange
inactive = {}
# : }}}
# : Input {{{
[input]
border = { fg = "#98C379" } # Green
title = {}
value = {}
selected = { reversed = true }
# : }}}
# : Completion {{{
[completion]
border = { fg = "#98C379" } # Green
# : }}}
# : Tasks {{{
[tasks]
border = { fg = "#98C379" } # Green
title = {}
hovered = { fg = "#E5C07B", underline = true } # Orange
# : }}}
# : Which {{{
[which]
mask = { bg = "#282C34" } # Darkened background
cand = { fg = "#98C379" } # Green
rest = { fg = "#ABB2BF" } # Light gray
desc = { fg = "#E5C07B" } # Orange
separator = "  "
separator_style = { fg = "#ABB2BF" } # Light gray
# : }}}
# : Help {{{
[help]
on = { fg = "#98C379" } # Green
run = { fg = "#E5C07B" } # Orange
hovered = { reversed = true, bold = true }
footer = { fg = "#ABB2BF", bg = "#000000" } # Light gray on Black
# : }}}
# : Notify {{{
[notify]
title_info = { fg = "#98C379" } # Green
title_warn = { fg = "#E06C75" } # Red
title_error = { fg = "#E5C07B" } # Orange
# : }}}
# : File-specific styles {{{
[filetype]
rules = [
# Images
{ mime = "image/*", fg = "#E5C07B" }, # Orange
# Media
{ mime = "video/*", fg = "#E06C75" }, # Red
{ mime = "audio/*", fg = "#E06C75" }, # Red
# Archives
{ mime = "application/zip", fg = "#C678DD" }, # Magenta
{ mime = "application/x-tar", fg = "#C678DD" }, # Magenta
{ mime = "application/x-bzip*", fg = "#C678DD" }, # Magenta
{ mime = "application/x-bzip2", fg = "#C678DD" }, # Magenta
{ mime = "application/x-7z-compressed", fg = "#C678DD" }, # Magenta
{ mime = "application/x-rar", fg = "#C678DD" }, # Magenta
{ mime = "application/x-xz", fg = "#C678DD" }, # Magenta
# Documents
{ mime = "application/doc", fg = "#98C379" }, # Green
{ mime = "application/pdf", fg = "#98C379" }, # Green
{ mime = "application/rtf", fg = "#98C379" }, # Green
{ mime = "application/vnd.*", fg = "#98C379" }, # Green
# Fallback
{ name = "*", fg = "#ABB2BF" }, # Blue
{ name = "*/", fg = "#61AFEF" } # Blue
]
# : }}}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 KiB

View File

@@ -1,560 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>author</key>
<string>Template: Chris Kempson, Scheme: Lalit Magant (http://github.com/tilal6991)</string>
<key>name</key>
<string>Base16 OneDark</string>
<key>semanticClass</key>
<string>theme.base16.onedark</string>
<key>colorSpaceName</key>
<string>sRGB</string>
<key>gutterSettings</key>
<dict>
<key>background</key>
<string>#353b45</string>
<key>divider</key>
<string>#353b45</string>
<key>foreground</key>
<string>#545862</string>
<key>selectionBackground</key>
<string>#3e4451</string>
<key>selectionForeground</key>
<string>#565c64</string>
</dict>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#282c34</string>
<key>caret</key>
<string>#abb2bf</string>
<key>foreground</key>
<string>#abb2bf</string>
<key>invisibles</key>
<string>#545862</string>
<key>lineHighlight</key>
<string>#54586255</string>
<key>selection</key>
<string>#3e4451</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Text</string>
<key>scope</key>
<string>variable.parameter.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#abb2bf</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comments</string>
<key>scope</key>
<string>comment, punctuation.definition.comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#545862</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Punctuation</string>
<key>scope</key>
<string>punctuation.definition.string, punctuation.definition.variable, punctuation.definition.string, punctuation.definition.parameters, punctuation.definition.string, punctuation.definition.array</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#abb2bf</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Delimiters</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#abb2bf</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Operators</string>
<key>scope</key>
<string>keyword.operator</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#abb2bf</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keywords</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variables</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Functions</string>
<key>scope</key>
<string>entity.name.function, meta.require, support.function.any-method, variable.function, variable.annotation, support.macro</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Labels</string>
<key>scope</key>
<string>entity.name.label</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#be5046</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Classes</string>
<key>scope</key>
<string>support.class, entity.name.class, entity.name.type.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Classes</string>
<key>scope</key>
<string>meta.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c8ccd4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Methods</string>
<key>scope</key>
<string>keyword.other.special-method</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage</string>
<key>scope</key>
<string>storage</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Support</string>
<key>scope</key>
<string>support.function</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#56b6c2</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Strings, Inherited Class</string>
<key>scope</key>
<string>string, constant.other.symbol, entity.other.inherited-class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Integers</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Floats</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Boolean</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Constants</string>
<key>scope</key>
<string>constant</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tags</string>
<key>scope</key>
<string>entity.name.tag</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Attributes</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Attribute IDs</string>
<key>scope</key>
<string>entity.other.attribute-name.id, punctuation.definition.entity</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Selector</string>
<key>scope</key>
<string>meta.selector</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Values</string>
<key>scope</key>
<string>none</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Headings</string>
<key>scope</key>
<string>markup.heading punctuation.definition.heading, entity.name.section</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#61afef</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Units</string>
<key>scope</key>
<string>keyword.other.unit</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Bold</string>
<key>scope</key>
<string>markup.bold, punctuation.definition.bold</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>bold</string>
<key>foreground</key>
<string>#e5c07b</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Italic</string>
<key>scope</key>
<string>markup.italic, punctuation.definition.italic</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Code</string>
<key>scope</key>
<string>markup.raw.inline</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Link Text</string>
<key>scope</key>
<string>string.other.link, punctuation.definition.string.end.markdown, punctuation.definition.string.begin.markdown</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Link Url</string>
<key>scope</key>
<string>meta.link</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Lists</string>
<key>scope</key>
<string>markup.list</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Quotes</string>
<key>scope</key>
<string>markup.quote</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#d19a66</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Separator</string>
<key>scope</key>
<string>meta.separator</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#3e4451</string>
<key>foreground</key>
<string>#abb2bf</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Inserted</string>
<key>scope</key>
<string>markup.inserted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#98c379</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Deleted</string>
<key>scope</key>
<string>markup.deleted</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#e06c75</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Changed</string>
<key>scope</key>
<string>markup.changed</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Colors</string>
<key>scope</key>
<string>constant.other.color</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#56b6c2</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Regular Expressions</string>
<key>scope</key>
<string>string.regexp</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#56b6c2</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Escape Characters</string>
<key>scope</key>
<string>constant.character.escape</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#56b6c2</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Embedded</string>
<key>scope</key>
<string>punctuation.section.embedded, variable.interpolation</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#c678dd</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Illegal</string>
<key>scope</key>
<string>invalid.illegal</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#e06c75</string>
<key>foreground</key>
<string>#c8ccd4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Broken</string>
<key>scope</key>
<string>invalid.broken</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#d19a66</string>
<key>foreground</key>
<string>#282c34</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Deprecated</string>
<key>scope</key>
<string>invalid.deprecated</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#be5046</string>
<key>foreground</key>
<string>#c8ccd4</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Unimplemented</string>
<key>scope</key>
<string>invalid.unimplemented</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#545862</string>
<key>foreground</key>
<string>#c8ccd4</string>
</dict>
</dict>
</array>
<key>uuid</key>
<string>uuid</string>
</dict>
</plist>

View File

@@ -1,11 +1,11 @@
[[plugin.deps]]
use = "yazi-rs/plugins:git"
rev = "d1c8baa"
hash = "63b6c222bf2103b3023389dde5e2ecfe"
rev = "2301ff8"
hash = "27ca02f49fd236b5cc7bf03c243859fe"
[[plugin.deps]]
use = "yazi-rs/plugins:smart-enter"
rev = "d1c8baa"
rev = "2301ff8"
hash = "56fdabc96fc1f4d53c96eb884b02a5be"
[[plugin.deps]]
@@ -20,8 +20,8 @@ hash = "e17c11b605d989568a1d1741ca17c584"
[[plugin.deps]]
use = "llanosrocas/yaziline"
rev = "e79b067"
hash = "f590c5b7d0730e8d6023b1b34ddf7ead"
rev = "e7042a8"
hash = "9c2ab18ff5368056904e4ebb61b17571"
[flavor]
deps = []

View File

@@ -190,7 +190,7 @@ local function setup(st, opts)
Linemode:children_add(function(self)
local url = self._file.url
local repo = st.dirs[tostring(url.base)]
local repo = st.dirs[tostring(url.base or url.parent)]
local code
if repo then
code = repo == CODES.excluded and CODES.ignored or st.repos[repo][tostring(url):sub(#repo + 2)]
@@ -208,7 +208,7 @@ end
---@type UnstableFetcher
local function fetch(_, job)
local cwd = job.files[1].url.base
local cwd = job.files[1].url.base or job.files[1].url.parent
local repo = root(cwd)
if not repo then
remove(tostring(cwd))

View File

@@ -2,37 +2,59 @@
Simple lualine-like status line for yazi.
Read more about features and configuration [here](#features).
![angly](https://github.com/llanosrocas/yaziline.yazi/blob/main/.github/images/angly.png)
![preview-fullscreen](https://github.com/llanosrocas/yaziline.yazi/blob/main/.github/images/preview-fullscreen.png)
![preview](https://github.com/llanosrocas/yaziline.yazi/blob/master/.github/images/preview.png)
All supported features are listed [here](#features). More presets are available [here](#presets).
## Requirements
- yazi version >= [25.5.28](https://github.com/sxyazi/yazi/releases/tag/v25.5.28)
- yazi version >= [917e1f5](https://github.com/sxyazi/yazi/commit/917e1f54a10445f2e25147c4b81a3c77d8233632)
- Font with symbol support. For example [Nerd Fonts](https://www.nerdfonts.com/).
## Compatibility
To keep the plugin up to date, there are two branches: `main` and `nightly`.
The `main` branch follows major yazi releases, while `nightly` is linked to specific yazi commits or changes.
This setup allows shipping stable versions on time, while giving early access to "cutting-edge" changes. See matrix below.
<details close>
<summary>Compatibility matrix</summary>
| yaziline | yazi |
| :------------------------------------------------------------------------: | ----------------------------------------------------------------------------------------- |
| [v2.5.2](https://github.com/llanosrocas/yaziline.yazi/releases/tag/v2.5.2) | [917e1f5](https://github.com/sxyazi/yazi/commit/917e1f54a10445f2e25147c4b81a3c77d8233632) |
| [v2.5.1](https://github.com/llanosrocas/yaziline.yazi/releases/tag/v2.5.1) | [917e1f5](https://github.com/sxyazi/yazi/commit/917e1f54a10445f2e25147c4b81a3c77d8233632) |
| [v2.5.0](https://github.com/llanosrocas/yaziline.yazi/releases/tag/v2.5.0) | [v25.5.28](https://github.com/sxyazi/yazi/releases/tag/v25.5.28) |
| [v2.4.0](https://github.com/llanosrocas/yaziline.yazi/releases/tag/v2.4.0) | [v25.4.8](https://github.com/sxyazi/yazi/releases/tag/v25.4.8) |
</details>
## Installation
1. Using yazi package manager
```sh
ya pkg add llanosrocas/yaziline
```
Or manually copy `main.lua` to the `~/.config/yazi/plugins/yaziline.yazi/main.lua`
_Or manually copy `main.lua` to the `~/.config/yazi/plugins/yaziline.yazi/main.lua`_
## Usage
Add this to your `~/.config/yazi/init.lua`:
2. Add this line to your `~/.config/yazi/init.lua`:
```lua
require("yaziline"):setup()
```
Optionally, configure line:
## Configuration
This is default config, if you want to see presets go to [this section](#presets).
```lua
require("yaziline"):setup({
color = "#98c379", -- main theme color
secondary_color = "#5A6078", -- secondary color
color = "#98c379",
secondary_color = "#5A6078",
default_files_color = "darkgray", -- color of the file counter when it's inactive
selected_files_color = "white",
yanked_files_color = "green",
@@ -51,10 +73,18 @@ require("yaziline"):setup({
filename_max_length = 24, -- truncate when filename > 24
filename_truncate_length = 6, -- leave 6 chars on both sides
filename_truncate_separator = "..." -- the separator of the truncated filename
filename_truncate_separator = "..."
})
```
By default yaziline uses color values from your `theme.toml`:
- mode and position font color: th.which.mask.bg
- default_files_color: which.separator_style.fg
- selected_files_color: mgr.count_selected.bg
- yanked_files_color: mgr.count_copied.bg
- cut_files_color: mgr.count_cut.bg
```
 MODE  size  long_file...name.md  S 0 Y 0
| | | | | | | | |
@@ -71,73 +101,16 @@ require("yaziline"):setup({
## Features
### Preconfigured separators
Choose your style:
### Presets
- `angly`
![angly](https://github.com/llanosrocas/yaziline.yazi/blob/master/.github/images/angly.png)
![angly](https://github.com/llanosrocas/yaziline.yazi/blob/main/.github/images/angly.png)
- `curvy`
![curvy](https://github.com/llanosrocas/yaziline.yazi/blob/master/.github/images/curvy.png)
![curvy](https://github.com/llanosrocas/yaziline.yazi/blob/main/.github/images/curvy.png)
- `liney`
![liney](https://github.com/llanosrocas/yaziline.yazi/blob/master/.github/images/liney.png)
![liney](https://github.com/llanosrocas/yaziline.yazi/blob/main/.github/images/liney.png)
- `empty`
![empty](https://github.com/llanosrocas/yaziline.yazi/blob/master/.github/images/empty.png)
### Separator customization
You can provide your own symbols for separators combined with preconfigured separators. For example:
```lua
require("yaziline"):setup({
-- Optinal config
separator_style = "angly", -- preconfigured style
separator_open = "", -- instead of 
separator_close = "", -- instead of 
separator_open_thin = "", -- change to anything
separator_close_thin = "", -- change to anything
separator_head = "", -- to match the style
separator_tail = "" -- to match the style
})
```
![empty](https://github.com/llanosrocas/yaziline.yazi/blob/master/.github/images/separator-combination.png)
_You can find more symbols [here](https://www.nerdfonts.com/cheat-sheet)_
### File actions icons
You can provide your own symbols for `select` and `yank`. For example:
```lua
require("yaziline"):setup({
-- Optinal config
select_symbol = "", -- "S" by default
yank_symbol = "󰆐" -- "Y" by default
})
```
![empty](https://github.com/llanosrocas/yaziline.yazi/blob/master/.github/images/file-actions.png)
_You can find more symbols [here](https://www.nerdfonts.com/cheat-sheet)_
### Colors and font weight
By default yaziline uses color values from your `theme.toml` (or flavor) but you can set custom colors in the `init.lua`:
```lua
require("yaziline"):setup({
color = "#98c379",
default_files_color = "darkgray",
selected_files_color = "white",
yanked_files_color = "green",
cut_files_color = "red",
})
```
For example, here is how my line looks like:
![preview-2](https://github.com/llanosrocas/yaziline.yazi/blob/master/.github/images/preview-2.png)
![empty](https://github.com/llanosrocas/yaziline.yazi/blob/main/.github/images/empty.png)
### Selected and Yanked Counter
@@ -149,9 +122,9 @@ Displays the truncated filename on the left, which is useful for smaller windows
```lua
require("yaziline"):setup({
filename_max_length = 24, -- truncate when filename > 24
filename_truncate_length = 6, -- leave 6 chars on both sides
filename_truncate_separator = "..." -- the separator of the truncated filename
filename_max_length = 24,
filename_truncate_length = 6,
filename_truncate_separator = "..."
})
```

View File

@@ -1,212 +1,212 @@
---@diagnostic disable: undefined-global
local function setup(_, options)
options = options or {}
options = options or {}
local default_separators = {
angly = { "", "", "", "" },
curvy = { "", "", "", "" },
liney = { "", "", "|", "|" },
empty = { "", "", "", "" },
}
local separators = default_separators[options.separator_style or "angly"]
local default_separators = {
angly = { "", "", "", "" },
curvy = { "", "", "", "" },
liney = { "", "", "|", "|" },
empty = { "", "", "", "" },
}
local separators = default_separators[options.separator_style or "angly"]
local config = {
separator_styles = {
separator_open = options.separator_open or separators[1],
separator_close = options.separator_close or separators[2],
separator_open_thin = options.separator_open_thin or separators[3],
separator_close_thin = options.separator_close_thin or separators[4],
separator_head = options.separator_head or "",
separator_tail = options.separator_tail or "",
},
select_symbol = options.select_symbol or "S",
yank_symbol = options.yank_symbol or "Y",
local config = {
separator_styles = {
separator_open = options.separator_open or separators[1],
separator_close = options.separator_close or separators[2],
separator_open_thin = options.separator_open_thin or separators[3],
separator_close_thin = options.separator_close_thin or separators[4],
separator_head = options.separator_head or "",
separator_tail = options.separator_tail or "",
},
select_symbol = options.select_symbol or "S",
yank_symbol = options.yank_symbol or "Y",
filename_max_length = options.filename_max_length or 24,
filename_truncate_length = options.filename_truncate_length or 6,
filename_truncate_separator = options.filename_truncate_separator or "...",
filename_max_length = options.filename_max_length or 24,
filename_truncate_length = options.filename_truncate_length or 6,
filename_truncate_separator = options.filename_truncate_separator or "...",
color = options.color or nil,
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",
or th.mgr.count_selected:bg()
or "white",
yanked_files_color = options.selected_files_color
or th.mgr.count_copied.bg
or "green",
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_cut:bg()
or "red",
}
local current_separator_style = config.separator_styles
local current_separator_style = config.separator_styles
function Header:count()
return ui.Line({})
end
function Header:count()
return ui.Line({})
end
function Status:mode()
local mode = tostring(self._tab.mode):upper()
function Status:mode()
local mode = tostring(self._tab.mode):upper()
local style = self:style()
return ui.Line({
ui.Span(current_separator_style.separator_head)
:fg(config.color or style.main.bg),
ui.Span(" " .. mode .. " ")
:fg(th.which.mask.bg)
:bg(config.color or style.main.bg),
})
end
local style = self:style()
return ui.Line({
ui.Span(current_separator_style.separator_head)
:fg(config.color or style.main:bg()),
ui.Span(" " .. mode .. " ")
:fg(th.which.mask:bg())
:bg(config.color or style.main:bg()),
})
end
function Status:size()
local h = self._current.hovered
local size = h and (h:size() or h.cha.len) or 0
function Status:size()
local h = self._current.hovered
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)
end
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())
end
function Status:utf8_sub(str, start_char, end_char)
local start_byte = utf8.offset(str, start_char)
local end_byte = end_char and (utf8.offset(str, end_char + 1) - 1) or #str
function Status:utf8_sub(str, start_char, end_char)
local start_byte = utf8.offset(str, start_char)
local end_byte = end_char and (utf8.offset(str, end_char + 1) - 1) or #str
if not start_byte or not end_byte then
return ""
end
if not start_byte or not end_byte then
return ""
end
return string.sub(str, start_byte, end_byte)
end
return string.sub(str, start_byte, end_byte)
end
function Status:truncate_name(filename, max_length)
local base_name, extension = filename:match("^(.+)(%.[^%.]+)$")
base_name = base_name or filename
extension = extension or ""
function Status:truncate_name(filename, max_length)
local base_name, extension = filename:match("^(.+)(%.[^%.]+)$")
base_name = base_name or filename
extension = extension or ""
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)
end
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)
end
return base_name .. extension
end
return base_name .. extension
end
function Status:name()
local h = self._current.hovered
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),
})
end
function Status:name()
local h = self._current.hovered
local style = self:style()
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()),
})
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)
local style = self:style()
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),
})
end
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()),
})
end
function Status:files()
local files_yanked = #cx.yanked
local files_selected = #cx.active.selected
local files_cut = cx.yanked.is_cut
function Status:files()
local files_yanked = #cx.yanked
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 yanked_fg = files_yanked > 0
and
(files_cut
and config.cut_files_color
or config.yanked_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
local yanked_text = files_yanked > 0
and config.yank_symbol .. " " .. files_yanked
or config.yank_symbol .. " 0"
local yanked_text = files_yanked > 0
and config.yank_symbol .. " " .. files_yanked
or config.yank_symbol .. " 0"
return ui.Line({
ui.Span(" " .. current_separator_style.separator_close_thin .. " ")
:fg(th.which.separator_style.fg),
ui.Span(config.select_symbol .. " " .. files_selected .. " ")
:fg(selected_fg),
ui.Span(yanked_text .. " ")
:fg(yanked_fg),
})
end
return ui.Line({
ui.Span(" " .. current_separator_style.separator_close_thin .. " ")
:fg(th.which.separator_style:fg()),
ui.Span(config.select_symbol .. " " .. files_selected .. " ")
:fg(selected_fg),
ui.Span(yanked_text .. " ")
:fg(yanked_fg),
})
end
function Status:modified()
local hovered = cx.active.current.hovered
function Status:modified()
local hovered = cx.active.current.hovered
if not hovered then
return ""
end
if not hovered then
return ""
end
local cha = hovered.cha
local time = (cha.mtime or 0) // 1
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)
end
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()
local percent = 0
local cursor = self._tab.current.cursor
local length = #self._tab.current.files
if cursor ~= 0 and length ~= 0 then
percent = math.floor((cursor + 1) * 100 / length)
end
function Status:percent()
local percent = 0
local cursor = self._tab.current.cursor
local length = #self._tab.current.files
if cursor ~= 0 and length ~= 0 then
percent = math.floor((cursor + 1) * 100 / length)
end
if percent == 0 then
percent = " Top "
elseif percent == 100 then
percent = " Bot "
else
percent = string.format(" %2d%% ", percent)
end
if percent == 0 then
percent = " Top "
elseif percent == 100 then
percent = " Bot "
else
percent = string.format(" %2d%% ", percent)
end
local style = self:style()
return ui.Line({
local style = self:style()
return ui.Line({
ui.Span(" " .. current_separator_style.separator_open)
: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),
ui.Span(current_separator_style.separator_open)
:fg(config.color or style.main.bg)
:bg(config.secondary_color or th.which.separator_style.fg),
})
end
: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()),
ui.Span(current_separator_style.separator_open)
:fg(config.color or style.main:bg())
:bg(config.secondary_color or th.which.separator_style:fg()),
})
end
function Status:position()
local cursor = self._tab.current.cursor
local length = #self._tab.current.files
function Status:position()
local cursor = self._tab.current.cursor
local length = #self._tab.current.files
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),
})
end
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()),
})
end
Status:children_add(Status.files, 4000, Status.LEFT)
Status:children_add(Status.modified, 0, Status.RIGHT)
Status:children_add(Status.files, 4000, Status.LEFT)
Status:children_add(Status.modified, 0, Status.RIGHT)
end
return { setup = setup }
return { setup = setup }

View File

@@ -1,97 +0,0 @@
{
"$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
"blocks": [
{
"alignment": "left",
"segments": [
{
"foreground": "#89b4fa",
"properties": {
"windows": "\ue62a"
},
"style": "plain",
"template": "{{.Icon}}",
"type": "os"
},
{
"foreground": "#89b4fa",
"style": "plain",
"template": " {{.UserName}}",
"type": "session"
},
{
"foreground": "#89b4fa",
"style": "plain",
"template": "@{{.HostName}}",
"type": "session"
},
{
"foreground": "#94e2d5",
"properties": {
"folder_separator_icon": "/",
"style": "letter"
},
"style": "powerline",
"template": " \uf07b {{ .Path }} ",
"type": "path"
},
{
"foreground": "#a6e3a1",
"powerline_symbol": "\ue0b1",
"properties": {
"branch_icon": " ",
"fetch_stash_count": true,
"fetch_status": true,
"fetch_upstream_icon": true,
"fetch_worktree_count": true
},
"style": "powerline",
"template": " {{ .UpstreamIcon }} {{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} \uf044 {{ .Working.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Staging.Changed }} \uf046 {{ .Staging.String }}{{ end }}{{ if gt .StashCount 0 }} \ueb4b {{ .StashCount }}{{ end }} ",
"type": "git"
},
{
"foreground": "#f9e2af",
"powerline_symbol": "\ue0b1",
"template": " \ue235 {{ if .Error }}{{ .Error }}{{ else }}{{ if .Venv }}{{ .Venv }} {{ end }}{{ .Full }}{{ end }} ",
"style": "powerline",
"type": "python"
}
],
"type": "prompt"
},
{
"alignment": "left",
"newline": true,
"segments": [
{
"foreground": "#fab387",
"style": "plain",
"template": "\ue3bf ",
"type": "root"
},
{
"foreground": "#89b4fa",
"foreground_templates": [
"{{ if gt .Code 0 }}#f38ba8{{ end }}"
],
"properties": {
"always_enabled": true
},
"style": "plain",
"template": "{{ if gt .Code 0 }}{{ .Code }} {{ end }}# ",
"type": "status"
}
],
"type": "prompt"
}
],
"transient_prompt": {
"background": "transparent",
"foreground_templates": [
"{{ if gt .Code 0 }}#f38ba8{{ end }}"
],
"foreground": "#89b4fa",
"template": "# "
},
"version": 3
}

View File

@@ -1,2 +0,0 @@
--ozone-platform-hint=auto
--enable-wayland-ime

View File

@@ -1,2 +0,0 @@
--ozone-platform-hint=auto
--enable-wayland-ime

View File

@@ -1,80 +1,61 @@
感觉不适合放到 stow 包里,毕竟这取决于系统上安装了哪些字体。那就放 memo 里好了。
### Font packages (involved in fontconfig)
> path: `~/.config/fontconfig/fonts.conf`.
- ttf-sarasa-gothi
- ttf-symbola (AUR)
- noto-fonts
- noto-fonts-cjk
- noto-fonts-emoji
- ttf-nerd-fonts-symbols
- maplemono-nf-cn (AUR)
### Other fonts (used but not involved in fontconfig)
- Sour Gummy
- Font Awesome 6 Free
- Meslo LGM Nerd Font Mono
### Fontconfig configuration
> `~/.config/fontconfig/fonts.conf`
```xml
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'urn:fontconfig:fonts.dtd'>
<fontconfig>
<!--
Artificial oblique for fonts without an italic or oblique version
-->
<match target="font">
<!-- check to see if the font is roman -->
<test name="slant">
<const>roman</const>
</test>
<!-- check to see if the pattern requested non-roman -->
<test compare="not_eq" name="slant" target="pattern">
<const>roman</const>
</test>
<!-- multiply the matrix to slant the font -->
<edit mode="assign" name="matrix">
<times>
<name>matrix</name>
<matrix>
<double>1</double>
<double>0.2</double>
<double>0</double>
<double>1</double>
</matrix>
</times>
</edit>
<!-- pretend the font is oblique now -->
<edit mode="assign" name="slant">
<const>oblique</const>
</edit>
<!-- and disable embedded bitmaps for artificial oblique -->
<edit mode="assign" name="embeddedbitmap">
<bool>false</bool>
</edit>
</match>
<!--
Synthetic emboldening for fonts that do not have bold face available
-->
<match target="font">
<!-- check to see if the weight in the font is less than medium which possibly need emboldening -->
<test compare="less_eq" name="weight">
<const>medium</const>
</test>
<!-- check to see if the pattern requests bold -->
<test compare="more_eq" name="weight" target="pattern">
<const>bold</const>
</test>
<!--
set the embolden flag
needed for applications using cairo, e.g. gucharmap, gedit, ...
-->
<edit mode="assign" name="embolden">
<bool>true</bool>
</edit>
<!--
set weight to bold
needed for applications using Xft directly, e.g. Firefox, ...
-->
<edit mode="assign" name="weight">
<const>bold</const>
</edit>
</match>
<match target="font">
<edit mode="assign" name="antialias"><bool>true</bool></edit>
<edit mode="assign" name="hinting"><bool>true</bool></edit>
<edit mode="assign" name="hintstyle"><const>hintslight</const></edit>
<edit mode="assign" name="rgba"><const>none</const></edit>
</match>
<!-- For danmuku -->
<match target="pattern">
<test name="family">
<string>Noto Sans CJK SC</string>
</test>
<edit name="family" mode="append">
<string>Symbola</string>
</edit>
</match>
<alias>
<family>sans-serif</family>
<prefer>
<family>Noto Sans</family>
<family>Noto Sans CJK SC</family>
<family>Noto Sans CJK JP</family>
<family>Noto Sans CJK KR</family>
<family>Maple Mono NF CN</family>
<family>Sarasa UI SC</family>
<family>Sarasa UI J</family>
<family>Noto Color Emoji</family>
<family>Symbols Nerd Font</family>
</prefer>
</alias>
<alias>
<family>system-ui</family>
<prefer>
<family>Sarasa UI SC</family>
<family>Sarasa UI J</family>
<family>Noto Color Emoji</family>
<family>Symbols Nerd Font</family>
</prefer>
</alias>
@@ -84,8 +65,8 @@
<family>Noto Serif</family>
<family>Noto Serif CJK SC</family>
<family>Noto Serif CJK JP</family>
<family>Noto Serif CJK KR</family>
<family>Maple Mono NF CN</family>
<family>Noto Color Emoji</family>
<family>Symbols Nerd Font</family>
</prefer>
</alias>
@@ -93,10 +74,10 @@
<family>monospace</family>
<prefer>
<family>Maple Mono NF CN</family>
<family>Noto Sans Mono</family>
<family>Noto Sans Mono CJK SC</family>
<family>Noto Sans Mono CJK JP</family>
<family>Noto Sans Mono CJK KR</family>
<family>Sarasa Mono SC</family>
<family>Sarasa Mono J</family>
<family>Noto Color Emoji</family>
<family>Symbols Nerd Font</family>
</prefer>
</alias>
</fontconfig>

108
memo/git-remote.md Normal file
View File

@@ -0,0 +1,108 @@
## Gitea with custom SSH port
Since in most cases Gitea will be deployed within containers, it cannot use the same port (`22`) as the host machine to handle git operations via SSH. In my case the port `222` is used instead. Meanwhiles, this can cause some extra steps to set the correct remote URL for local repositories, since the port is different from the default one.
There are few approaches to handle this:
1. Edit `~/.ssh/config`
like:
```
Host gitea
HostName git.example.tld
User git
Port 222
IdentityFile /path/to/private/key
```
then:
```sh
git remote add origin gitea:username/repo.git
```
2. Use full URL with port
> [!WARNING]
>
> The `scp`-like syntax `user@host:repo.git` does not support port specification. `ssh://` URL scheme must be used instead.
```sh
git remote add origin ssh://git@example.tld:222/username/repo.git
```
3. Use `GIT_SSH_COMMAND` environment variable
```sh
GIT_SSH_COMMAND='ssh -p 222'
```
4. and more...
- aliases
- scripts
- etc.
## Local Repo with Multiple Remotes
> [!TIP]
>
> Check current remotes with:
>
> ```sh
> git remote -v
> ```
>
> and remove a remote with:
>
> ```sh
> git remote remove <name>
> ```
Local repositories can have multiple remotes configured. This is useful when pushing to the primary repository and also to a backup repository.
And there are multiple ways to add multiple remotes:
1. Use different names for remotes
```sh
git remote add origin ssh://git@example.tld/username/repo.git
git remote add backup ssh://git@backup.tld/username/repo.git
```
then push to both remotes:
```sh
git push origin main
git push backup main
```
Without referring to remotes explicitly, git will use `origin` by default. To fetch from all remotes, use `git fetch --all` or directly `git pull --all`.
> [!NOTE]
>
> The command `git push --all` is for pushing all local branches, not for pushing to all configured remote URLs.
2. Use `git remote set-url --add`
```sh
git remote add origin ssh://git@example.tld/username/repo.git
git remote set-url --add origin ssh://git@backup.tld/username/repo.git
```
then push to both remotes:
```sh
git push origin
```
A single `git push origin` will push to all URLs configured for the `origin` remote.
> [!IMPORTANT]
>
> By default, `git fetch` and `git pull` will only interact with the first (fetch) URL configured for a remote.
>
> The `git fetch --all` command fetches from all configured **remotes** (e.g., `origin`, `backup`), not from all URLs within a single remote.
>
> Similarly, `git pull --all` will first run `git fetch --all` and then merge the current local branch with its upstream branch. The merge action only applies to the current branch.

View File

@@ -1,6 +1,6 @@
things I have installed:
full KDE Plasma 6 setup # non-essential for sure
full KDE Plasma 6 setup
which can provide:
SDDM theme # Breeze is enough
kcalc/kalc # calculator(s), what's the difference?

116
memo/kvm-virtio-accel3D.md Normal file
View File

@@ -0,0 +1,116 @@
这是一个关于 **Linux (Arch) 宿主机** + **Linux (Gentoo) 客户机** 在 KVM/QEMU 环境下启用 **Virtio-GPU 3D 加速** 遇到黑屏问题的**非完整**排查与解决记录。
## What
- **环境**Host: Arch Linux (Kernel 6.x) | Guest: Gentoo Linux | QEMU v10.x。
- **配置**:使用 virtio-vga 或 virtio-gpu 显卡,配合 SPICE 协议。
```xml
<video>
<model type="virtio" heads="1" primary="yes">
<acceleration accel3d="yes"/>
</model>
</video>
<graphics type="spice">
<listen type="none"/>
<image compression="off"/>
<gl enable="yes" rendernode="/dev/dri/by-path/pci-0000:00:02.0-render"/>
</graphics>
```
- **现象**
- 虚拟机启动后黑屏,无法进入图形界面。
- SSH 连接正常,系统内核正常运行。
- SPICE 窗口内能看到鼠标光标(表示连接建立),但无画面。
- 关键日志 (`/var/log/libvirt/qemu/xxx.log`)
```log
warning: console: no gl-unblock within one second
warning: spice: no gl-draw-done within one second
```
## Why
初步分析为 OpenGL 渲染死锁,可能由以下因素叠加导致:
1. 权限不足QEMU 进程无法访问宿主机的 **/dev/dri/\*** 设备。
2. XML 配置缺失:未显式开启 virtio 设备的 3D 加速位。
3. 渲染管线冲突SPICE 直接处理 GL 上下文容易发生阻塞,尤其是当 Guest 从 VGA 模式切换到 GL 模式时。
## How
### 1. 确认权限
QEMU 运行用户(通常是 `libvirt-qemu`)必须有权访问 GPU 渲染节点。
```bash
# 检查组
groups libvirt-qemu
# 如果没有 'render' 组,将加之
sudo gpasswd -a libvirt-qemu render
# 重启
sudo systemctl restart libvirtd
```
### 2. EGL-Headless 分离渲染
这是解决黑屏死锁最稳妥的方案。通过引入 `egl-headless`,将 OpenGL 渲染(在后台 GPU 完成)与 SPICE 显示传输(在前台完成)解耦。
1. **显卡部分** (`<video>`):必须显式开启 `accel3d`。
```xml
<video>
<model type="virtio" heads="1" primary="yes">
<acceleration accel3d="yes"/>
</model>
...
</video>
```
2. **图形部分** (`<graphics>`):移除 `<gl>` 节点,改为使用 `egl-headless`。
```xml
<graphics type="egl-headless">
<gl rendernode="/dev/dri/by-path/pci-0000:00:02.0-render"/>
</graphics>
<graphics type="spice">
<listen type="none"/>
<image compression="off"/>
</graphics>
```
### 3. 客户机配置 Gentoo
在 Gentoo Guest 内部,确保驱动栈完整。
1. **Portage 配置** ( 或其他位置)
- `/etc/portage/make.conf`
```conf
VIDEO_CARDS="virtgl"
```
- 或 `/etc/portage/package.use/00video_cards`
```conf
*/* VIDEO_CARDS: virtgl
```
2. **安装必要软件包**
```sh
emerge --ask --changed-use media-libs/mesa
```
3. **内核配置** 需开启 DRM 和 Virtio GPU 支持。
### 4. 重启

View File

@@ -1,6 +1,7 @@
things I have installed:
everything in `./hyprland-ricing.txt`, in addition to:
in addition to everything in `./hyprland-ricing.txt`:
xwayland-satellite
wlsunset
grim # see /config/scripts/.local/scripts/screenshot-script