Compare commits

..

40 Commits

Author SHA1 Message Date
800074e1d2 chbg:现在用于systemd服务也能正常清理下载下来的临时文件叻 2026-04-04 23:18:51 +08:00
2dbf833d80 awww -> awww + swaybg
顺便修修readme:减少md代码块以应对ly gitea黑色主题的蜜汁渲染
2026-04-02 02:55:48 +08:00
bd658f9101 awww -> swww-git 2026-04-01 22:37:33 +08:00
d4d77b1cdc Revert "swww -> awww"
This reverts commit e8e33898be.
2026-04-01 22:21:52 +08:00
ae49f96777 startb:journal自带时间,不需要在标签名里重申 2026-04-01 12:07:10 +08:00
10d03bc051 又用 vscode copilot 重写 startb
至少这次可读性还不错。
2026-03-28 04:23:22 +08:00
e8e33898be swww -> awww 2026-03-28 03:03:16 +08:00
31c2797f44 修修startb
- 现在会自动清理临时服务了
- 能正常展开别名了(如~)
2026-03-24 19:08:46 +08:00
a1a0c2bdb6 重写 startb;补点中文注释;删减多余的定时模板
为啥又中文了?英文太长,中英夹杂键起来别扭。

不会真有人用我原始人般的配置吧?
2026-03-24 03:08:10 +08:00
8b9589c8c9 bugfix after several pkg updates. new alias introduced. 2026-03-23 00:19:48 +08:00
f789134483 solve foot deprecates 2026-03-04 22:19:12 +08:00
5fad8f6212 Partial Revert "update fuzzel-*"
This reverts commit 82b27d7313.
2026-02-15 00:22:22 +08:00
82b27d7313 update fuzzel-* 2026-02-05 23:49:05 +08:00
b482dd4cdc chbg: full argparse support
merged from `chbg-slide`.
2026-02-05 06:05:02 +08:00
f5948bf3c4 fuzzel-vsc: avoid app shortcuts (mainly wine and chrome) 2026-01-12 03:47:11 +08:00
816016c6f0 fuzzel-vsc: make prompt more clear 2026-01-08 00:53:30 +08:00
ca2f125654 chbg: avoid the TOCTOU race 2026-01-06 19:31:05 +08:00
0ef0ef2562 yacd -> metacubexd (or I shall say, mihomo-web) 2026-01-05 21:11:14 +08:00
29d1278104 niri: bililive status layout 2026-01-03 14:41:11 +08:00
e610a3d878 update chbg
- better tmp file generation and autoclean.
- further param validation before real work.
2026-01-02 03:41:02 +08:00
f1c943a99b shorten timer trigger delay 2026-01-01 20:35:12 +08:00
46ac48e963 replace MonacoNFMono with MesloLGS_NF due to foot terminal terrible rendering. 2026-01-01 20:12:45 +08:00
630f196a5e battery-warn: no need to check whole battery info. 2026-01-01 01:19:20 +08:00
f27a5ff07b chbg: make sure resizing output is reachable. 2025-12-31 21:03:40 +08:00
6e2299e23f update shell history path
py is less convenient accessing inodes than pwsh, no need to redirect.
2025-12-31 20:51:18 +08:00
4cb055fac4 chbg: utilize XDG directories 2025-12-31 20:06:56 +08:00
8683a7db3b battery notify script
the script, originally written in bash, is rewrited into python,
whose UPower implements (from `wogscpar/upower-python`) make me lazy to check out again.
2025-12-29 02:40:36 +08:00
78ca119a76 rename script to better avoid unwanted auto-suggestions. 2025-12-22 00:36:27 +08:00
1a286af126 update readme 2025-12-20 21:18:15 +08:00
0ef960ef7b niri: appending powerkey customization hinting 2025-12-16 03:12:19 +08:00
271487380c runbg regenerate.
- make use of journald
- avoid command once invoke being triggered twice (or even more).
2025-12-16 02:58:04 +08:00
3446d3d97e replace ykrun with runbg for background command execution
... to solve the nvm-sh environment issue.(mostly vscode)

NOTE: may also import other envvars, tbc.
2025-12-16 02:21:20 +08:00
b4932ef737 misc update
- known dependencies
- lessen symlink checking (might not easily break)
- move shell history into `/tmp`.
(py didn't often operate filesystem which doesn't matter)
2025-12-15 11:32:59 +08:00
3a1b467e5c niri: split configs often being edited.
- unable to split them completely into different parts: some visual effect may not functional.
2025-12-13 23:32:04 +08:00
fe4232ec8e switch to chrome due to using habit differences. 2025-12-13 00:40:49 +08:00
3fa8251a7f from gwenview to krita.
it's not really convenient to do basic edits in wayland in gwenview.
2025-12-05 05:33:25 +08:00
ee77c305e4 window-rules update for some apps. 2025-12-04 16:02:50 +08:00
182cdb5e09 replace yazi default player with my installation. 2025-12-04 15:52:17 +08:00
f70b115dd1 misc niri config update. 2025-12-04 02:55:04 +08:00
4e61fd5b6b cron (x) systemd timer (o) user generic template.
basically hrs, weeks, months.
2025-12-04 02:54:24 +08:00
27 changed files with 896 additions and 405 deletions

View File

@@ -1,6 +1,5 @@
# Proxy, set in environment.d?
http_proxy="http://127.0.0.1:7890/"
https_proxy="http://127.0.0.1:7890/"
# also needs manual configuration in firefox.
# firefox 记得单独配一次.
no_proxy="127.*,192.168.*,10.147.17.*"

View File

@@ -43,7 +43,7 @@
<string>LXGW WenKai Mono</string>
<string>Noto Sans Mono CJK SC</string>
<string>Noto Color Emoji</string>
<string>Monaco Nerd Font Mono</string>
<string>MesloLGS NF</string>
<string>Comic Mono</string>
</edit>
</match>
@@ -75,7 +75,7 @@
</test>
<edit binding="strong" mode="prepend" name="family">
<string>Comic Mono</string>
<string>Monaco Nerd Font Mono</string>
<string>MesloLGS NF</string>
<string>Noto Sans Mono CJK SC</string>
<string>Noto Color Emoji</string>
</edit>

View File

@@ -9,7 +9,7 @@
# title=foot
# locked-title=no
font=Monaco Nerd Font Mono:size=11
font=MesloLGS NF:size=11
# font-bold=<bold variant of regular font>
# font-italic=<italic variant of regular font>
# font-bold-italic=<bold+italic variant of regular font>
@@ -64,7 +64,7 @@ osc8-underline=always
# hide-when-typing=no
# alternate-scroll-mode=yes
[colors]
[colors-dark]
alpha=0.9
# foreground=dcdccc
# background=000000

View File

@@ -1,6 +1,6 @@
[main]
anchor = top
font = MonacoNerdFontMono:size=13
font = MesloLGSNF:size=13
#dpi-aware = no
terminal = foot
y-margin = 16

View File

@@ -2,12 +2,12 @@
transition: 1000ms ease ;
}
window {
background-image: url("/home/agxcoy/.local/share/.wallpaper_blur");
background-image: url("/home/agxcoy/.cache/wallpaper_blur");
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-color: rgba(0,0,0,0.3);
font-family: 'Monaco Nerd Font Mono';
font-family: 'monospace';
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.7)
}
#input-field {

View File

@@ -11,7 +11,7 @@ icons=1
max-icon-size=32
default-timeout=5000
ignore-timeout=1
font=MonacoNerdFontMono 11
font=MesloLGSNF 11
padding=8
[urgency=low]

View File

