Compare commits
37 Commits
a75c34e1dc
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
3cf506fedf
|
|||
|
4a9d8189b1
|
|||
|
7483a7acb4
|
|||
|
673a30e1df
|
|||
|
809de7f5a3
|
|||
|
8fc4fd63ff
|
|||
|
d5ed8744f6
|
|||
| 915dc9faf0 | |||
|
a1696ee49b
|
|||
|
f23fe5506d
|
|||
|
ed5688306b
|
|||
|
b4e8dcd75a
|
|||
|
3e56a80ee2
|
|||
|
916bd6b61e
|
|||
|
4a525e2822
|
|||
|
8063f91d8a
|
|||
|
bfda6f4ac2
|
|||
|
8f9df4c730
|
|||
|
34a2f71c6d
|
|||
|
9f469589b4
|
|||
|
a6608b1d81
|
|||
|
3d0c6f8de0
|
|||
|
915f19f142
|
|||
|
94bb499764
|
|||
|
1bf6921992
|
|||
|
4c69672211
|
|||
|
fd35954c9d
|
|||
|
920b4451d9
|
|||
|
1c39877f14
|
|||
|
0c11bfbf80
|
|||
|
d4b4904b0e
|
|||
|
7b6b31204d
|
|||
|
02cace931d
|
|||
|
a78a899f02
|
|||
|
7ce1babeed
|
|||
|
f5a9a20a1f
|
|||
|
3175db4900
|
19
README.md
19
README.md
@@ -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).
|
||||
|
||||
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ cpu_load_color=39F900,FDFD09,FF0000
|
||||
|
||||
### Display per process memory usage
|
||||
## Show resident memory and other types, if enabled
|
||||
procmem
|
||||
# procmem
|
||||
# procmem_shared
|
||||
# procmem_virt
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
164
config/niri/.config/niri/config/binds.kdl
Normal file
164
config/niri/.config/niri/config/binds.kdl
Normal 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)"; }
|
||||
}
|
||||
22
config/niri/.config/niri/config/envs.kdl
Normal file
22
config/niri/.config/niri/config/envs.kdl
Normal 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"
|
||||
}
|
||||
39
config/niri/.config/niri/config/execs.kdl
Normal file
39
config/niri/.config/niri/config/execs.kdl
Normal 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"
|
||||
29
config/niri/.config/niri/config/input.kdl
Normal file
29
config/niri/.config/niri/config/input.kdl
Normal 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%"
|
||||
}
|
||||
11
config/niri/.config/niri/config/misc.kdl
Normal file
11
config/niri/.config/niri/config/misc.kdl
Normal 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
|
||||
// }
|
||||
// }
|
||||
22
config/niri/.config/niri/config/monitors.kdl
Normal file
22
config/niri/.config/niri/config/monitors.kdl
Normal file
@@ -0,0 +1,22 @@
|
||||
output "eDP-1" {
|
||||
// off
|
||||
mode "2560x1600@60.002"
|
||||
scale 1.25
|
||||
background-color "#1e1e2e"
|
||||
backdrop-color "#1e1e2e"
|
||||
}
|
||||
|
||||
output "eDP-2" {
|
||||
mode "2560x1600@60.002"
|
||||
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"
|
||||
}
|
||||
89
config/niri/.config/niri/config/rules.kdl
Normal file
89
config/niri/.config/niri/config/rules.kdl
Normal 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"
|
||||
}
|
||||
86
config/niri/.config/niri/config/styles.kdl
Normal file
86
config/niri/.config/niri/config/styles.kdl
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -131,7 +131,6 @@ Variants {
|
||||
}
|
||||
|
||||
CavaBar {
|
||||
count: 6
|
||||
}
|
||||
|
||||
Item {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -16,7 +16,7 @@ MonitorItem {
|
||||
action.signal(15);
|
||||
return ;
|
||||
}
|
||||
action.exec(["ghostty", "-e", "btop"]);
|
||||
action.exec(["kitty", "-e", "btop"]);
|
||||
}
|
||||
|
||||
Process {
|
||||
|
||||
@@ -16,7 +16,7 @@ MonitorItem {
|
||||
action.signal(15);
|
||||
return ;
|
||||
}
|
||||
action.exec(["ghostty", "-e", "btop"]);
|
||||
action.exec(["kitty", "-e", "btop"]);
|
||||
}
|
||||
|
||||
Process {
|
||||
|
||||
@@ -19,7 +19,7 @@ MonitorItem {
|
||||
action.signal(15);
|
||||
return ;
|
||||
}
|
||||
action.exec(["ghostty", "-e", "btop"]);
|
||||
action.exec(["kitty", "-e", "btop"]);
|
||||
}
|
||||
onRightClicked: {
|
||||
_showPercent = !_showPercent;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
17
config/quickshell/.config/quickshell/Services/Screenshot.qml
Normal file
17
config/quickshell/.config/quickshell/Services/Screenshot.qml
Normal 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]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ ShellRoot {
|
||||
sourceComponent: Item {
|
||||
Component.onCompleted: {
|
||||
SunsetService;
|
||||
Niri.onScreenshotCaptured = Screenshot.onScreenshotCaptured;
|
||||
}
|
||||
|
||||
Notification {
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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
|
||||
|
||||
54
config/scripts/.local/scripts/ghostty-capture
Executable file
54
config/scripts/.local/scripts/ghostty-capture
Executable 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
|
||||
56
config/scripts/.local/scripts/playlive
Executable file
56
config/scripts/.local/scripts/playlive
Executable 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"
|
||||
@@ -7,5 +7,7 @@ for child in $(pgrep -P "$pid" 2>/dev/null); do
|
||||
kill "$child"
|
||||
done
|
||||
|
||||
sleep 0.3
|
||||
|
||||
kill "$pid"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
@@ -4,12 +4,16 @@
|
||||
[ -f "$HOME/.cargo/env" ] && source "$HOME/.cargo/env"
|
||||
[ -d "$HOME/go/bin" ] && export PATH="$HOME/go/bin:$PATH"
|
||||
|
||||
[ -x "$HOME/.local/scripts/ssh-init" ] && eval "$(ssh-init)"
|
||||
[ -x "$HOME/.local/scripts/ssh-init" ] && eval "$(ssh-init)" >/dev/null 2>&1
|
||||
|
||||
command -v nvim >/dev/null 2>&1 && {
|
||||
export EDITOR=nvim
|
||||
export VISUAL=nvim
|
||||
}
|
||||
|
||||
command -v f >/dev/null 2>&1 || {
|
||||
alias f="exec fish"
|
||||
}
|
||||
|
||||
[ -f "$HOME/.profile" ] && . "$HOME/.profile"
|
||||
[ -f "$HOME/.bashrc" ] && . "$HOME/.bashrc"
|
||||
|
||||
4
config/shell/.config/fish/post.d/.gitignore
vendored
4
config/shell/.config/fish/post.d/.gitignore
vendored
@@ -1,6 +1,6 @@
|
||||
*
|
||||
!.gitignore
|
||||
!fetch.fish
|
||||
!fetch.fish.template
|
||||
!sshs.fish
|
||||
!alias.fish
|
||||
!alias.fish
|
||||
!ghostty.fish
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
1
config/shell/.config/fish/prev.d/.gitignore
vendored
1
config/shell/.config/fish/prev.d/.gitignore
vendored
@@ -3,3 +3,4 @@
|
||||
!prompt.fish
|
||||
!theme.fish
|
||||
!env.fish
|
||||
!niri-env.fish
|
||||
22
config/shell/.config/fish/prev.d/niri-env.fish
Normal file
22
config/shell/.config/fish/prev.d/niri-env.fish
Normal 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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Submodule config/wallpaper/Pictures/backgrounds updated: cbe612b64b...719962a4ae
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
--password-store=gnome-libsecret
|
||||
--enable-features=UseOzonePlatform
|
||||
--ozone-platform=wayland
|
||||
--use-gl=desktop
|
||||
--enable-wayland-ime
|
||||
@@ -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",
|
||||
-- }
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
]
|
||||
|
||||
# : }}}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1010 KiB |
@@ -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>
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -2,37 +2,59 @@
|
||||
|
||||
Simple lualine-like status line for yazi.
|
||||
|
||||
Read more about features and configuration [here](#features).
|
||||

|
||||

|
||||
|
||||

|
||||
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`
|
||||

|
||||

|
||||
- `curvy`
|
||||

|
||||

|
||||
- `liney`
|
||||

|
||||

|
||||
- `empty`
|
||||

|
||||
|
||||
### 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
|
||||
})
|
||||
```
|
||||
|
||||

|
||||
|
||||
_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
|
||||
})
|
||||
```
|
||||
|
||||

|
||||
|
||||
_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:
|
||||
|
||||

|
||||

|
||||
|
||||
### 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 = "..."
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
--ozone-platform-hint=auto
|
||||
--enable-wayland-ime
|
||||
@@ -1,2 +0,0 @@
|
||||
--ozone-platform-hint=auto
|
||||
--enable-wayland-ime
|
||||
@@ -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
108
memo/git-remote.md
Normal 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.
|
||||
@@ -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?
|
||||
|
||||
133
memo/kmscon.md
Normal file
133
memo/kmscon.md
Normal file
@@ -0,0 +1,133 @@
|
||||
> 鬼知道这有什么意义,但是在 tty 里用 monospace 和 cjk 字体真的很酷
|
||||
|
||||
## 安装
|
||||
|
||||
AUR - `kmscon`
|
||||
|
||||
## 启用
|
||||
|
||||
```sh
|
||||
sudo ln -s '/usr/lib/systemd/system/kmsconvt@.service' '/etc/systemd/system/autovt@.service'
|
||||
```
|
||||
|
||||
以及保留 tty1 为 getty:
|
||||
|
||||
```sh
|
||||
sudo systemctl enable getty@tty1.service
|
||||
```
|
||||
|
||||
理由:
|
||||
|
||||
- kmscon 中通过命令行启动图形 session 会遇到问题。
|
||||
- 通常 tty1 用于运行图形界面,不需要 kmscon 提供的各种特性。
|
||||
|
||||
## 修改默认配置
|
||||
|
||||
```sh
|
||||
sudo systemctl edit autovt@.service
|
||||
```
|
||||
|
||||
然后添加以下内容:
|
||||
|
||||
```ini
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/bin/kmscon --vt=%I --seats=seat0 --no-switchvt
|
||||
```
|
||||
|
||||
解释:
|
||||
|
||||
- 默认的 `ExecStart` 携带的参数会覆盖所有配置文件中的对应项,需要先清空。
|
||||
- 保留默认参数 `--login` 前的部分,因为我看不出不这么做的理由。
|
||||
|
||||
然后就可以在 `/etc/kmscon/kmscon.conf` 添加自定义的配置了。
|
||||
|
||||
例如:
|
||||
|
||||
```conf
|
||||
login=/bin/login -p -f kolkas
|
||||
font-name=MesloLGM Nerd Font Mono, Maple Mono NF CN
|
||||
font-size=14
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `login=/bin/login -p -f kolkas` 指定自动登录用户。
|
||||
- `font-name` 和 `font-size` 用于设置字体和大小。
|
||||
|
||||
### 关于自动登录的补充说明
|
||||
|
||||
如果想让 kmscon 在自动登录的同时启动非登陆 shell (例如 fish),可以将配置改为:
|
||||
|
||||
```conf
|
||||
login=/usr/bin/su - kolkas -s /usr/local/bin/fish-login-wrapper
|
||||
```
|
||||
|
||||
其中 `fish-login-wrapper` 内容为:
|
||||
|
||||
```sh
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f /etc/profile ]; then
|
||||
source /etc/profile
|
||||
fi
|
||||
|
||||
if [ -f ~/.bash_profile ]; then
|
||||
source ~/.bash_profile
|
||||
fi
|
||||
|
||||
# 设置环境变量以供后续脚本检测
|
||||
export IS_KMSCON=1
|
||||
|
||||
# 设定 fastfetch 的 logo 类型为 logo (config/shell/.config/fish/post.d/fetch.fish 会读取该变量)
|
||||
export fetch_logo_type=logo
|
||||
|
||||
exec /usr/bin/fish
|
||||
```
|
||||
|
||||
如果不需要读取 profile 文件而直接将 fish 作为登陆 shell,可以将配置改为:
|
||||
|
||||
```conf
|
||||
login=/usr/bin/su - kolkas -s /usr/bin/fish --login
|
||||
```
|
||||
|
||||
### 关于字体的补充说明
|
||||
|
||||
- kmscon 上 2 字符宽的 nerd font 图标会被裁剪至 1 字符宽。可以使用带有 Mono 后缀的 Meslo 系字体作为首选字体,它的图标字符只有 1 字符宽。
|
||||
|
||||
- [Archwiki - KMSCON](https://wiki.archlinux.org/title/KMSCON) 上提供了另一种更改字体的方法,即修改 fontconfig 配置,具体做法为在 monospace 字体族中前置添加字体。但这会影响所有使用 fontconfig 和 monospace 字体族的程序,个人认为并非首选。
|
||||
|
||||
## 检测当前终端是否为 kmscon
|
||||
|
||||
> 以下代码片段适用于 fish shell
|
||||
|
||||
检测所有祖先进程中是否有 kmscon:
|
||||
|
||||
```sh
|
||||
function is_kmscon
|
||||
set -l pid %self
|
||||
while test $pid -gt 1
|
||||
set -l info (ps -o ppid=,comm= -p $pid | string trim)
|
||||
set -l ppid (echo $info | awk '{print $1}')
|
||||
set -l name (echo $info | awk '{print $2}')
|
||||
|
||||
if string match -q "kmscon" $name
|
||||
return 0
|
||||
end
|
||||
set pid $ppid
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
if is_kmscon
|
||||
# 在 kmscon 中
|
||||
end
|
||||
```
|
||||
|
||||
或如果配置了 [环境变量](#关于自动登录的补充说明):
|
||||
|
||||
```sh
|
||||
if test -n "$IS_KMSCON"
|
||||
# 在 kmscon 中
|
||||
end
|
||||
```
|
||||
116
memo/kvm-virtio-accel3D.md
Normal file
116
memo/kvm-virtio-accel3D.md
Normal 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. 重启
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user