@@ -0,0 +1,224 @@
// 芝士快捷键表。我自己也很难说能熟练用,所以从`config.kdl`里拆出来了。所幸它支持 include。
binds {
Mod+F1 hotkey-overlay-title="显示帮助喵" { show-hotkey-overlay; }
Mod+Return hotkey-overlay-title="终端 (foot)" { spawn "foot"; }
Mod+F9 hotkey-overlay-title="浏览器 (chrome)" { spawn "google-chrome-stable"; }
Mod+E hotkey-overlay-title="文件管理器 (yazi)" { spawn "foot" "yazi"; }
//Shift+Mod+Return hotkey-overlay-title="Steam" { spawn "steam"; }
Mod+F12 hotkey-overlay-title="打开桌面项目 ... (VSCode)" { spawn ".fuzzel-vscode"; }
Mod+Space hotkey-overlay-title="运行命令 ... (fuzzel)" { spawn ".fuzzel-startb"; }
// Applications such as remote-desktop clients and software KVM switches may
// request that niri stops processing the keyboard shortcuts defined here
// so they may, for example, forward the key presses as-is to a remote machine.
// It's a good idea to bind an escape hatch to toggle the inhibitor,
// so a buggy application can't hold your session hostage.
//
// The allow-inhibiting=false property can be applied to other binds as well,
// which ensures niri always processes them, even when an inhibitor is active.
Mod+Alt+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
XF86PowerOff hotkey-overlay-title="大退(注销)" { quit; }
Super+Escape hotkey-overlay-title="锁屏 (gtklock)" { spawn "gtklock" "-d"; }
Mod+Delete hotkey-overlay-title="锁屏并休眠 ..." allow-when-locked=true {
spawn-sh "(pgrep gtklock || gtklock -d) && sleep 1.5 && systemctl hibernate";
}
XF86AudioRaiseVolume hotkey-overlay-title=null allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05+"; }
XF86AudioLowerVolume hotkey-overlay-title=null allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05-"; }
XF86AudioMute hotkey-overlay-title=null allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
XF86AudioMicMute hotkey-overlay-title=null allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; }
//Mod+Shift+M allow-when-locked=true { spawn "swayosd-client" "--input-volume" "mute-toggle"; }
XF86AudioPlay allow-when-locked=true { spawn "playerctl" "play-pause"; }
XF86AudioPause allow-when-locked=true { spawn "playerctl" "play-pause"; }
XF86AudioNext allow-when-locked=true { spawn "playerctl" "next"; }
XF86AudioPrev allow-when-locked=true { spawn "playerctl" "previous"; }
XF86AudioStop allow-when-locked=true { spawn "playerctl" "stop"; }
XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "+5%"; }
XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "5%-"; }
// 有些键位懒得补汉化说明,自己摁两下就知道了。
Alt+Tab hotkey-overlay-title=null repeat=false { toggle-overview; }
Alt+F4 { close-window; }
Mod+Left { focus-column-left; }
Mod+Down { focus-window-down; }
Mod+Up { focus-window-up; }
Mod+Right { focus-column-right; }
//Mod+H { focus-column-left; }
//Mod+J { focus-window-down; }
//Mod+K { focus-window-up; }
//Mod+L { focus-column-right; }
Mod+Shift+Left { move-column-left; }
Mod+Shift+Down { move-window-down; }
Mod+Shift+Up { move-window-up; }
Mod+Shift+Right { move-column-right; }
//Mod+Shift+H { move-column-left; }
//Mod+Shift+J { move-window-down; }
//Mod+Shift+K { move-window-up; }
//Mod+Shift+L { move-column-right; }
// Alternative commands that move across workspaces when reaching
// the first or last window in a column.
// Mod+J { focus-window-or-workspace-down; }
// Mod+K { focus-window-or-workspace-up; }
// Mod+Ctrl+J { move-window-down-or-to-workspace-down; }
// Mod+Ctrl+K { move-window-up-or-to-workspace-up; }
Mod+Home { focus-column-first; }
Mod+End { focus-column-last; }
Mod+Ctrl+Home { move-column-to-first; }
Mod+Ctrl+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+Ctrl+H { focus-monitor-left; }
//Mod+Ctrl+J { focus-monitor-down; }
//Mod+Ctrl+K { focus-monitor-up; }
//Mod+Ctrl+L { focus-monitor-right; }
Mod+Shift+Ctrl+Left { move-column-to-monitor-left; }
Mod+Shift+Ctrl+Down { move-column-to-monitor-down; }
Mod+Shift+Ctrl+Up { move-column-to-monitor-up; }
Mod+Shift+Ctrl+Right { move-column-to-monitor-right; }
//Mod+Shift+Ctrl+H { move-column-to-monitor-left; }
//Mod+Shift+Ctrl+J { move-column-to-monitor-down; }
//Mod+Shift+Ctrl+K { move-column-to-monitor-up; }
//Mod+Shift+Ctrl+L { move-column-to-monitor-right; }
// Alternatively, there are commands to move just a single window:
// Mod+Shift+Ctrl+Left { move-window-to-monitor-left; }
// ...
// And you can also move a whole workspace to another monitor:
// Mod+Shift+Ctrl+Left { move-workspace-to-monitor-left; }
// ...
Mod+Page_Down { focus-workspace-down; }
Mod+Page_Up { focus-workspace-up; }
//Mod+U { focus-workspace-down; }
//Mod+I { focus-workspace-up; }
Mod+Shift+Page_Down { move-column-to-workspace-down; }
Mod+Shift+Page_Up { move-column-to-workspace-up; }
//Mod+Shift+U { move-column-to-workspace-down; }
//Mod+Shift+I { move-column-to-workspace-up; }
// Alternatively, there are commands to move just a single window:
// Mod+Ctrl+Page_Down { move-window-to-workspace-down; }
// ...
Mod+Ctrl+Page_Down { move-workspace-down; }
Mod+Ctrl+Page_Up { move-workspace-up; }
//Mod+Ctrl+U { move-workspace-down; }
//Mod+Ctrl+I { 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 { focus-column-right; }
Mod+WheelScrollLeft { focus-column-left; }
Mod+Shift+WheelScrollRight { move-column-right; }
Mod+Shift+WheelScrollLeft { move-column-left; }
// Usually scrolling up and down with Shift in applications results in
// horizontal scrolling; these binds replicate that.
// Mod+Shift+WheelScrollDown { focus-column-right; }
// Mod+Shift+WheelScrollUp { focus-column-left; }
// Mod+Ctrl+Shift+WheelScrollDown { move-column-right; }
// Mod+Ctrl+Shift+WheelScrollUp { move-column-left; }
// Similarly, you can bind touchpad scroll "ticks".
// Touchpad scrolling is continuous, so for these binds it is split into
// discrete intervals.
// These binds are also affected by touchpad's natural-scroll, so these
// example binds are "inverted", since we have natural-scroll enabled for
// touchpads by default.
// Mod+TouchpadScrollDown { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02+"; }
// Mod+TouchpadScrollUp { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02-"; }
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+Shift+1 { move-column-to-workspace focus=false 1 ; }
Mod+Shift+2 { move-column-to-workspace focus=false 2 ; }
Mod+Shift+3 { move-column-to-workspace focus=false 3 ; }
Mod+Shift+4 { move-column-to-workspace focus=false 4 ; }
Mod+Shift+5 { move-column-to-workspace focus=false 5 ; }
Mod+Shift+6 { move-column-to-workspace focus=false 6 ; }
Mod+Shift+7 { move-column-to-workspace focus=false 7 ; }
Mod+Shift+8 { move-column-to-workspace focus=false 8 ; }
Mod+Shift+9 { move-column-to-workspace focus=false 9 ; }
// Alternatively, there are commands to move just a single window:
// Mod+Ctrl+1 { move-window-to-workspace 1; }
// Switches focus between the current and the previous workspace.
// Mod+Tab { focus-workspace-previous; }
Mod+Comma { consume-window-into-column; }
Mod+Period { expel-window-from-column; }
Mod+Shift+V { toggle-window-floating; }
Mod+V { switch-focus-between-floating-and-tiling; }
Mod+BracketLeft { consume-or-expel-window-left; }
Mod+BracketRight { consume-or-expel-window-right; }
Mod+R { switch-preset-column-width; }
Mod+Shift+R { switch-preset-window-height; }
Mod+Ctrl+R { reset-window-height; }
Mod+F11 { maximize-column; }
Mod+Shift+F11 { toggle-windowed-fullscreen; }
Mod+Ctrl+F11 { fullscreen-window; }
Mod+T hotkey-overlay-title="Expand column to available width" { expand-column-to-available-width; }
Mod+W hotkey-overlay-title="Tabbed Column Mode" { toggle-column-tabbed-display; }
Mod+C { center-column; }
Mod+Ctrl+C { center-visible-columns; }
// Finer width adjustments.
// This command can also:
// * set width in pixels: "1000"
// * adjust width in pixels: "-5" or "+5"
// * set width as a percentage of screen width: "25%"
// * adjust width as a percentage of screen width: "-10%" or "+10%"
// Pixel sizes use logical, or scaled, pixels. I.e. on an output with scale 2.0,
// set-column-width "100" will make the column occupy 200 physical screen pixels.
Mod+Minus { set-column-width "-5%"; }
Mod+Equal { set-column-width "+5%"; }
// Finer height adjustments when in column with other windows.
Mod+Shift+Minus { set-window-height "-5%"; }
Mod+Shift+Equal { set-window-height "+5%"; }
// Actions to switch layouts.
// Note: if you uncomment these, make sure you do NOT have
// a matching layout switch hotkey configured in xkb options above.
// Having both at once on the same hotkey will break the switching,
// since it will switch twice upon pressing the hotkey (once by xkb, once by niri).
// Mod+Space { switch-layout "next"; }
// Mod+Shift+Space { switch-layout "prev"; }
Print hotkey-overlay-title=null { screenshot-screen; }
Mod+Shift+S hotkey-overlay-title="截取矩形画面(和 Win11 类似)" { screenshot; }
Alt+Print { screenshot-window; }
// Powers off the monitors. To turn them back on, do any input like
// moving the mouse or pressing any other key.
//Mod+Shift+P { power-off-monitors; }
}

View File

@@ -0,0 +1,71 @@
// 这是窗口布局表。由于经常改,也拆出来了。
window-rule {
geometry-corner-radius 6
clip-to-geometry true
}
window-rule {
match app-id="QQ"
match app-id="Electron" title="MetaCubeXD$"
match app-id="wechat"
match app-id="org.telegram.desktop"
match app-id="nwjs" // mainly for rpgmaker games.
// open-on-output "eDP-1"
block-out-from "screencast"
}
window-rule {
match app-id="com.obsproject.Studio"
// default-column-width { proportion 0.6; }
open-on-output "eDP-1"
}
window-rule {
match app-id="Electron" title="Test" // frg2089.BiliLive.Observer
match app-id="Electron" title="Blivechat-Openlive" // blivechat
default-column-width { proportion 0.42; }
// default-column-display "tabbed"
open-on-output "eDP-1"
}
window-rule {
match title="^(图片查看器|.*记录)$" app-id="QQ"
match app-id="pavucontrol-qt"
default-column-width { proportion 0.6; }
default-window-height { proportion 0.85; }
open-floating true
}
window-rule {
match app-id="nwjs"
default-column-width { proportion 0.85; }
default-window-height { proportion 0.9; }
open-floating true
}
window-rule {
match app-id=r#"^org\.keepassxc\.KeePassXC$"#
match app-id=r#"^org\.gnome\.World\.Secrets$"#
match app-id=r#"^org\.kde\.ksecretd$"#
open-floating true
}
window-rule {
// match title=r#"Code - OSS$"#
match title=r#"Visual Studio Code$"# app-id=r#"^code"#
// match app-id="krita"
open-maximized true
}
window-rule {
// match app-id=r#"firefox$"# title="^Picture-in-Picture$"
match app-id="steam" title="Friends List"
open-floating true
default-floating-position x=8 y=8 relative-to="bottom-right"
open-focused false
open-on-output "eDP-1"
}

View File

@@ -41,7 +41,8 @@ input {
}
warp-mouse-to-focus
// 还需要在`/etc/systemd/logind.conf`里屏蔽关机键行为.
disable-power-key-handling
focus-follows-mouse max-scroll-amount="0%"
}
@@ -119,35 +120,16 @@ layout {
}
}
hotkey-overlay {
skip-at-startup
}
layer-rule {
match namespace="^swww-daemonblur$"
place-within-backdrop true
}
overview {
backdrop-color "#26233a"
zoom 0.5
}
switch-events {
lid-close { spawn "bash" "-c" "swaylock" "&&" "systemctl" "suspend"; }
layer-rule {
match namespace="^wallpaper$"
place-within-backdrop true
}
gestures {
hot-corners {
off
}
}
prefer-no-csd
screenshot-path "~/.tmp/Screenshots/%Y-%m-%d %H-%M-%S.png"
// screenshot-path null
animations {
// off
@@ -192,73 +174,15 @@ animations {
}
}
window-rule {
geometry-corner-radius 6
clip-to-geometry true
}
// 我看雪叶是把能拆的大板块都拆得一干二净,但就我这份配置而言,拆两份常用的出来已经足矣了。
// 部分视觉效果拆出去可能还会失效(比如窗口边框)。
include "config-window.kdl"
include "config-keyboard.kdl"
window-rule {
match app-id="QQ"
match app-id="Electron" title="yacd" // Yet Another Clash Daemon (webui)
match app-id="wechat"
// open-on-output "eDP-1"
block-out-from "screencast"
}
window-rule {
match app-id="com.obsproject.Studio"
match app-id="Electron" title="Test" // frg2089.BiliLive.Observer
match app-id="Electron" title="Blivechat-Openlive" // blivechat
default-column-display "tabbed"
open-on-output "eDP-1"
}
window-rule {
match title="^(图片查看器|.*记录)$" app-id="QQ"
match app-id="pavucontrol-qt"
default-column-width { proportion 0.6; }
default-window-height { proportion 0.85; }
open-floating true
}
window-rule {
match app-id="nwjs"
default-column-width { proportion 0.85; }
default-window-height { proportion 0.9; }
open-floating true
block-out-from "screencast"
}
window-rule {
match app-id=r#"^org\.keepassxc\.KeePassXC$"#
match app-id=r#"^org\.gnome\.World\.Secrets$"#
match app-id=r#"^org\.kde\.ksecretd$"#
open-floating true
}
window-rule {
match title=r#"Code - OSS$"#
match title=r#"Visual Studio Code$"#
match app-id=r#"^code"#
open-maximized true
}
window-rule {
match app-id=r#"firefox$"# title="^Picture-in-Picture$"
match app-id="steam" title="Friends List"
open-floating true
default-floating-position x=8 y=8 relative-to="bottom-right"
open-focused false
open-on-output "eDP-1"
}
spawn-at-startup "waybar"
spawn-at-startup "swww-daemon"
spawn-at-startup "swww-daemon" "--namespace" "blur"
// spawn-at-startup "waybar" // 建议交给 systemctl --user它自己没法热重载。
// spawn-sh-at-startup "wl-paste --watch cliphist store"
spawn-at-startup "awww-daemon"
// spawn-at-startup "awww-daemon" "--namespace" "blur"
spawn-at-startup "xwayland-satellite"
spawn-at-startup "/usr/lib/pam_kwallet_init"
@@ -267,224 +191,19 @@ environment {
ELECTRON_OZONE_PLATFORM_HINT "auto"
}
binds {
Mod+F1 { show-hotkey-overlay; }
Mod+Return hotkey-overlay-title="Terminal (foot)" { spawn "foot"; }
Mod+F9 hotkey-overlay-title="Browser (firefox)" { spawn "firefox"; }
Mod+E hotkey-overlay-title="Explorer (yazi)" { spawn "foot" "yazi"; }
//Shift+Mod+Return hotkey-overlay-title="Steam" { spawn "steam"; }
Mod+F12 hotkey-overlay-title="Open Projects ... (VSCode)" { spawn "~/.niri-dotfiles/bin/fuzzel-vsc-entries.sh"; }
Mod+Space hotkey-overlay-title="Run a command ... (fuzzel)" { spawn "~/.niri-dotfiles/bin/fuzzel-win+r.sh"; }
// Applications such as remote-desktop clients and software KVM switches may
// request that niri stops processing the keyboard shortcuts defined here
// so they may, for example, forward the key presses as-is to a remote machine.
// It's a good idea to bind an escape hatch to toggle the inhibitor,
// so a buggy application can't hold your session hostage.
//
// The allow-inhibiting=false property can be applied to other binds as well,
// which ensures niri always processes them, even when an inhibitor is active.
Mod+Alt+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
Mod+Shift+Escape hotkey-overlay-title="Exit niri" { quit; }
Super+Escape hotkey-overlay-title="Lock the Screen (gtklock)" { spawn "gtklock" "-d"; }
Mod+Delete hotkey-overlay-title="Lock screen and Hibernate ..." allow-when-locked=true {
spawn-sh "(pgrep gtklock || gtklock -d) && sleep 1 && systemctl hibernate";
}
XF86AudioRaiseVolume hotkey-overlay-title=null allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05+"; }
XF86AudioLowerVolume hotkey-overlay-title=null allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.05-"; }
XF86AudioMute hotkey-overlay-title=null allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
XF86AudioMicMute hotkey-overlay-title=null allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; }
//Mod+Shift+M allow-when-locked=true { spawn "swayosd-client" "--input-volume" "mute-toggle"; }
XF86AudioPlay allow-when-locked=true { spawn "playerctl" "play-pause"; }
XF86AudioPause allow-when-locked=true { spawn "playerctl" "play-pause"; }
XF86AudioNext allow-when-locked=true { spawn "playerctl" "next"; }
XF86AudioPrev allow-when-locked=true { spawn "playerctl" "previous"; }
XF86AudioStop allow-when-locked=true { spawn "playerctl" "stop"; }
XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "+5%"; }
XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "5%-"; }
Alt+Tab hotkey-overlay-title=null repeat=false { toggle-overview; }
Alt+F4 { close-window; }
Mod+Left { focus-column-left; }
Mod+Down { focus-window-down; }
Mod+Up { focus-window-up; }
Mod+Right { focus-column-right; }
//Mod+H { focus-column-left; }
//Mod+J { focus-window-down; }
//Mod+K { focus-window-up; }
//Mod+L { focus-column-right; }
Mod+Shift+Left { move-column-left; }
Mod+Shift+Down { move-window-down; }
Mod+Shift+Up { move-window-up; }
Mod+Shift+Right { move-column-right; }
//Mod+Shift+H { move-column-left; }
//Mod+Shift+J { move-window-down; }
//Mod+Shift+K { move-window-up; }
//Mod+Shift+L { move-column-right; }
// Alternative commands that move across workspaces when reaching
// the first or last window in a column.
// Mod+J { focus-window-or-workspace-down; }
// Mod+K { focus-window-or-workspace-up; }
// Mod+Ctrl+J { move-window-down-or-to-workspace-down; }
// Mod+Ctrl+K { move-window-up-or-to-workspace-up; }
Mod+Home { focus-column-first; }
Mod+End { focus-column-last; }
Mod+Ctrl+Home { move-column-to-first; }
Mod+Ctrl+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+Ctrl+H { focus-monitor-left; }
//Mod+Ctrl+J { focus-monitor-down; }
//Mod+Ctrl+K { focus-monitor-up; }
//Mod+Ctrl+L { focus-monitor-right; }
Mod+Shift+Ctrl+Left { move-column-to-monitor-left; }
Mod+Shift+Ctrl+Down { move-column-to-monitor-down; }
Mod+Shift+Ctrl+Up { move-column-to-monitor-up; }
Mod+Shift+Ctrl+Right { move-column-to-monitor-right; }
//Mod+Shift+Ctrl+H { move-column-to-monitor-left; }
//Mod+Shift+Ctrl+J { move-column-to-monitor-down; }
//Mod+Shift+Ctrl+K { move-column-to-monitor-up; }
//Mod+Shift+Ctrl+L { move-column-to-monitor-right; }
// Alternatively, there are commands to move just a single window:
// Mod+Shift+Ctrl+Left { move-window-to-monitor-left; }
// ...
// And you can also move a whole workspace to another monitor:
// Mod+Shift+Ctrl+Left { move-workspace-to-monitor-left; }
// ...
Mod+Page_Down { focus-workspace-down; }
Mod+Page_Up { focus-workspace-up; }
//Mod+U { focus-workspace-down; }
//Mod+I { focus-workspace-up; }
Mod+Shift+Page_Down { move-column-to-workspace-down; }
Mod+Shift+Page_Up { move-column-to-workspace-up; }
//Mod+Shift+U { move-column-to-workspace-down; }
//Mod+Shift+I { move-column-to-workspace-up; }
// Alternatively, there are commands to move just a single window:
// Mod+Ctrl+Page_Down { move-window-to-workspace-down; }
// ...
Mod+Ctrl+Page_Down { move-workspace-down; }
Mod+Ctrl+Page_Up { move-workspace-up; }
//Mod+Ctrl+U { move-workspace-down; }
//Mod+Ctrl+I { 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 { focus-column-right; }
Mod+WheelScrollLeft { focus-column-left; }
Mod+Shift+WheelScrollRight { move-column-right; }
Mod+Shift+WheelScrollLeft { move-column-left; }
// Usually scrolling up and down with Shift in applications results in
// horizontal scrolling; these binds replicate that.
// Mod+Shift+WheelScrollDown { focus-column-right; }
// Mod+Shift+WheelScrollUp { focus-column-left; }
// Mod+Ctrl+Shift+WheelScrollDown { move-column-right; }
// Mod+Ctrl+Shift+WheelScrollUp { move-column-left; }
// Similarly, you can bind touchpad scroll "ticks".
// Touchpad scrolling is continuous, so for these binds it is split into
// discrete intervals.
// These binds are also affected by touchpad's natural-scroll, so these
// example binds are "inverted", since we have natural-scroll enabled for
// touchpads by default.
// Mod+TouchpadScrollDown { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02+"; }
// Mod+TouchpadScrollUp { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02-"; }
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+Shift+1 { move-column-to-workspace focus=false 1 ; }
Mod+Shift+2 { move-column-to-workspace focus=false 2 ; }
Mod+Shift+3 { move-column-to-workspace focus=false 3 ; }
Mod+Shift+4 { move-column-to-workspace focus=false 4 ; }
Mod+Shift+5 { move-column-to-workspace focus=false 5 ; }
Mod+Shift+6 { move-column-to-workspace focus=false 6 ; }
Mod+Shift+7 { move-column-to-workspace focus=false 7 ; }
Mod+Shift+8 { move-column-to-workspace focus=false 8 ; }
Mod+Shift+9 { move-column-to-workspace focus=false 9 ; }
// Alternatively, there are commands to move just a single window:
// Mod+Ctrl+1 { move-window-to-workspace 1; }
// Switches focus between the current and the previous workspace.
// Mod+Tab { focus-workspace-previous; }
Mod+Comma { consume-window-into-column; }
Mod+Period { expel-window-from-column; }
Mod+Shift+V { toggle-window-floating; }
Mod+V { switch-focus-between-floating-and-tiling; }
Mod+BracketLeft { consume-or-expel-window-left; }
Mod+BracketRight { consume-or-expel-window-right; }
Mod+R { switch-preset-column-width; }
Mod+Shift+R { switch-preset-window-height; }
Mod+Ctrl+R { reset-window-height; }
Mod+F11 { maximize-column; }
Mod+Shift+F11 { toggle-windowed-fullscreen; }
Mod+Ctrl+F11 { fullscreen-window; }
Mod+T hotkey-overlay-title="Expand column to available width" { expand-column-to-available-width; }
Mod+W hotkey-overlay-title="Tabbed Column Mode" { toggle-column-tabbed-display; }
Mod+C { center-column; }
Mod+Ctrl+C { center-visible-columns; }
// Finer width adjustments.
// This command can also:
// * set width in pixels: "1000"
// * adjust width in pixels: "-5" or "+5"
// * set width as a percentage of screen width: "25%"
// * adjust width as a percentage of screen width: "-10%" or "+10%"
// Pixel sizes use logical, or scaled, pixels. I.e. on an output with scale 2.0,
// set-column-width "100" will make the column occupy 200 physical screen pixels.
Mod+Minus { set-column-width "-5%"; }
Mod+Equal { set-column-width "+5%"; }
// Finer height adjustments when in column with other windows.
Mod+Shift+Minus { set-window-height "-5%"; }
Mod+Shift+Equal { set-window-height "+5%"; }
// Actions to switch layouts.
// Note: if you uncomment these, make sure you do NOT have
// a matching layout switch hotkey configured in xkb options above.
// Having both at once on the same hotkey will break the switching,
// since it will switch twice upon pressing the hotkey (once by xkb, once by niri).
// Mod+Space { switch-layout "next"; }
// Mod+Shift+Space { switch-layout "prev"; }
Print hotkey-overlay-title=null { screenshot-screen; }
Mod+Shift+S { screenshot; }
Alt+Print { screenshot-window; }
// Powers off the monitors. To turn them back on, do any input like
// moving the mouse or pressing any other key.
//Mod+Shift+P { power-off-monitors; }
hotkey-overlay {
skip-at-startup
hide-not-bound
}
gestures {
hot-corners {
off
}
}
prefer-no-csd
// 我自己搞了个软链接,把截图丢进 tmpfs 里了。参见 .zshrc。
screenshot-path "~/.tmp/Screenshots/%Y-%m-%d %H-%M-%S.png"
// screenshot-path null

View File

@@ -0,0 +1,16 @@
# 像这种有明显时间规律的定时任务我懒得专门搞配套。不觉得 timer 非得跟 service 同名搞绑定很啰嗦吗?
# 我完全可以 on-hourly@renew-wallpaper.timer、on-hourly@webapi-health-check.timer 嘛。
# 当然这样也算不上简洁,但至少不需要另装个 cronarch 不自带)。
[Unit]
Description=Run %i hourly
[Timer]
OnCalendar=hourly
RandomizedDelaySec=1m
AccuracySec=1s
Persistent=true
Unit=%i.service
[Install]
WantedBy=timers.target

View File

@@ -3,6 +3,7 @@
"modules-left": [
"niri/workspaces",
"niri/window"
// 我参考的那两人还用 sway甚至大杂烩。而我只知道 niri甚至一度也不想知道不习惯
],
"modules-center": [
"clock"
@@ -35,11 +36,13 @@
"icon-size": 14,
"rewrite": {
"(.*) — Mozilla Firefox": "$1",
"(.*) - Google Chrome": "$1",
"(.*) - Visual Studio Code": "$1",
"(.*) - VLC media player": "$1",
"Yazi: (.*)": "$1"
}
},
// 这玩意说实话没想象中那么好用,但移植雪叶那个脚本,效果甚至还没这个好。
"mpris": {
"format": "{status_icon} {title}",
"format-stopped": "",
@@ -58,6 +61,7 @@
"tray": {
"spacing": 12
},
// 忘记用哪个工具确定设备节点了。实在不行终端开 yazi 来回翻。
"temperature": {
// "thermal-zone": 1,
"hwmon-path-abs": "/sys/devices/platform/coretemp.0/hwmon",

View File

@@ -7,7 +7,7 @@ window#waybar {
}
* {
font-family: "MonaspiceNe Nerd Font";
font-family: "Symbols Nerd Font", monospace;
font-size: 10pt;
}

View File

@@ -2,5 +2,6 @@
default=gnome;gtk;
org.freedesktop.impl.portal.Access=gtk
org.freedesktop.impl.portal.Notification=gtk
# gnome 密码框太丑了。
org.freedesktop.impl.portal.Secret=kwallet
org.freedesktop.impl.portal.FileChooser=gtk

View File

@@ -1,3 +1,10 @@
[mgr]
show_hidden = true
show_symlink = true
[opener]
play = [
{ run = 'vlc "$@"', orphan = true, for = "unix" },
{ run = 'mpv --force-window %*', orphan = true, for = "windows" },
{ run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" },
]

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
*.tar.zst
fake-nautilus/**/
.config/environment.d/[^0-9]*.conf

View File

@@ -1,12 +1,10 @@
# init at each startup
mkdir -p /tmp/$USER
#ln -sf /tmp/$UID $HOME/.tmp
[ $(readlink -f $HOME/.tmp) = "/tmp/$USER" ] || {
rm -f $HOME/.tmp
ln -s /tmp/$USER $HOME/.tmp
}
# 重启自动清空终端记录。毕竟万一露出什么不太好的关键词就丸辣。
# zsh (HISTFILE) => /tmp/$USER/terminal/zsh_history
# bash (HISTFILE) => /tmp/$USER/terminal/bash_history
# pwsh (.local/share/powershell/PSReadLine) -> /tmp/$USER/terminal
mkdir -p /tmp/$USER/terminal
# avoid dumplicate env appending.
# 本意是为了防止 PATH 重复记录(算是我的强迫症吧),现在没必要了。
# [[ -f /run/user/$UID/.zshlogon ]] && return
# touch /run/user/$UID/.zshlogon

15
.zshrc
View File

@@ -19,6 +19,9 @@ fi
# History
#
# also export this in bashrc.
HISTFILE=/tmp/$USER/terminal/zsh_history
# Remove older command from the history if a duplicate is to be added.
setopt HIST_IGNORE_ALL_DUPS
@@ -139,20 +142,14 @@ unset key
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
# USER MANUAL CONFIGS
# 俺的配置
# https://unix.stackexchange.com/questions/608842/zshrc-export-gpg-tty-tty-says-not-a-tty
export GPG_TTY=$TTY
alias uvenv='source .venv/bin/activate'
alias noproxy='env -u http_proxy -u https_proxy'
alias ykls='systemctl --user status YukiLauncher.slice'
alias yksuspend='systemctl --user freeze YukiLauncher.slice'
alias ykcout='systemctl --user thaw YukiLauncher.slice'
alias ykexit='systemctl --user stop YukiLauncher.slice'
alias susp="systemctl --user freeze"
alias cont="systemctl --user thaw"
alias savebg='cp ~/.local/share/.wallpaper ~/Pictures/img.webp'
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

192
bin/.battery-warn Executable file
View File

@@ -0,0 +1,192 @@
#!/usr/bin/env python
# SPDX-License-Identifier: CC0-1.0
# Copyright © 2021 mpan; <https://mpan.pl/>; CC0 1.0 (THIS SCRIPT!)
# Arch 论坛贴: <https://bbs.archlinux.org/viewtopic.php?id=269453>
# UPower 实现: https://github.com/wogscpar/upower-python
# -*- encoding: utf-8 -*-
# @File : .battery-warn
# @Time : 2025/12/29 00:32:53
# @Author : SilverAg.L
# 搁 bash 折腾管道还是太原始了。
# 包依赖 (pacman): python-dbus, libnotify, pipewire-audio
import dbus
from os import getenv
from subprocess import run as start_process
# region config
# 哪怕设备名也不稳定。pipewire 滚了几轮,桌面扬声器居然不见了。
# AUD_OUT_EMB = ( # my laptop embedded speaker
# "alsa_output.pci-0000_00_1f"
# ".3-platform-skl_hda_dsp_generic.HiFi__Speaker__sink"
# )
# 环境变量展开这一块。同时也是为了查找起来方便。毕竟已经有 gtklock 这个例外了。
AUD_FILE = "~/.local/share/.low_power.wav".replace("~", getenv("HOME"), 1)
# endregion config
class UPowerManager():
def __init__(self):
self.UPOWER_NAME = "org.freedesktop.UPower"
self.UPOWER_PATH = "/org/freedesktop/UPower"
self.DBUS_PROPERTIES = "org.freedesktop.DBus.Properties"
self.bus = dbus.SystemBus()
def __upower(self, subpath=None, *, interface=None):
if not subpath:
subpath = ""
upower_proxy = self.bus.get_object(
self.UPOWER_NAME,
self.UPOWER_PATH + subpath
)
if interface is None:
interface = self.UPOWER_NAME
return dbus.Interface(upower_proxy, interface)
def __device(self, devpath):
devproxy = self.bus.get_object(self.UPOWER_NAME, devpath)
return dbus.Interface(devproxy, self.DBUS_PROPERTIES)
def detect_devices(self):
return self.__upower().EnumerateDevices()
def get_display_device(self):
return self.__upower().GetDisplayDevice()
def get_critical_action(self):
return self.__upower().GetCriticalAction()
def get_device_info(self, dev, property):
return self.__device(dev).Get(
self.UPOWER_NAME + ".Device", property)
def get_full_device_info(self, dev):
return {
'HasHistory': self.get_device_info(dev, "HasHistory"),
'HasStatistics': self.get_device_info(dev, "HasStatistics"),
'IsPresent': self.get_device_info(dev, "IsPresent"),
'IsRechargeable': self.get_device_info(dev, "IsRechargeable"),
'Online': self.get_device_info(dev, "Online"),
'PowerSupply': self.get_device_info(dev, "PowerSupply"),
'Capacity': self.get_device_info(dev, "Capacity"),
'Energy': self.get_device_info(dev, "Energy"),
'EnergyEmpty': self.get_device_info(dev, "EnergyEmpty"),
'EnergyFull': self.get_device_info(dev, "EnergyFull"),
'EnergyFullDesign': self.get_device_info(dev, "EnergyFullDesign"),
'EnergyRate': self.get_device_info(dev, "EnergyRate"),
'Luminosity': self.get_device_info(dev, "Luminosity"),
'Percentage': self.get_device_info(dev, "Percentage"),
'Temperature': self.get_device_info(dev, "Temperature"),
'Voltage': self.get_device_info(dev, "Voltage"),
'TimeToEmpty': self.get_device_info(dev, "TimeToEmpty"),
'TimeToFull': self.get_device_info(dev, "TimeToFull"),
'IconName': self.get_device_info(dev, "IconName"),
'Model': self.get_device_info(dev, "Model"),
'NativePath': self.get_device_info(dev, "NativePath"),
'Serial': self.get_device_info(dev, "Serial"),
'Vendor': self.get_device_info(dev, "Vendor"),
'State': self.get_device_info(dev, "State"),
'Technology': self.get_device_info(dev, "Technology"),
'Type': self.get_device_info(dev, "Type"),
'WarningLevel': self.get_device_info(dev, "WarningLevel"),
'UpdateTime': self.get_device_info(dev, "UpdateTime")
}
def is_lid_present(self):
return bool(self.__upower(interface=self.DBUS_PROPERTIES).Get(
self.UPOWER_NAME, 'LidIsPresent'))
def is_lid_closed(self):
return bool(self.__upower(interface=self.DBUS_PROPERTIES).Get(
self.UPOWER_NAME, 'LidIsClosed'))
def on_battery(self):
return bool(self.__upower(interface=self.DBUS_PROPERTIES).Get(
self.UPOWER_NAME, 'OnBattery'))
def has_wakeup_capabilities(self):
return bool(self.__upower(
"/Wakeups",
interface=self.DBUS_PROPERTIES
).Get(self.UPOWER_NAME + '.Wakeups', 'HasCapability'))
def get_wakeups_data(self):
return self.__upower(
"/Wakeups",
interface=self.UPOWER_NAME + '.Wakeups'
).GetData()
def get_wakeups_total(self):
return self.__upower(
"/Wakeups",
interface=self.UPOWER_NAME + '.Wakeups'
).GetTotal()
def is_loading(self, battery):
return int(self.get_device_info(battery, "State")) == 1
def get_state(self, battery):
return {
0: "Unknown",
1: "Loading",
2: "Discharging",
3: "Empty",
4: "Fully charged",
5: "Pending charge",
6: "Pending discharge"
}.get(int(self.get_device_info(battery, "State")), "Unknown")
def is_supplying_battery(self, battery):
return (
int(self.get_device_info(battery, "Type")) == 2
and bool(self.get_device_info(battery, "PowerSupply"))
)
def push_notification(title, message, timeout=10000):
BUS_NAME = INTERFACE = "org.freedesktop.Notifications"
OBJECT_PATH = "/org/freedesktop/Notifications"
notify = dbus.Interface(
dbus.SessionBus().get_object(BUS_NAME, OBJECT_PATH),
INTERFACE
)
notify.Notify(
"battery-warn-script", # app_name
0, # replaces_id
"", # app_icon
title, # summary
message, # body
[], # actions
{}, # hints
timeout # expire_timeout
)
if __name__ == "__main__":
upowr = UPowerManager()
if not upowr.on_battery():
exit(0)
low_power_detected = False
for device in upowr.detect_devices():
if not upowr.is_supplying_battery(device):
continue
# seems waybar visual is 1% lower than actual.
if upowr.get_device_info(device, "Percentage") < 21:
bat_id = upowr.get_device_info(device, "NativePath")
push_notification(
"Power Hint",
f"{bat_id} is running out. Recharge soon!"
)
low_power_detected = True
if low_power_detected:
start_process([
"pw-play",
# f"--target={AUD_OUT_EMB}",
AUD_FILE
])

3
bin/.fuzzel-startb Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
option=$(fuzzel --dmenu --prompt-only="Run command: ")
[ -z "$option" ] || exec startb $option

18
bin/.fuzzel-vscode Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
desktop="$HOME/Desktop"
options=""
for item in $(ls $desktop); do
if [[ "$item" == *.desktop && -f "$desktop/$item" ]]; then
continue
fi
options+=$item
if [[ -d "$desktop/$item" ]]; then
options+='/'
fi
options+="\n"
done
fsarg=$(echo -en "$options" | fuzzel --dmenu --prompt="Open with VSCode: Desktop/")
[ -z "$fsarg" ] || exec startb code "$desktop/$fsarg"

165
bin/chbg
View File

@@ -1,24 +1,153 @@
#!/bin/bash
# to change wallpapers both niri workspace and niri tab view
[ -z "$1" ] && { echo "Usage: $0 <image-file>"; exit 1; }
set -euo pipefail
WP_DIR="$HOME/.local/share"
finput=$1
THIS_COMMAND=$(basename "$0")
IMG_MAGICK="magick"
SERVICE_NAME="swaybg.service"
fw=$(magick identify -format "%w\n" $1)
fh=$(magick identify -format "%h\n" $1)
if [[ $((fw)) > 3840 && $((fh)) > 2160 ]]
then
finput="/tmp/${finput##*/}"
echo "WARNING: image too large, resizing ..."
magick $1 -resize "3840x2160^" $finput
WP_DIR="${XDG_CACHE_HOME:-$HOME/.cache}"
WP_FILE="$WP_DIR/wallpaper"
BLUR_WP="$WP_DIR/wallpaper_blur"
usage() {
cat << EOF
Usage: $THIS_COMMAND [-d output_path] image1 image2 ...
Options:
-d, --dir OUTPUT_PATH Directory to store chosen image. (default: $WP_DIR)
-h, --help Show this help message and exit.
Notes:
- This script needs 'swaybg.service' to be set up for overview background support (unless awww #521 solved). See niri documentation for details.
- Web URLs are also supported (via 'curl' or 'wget' downloading).
- If multiple images are provided, one will be randomly picked each time the script is executed.
EOF
exit 1
}
# showing help shouldn't require any dependencies.
while [[ $# -gt 0 ]]; do
case "$1" in
-d|--dir)
if [[ $# -lt 2 ]]; then echo "Missing argument for $1"; usage; fi
WP_DIR="$2"; shift 2 ;;
-h|--help)
usage ;;
--)
shift; break ;;
-*)
echo "Unknown option: $1"; usage ;;
*)
break ;;
esac
done
if ! command -v awww >/dev/null 2>&1; then
echo "x) 'awww' not found. Unable to comply." >&2
exit 2
fi
echo -n "making blurred version of $finput ... "
mkdir -p $WP_DIR
cp $finput $WP_DIR/.wallpaper
magick $WP_DIR/.wallpaper -filter Gaussian -blur 0x30 $WP_DIR/.wallpaper_blur
echo "Done."
if command -v magick >/dev/null 2>&1; then
IMG_MAGICK="magick"
elif command -v convert >/dev/null 2>&1; then
IMG_MAGICK="convert"
else
echo "x) 'magick' or 'convert' not found. Image processing unavailable." >&2
exit 2
fi
swww img $WP_DIR/.wallpaper
swww img $WP_DIR/.wallpaper_blur --namespace blur
set_wallpaper() {
fsize=$($IMG_MAGICK identify -format "%w %h" -- "$1" 2>/dev/null) || {
echo "Invalid image file." >&2
exit 10
}
finput=$1
read -r fw fh <<< "$fsize"
(( fw > 3840 && fh > 2160 )) && {
finput=$(mktemp "${TMPDIR:-/tmp}/chbg.XXXXXXXX.webp")
trap 'rm -f "$finput"' EXIT
echo " -> image too large, resizing ..."
# echo "DEBUG: resized image at $finput"
$IMG_MAGICK "$1" -resize "3840x2160^" \
-quality 90 \
-define webp:method=6 \
-define webp:alpha-quality=100 "$finput"
}
echo -n " -> making blurred version of '${1##*/}' ... "
cp "$finput" "$WP_FILE"
$IMG_MAGICK "$WP_FILE" -filter Gaussian -blur 0x30 "$BLUR_WP"
echo "Done."
awww img "$WP_FILE" --transition-type=random
if [[ -f "$HOME/.config/systemd/user/niri.service.wants/$SERVICE_NAME" ]]; then
echo " -> restarting $SERVICE_NAME ..."
systemctl --user restart "$SERVICE_NAME"
else
echo " !) '$SERVICE_NAME' didn't loaded by niri. Won't reload backdrop." >&2
echo " => See niri documentation for setting up swaybg systemd unit." >&2
echo " => Or wait for awww #521 on codeberg being resolved." >&2
exit 1
fi
}
imagepool=()
lastresult=""
# Read from stdin if data is available
if [ ! -t 0 ]; then
while IFS= read -r line; do
imagepool+=("$line")
done
fi
# Read from positional arguments
for arg in "$@"; do
imagepool+=("$arg")
done
if [ ${#imagepool[@]} -eq 0 ]; then
echo "No images provided." >&2
exit 1
fi
mkdir -p -- "$WP_DIR" "/tmp/$USER"
if [ -f "/tmp/$USER/chbg.last-slide.log" ]; then
lastresult=$(cat "/tmp/$USER/chbg.last-slide.log")
fi
# Select a random image from the pool and set it as wallpaper
while : ; do
randomimage="${imagepool[RANDOM % ${#imagepool[@]}]}"
if [[ "$randomimage" == "http://"* || "$randomimage" == "https://"* ]]; then
break
fi
if [ "$randomimage" != "$lastresult" ] || [ ${#imagepool[@]} -eq 1 ]; then
echo "$randomimage" > "/tmp/$USER/chbg.last-slide.log"
break
fi
done
echo "Selected: $randomimage"
request_wallpaper() {
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT
if command -v curl >/dev/null 2>&1; then
curl -sSL "$1" -o "$tmpfile"
elif command -v wget >/dev/null 2>&1; then
wget -qO "$tmpfile" "$1"
else
echo "x) Unable to fetch image without 'curl' or 'wget'." >&2
exit 2
fi
set_wallpaper "$tmpfile"
}
if [ -f "$randomimage" ]; then
set_wallpaper "$randomimage"
else
request_wallpaper "$randomimage"
fi

View File

@@ -1,15 +0,0 @@
#!/bin/bash
desktop="$HOME/Desktop"
options=""
for item in $(ls $desktop); do
options+=$item
if [[ -d "$desktop/$item" ]]; then
options+='/'
fi
options+="\n"
done
fsarg=$(echo -en "$options" | fuzzel --dmenu --prompt="Open Project with VSCode:")
[ -z "$fsarg" ] || ykrun code "$desktop/$fsarg"

View File

@@ -1,3 +0,0 @@
#!/bin/bash
option=$(fuzzel --dmenu --prompt-only="Command to execute: ")
[ -z "$option" ] || ykrun $option

99
bin/startb Executable file
View File

@@ -0,0 +1,99 @@
#!/usr/bin/env bash
# startb: start a command in the background with journald or file logging fallback.
# Usage:
# startb [--log logfile] COMMAND [ARGS...]
# Environment:
# STARTB_LOGFILE=... to force a log file.
set -euo pipefail
# PATH may be incomplete from systemd login env; restore from interactive shell.
PATH=$(zsh -c -i 'echo $PATH')
export PATH
SCRIPT_NAME="${0##*/}"
usage() {
cat <<EOF
Usage: ${SCRIPT_NAME} [--log logfile] COMMAND [ARGS...]
--log logfile Write logs to this file instead of journald.
Environment variable STARTB_LOGFILE can also provide a logfile path.
EOF
exit 1
}
if [ $# -lt 1 ]; then
usage
fi
LOG_FILE=${STARTB_LOGFILE:-}
if [ "$1" = "--log" ] || [ "$1" = "-l" ]; then
shift
if [ $# -lt 1 ]; then
echo "Error: --log requires a logfile path." >&2
usage
fi
LOG_FILE=$1
shift
fi
if [ $# -lt 1 ]; then
usage
fi
COMMAND=("$@")
UNIT_BASE=$(basename "${COMMAND[0]}")
UNIT_BASE=${UNIT_BASE:-cmd}
UNIT_NAME="${UNIT_BASE}-$(date +%Y%m%d_%H%M%S_%N)"
log_to_file() {
local path=$1
mkdir -p "$(dirname "$path")" 2>/dev/null || true
touch "$path" || {
echo "Failed to touch logfile: $path" >&2
return 1
}
nohup setsid "${COMMAND[@]}" >>"$path" 2>&1 &
local pid=$!
disown "$pid" 2>/dev/null || true
echo "$UNIT_NAME started in background (pid=$pid), logs: $path"
return 0
}
log_to_journal() {
# 不按雪叶那样写基本记录不到输出,懒得调了,直接屏蔽。
# if command -v systemd-run >/dev/null 2>&1; then
# systemd-run --user --scope --unit="$UNIT_NAME" -- "${COMMAND[@]}" >/dev/null 2>&1 &
# local pid=$!
# echo "$UNIT_NAME started in systemd scope (pid=$pid), journald logging enabled"
# return 0
# fi
if command -v systemd-cat >/dev/null 2>&1; then
nohup setsid systemd-cat -t "$UNIT_BASE" -- "${COMMAND[@]}" >/dev/null 2>&1 &
local pid=$!
disown "$pid" 2>/dev/null || true
echo "$UNIT_BASE started with systemd-cat (pid=$pid), journald logging enabled"
return 0
fi
return 1
}
if [ -n "$LOG_FILE" ]; then
log_to_file "$LOG_FILE" || exit 1
exit 0
fi
if log_to_journal; then
exit 0
fi
# Fallback log path when journald is unavailable.
LOG_FILE="/tmp/${USER:-unknown}/startb/${UNIT_NAME}.log"
log_to_file "$LOG_FILE"

View File

@@ -1,2 +0,0 @@
#!/bin/bash
systemd-run --user --scope --slice=YukiLauncher.slice --unit="$1-$$".scope /bin/sh -c '"$@"' _ "$@"

View File

@@ -0,0 +1,17 @@
// ==UserScript==
// @name Genshin Cloud
// @namespace http://tampermonkey.net/
// @version 0.1
// @description fix a Genshin Impact cloud game bug
// @match https://*.mihoyo.com/cloud/*
// @grant none
// ==/UserScript==
(function () {
'use strict';
const origin = HTMLElement.prototype.requestPointerLock
HTMLElement.prototype.requestPointerLock = function () {
return origin.call(this)
}
})();

View File

@@ -3,43 +3,59 @@
都怪雪叶!(
> [!note]
> 本仓库的配置管理方案比较原始:逐个做软链接处理。有觉得`stow`等工具好用,想要重新组织文件树的欢迎 pr.
> - `.config/**` -> `$HOME/.config/**`
> - `bin`直接并入`$PATH`
> > 其中带后缀(如`.sh`)的脚本仅供 niri 等组件调用,设计上并不希望在终端里启动
>
> - `.z*` -> `$HOME/.z*`
> - `default-electron-flags` -> `$HOME/.config/*-flags.conf` (code, chrome)
> > QQ 不适用这组参数,另见 archlinuxcn 社区论坛。
> [!note]
> 还有一些较早做的配置,以及全局配置(像 sddm并不方便合进来由于篇幅和复述可靠性有限就略过罢。
> 1. Liteyuki Gitea 黑色主题对`这样的`代码块不太友好,为方便阅读我尽量少这么写。
> 1. 本仓库的配置管理方案比较原始:逐个做软链接处理。
> 个人并不打算无脑用 stow ,尤其不希望一些 systemd 服务和涉及 token 的自用小工具混进来。
> 2. 还有一些早期配置在整理本仓库时已经淡忘,由于篇幅和复述可靠性有限,亦不考虑收纳
> 3. 涉及家目录`$HOME`、`~`、`%h`开头(最后一个多见于 systemd 服务)的路径建议自行适配——我的设定你不一定会满意。
> - 另注gtklock 没有办法取巧,只能填绝对路径。算是为数不多的漏网之鱼。
## 鸣谢
- 雪叶 [@Vescrity](https://github.com/Vescrity) (Yukitoha)
- fizzyizzy05 (Isabelle Jackson, mtf)
- fizzyizzy05 (Isabelle Jackson)
## 已知依赖
> 仅列出**想得起来的**依赖软件包。
|依赖|配置|备注|
|-|-|-|
|niri|`.config/niri/config.kdl`|
|`fuzzel`|`.config/fuzzel/*`|
|`mako`|`.config/mako/*`|
|`swww`|`bin/chbg` (bash)|另需`imagemagick`来生成图片的高斯模糊版本。|
|`gtklock`|`.config/gtklock/*`|现阶段无法跟`swayidle`合用,尽管其 Wiki 鼓励这么做。|
|`xdg-desktop-portal-(gtk\|gnome)`|`.config/xdg-desktop-portal/*-portals.conf`|GNOME 支持最全,但称不上好看(|
|`kwallet`|VSCode 试图登录时会弹出向导让你配的。|替代`gnome-keyring`。建议加装`kwallet-pam`,免得 VSCode 自己尝试解锁结果闪退。|
|foot|`.config/foot/foot.ini`|
|yazi|`.config/yazi/*`|替代`nautilus`;另用`fake-nautilus`移除`nautilus`包。|
|fastfetch|`.config/fastfetch/*`|
|waybar|`.config/waybar/*`|
|`mpris`|`systemctl --user`服务|参见 ArchWiki.|
|`pavucontrol-qt`|
|依赖|配置|
|-|-|
|niri|.config/niri/config*.kdl|
|➡️ fuzzel|.config/fuzzel/*|
|➡️ mako|.config/mako/*|
|➡️ awww 和 swaybg|bin/chbg (bash 脚本)swaybg.service|
|➡️ gtklock|.config/gtklock/*|
|➡️ xdg-desktop-portal-(gtk\|gnome)|.config/xdg-desktop-portal/*-portals.conf|
|➡️ kwallet|VSCode 试图登录时会弹出向导让你配的。|
|foot|.config/foot/foot.ini|
|➡️ zsh|.zshrc .zprofile|
|yazi|.config/yazi/*|
|fastfetch|.config/fastfetch/*|
|waybar|.config/waybar/*|
|➡️ mpris|`systemctl --user`配置自启动(参见 ArchWiki|
|➡️ pavucontrol-qt|
> 备注(太长了统一丢在下面):
> - niri 配置只拆分了 window-rule 和 binds即`config-*.kdl拆太碎有些视觉效果会失效。
> - Chrome 的消息推送都是无脑标 CRITICAL 级,没有办法调持续时间,量一多还会卡在那里,只能`makoctl reload`强制重载。
> - chbg 依赖 imagemagick。awww 目前对多个命名空间的加载有问题:[\#521](https://codeberg.org/LGFae/awww/issues/521)。经各种方案的实地测试,最终还是考虑 swaybg 服务 + awww 混合。
> - gtklock 自身无法挂多个后台(即只能有一个`gtklock -d`,多了会报错),虽然也算侧面实现了单例,但搭配 swayidle 和 swaylock 可能不算好用。我是只在休眠快捷键里简单做了下 pgrep 检查。
> - niri 设计上就是用 gnome 作 xdg 后端,也就它支持最全。但 gnome 界面算不上好看,所以也有一些人考虑局部更换为 gtk 或 kde。像我就用 kwallet 替代 gnome-keyring。
> - 装 kwallet 建议加装 kwallet-pam免得 VSCode 自己尝试解锁结果闪退。
> - 有关终端:我另使用了 ZshIM 和 powerlevel10k 主题,因此会掺入自动生成的初始化命令。
> - 有关 fake-nautilus我是觉得 nautilus 不好看。在雪叶的指导下搞了个空包替代这个 gnome 后端必需的依赖。
> - waybar和 swaybg重载建议搭配`systemctl --user`(参见 [niri 在线文档](https://yalter.github.io/niri/Example-systemd-Setup.html))以便重载配置。若是用 niri 的 spawn-at-startup杀进程重启、挂后台可谓相当麻烦。
## 参考资源与备注
- Nerd 字体:[Monaco Nerd Font Mono (MelodyEcho ver.)](https://glowmem.com/upload/articles/archlinux-note/Monaco_Nerd_Font_Mono-Regular.ttf) 或直接`ttf-monaco-nerd-font`(AUR)。后者在 VSCode 里表现不咋地
- Nerd 字体:[Monaco Nerd Font Mono](https://glowmem.com/upload/articles/archlinux-note/Monaco_Nerd_Font_Mono-Regular.ttf) (MelodyEcho ver.) 或直接 [MesloLGS NF](https://github.com/romkatv/powerlevel10k/blob/master/font.md) (for p10k)
> 深色模式:`gsettings set org.gnome.desktop.interface color-scheme prefer-dark`
> GTK/QT 深色主题:参见 [Arch Wiki](https://wiki.archlinux.org/title/Uniform_look_for_Qt_and_GTK_applications#Styles_for_both_Qt_and_GTK).
- 深色模式:`gsettings set org.gnome.desktop.interface color-scheme prefer-dark`
GTK/QT 深色主题:参见 [Arch Wiki](https://wiki.archlinux.org/title/Uniform_look_for_Qt_and_GTK_applications#Styles_for_both_Qt_and_GTK)。我摆烂了。
- 大多数 Electron 应用需要读`~/.config/*-flags.conf`来适配 Wayland
你可以把`default-electron-flags`相应地软链接过去。**特别地QQ 需要多加一条**`--wayland-text-input-version=3`
- `hoyocloud-chromium-userscript.js`顾名思义,用于**在 Chrome 里**firefox 不需要)游玩米哈游云游戏的油猴脚本。
参见 [Bilibili 专栏](https://www.bilibili.com/opus/842314310196658193)。
- `bin/.battery-warn`虽说也是自用,但一是配置项并不算敏感,稍微改改`config`段也可以泛用;二是参考文献写得有点啰嗦,我懒得再缝第二遍。
个人建议用于**定时任务cron 或 systemd timer**。