dots

default description
git clone https://git.awy.one/dots.git
Log | Files | Refs | README | LICENSE

commit 4bfcf634ed639174f66675073230732a90a3d51b
Author: awy <awy@awy.one>
Date:   Sun, 14 Dec 2025 00:18:52 +0300

init

Diffstat:
A.config/fnott/fnott.ini | 26++++++++++++++++++++++++++
A.config/fontconfig/fonts.conf | 40++++++++++++++++++++++++++++++++++++++++
A.config/foot/foot.ini | 18++++++++++++++++++
A.config/lazygit/config.yml | 11+++++++++++
A.config/lazygit/themes.gitconfig | 667+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/mimeapps.list | 20++++++++++++++++++++
A.config/mpd/mpd.conf | 19+++++++++++++++++++
A.config/mpv/input.conf | 5+++++
A.config/mpv/mpv.conf | 15+++++++++++++++
A.config/newsraft/config | 11+++++++++++
A.config/nvim/init.lua | 19+++++++++++++++++++
A.config/nvim/lua/options.lua | 17+++++++++++++++++
A.config/nvim/lua/plugins.lua | 1+
A.config/nvim/lua/plugins/comment.lua | 8++++++++
A.config/nvim/lua/plugins/completions.lua | 44++++++++++++++++++++++++++++++++++++++++++++
A.config/nvim/lua/plugins/explorer.lua | 27+++++++++++++++++++++++++++
A.config/nvim/lua/plugins/gitsigns.lua | 11+++++++++++
A.config/nvim/lua/plugins/indent.lua | 8++++++++
A.config/nvim/lua/plugins/lsp.lua | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/nvim/lua/plugins/lualine.lua | 10++++++++++
A.config/nvim/lua/plugins/markdown.lua | 10++++++++++
A.config/nvim/lua/plugins/telescope.lua | 25+++++++++++++++++++++++++
A.config/nvim/lua/plugins/theme.lua | 7+++++++
A.config/nvim/lua/plugins/treesitter.lua | 14++++++++++++++
A.config/nvim/lua/plugins/trim.lua | 8++++++++
A.config/paru/paru.conf | 3+++
A.config/pipewire/pipewire.conf.d/user-session.conf | 9+++++++++
A.config/qt5ct/qt5ct.conf | 32++++++++++++++++++++++++++++++++
A.config/qt6ct/qt6ct.conf | 32++++++++++++++++++++++++++++++++
A.config/rmpc/config.ron | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/rmpc/scripts/statusbar | 2++
A.config/rmpc/themes/def.ron | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/shell/aliasrc | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/shell/bm-dirs | 14++++++++++++++
A.config/shell/bm-files | 16++++++++++++++++
A.config/shell/inputrc | 19+++++++++++++++++++
A.config/shell/profile | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/sway/config | 272+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/swayidle/config | 3+++
A.config/swayimg/config | 38++++++++++++++++++++++++++++++++++++++
A.config/swayimg/key-handler | 35+++++++++++++++++++++++++++++++++++
A.config/swaylock/config | 1+
A.config/tmux/tmux.conf | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/user-dirs.dirs | 1+
A.config/wget/wgetrc | 1+
A.config/xdg-desktop-portal-termfilechooser/config | 2++
A.config/xdg-desktop-portal-wlr/config | 3+++
A.config/xdg-desktop-portal/config | 3+++
A.config/yazi/init.lua | 4++++
A.config/yazi/plugins/git.yazi/main.lua | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.config/yazi/yazi.toml | 12++++++++++++
A.config/yt-dlp/config | 1+
A.config/zathura/zathurarc | 25+++++++++++++++++++++++++
A.config/zsh/.zshrc | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/bookmarks | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/booksplit | 43+++++++++++++++++++++++++++++++++++++++++++
A.local/bin/check_lyrics | 21+++++++++++++++++++++
A.local/bin/chkmd5m | 31+++++++++++++++++++++++++++++++
A.local/bin/chooseprofile | 12++++++++++++
A.local/bin/compiler | 42++++++++++++++++++++++++++++++++++++++++++
A.local/bin/cron/README.md | 14++++++++++++++
A.local/bin/cron/checkup | 13+++++++++++++
A.local/bin/cron/crontog | 6++++++
A.local/bin/cron/mailup | 9+++++++++
A.local/bin/cron/newsup | 15+++++++++++++++
A.local/bin/define | 37+++++++++++++++++++++++++++++++++++++
A.local/bin/displayselect | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/dmenuhandler | 21+++++++++++++++++++++
A.local/bin/dmenumountcifs | 19+++++++++++++++++++
A.local/bin/dmenupass | 6++++++
A.local/bin/dmenurecord | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/doas_askpass | 47+++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/getbib | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/getcomproot | 9+++++++++
A.local/bin/ifinstalled | 12++++++++++++
A.local/bin/linkhandler | 32++++++++++++++++++++++++++++++++
A.local/bin/maimpick | 33+++++++++++++++++++++++++++++++++
A.local/bin/mounter | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/noisereduce | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/opout | 13+++++++++++++
A.local/bin/pauseallmpv | 10++++++++++
A.local/bin/peertubetorrent | 9+++++++++
A.local/bin/podentr | 7+++++++
A.local/bin/portal | 7+++++++
A.local/bin/qndl | 12++++++++++++
A.local/bin/queueandnotify | 14++++++++++++++
A.local/bin/randombg | 35+++++++++++++++++++++++++++++++++++
A.local/bin/rssadd | 18++++++++++++++++++
A.local/bin/rssget | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/sd | 22++++++++++++++++++++++
A.local/bin/setbg | 42++++++++++++++++++++++++++++++++++++++++++
A.local/bin/shortcuts | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/singboxwrap | 33+++++++++++++++++++++++++++++++++
A.local/bin/slider | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/spec | 23+++++++++++++++++++++++
A.local/bin/start-pipewire | 5+++++
A.local/bin/statusbar/sb-microphone | 26++++++++++++++++++++++++++
A.local/bin/statusbar/sb-news | 9+++++++++
A.local/bin/statusbar/sb-pacpackages | 9+++++++++
A.local/bin/statusbar/sb-popupgrade | 9+++++++++
A.local/bin/sysact | 36++++++++++++++++++++++++++++++++++++
A.local/bin/tag | 49+++++++++++++++++++++++++++++++++++++++++++++++++
A.local/bin/td-toggle | 12++++++++++++
A.local/bin/torwrap | 7+++++++
A.local/bin/transadd | 9+++++++++
A.local/bin/unix | 26++++++++++++++++++++++++++
A.local/bin/unmounter | 25+++++++++++++++++++++++++
A.local/bin/weath | 25+++++++++++++++++++++++++
A.local/bin/xdg-terminal-exec | 3+++
A.local/share/applications/file.desktop | 4++++
A.local/share/applications/img.desktop | 4++++
A.local/share/applications/mail.desktop | 4++++
A.local/share/applications/pdf.desktop | 4++++
A.local/share/applications/rss.desktop | 4++++
A.local/share/applications/text.desktop | 4++++
A.local/share/applications/torrent.desktop | 4++++
A.local/share/applications/video.desktop | 4++++
ALICENSE | 674+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME.md | 9+++++++++
Aaurdeps.txt | 7+++++++
Adependencies.txt | 131+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arice.sh | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
122 files changed, 5540 insertions(+), 0 deletions(-)

diff --git a/.config/fnott/fnott.ini b/.config/fnott/fnott.ini @@ -0,0 +1,26 @@ +layer=overlay + +max-width=300 +min-width=300 +max-icon-size=32 + +background=000000FF +border-color=285577FF +border-size=2 + +title-font=monospace:size=14 +title-color=FFFFFFFF +title-format= +summary-font=monospace:size=14 +summary-color=FFFFFFFF +body-font=monospace:size=14 +body-color=FFFFFFFF + +progress-bar-color=285577FF + +default-timeout=5 +idle-timeout=120 + +[critical] +border-color=900000FF +default-timeout=15 diff --git a/.config/fontconfig/fonts.conf b/.config/fontconfig/fonts.conf @@ -0,0 +1,40 @@ +<?xml version='1.0'?> +<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'> +<fontconfig> + <alias> + <family>serif</family> + <prefer> + <family>FreeSerif</family> + <family>Noto Color Emoji</family> + <family>FontAwesome</family> + <family>SymbolsNerdFont Propo</family> + </prefer> + </alias> + <alias> + <family>sans-serif</family> + <prefer> + <family>FreeSans</family> + <family>Noto Color Emoji</family> + <family>FontAwesome</family> + <family>SymbolsNerdFont Propo</family> + </prefer> + </alias> + <alias> + <family>sans</family> + <prefer> + <family>FreeSans</family> + <family>Noto Color Emoji</family> + <family>FontAwesome</family> + <family>SymbolsNerdFont Propo</family> + </prefer> + </alias> + <alias> + <family>monospace</family> + <prefer> + <family>FreeMono</family> + <family>Noto Color Emoji</family> + <family>FontAwesome</family> + <family>SymbolsNerdFont Propo</family> + </prefer> + </alias> +</fontconfig> diff --git a/.config/foot/foot.ini b/.config/foot/foot.ini @@ -0,0 +1,18 @@ +[main] +include=/usr/share/foot/themes/xterm +font=monospace:size=14 +font-size-adjustment=3 +gamma-correct-blending=false +resize-by-cells=false +initial-window-size-chars=80x24 + +[key-bindings] +clipboard-copy=Mod1+c +clipboard-paste=Mod1+v +scrollback-up-line=Mod1+k +scrollback-down-line=Mod1+j +font-increase=Mod1+Shift+k +font-decrease=Mod1+Shift+j +font-reset=Mod1+0 +show-urls-launch=Mod1+l +show-urls-copy=Mod1+y diff --git a/.config/lazygit/config.yml b/.config/lazygit/config.yml @@ -0,0 +1,11 @@ +git: + paging: + colorArg: always + pager: delta --dark --paging=never +keybinding: + universal: + nextBlock-alt2: <disabled> + nextTab: <tab> + prevTab: <disabled> +gui: + border: single diff --git a/.config/lazygit/themes.gitconfig b/.config/lazygit/themes.gitconfig @@ -0,0 +1,667 @@ +# To use these themes, first include this file in your own gitconfig file: +# +# [include] +# path = PATH/TO/delta/themes.gitconfig +# +# Then, in your own gitconfig file, activate the chosen theme, e.g. +# +# [delta] +# features = kingfisher +# +# Please add your own themes to this file, and open a PR. +# +# Instructions: +# +# 1. The name of the theme must be the name of some sort of wild organism: +# mammal, bird, plant, mollusk -- whatever. It can be in any language. +# +# 2. Use `delta --show-config` to list all the style settings that are active in +# your current delta environment, but only include settings in your theme +# that are essential to its appearance. +# +# 3. Include either `dark = true` or `light = true` according to whether it is +# designed for a light or dark terminal background. (This marks a feature as +# a "theme", causing it to be picked up by `delta --show-themes`). +# +# 4. Feel free to include a comment line indicating who is the author of the +# theme. E.g. a link to your github user page. + +[delta "colibri"] + # author: https://github.com/pablospe + # Based on woolly-mammoth: https://github.com/Kr1ss-XD. + commit-decoration-style = 130 box + dark = true + file-decoration-style = "#606018" overline + file-added-label = [●] + file-copied-label = [C] + file-modified-label = [+] + file-removed-label = [⛌] + file-renamed-label = [→] + file-style = 232 bold 184 + hunk-header-style = syntax bold italic 237 + line-numbers = true + line-numbers-left-format = "{nm:>1}┊" + line-numbers-left-style = red + line-numbers-minus-style = red bold + line-numbers-plus-style = green bold + line-numbers-right-format = " {np:>1}┊" + line-numbers-right-style = green + line-numbers-zero-style = "#545474" italic + minus-emph-style = normal "#80002a" + minus-style = normal "#5e0000" + plus-emph-style = syntax bold "#007e5e" + plus-style = syntax "#003500" + syntax-theme = OneHalfDark + whitespace-error-style = "#80002a" reverse + zero-style = syntax + blame-format = "{author:<18} ({commit:>7}) ┊{timestamp:^16}┊ " + +[delta "collared-trogon"] + # author: https://github.com/clnoll + commit-decoration-style = bold box ul + dark = true + file-decoration-style = none + file-style = omit + hunk-header-decoration-style = "#022b45" box ul + hunk-header-file-style = "#999999" + hunk-header-line-number-style = bold "#003300" + hunk-header-style = file line-number syntax + line-numbers = true + line-numbers-left-style = "#022b45" + line-numbers-minus-style = "#80002a" + line-numbers-plus-style = "#003300" + line-numbers-right-style = "#022b45" + line-numbers-zero-style = "#999999" + minus-emph-style = normal "#80002a" + minus-style = normal "#330011" + plus-emph-style = syntax "#003300" + plus-style = syntax "#001a00" + syntax-theme = Nord + +[delta "coracias-caudatus"] + # author: https://github.com/clnoll + commit-decoration-style = ol "#7536ff" + commit-style = "#200078" + file-decoration-style = none + file-style = omit + hunk-header-decoration-style = "#cfd6ff" ul + hunk-header-file-style = "#858dff" + hunk-header-line-number-style = "#7536ff" + hunk-header-style = file line-number syntax + light = true + line-numbers = true + line-numbers-left-format = "{nm:>4} ." + line-numbers-left-style = "#e3ab02" + line-numbers-minus-style = "#ff38b6" + line-numbers-plus-style = "#00e0c2" + line-numbers-right-format = "{np:>4} " + line-numbers-right-style = white + line-numbers-zero-style = "#cccccc" + minus-emph-style = bold "#ff3838" "#ffe3f7" + minus-style = "#ff0080" + plus-emph-style = "#008a81" bold "#00ffbf" + plus-style = syntax "#cffff3" + syntax-theme = GitHub + +[delta "hoopoe"] + # author: https://github.com/dandavison + light = true + pink = "#ffe0e0" + dark-pink = "#ffc0c0" + green = "#d0ffd0" + dark-green = "#a0efa0" + dark-green-2 = "#067a00" + minus-style = normal hoopoe.pink + minus-emph-style = normal hoopoe.dark-pink + minus-non-emph-style = minus-style + plus-style = syntax hoopoe.green + plus-emph-style = syntax hoopoe.dark-green + plus-non-emph-style = plus-style + minus-empty-line-marker-style = minus-style + plus-empty-line-marker-style = plus-style + commit-decoration-style = blue ol + commit-style = raw + file-style = omit + hunk-header-decoration-style = blue box + hunk-header-file-style = red + hunk-header-line-number-style = hoopoe.dark-green-2 + hunk-header-style = file line-number syntax + syntax-theme = GitHub + zero-style = syntax + +[delta "tangara-chilensis"] + # author: https://github.com/clnoll + commit-decoration-style = bold box ul "#34fd50" + dark = true + file-decoration-style = none + file-style = omit + hunk-header-decoration-style = "#00b494" box ul + hunk-header-file-style = "#999999" + hunk-header-line-number-style = bold "#03a4ff" + hunk-header-style = file line-number syntax + line-numbers = true + line-numbers-left-style = black + line-numbers-minus-style = "#B10036" + line-numbers-plus-style = "#03a4ff" + line-numbers-right-style = black + line-numbers-zero-style = "#999999" + minus-emph-style = normal "#de004e" + minus-style = normal "#990017" + plus-emph-style = syntax "#03a4ff" + plus-style = syntax "#450eff" + side-by-side = true + syntax-theme = Vibrant Sunburst + +[delta "villsau"] + # author: https://github.com/torarnv + dark = true + file-style = omit + hunk-header-decoration-style = omit + hunk-header-file-style = magenta + hunk-header-line-number-style = dim magenta + hunk-header-style = file line-number syntax + line-numbers = false + minus-emph-style = bold red 52 + minus-empty-line-marker-style = normal "#3f0001" + minus-non-emph-style = dim red + minus-style = bold red + plus-emph-style = bold green 22 + plus-empty-line-marker-style = normal "#002800" + plus-non-emph-style = dim green + plus-style = bold green + syntax-theme = OneHalfDark + whitespace-error-style = reverse red + zero-style = dim syntax + +[delta "woolly-mammoth"] + # author: https://github.com/Kr1ss-XD + commit-decoration-style = 232 130 box + commit-style = 232 bold italic 130 + dark = true + file-added-label = [+] + file-copied-label = [C] + file-decoration-style = "#606018" overline + file-modified-label = [M] + file-removed-label = [-] + file-renamed-label = [R] + file-style = 232 bold 184 + hunk-header-decoration-style = none + hunk-header-style = syntax bold italic 237 + line-numbers = true + line-numbers-left-format = "{nm:>1}┊" + line-numbers-left-style = red + line-numbers-minus-style = red italic black + line-numbers-plus-style = green italic black + line-numbers-right-format = "{np:>1}┊" + line-numbers-right-style = green + line-numbers-zero-style = "#545474" italic + minus-emph-style = syntax bold "#780000" + minus-style = syntax "#400000" + plus-emph-style = syntax bold "#007800" + plus-style = syntax "#004000" + syntax-theme = Vibrant Sunburst + whitespace-error-style = "#280050" reverse + zero-style = syntax + blame-format = "{author:<18} ({commit:>7}) ┊{timestamp:^16}┊ " + blame-palette = "#101010 #200020 #002800 #000028 #202000 #280000 #002020 #002800 #202020" + +[delta "calochortus-lyallii"] + # author: https://github.com/manojkarthick + commit-decoration-style = none + dark = true + file-added-label = [+] + file-copied-label = [C] + file-decoration-style = none + file-modified-label = [M] + file-removed-label = [-] + file-renamed-label = [R] + file-style = 232 bold 184 + hunk-header-decoration-style = none + hunk-header-file-style = "#999999" + hunk-header-line-number-style = bold "#03a4ff" + hunk-header-style = file line-number syntax + line-numbers = true + line-numbers-left-style = black + line-numbers-minus-style = "#B10036" + line-numbers-plus-style = "#03a4ff" + line-numbers-right-style = black + line-numbers-zero-style = "#999999" + minus-emph-style = syntax bold "#780000" + minus-style = syntax "#400000" + plus-emph-style = syntax bold "#007800" + plus-style = syntax "#004000" + whitespace-error-style = "#280050" reverse + zero-style = syntax + syntax-theme = Nord + +[delta "mantis-shrimp"] + #author: https://github.com/2kabhishek + dark = true + side-by-side = true + keep-plus-minus-markers = true + hyperlinks = true + file-added-label = [+] + file-copied-label = [==] + file-modified-label = [*] + file-removed-label = [-] + file-renamed-label = [->] + file-style = omit + zero-style = syntax + syntax-theme = Monokai Extended + commit-decoration-style ="#11ce16" box + commit-style = "#ffd21a" bold italic + hunk-header-decoration-style = "#1688f0" box ul + hunk-header-file-style = "#c63bee" ul bold + hunk-header-line-number-style = "#ffd21a" box bold + hunk-header-style = file line-number syntax bold italic + line-numbers = true + line-numbers-left-format = "{nm:>1}|" + line-numbers-left-style = "#1688f0" + line-numbers-minus-style = "#ff0051" bold + line-numbers-plus-style = "#03e57f" bold + line-numbers-right-format = "{np:>1}|" + line-numbers-right-style = "#1688f0" + line-numbers-zero-style = "#aaaaaa" italic + minus-emph-style = syntax bold "#b80000" + minus-style = syntax "#5d001e" + plus-emph-style = syntax bold "#007800" + plus-style = syntax "#004433" + whitespace-error-style = "#280050" + +[delta "mantis-shrimp-lite"] + #author: https://github.com/2kabhishek + dark = true + side-by-side = true + keep-plus-minus-markers = true + file-added-label = [+] + file-copied-label = [==] + file-modified-label = [*] + file-removed-label = [-] + file-renamed-label = [->] + file-style = omit + zero-style = syntax + syntax-theme = Monokai Extended + commit-decoration-style = green box + commit-style = yellow bold italic + hunk-header-decoration-style = blue box ul + hunk-header-file-style = purple ul bold + hunk-header-line-number-style = yellow box bold + hunk-header-style = file line-number syntax bold italic + line-numbers = true + line-numbers-left-format = "{nm:>1}|" + line-numbers-left-style = blue + line-numbers-minus-style = red bold + line-numbers-plus-style = green bold + line-numbers-right-format = "{np:>1}|" + line-numbers-right-style = blue + line-numbers-zero-style = white italic + minus-emph-style = syntax bold red + plus-emph-style = syntax bold green + whitespace-error-style = purple bold + +[delta "zebra-dark"] + minus-style = syntax "#330f0f" + minus-emph-style = syntax "#4f1917" + plus-style = syntax "#0e2f19" + plus-emph-style = syntax "#174525" + map-styles = \ + bold purple => syntax "#330f29", \ + bold blue => syntax "#271344", \ + bold cyan => syntax "#0d3531", \ + bold yellow => syntax "#222f14" + zero-style = syntax + whitespace-error-style = "#aaaaaa" + +[delta "zebra-light"] + minus-style = syntax "#fbdada" + minus-emph-style = syntax "#f6b6b6" + plus-style = syntax "#d6ffd6" + plus-emph-style = syntax "#adffad" + map-styles = \ + bold purple => syntax "#feecf7", \ + bold blue => syntax "#e5dff6", \ + bold cyan => syntax "#d8fdf6", \ + bold yellow => syntax "#f4ffe0" + zero-style = syntax + whitespace-error-style = "#aaaaaa" + +[delta "chameleon"] + #author: https://github.com/AirOnSkin + dark = true + line-numbers = true + side-by-side = true + keep-plus-minus-markers = false + syntax-theme = Nord + file-style = "#434C5E" bold + file-decoration-style = "#434C5E" ul + file-added-label = [+] + file-copied-label = [==] + file-modified-label = [*] + file-removed-label = [-] + file-renamed-label = [->] + hunk-header-style = omit + line-numbers-left-format = " {nm:>3} │" + line-numbers-left-style = red + line-numbers-right-format = " {np:>3} │" + line-numbers-right-style = green + line-numbers-minus-style = red italic black + line-numbers-plus-style = green italic black + line-numbers-zero-style = "#434C5E" italic + minus-style = bold red + minus-emph-style = bold "#202020" "#FF5555" + minus-non-emph-style = bold + plus-style = bold green + plus-emph-style = bold "#202020" "#50FA7B" + plus-non-emph-style = bold + zero-style = syntax + blame-code-style = syntax + blame-format = "{author:<18} ({commit:>9}) {timestamp:^16}" + blame-palette = "#2E3440" "#3B4252" "#434C5E" "#4C566A" + merge-conflict-begin-symbol = ~ + merge-conflict-end-symbol = ~ + merge-conflict-ours-diff-header-style = "#F1FA8C" bold + merge-conflict-ours-diff-header-decoration-style = "#434C5E" box + merge-conflict-theirs-diff-header-style = "#F1FA8C" bold + merge-conflict-theirs-diff-header-decoration-style = "#434C5E" box + +[delta "gruvmax-fang"] + # author: https://github.com/maxfangx + # General appearance + dark = true + syntax-theme = gruvbox-dark + # File + file-style = "#FFFFFF" bold + file-added-label = [+] + file-copied-label = [==] + file-modified-label = [*] + file-removed-label = [-] + file-renamed-label = [->] + file-decoration-style = "#434C5E" ul + file-decoration-style = "#84786A" ul + # No hunk headers + hunk-header-style = omit + # Line numbers + line-numbers = true + line-numbers-left-style = "#84786A" + line-numbers-right-style = "#84786A" + line-numbers-minus-style = "#A02A11" + line-numbers-plus-style = "#479B36" + line-numbers-zero-style = "#84786A" + line-numbers-left-format = " {nm:>3} │" + line-numbers-right-format = " {np:>3} │" + # Diff contents + inline-hint-style = syntax + minus-style = syntax "#330011" + minus-emph-style = syntax "#80002a" + minus-non-emph-style = syntax auto + plus-style = syntax "#001a00" + plus-emph-style = syntax "#003300" + plus-non-emph-style = syntax auto + whitespace-error-style = "#FB4934" reverse + # Commit hash + commit-decoration-style = normal box + commit-style = "#ffffff" bold + # Blame + blame-code-style = syntax + blame-format = "{author:>18} ({commit:>8}) {timestamp:<13} " + blame-palette = "#000000" "#1d2021" "#282828" "#3c3836" + # Merge conflicts + merge-conflict-begin-symbol = ⌃ + merge-conflict-end-symbol = ⌄ + merge-conflict-ours-diff-header-style = "#FABD2F" bold + merge-conflict-theirs-diff-header-style = "#FABD2F" bold overline + merge-conflict-ours-diff-header-decoration-style = '' + merge-conflict-theirs-diff-header-decoration-style = '' + +[delta "discord"] + commit-style = omit + file-style = 34 ul + file-decoration-style = none + hunk-header-style = omit + minus-style = 31 + minus-non-emph-style = 31 + minus-emph-style = 40 + minus-empty-line-marker-style = 31 + zero-style = 30 + plus-style = 32 + plus-non-emph-style = 32 + plus-emph-style = 40 + grep-file-style = 34 + grep-line-number-style = 34 + whitespace-error-style = 41 + blame-code-style = omit + true-color = never + file-modified-label = changed: + right-arrow = > + hyperlinks = false + keep-plus-minus-markers = true + diff-stat-align-width = 10 + syntax-theme = none + width = variable + +[delta "mellow-barbet"] + # author: https://github.com/kvrohit + # To configure terminal colors see https://github.com/kvrohit/mellow.nvim#terminals + dark = true + syntax-theme = base16 + line-numbers = true + side-by-side = true + file-style = brightwhite + file-decoration-style = none + file-added-label = [+] + file-copied-label = [==] + file-modified-label = [*] + file-removed-label = [-] + file-renamed-label = [->] + hunk-header-decoration-style = "#3e3e43" box ul + plus-style = brightgreen black + plus-emph-style = black green + minus-style = brightred black + minus-emph-style = black red + line-numbers-minus-style = brightred + line-numbers-plus-style = brightgreen + line-numbers-left-style = "#3e3e43" + line-numbers-right-style = "#3e3e43" + line-numbers-zero-style = "#57575f" + zero-style = syntax + whitespace-error-style = black bold + blame-code-style = syntax + blame-palette = "#161617" "#1b1b1d" "#2a2a2d" "#3e3e43" + merge-conflict-begin-symbol = ~ + merge-conflict-end-symbol = ~ + merge-conflict-ours-diff-header-style = yellow bold + merge-conflict-ours-diff-header-decoration-style = "#3e3e43" box + merge-conflict-theirs-diff-header-style = yellow bold + merge-conflict-theirs-diff-header-decoration-style = "#3e3e43" box + +[delta "arctic-fox"] + # author: https://github.com/anthony-halim + dark = true + syntax-theme = Nord + file-added-label = [+] + file-copied-label = [==] + file-modified-label = [*] + file-removed-label = [-] + file-renamed-label = [->] + file-style = omit + hunk-header-decoration-style = "#5E81AC" ul + hunk-header-file-style = blue ul bold + hunk-header-line-number-style = yellow box bold + hunk-header-style = file line-number syntax bold italic + plus-style = brightgreen + plus-emph-style = black green + minus-style = brightred + minus-emph-style = black red + line-numbers = true + line-numbers-minus-style = brightred + line-numbers-plus-style = brightgreen + line-numbers-left-style = "#5E81AC" + line-numbers-right-style = "#5E81AC" + line-numbers-zero-style = "#4C566A" + zero-style = syntax + whitespace-error-style = black bold + blame-code-style = syntax + blame-format = "{author:<18} {commit:<6} {timestamp:<15}" + blame-palette = "#2E3440" "#3B4252" "#434C5E" + merge-conflict-begin-symbol = ~ + merge-conflict-end-symbol = ~ + merge-conflict-ours-diff-header-style = yellow bold + merge-conflict-ours-diff-header-decoration-style = "#5E81AC" box + merge-conflict-theirs-diff-header-style = yellow bold + merge-conflict-theirs-diff-header-decoration-style = "#5E81AC" box + +[delta "corvus"] + # author: https://github.com/evilwaveforms + dark = true + commit-style = "#949494" + file-style = omit + syntax-theme = none + hunk-header-decoration-style = "#949494" ul + hunk-header-file-style = "#949494" + hunk-header-style = "#949494" + line-numbers = true + line-numbers-left-style = "#949494" + line-numbers-right-style = "#949494" + line-numbers-left-format = "{nm:>2}|" + line-numbers-right-format = "{np:>3} " + line-numbers-plus-style = "#54c047" + line-numbers-minus-style = bold "#591102" + plus-style = "#54c047" + plus-emph-style = bold "#54c047" + plus-non-emph-style = dim "#54c047" + minus-style = normal "#591102" + minus-emph-style = normal "#591102" + minus-non-emph-style = bold "#591102" + blame-code-style = omit + blame-format = "{author:<18} {commit:<6} {timestamp:<15}" + blame-palette = "#000000" "#343434" + zero-style = dim + +[delta "platypus"] + # Author: https://github.com/sarpuser + dark = true + side-by-side = true + true-color = always + file-added-label = [+] + file-copied-label = [==] + file-modified-label = [M] + file-removed-label = [-] + file-renamed-label = [->] + file-style = "#ff9b00" ul bold + file-decoration-style = "#ea00ff" box ul + zero-style = syntax + syntax-theme = Solarized (dark) + commit-decoration-style ="#ea00ff" ul + hunk-header-decoration-style = omit + hunk-header-file-style = "#ff9b00" ul bold + hunk-header-line-number-style = "#ffd21a" bold + hunk-header-style = line-number syntax bold italic + line-numbers = true + line-numbers-left-format = "{nm:>1}|" + line-numbers-left-style = "#ea00ff" + line-numbers-minus-style = "#ff0051" bold + line-numbers-plus-style = "#1ac71e" bold + line-numbers-right-format = "{np:>1}|" + line-numbers-right-style = "#ea00ff" + line-numbers-zero-style = "#aaaaaa" italic + minus-emph-style = syntax bold "#b80000" + minus-style = syntax "#5d001e" + plus-emph-style = syntax bold "#1a861a" + plus-style = syntax "#2a5e37" + whitespace-error-style = "#280050" + wrap-max-lines = unlimited + wrap-right-percent = 1 + +[delta "weeping-willow"] + # See 'mirthful-willow' for light mode version. + # Heavily inspired by the themes above. Thank you. <3 + # author: https://github.com/lvdh + # colors: `pastel list` + dark = true + right-arrow = > + syntax-theme = Coldark-Dark + # -- git + blame-format = " ({commit:>7}) {author:<18} {timestamp:^13} " + blame-palette = "normal midnightblue indigo navy darkblue darkslateblue purple rebeccapurple darkviolet" + commit-decoration-style = none + commit-style = bold black orange + # -- grep + grep-file-style = olive + grep-line-number-style = olive + # -- diff + keep-plus-minus-markers = true + line-numbers = true + line-numbers-minus-style = red + line-numbers-plus-style = green + line-numbers-left-style = gray + line-numbers-left-format = "{nm:>1}┊" + line-numbers-right-style = gray + line-numbers-right-format = "{np:>1}┊" + line-numbers-zero-style = gray + minus-emph-style = ghostwhite crimson underline + minus-empty-line-marker-style = syntax maroon + minus-style = syntax darkred + plus-emph-style = ghostwhite olivedrab underline + plus-empty-line-marker-style = syntax seagreen + plus-style = syntax darkgreen + whitespace-error-style = black ghostwhite + zero-style = syntax + # -- decorations + file-decoration-style = olive overline + file-added-label = [+] + file-copied-label = [*] + file-modified-label = [~] + file-removed-label = [-] + file-renamed-label = [>] + file-style = orange bold + hunk-header-decoration-style = none + hunk-header-file-style = olive + hunk-header-line-number-style = orange + hunk-header-style = file line-number orchid + +[delta "mirthful-willow"] + # See 'weeping-willow' for dark mode version. + # Heavily inspired by the themes above. Thank you. <3 + # author: https://github.com/lvdh + # colors: `pastel list` + light = true + right-arrow = > + syntax-theme = Coldark-Cold + # -- git + blame-format = " ({commit:>7}) {author:<18} {timestamp:^13} " + blame-palette = "powderblue papayawhip thistle skyblue lavender plum paleturquoise lightcyan violet" + commit-decoration-style = none + commit-style = bold black orange + # -- grep + grep-file-style = darkgoldenrod + grep-line-number-style = darkgoldenrod + # -- diff + keep-plus-minus-markers = true + line-numbers = true + line-numbers-minus-style = red + line-numbers-plus-style = green + line-numbers-left-style = gray + line-numbers-left-format = "{nm:>1}┊" + line-numbers-right-style = gray + line-numbers-right-format = "{np:>1}┊" + line-numbers-zero-style = gray + minus-emph-style = ghostwhite tomato underline + minus-empty-line-marker-style = syntax orangered + minus-style = syntax lightpink + plus-emph-style = ghostwhite limegreen underline + plus-empty-line-marker-style = syntax forestgreen + plus-style = syntax lightgreen + whitespace-error-style = black ghostwhite + zero-style = syntax + # -- decorations + file-decoration-style = darkgoldenrod overline + file-added-label = [+] + file-copied-label = [*] + file-modified-label = [~] + file-removed-label = [-] + file-renamed-label = [>] + file-style = orange bold + hunk-header-decoration-style = none + hunk-header-file-style = darkgoldenrod + hunk-header-line-number-style = orange + hunk-header-style = file line-number darkviolet diff --git a/.config/mimeapps.list b/.config/mimeapps.list @@ -0,0 +1,20 @@ +[Default Applications] + +# xdg-open will use these settings to determine how to open filetypes. +# These .desktop entries can also be seen and changed in ~/.local/share/applications/ + +text/x-shellscript=text.desktop; +x-scheme-handler/magnet=torrent.desktop; +application/x-bittorrent=torrent.desktop; +x-scheme-handler/mailto=mail.desktop; +text/plain=text.desktop; +application/postscript=pdf.desktop; +application/pdf=pdf.desktop; +image/png=img.desktop; +image/jpeg=img.desktop; +image/gif=img.desktop; +application/rss+xml=rss.desktop +video/x-matroska=video.desktop +video/mp4=video.desktop +x-scheme-handler/lbry=lbry.desktop +inode/directory=file.desktop diff --git a/.config/mpd/mpd.conf b/.config/mpd/mpd.conf @@ -0,0 +1,19 @@ +music_directory "/mnt/ssd/music" +playlist_directory "~/.config/mpd/playlists" + +auto_update "yes" +bind_to_address "127.0.0.1" +restore_paused "yes" +max_output_buffer_size "16384" + +audio_output { + type "pipewire" + name "PipeWire Sound Server" +} + +audio_output { + type "fifo" + name "Visualizer feed" + path "/tmp/mpd.fifo" + format "44100:16:2" +} diff --git a/.config/mpv/input.conf b/.config/mpv/input.conf @@ -0,0 +1,5 @@ +l seek 5 +h seek -5 +j seek -60 +k seek 60 +S cycle sub diff --git a/.config/mpv/mpv.conf b/.config/mpv/mpv.conf @@ -0,0 +1,15 @@ +# uosc provides seeking & volume indicators (via flash-timeline and flash-volume commands) +# if you decide to use them, you don't need osd-bar +osd-bar=no + +# uosc will draw its own window controls and border if you disable window border +border=no + +profile=high-quality +vo=gpu-next +gpu-api=vulkan +gpu-context=waylandvk +hwdec=auto +target-colorspace-hint=yes +alang=eng +slang= diff --git a/.config/newsraft/config b/.config/newsraft/config @@ -0,0 +1,11 @@ +open-in-browser-command linkhandler "%l" +#bind t exec qndl "%l" +#bind a exec "tsp yt-dlp --embed-metadata -xic -f bestaudio/best --restrict-filenames" +unbind d +unbind a +unbind A +unbind v +bind a mark-read; jump-to-next +bind A mark-unread; jump-to-next +bind v exec setsid -f mpv "%l" +bind d exec setsid -f dmenuhandler "%l" diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua @@ -0,0 +1,19 @@ +-- Bootstrap lazy.nvim +local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" +if not (vim.uv or vim.loop).fs_stat(lazypath) then + local lazyrepo = "https://github.com/folke/lazy.nvim.git" + local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) + if vim.v.shell_error ~= 0 then + vim.api.nvim_echo({ + { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, + { out, "WarningMsg" }, + { "\nPress any key to exit..." }, + }, true, {}) + vim.fn.getchar() + os.exit(1) + end +end +vim.opt.rtp:prepend(lazypath) + +require("options") +require("lazy").setup("plugins") diff --git a/.config/nvim/lua/options.lua b/.config/nvim/lua/options.lua @@ -0,0 +1,17 @@ +vim.o.number = true +vim.o.relativenumber = true +vim.o.wrap = false +vim.o.tabstop = 2 +vim.o.shiftwidth = 2 +vim.o.softtabstop = 2 +vim.o.expandtab = true +vim.o.swapfile = false +vim.g.mapleader = " " +vim.o.clipboard = "unnamedplus" +vim.o.undofile = true +vim.o.cursorline = true +vim.o.cursorlineopt = "number" +vim.o.title = true +vim.o.signcolumn = "yes" +vim.g.mapleader = " " +vim.api.nvim_set_keymap('n', ',c', ':w! | !compiler "%:p"<CR>', { noremap = true, silent = true }) diff --git a/.config/nvim/lua/plugins.lua b/.config/nvim/lua/plugins.lua @@ -0,0 +1 @@ +return {} diff --git a/.config/nvim/lua/plugins/comment.lua b/.config/nvim/lua/plugins/comment.lua @@ -0,0 +1,8 @@ +return { + { + "numToStr/Comment.nvim", + config = function() + require('Comment').setup() + end + } +} diff --git a/.config/nvim/lua/plugins/completions.lua b/.config/nvim/lua/plugins/completions.lua @@ -0,0 +1,44 @@ +return { + { + "hrsh7th/cmp-nvim-lsp" + }, + { + "L3MON4D3/LuaSnip", + dependencies = { + "saadparwaiz1/cmp_luasnip", + "rafamadriz/friendly-snippets", + }, + }, + { + "hrsh7th/nvim-cmp", + config = function() + local cmp = require("cmp") + require("luasnip.loaders.from_vscode").lazy_load() + + cmp.setup({ + snippet = { + expand = function(args) + require("luasnip").lsp_expand(args.body) + end, + }, + window = { + -- completion = cmp.config.window.bordered(), + -- documentation = cmp.config.window.bordered(), + }, + mapping = cmp.mapping.preset.insert({ + ["<C-b>"] = cmp.mapping.scroll_docs(-4), + ["<C-f>"] = cmp.mapping.scroll_docs(4), + ["<C-Space>"] = cmp.mapping.complete(), + ["<C-e>"] = cmp.mapping.abort(), + ["<CR>"] = cmp.mapping.confirm({ select = true }), + }), + sources = cmp.config.sources({ + { name = "nvim_lsp" }, + { name = "luasnip" }, -- For luasnip users. + }, { + { name = "buffer" }, + }), + }) + end, + }, +} diff --git a/.config/nvim/lua/plugins/explorer.lua b/.config/nvim/lua/plugins/explorer.lua @@ -0,0 +1,27 @@ +--[[ return { + "nvim-neo-tree/neo-tree.nvim", + branch = "v3.x", + dependencies = { + "nvim-lua/plenary.nvim", + "nvim-mini/mini.icons", + "MunifTanjim/nui.nvim", + }, + config = function() + vim.keymap.set("n", "<C-n>", ":Neotree filesystem reveal left<CR>", {}) + vim.keymap.set("n", "<leader>bf", ":Neotree buffers reveal float<CR>", {}) + require("mini.icons").mock_nvim_web_devicons() + end, +} ]] + +return { + { + 'stevearc/oil.nvim', + ---@module 'oil' + ---@type oil.SetupOpts + opts = {}, + dependencies = { { "nvim-mini/mini.icons", opts = {} } }, + lazy = false, + vim.keymap.set("n", "<leader>e", "<cmd>Oil<CR>", {}), + -- vim.keymap.set("n", "<leader>bf", "<cmd>Oil --float<CR>", {}) + } +} diff --git a/.config/nvim/lua/plugins/gitsigns.lua b/.config/nvim/lua/plugins/gitsigns.lua @@ -0,0 +1,11 @@ +return { + { + "tpope/vim-fugitive", + }, + { + "lewis6991/gitsigns.nvim", + config = function() + require("gitsigns").setup() + end, + } +} diff --git a/.config/nvim/lua/plugins/indent.lua b/.config/nvim/lua/plugins/indent.lua @@ -0,0 +1,8 @@ +return { + { + "lukas-reineke/indent-blankline.nvim", + config = function() + require("ibl").setup() + end, + } +} diff --git a/.config/nvim/lua/plugins/lsp.lua b/.config/nvim/lua/plugins/lsp.lua @@ -0,0 +1,57 @@ +return { + { + "williamboman/mason.nvim", + lazy = false, + config = function() + require("mason").setup() + end, + }, + { + "williamboman/mason-lspconfig.nvim", + lazy = false, + opts = { + auto_install = true, + ensure_installed = { "helm_ls", "dockerls" }, + }, + }, + { + "neovim/nvim-lspconfig", + lazy = false, + config = function() + local capabilities = require('cmp_nvim_lsp').default_capabilities() + + vim.lsp.enable({ + "lua_ls", "clangd", "rust_analyzer", "bashls", + "html", "cssls", "jsonls", "helm_ls", "yamlls", + "dockerfile-language-server", "tinymist", "gopls", + "pylsp" + }) + + vim.lsp.config("*", { + capabilities = capabilities + }) + + vim.diagnostic.config({ + virtual_text = { + prefix = '', + spacing = 4, + }, + signs = { + text = { + [vim.diagnostic.severity.ERROR] = '', + [vim.diagnostic.severity.WARN] = '', + }, + }, + underline = true, + update_in_insert = true, + severity_sort = true, + }) + + vim.keymap.set('n', '<leader>lf', vim.lsp.buf.format) + -- vim.keymap.set("n", "K", vim.lsp.buf.hover, {}) + -- vim.keymap.set("n", "<leader>gd", vim.lsp.buf.definition, {}) + -- vim.keymap.set("n", "<leader>gr", vim.lsp.buf.references, {}) + -- vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, {}) + end, + }, +} diff --git a/.config/nvim/lua/plugins/lualine.lua b/.config/nvim/lua/plugins/lualine.lua @@ -0,0 +1,10 @@ +return { + { + 'nvim-lualine/lualine.nvim', + dependencies = { 'nvim-mini/mini.icons' }, + config = function() + require('lualine').setup() + require("mini.icons").mock_nvim_web_devicons() + end, + } +} diff --git a/.config/nvim/lua/plugins/markdown.lua b/.config/nvim/lua/plugins/markdown.lua @@ -0,0 +1,10 @@ +return { + { + "MeanderingProgrammer/render-markdown.nvim", + -- dependencies = { 'nvim-treesitter/nvim-treesitter', 'nvim-mini/mini.nvim' }, -- if you use the mini.nvim suite + dependencies = { 'nvim-treesitter/nvim-treesitter', 'nvim-mini/mini.icons' }, -- if you use standalone mini plugins + ---@module 'render-markdown' + ---@type render.md.UserConfig + opts = {} + } +} diff --git a/.config/nvim/lua/plugins/telescope.lua b/.config/nvim/lua/plugins/telescope.lua @@ -0,0 +1,25 @@ +return { + { + "nvim-telescope/telescope-ui-select.nvim", + }, + { + "nvim-telescope/telescope.nvim", + tag = "0.1.9", + dependencies = { "nvim-lua/plenary.nvim" }, + config = function() + require("telescope").setup({ + extensions = { + ["ui-select"] = { + require("telescope.themes").get_dropdown({}), + }, + }, + }) + local builtin = require("telescope.builtin") + vim.keymap.set("n", "<C-p>", builtin.find_files, {}) + vim.keymap.set("n", "<leader>fg", builtin.live_grep, {}) + vim.keymap.set("n", "<leader><leader>", builtin.oldfiles, {}) + + require("telescope").load_extension("ui-select") + end, + }, +} diff --git a/.config/nvim/lua/plugins/theme.lua b/.config/nvim/lua/plugins/theme.lua @@ -0,0 +1,7 @@ +return { + "bjarneo/pixel.nvim", + priority = 1000, + config = function() + vim.cmd.colorscheme("pixel") + end, +} diff --git a/.config/nvim/lua/plugins/treesitter.lua b/.config/nvim/lua/plugins/treesitter.lua @@ -0,0 +1,14 @@ +return { + { + "nvim-treesitter/nvim-treesitter", + build = ":TSUpdate", + config = function() + local config = require("nvim-treesitter.configs") + config.setup({ + auto_install = true, + highlight = { enable = true }, + indent = { enable = true }, + }) + end + } +} diff --git a/.config/nvim/lua/plugins/trim.lua b/.config/nvim/lua/plugins/trim.lua @@ -0,0 +1,8 @@ +return { + { + "cappyzawa/trim.nvim", + config = function() + require('trim').setup() + end + } +} diff --git a/.config/paru/paru.conf b/.config/paru/paru.conf @@ -0,0 +1,3 @@ +[bin] +Sudo = doas +FileManager = nvim diff --git a/.config/pipewire/pipewire.conf.d/user-session.conf b/.config/pipewire/pipewire.conf.d/user-session.conf @@ -0,0 +1,9 @@ +context.exec = [ + { path = "/usr/bin/wireplumber" args = "" condition = [ { exec.session-manager = null } { exec.session-manager = true } ] } + { path = "/usr/bin/pipewire" args = "-c pipewire-pulse.conf" condition = [ { exec.pipewire-pulse = null } { exec.pipewire-pulse = true } ] } +] + +context.properties = { + default.clock.rate = 192000 + default.clock.allowed-rates = [ 44100 48000 96000 192000 ] +} diff --git a/.config/qt5ct/qt5ct.conf b/.config/qt5ct/qt5ct.conf @@ -0,0 +1,32 @@ +[Appearance] +color_scheme_path=/usr/share/qt5ct/colors/darker.conf +custom_palette=true +icon_theme=breeze-dark +standard_dialogs=xdgdesktopportal +style=Fusion + +[Fonts] +fixed="FreeMono,12,-1,5,50,0,0,0,0,0,Regular" +general="FreeSans,12,-1,5,50,0,0,0,0,0,Regular" + +[Interface] +activate_item_on_single_click=1 +buttonbox_layout=0 +cursor_flash_time=1000 +dialog_buttons_have_icons=1 +double_click_interval=400 +gui_effects=@Invalid() +keyboard_scheme=2 +menus_have_icons=true +show_shortcuts_in_context_menus=true +stylesheets=@Invalid() +toolbutton_style=4 +underline_shortcut=1 +wheel_scroll_lines=3 + +[SettingsWindow] +geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\n\0\0\0\0\0\0\0\xe\xfb\0\0\x5s\0\0\n\0\0\0\0\0\0\0\xe\xff\0\0\x2\xf3\0\0\0\0\x2\0\0\0\n\0\0\0\n\0\0\0\0\0\0\0\xe\xfb\0\0\x5s) + +[Troubleshooting] +force_raster_widgets=1 +ignored_applications=@Invalid() diff --git a/.config/qt6ct/qt6ct.conf b/.config/qt6ct/qt6ct.conf @@ -0,0 +1,32 @@ +[Appearance] +color_scheme_path=/usr/share/qt6ct/colors/darker.conf +custom_palette=true +icon_theme=breeze-dark +standard_dialogs=xdgdesktopportal +style=Fusion + +[Fonts] +fixed="FreeMono,12,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular" +general="FreeSans,12,-1,5,400,0,0,0,0,0,0,0,0,0,0,1,Regular" + +[Interface] +activate_item_on_single_click=1 +buttonbox_layout=0 +cursor_flash_time=1000 +dialog_buttons_have_icons=1 +double_click_interval=400 +gui_effects=@Invalid() +keyboard_scheme=2 +menus_have_icons=true +show_shortcuts_in_context_menus=true +stylesheets=@Invalid() +toolbutton_style=4 +underline_shortcut=1 +wheel_scroll_lines=3 + +[SettingsWindow] +geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\n\0\0\0\0\0\0\0\xe\xfb\0\0\x5s\0\0\n\0\0\0\0\0\0\0\xe\xfb\0\0\x5s\0\0\0\0\0\0\0\0\n\0\0\0\n\0\0\0\0\0\0\0\xe\xfb\0\0\x5s) + +[Troubleshooting] +force_raster_widgets=1 +ignored_applications=@Invalid() diff --git a/.config/rmpc/config.ron b/.config/rmpc/config.ron @@ -0,0 +1,201 @@ +#![enable(implicit_some)] +#![enable(unwrap_newtypes)] +#![enable(unwrap_variant_newtypes)] +( + address: "127.0.0.1:6600", + volume_step: 5, + scrolloff: 2, + max_fps: 60, + wrap_navigation: true, + theme: "def", + lyrics_dir: "/mnt/ssd/music", + on_song_change: ["~/.config/rmpc/scripts/statusbar"], + on_resize: None, + status_update_interval_ms: 1000, + enable_mouse: true, + enable_config_hot_reload: true, + album_art: ( + method: Auto, + max_size_px: (width: 0, height: 0), + vertical_align: Top, + horizontal_align: Center, + ), + keybinds: ( + global: { + ":": CommandMode, + ",": VolumeDown, + "s": Stop, + ".": VolumeUp, + "<Tab>": NextTab, + "<S-Tab>": PreviousTab, + "1": SwitchToTab("Queue"), + // "2": SwitchToTab("Directories"), + "2": SwitchToTab("Artists"), + "3": SwitchToTab("Albums"), + "4": SwitchToTab("Genres"), + "5": SwitchToTab("Playlists"), + "6": SwitchToTab("Search"), + "q": Quit, + ">": NextTrack, + "p": TogglePause, + "<": PreviousTrack, + "f": SeekForward, + "z": ToggleRepeat, + "x": ToggleRandom, + "c": ToggleConsume, + "v": ToggleSingle, + "b": SeekBack, + "~": ShowHelp, + "I": ShowCurrentSongInfo, + "O": ShowOutputs, + "P": ShowDecoders, + }, + navigation: { + "k": Up, + "j": Down, + "h": Left, + "l": Right, + "<Up>": Up, + "<Down>": Down, + "<Left>": Left, + "<Right>": Right, + "<C-k>": PaneUp, + "<C-j>": PaneDown, + "<C-h>": PaneLeft, + "<C-l>": PaneRight, + "<C-u>": UpHalf, + "N": PreviousResult, + "a": Add, + "A": AddAll, + "r": Rename, + "n": NextResult, + "g": Top, + "<Space>": Select, + "<C-Space>": InvertSelection, + "G": Bottom, + "<CR>": Confirm, + "i": FocusInput, + "J": MoveDown, + "<C-d>": DownHalf, + "/": EnterSearch, + "<C-c>": Close, + "<Esc>": Close, + "K": MoveUp, + "D": Delete, + }, + queue: { + "D": DeleteAll, + "<CR>": Play, + "<C-s>": Save, + "a": AddToPlaylist, + "d": Delete, + "i": ShowInfo, + "C": JumpToCurrent, + }, + ), + search: ( + case_sensitive: false, + mode: Contains, + tags: [ + (value: "any", label: "Any Tag"), + (value: "artist", label: "Artist"), + (value: "album", label: "Album"), + (value: "albumartist", label: "Album Artist"), + (value: "title", label: "Title"), + ], + ), + tabs: [ + ( + name: "Queue", + pane: Split( + direction: Vertical, + panes: [ + ( + size: "100%", + borders: "NONE", + pane: Split( + borders: "NONE", + direction: Horizontal, + panes: [ + ( + size: "70%", + borders: "ALL", + pane: Pane(Queue), + ), + ( + size: "30%", + borders: "ALL", + pane: Split( + direction: Vertical, + panes: [ + ( + size: "75%", + borders: "NONE", + pane: Pane(AlbumArt), + ), + ( + size: "35%", + borders: "NONE", + pane: Split( + direction: Vertical, + panes: [ + ( + size: "100%", + pane: Pane(Lyrics), + ), + ] + ), + ), + ] + ), + ), + ] + ), + ), + ], + ), + ), + // ( + // name: "Directories", + // pane: Split( + // direction: Horizontal, + // panes: [(size: "100%", borders: "ALL", pane: Pane(Directories))], + // ), + // ), + ( + name: "Artists", + pane: Split( + direction: Horizontal, + panes: [(size: "100%", borders: "ALL", pane: Pane(Artists))], + ), + ), + ( + name: "Albums", + pane: Split( + direction: Horizontal, + panes: [(size: "100%", borders: "ALL", pane: Pane(Albums))], + ), + ), + ( + name: "Genres", + pane: Split( + direction: Horizontal, + panes: [(size: "100%", borders: "ALL", pane: Pane(Browser(root_tag: "genre", separator: ";")))], + ), + ), + ( + name: "Playlists", + pane: Split( + direction: Horizontal, + panes: [(size: "100%", borders: "ALL", pane: Pane(Playlists))], + ), + ), + ( + name: "Search", + pane: Split( + direction: Horizontal, + panes: [(size: "100%", borders: "ALL", pane: Pane(Search))], + ), + ), + ], +) diff --git a/.config/rmpc/scripts/statusbar b/.config/rmpc/scripts/statusbar @@ -0,0 +1,2 @@ +#!/bin/sh +/usr/bin/pkill -RTMIN+11 i3blocks diff --git a/.config/rmpc/themes/def.ron b/.config/rmpc/themes/def.ron @@ -0,0 +1,193 @@ +#![enable(implicit_some)] +#![enable(unwrap_newtypes)] +#![enable(unwrap_variant_newtypes)] +( + default_album_art_path: None, + show_song_table_header: true, + draw_borders: true, + browser_column_widths: [20, 38, 42], + background_color: None, + modal_backdrop: true, + text_color: "white", + header_background_color: None, + modal_background_color: None, + preview_label_style: (fg: "#b48ead"), + preview_metadata_group_style: (fg: "#88c0d0"), + tab_bar: ( + enabled: true, + active_style: (fg: "black", bg: "yellow", modifiers: "Bold"), + inactive_style: (fg: "white", bg: None, modifiers: ""), + ), + highlighted_item_style: (fg: "magenta", modifiers: "Bold"), + current_item_style: (fg: "black", bg: "yellow", modifiers: "Bold"), + borders_style: (fg: "yellow", modifiers: "Bold"), + highlight_border_style: (fg: "yellow"), + symbols: (song: "󰝚 ", dir: " ", playlist: "󰲸 ", marker: "* ", ellipsis: "..."), + progress_bar: ( + symbols: ["█", "█", "█", "█", "█"], + track_style: (fg: "dark_gray"), + elapsed_style: (fg: "yellow"), + thumb_style: (fg: "yellow"), + ), + scrollbar: ( + symbols: ["│", "█", "▲", "▼"], + track_style: (fg: "yellow"), + ends_style: (fg: "light_yellow"), + thumb_style: (fg: "light_yellow"), + ), + song_table_format: [ + ( + prop: (kind: Property(Artist), style: (fg: "green"), + default: (kind: Text("Unknown"), style: (fg: "green")) + ), + width: "20%", + ), + ( + prop: (kind: Property(Title), style: (fg: "white"), + highlighted_item_style: (fg: "white", modifiers: "Bold"), + default: (kind: Property(Filename), style: (fg: "white"),) + ), + width: "35%", + ), + ( + prop: (kind: Property(Album), style: (fg: "blue"), + default: (kind: Text("Unknown Album"), style: (fg: "gray")) + ), + width: "30%", + ), + ( + prop: (kind: Property(Duration), style: (fg: "magenta"), + default: (kind: Text("-")) + ), + width: "15%", + alignment: Right, + ), + ], + layout: Split( + direction: Vertical, + panes: [ + ( + size: "3", + pane: Pane(Tabs), + ), + ( + size: "5", + pane: Split( + direction: Horizontal, + panes: [ + ( + size: "100%", + borders: "ALL", + pane: Split( + direction: Vertical, + panes: [ + ( + size: "4", + borders: "NONE", + pane: Pane(Header), + ), + ( + size: "1", + borders: "NONE", + pane: Pane(ProgressBar), + ), + ] + ) + ), + ] + ), + ), + ( + size: "100%", + pane: Split( + direction: Horizontal, + panes: [ + ( + size: "100%", + borders: "NONE", + pane: Pane(TabContent), + ), + ] + ), + ), + ], + ), + header: ( + rows: [ + ( + left: [ + (kind: Text(""), style: (fg: "light_yellow", modifiers: "Bold")), + (kind: Property(Status(StateV2(playing_label: "  ", paused_label: "  ", stopped_label: "  ")))), + (kind: Text(" "), style: (fg: "light_yellow", modifiers: "Bold")), + (kind: Property(Widget(ScanStatus))) + ], + center: [ + (kind: Property(Song(Title)), style: (fg: "white",modifiers: "Bold"), + default: (kind: Property(Song(Filename)), style: (fg: "white",modifiers: "Bold")) + ) + ], + right: [ + (kind: Text("󱡬"), style: (fg: "light_yellow", modifiers: "Bold")), + (kind: Property(Status(Volume)), style: (fg: "white", modifiers: "Bold")), + (kind: Text("%"), style: (fg: "light_yellow", modifiers: "Bold")) + ] + ), + ( + left: [ + (kind: Text("[ "),style: (fg: "light_yellow", modifiers: "Bold")), + (kind: Property(Status(Elapsed)),style: (fg: "white")), + (kind: Text(" / "),style: (fg: "yellow", modifiers: "Bold")), + (kind: Property(Status(Duration)),style: (fg: "white")), + (kind: Text(" | "),style: (fg: "yellow")), + (kind: Property(Status(Bitrate)),style: (fg: "white")), + (kind: Text(" kbps"),style: (fg: "yellow")), + (kind: Text("]"),style: (fg: "light_yellow", modifiers: "Bold")) + ], + center: [ + (kind: Property(Song(Artist)), style: (fg: "white", modifiers: "Bold"), + default: (kind: Text("Unknown Artist"), style: (fg: "white", modifiers: "Bold")) + ), + (kind: Text(" - ")), + (kind: Property(Song(Album)),style: (fg: "white" ), + default: (kind: Text("Unknown Album"), style: (fg: "white", modifiers: "Bold")) + ) + ], + right: [ + (kind: Text("[ "),style: (fg: "light_yellow")), + (kind: Property(Status(RepeatV2( + on_label: "", off_label: "", + on_style: (fg: "white", modifiers: "Bold"), off_style: (fg: "dark_gray", modifiers: "Bold"))))), + (kind: Text(" | "),style: (fg: "yellow")), + (kind: Property(Status(RandomV2( + on_label: "", off_label: "", + on_style: (fg: "white", modifiers: "Bold"), off_style: (fg: "dark_gray", modifiers: "Bold"))))), + (kind: Text(" | "),style: (fg: "yellow")), + (kind: Property(Status(ConsumeV2( + on_label: "󰮯", off_label: "󰮯", oneshot_label: "󰮯󰇊", + on_style: (fg: "white", modifiers: "Bold"), off_style: (fg: "dark_gray", modifiers: "Bold"))))), + (kind: Text(" | "),style: (fg: "yellow")), + (kind: Property(Status(SingleV2( + on_label: "󰎤", off_label: "󰎦", oneshot_label: "󰇊", off_oneshot_label: "󱅊", + on_style: (fg: "white", modifiers: "Bold"), off_style: (fg: "dark_gray", modifiers: "Bold"))))), + (kind: Text(" ]"),style: (fg: "light_yellow")), + ] + ), + ], + ), + browser_song_format: [ + ( + kind: Group([ + (kind: Property(Track)), + (kind: Text(" ")), + ]) + ), + ( + kind: Group([ + (kind: Property(Artist)), + (kind: Text(" - ")), + (kind: Property(Title)), + ]), + default: (kind: Property(Filename)) + ), + ], +) diff --git a/.config/shell/aliasrc b/.config/shell/aliasrc @@ -0,0 +1,75 @@ +#!/bin/sh + +# Use neovim for vim if present. +[ -x "$(command -v nvim)" ] && alias vim="nvim" vimdiff="nvim -d" + +[ -f "$MBSYNCRC" ] && alias mbsync="mbsync -c $MBSYNCRC" + +# doas not required for some system commands +for command in mount umount dinitctl pacman updatedb su shutdown poweroff reboot ; do + alias $command="doas $command" +done; unset command + +se() { + choice="$(fd . --base-directory=$HOME/.local/bin -t f --min-depth=1 | fzf)" + [ -f "$HOME/.local/bin/$choice" ] && $EDITOR "$HOME/.local/bin/$choice" +} + +# Verbosity and settings that you pretty much just always are going to want. +alias \ + cp="cp -iv" \ + mv="mv -iv" \ + rm="rm -vI" \ + bc="bc -ql" \ + rsync="rsync -vrPlu" \ + mkd="mkdir -pv" \ + yt="yt-dlp --embed-metadata -i" \ + yta="yt -x -f bestaudio/best" \ + ytt="yt --skip-download --write-thumbnail" \ + ffmpeg="ffmpeg -hide_banner" + +# Colorize commands when possible. +alias \ + ll="eza -lh --group-directories-first --color=auto --color-scale=all --color-scale-mode=fixed --icons --no-quotes --smart-group -s size --total-size --no-user --no-permissions" \ + ls="eza --group-directories-first --color=auto --icons --no-quotes -s name" \ + lsf="eza -f --group-directories-first --color=auto --icons --no-quotes -s name" \ + lsd="eza -D --group-directories-first --color=auto --icons --no-quotes -s name" \ + lst="eza -lh --group-directories-first --color=auto --color-scale --icons --no-quotes --smart-group -s time --total-size --no-user --no-permissions" \ + cat="bat -P" \ + ccat="highlight --out-format=ansi" \ + ip="ip -color=auto" + +# These common commands are just too long! Abbreviate them. +alias \ + cl="clear" \ + ka="killall" \ + trem="transmission-remote" \ + YT="youtube-viewer" \ + sdn="shutdown -h now" \ + e="$EDITOR" \ + v="$EDITOR" \ + p="paru" \ + z="zathura" \ + lg="lazygit" \ + sx="swayimg" \ + md="monerod --prune-blockchain --fast-block-sync 1 --data-dir /mnt/ssd/monero/bitmonero" \ + fetch="fastfetch" \ + de="doasedit" + +# Git related +alias \ + g="git" \ + gs="git status --short" \ + gd="git diff" \ + ga="git add" \ + gc="git commit" \ + gp="git push" \ + gu="git pull" \ + gl="git log" \ + gb="git branch" \ + gi="git init" \ + gcl="git clone" + +alias \ + magit="nvim -c MagitOnly" \ + ref="shortcuts >/dev/null; source ${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutrc ; source ${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutenvrc ; source ${XDG_CONFIG_HOME:-$HOME/.config}/shell/zshnameddirrc" diff --git a/.config/shell/bm-dirs b/.config/shell/bm-dirs @@ -0,0 +1,14 @@ +# You can add comments to these files with # +cac ${XDG_CACHE_HOME:-$HOME/.cache} +cf ${XDG_CONFIG_HOME:-$HOME/.config} +D ${XDG_DOWNLOAD_DIR:-$HOME/Downloads} +d ${XDG_DOCUMENTS_DIR:-$HOME/Documents} +dt ${XDG_DATA_HOME:-$HOME/.local/share} +rr /mnt/ssd/projects +h $HOME +m ${XDG_MUSIC_DIR:-$HOME/Music} +mn /mnt +pp ${XDG_PICTURES_DIR:-$HOME/Pictures} +sc $HOME/.local/bin +src $HOME/.local/src +vv ${XDG_VIDEOS_DIR:-$HOME/Videos} diff --git a/.config/shell/bm-files b/.config/shell/bm-files @@ -0,0 +1,16 @@ +# These files automatically update when edited/saved in vim: + +# keys filename description +bf ${XDG_CONFIG_HOME:-$HOME/.config}/shell/bm-files # This file, a list of bookmarked files +bd ${XDG_CONFIG_HOME:-$HOME/.config}/shell/bm-dirs # A list of bookmarked directories similar to this file + +# These do not update automatically, but on the next new instance of a program: + +cfv ${XDG_CONFIG_HOME:-$HOME/.config}/nvim/init.vim # vim/neovim config +cfz $ZDOTDIR/.zshrc # zsh (shell) config +cfa ${XDG_CONFIG_HOME:-$HOME/.config}/shell/aliasrc # aliases used by zsh (and potentially other shells) +cfp ${XDG_CONFIG_HOME:-$HOME/.config}/shell/profile # profile file for login settings for zsh +cfm ${XDG_CONFIG_HOME:-$HOME/.config}/mutt/muttrc # mutt (email client) config +cfn ${XDG_CONFIG_HOME:-$HOME/.config}/newsboat/config # newsboat (RSS reader) +cfu ${XDG_CONFIG_HOME:-$HOME/.config}/newsboat/urls # RSS urls for newsboat +cfmc ${XDG_CONFIG_HOME:-$HOME/.config}/rmpc/config.ron # rmpc (music player) config diff --git a/.config/shell/inputrc b/.config/shell/inputrc @@ -0,0 +1,19 @@ +$include /etc/inputrc +set editing-mode vi +$if mode=vi + +set show-mode-in-prompt on +set vi-ins-mode-string \1\e[6 q\2 +set vi-cmd-mode-string \1\e[2 q\2 + +set keymap vi-command +# these are for vi-command mode +Control-l: clear-screen +Control-a: beginning-of-line + +set keymap vi-insert +# these are for vi-insert mode +Control-l: clear-screen +Control-a: beginning-of-line + +$endif diff --git a/.config/shell/profile b/.config/shell/profile @@ -0,0 +1,115 @@ +#!/bin/sh +# shellcheck disable=SC2155 + +# Profile file, runs on login. Environmental variables are set here. + +# Add all directories in `~/.local/bin` to $PATH +export PATH="$PATH:$(find ~/.local/bin -type d | paste -sd ':' -)" + +unsetopt PROMPT_SP 2>/dev/null + +# Default programs: +export EDITOR="nvim" +export MANPAGER='nvim +Man!' +export TERMINAL="footclient" +export TERMINAL_PROG="footclient" +export TERMCMD="footclient" +export BROWSER="icecat" + +unset SSH_AGENT_PID +if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then + export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)" +fi + +# Change the default crypto/weather monitor sites. +# export CRYPTOURL="rate.sx" +# export WTTRURL="wttr.in" + +# ~/ Clean-up: +export XDG_CONFIG_HOME="$HOME/.config" +export XDG_DATA_HOME="$HOME/.local/share" +export XDG_CACHE_HOME="$HOME/.cache" +export XDG_STATE_HOME="$HOME/.local/state" +export XDG_MUSIC_DIR="/mnt/ssd/music" +export NOTMUCH_CONFIG="$XDG_CONFIG_HOME/notmuch-config" +export WGETRC="$XDG_CONFIG_HOME/wget/wgetrc" +export INPUTRC="$XDG_CONFIG_HOME/shell/inputrc" +export ZDOTDIR="$XDG_CONFIG_HOME/zsh" +#export GNUPGHOME="$XDG_DATA_HOME/gnupg" +export WINEPREFIX="$XDG_DATA_HOME/wineprefixes/default" +export KODI_DATA="$XDG_DATA_HOME/kodi" +export PASSWORD_STORE_DIR="$XDG_DATA_HOME/password-store" +export TMUX_TMPDIR="$XDG_RUNTIME_DIR" +export ANDROID_SDK_HOME="$XDG_CONFIG_HOME/android" +export CARGO_HOME="$XDG_DATA_HOME/cargo" +export GOPATH="$XDG_DATA_HOME/go" +export GOMODCACHE="$XDG_CACHE_HOME/go/mod" +export ANSIBLE_CONFIG="$XDG_CONFIG_HOME/ansible/ansible.cfg" +export UNISON="$XDG_DATA_HOME/unison" +export HISTFILE="$XDG_DATA_HOME/history" +export MBSYNCRC="$XDG_CONFIG_HOME/mbsync/config" +export ELECTRUMDIR="$XDG_DATA_HOME/electrum" +export PYTHONSTARTUP="$XDG_CONFIG_HOME/python/pythonrc" +export SQLITE_HISTORY="$XDG_DATA_HOME/sqlite_history" +export NPM_CONFIG_USERCONFIG="$XDG_CONFIG_HOME/npm/npmrc" +export CUDA_CACHE_PATH="$XDG_CACHE_HOME/nv" +export KUBECONFIG="$XDG_CONFIG_HOME/kube/config" +export KUBECACHEDIR="$XDG_CACHE_HOME/kube" +export DOCKER_CONFIG="$XDG_CONFIG_HOME/docker" +export PASSWORD_STORE_CLIP_TIME=5 +export W3M_DIR="$XDG_STATE_HOME/w3m" + +# Other program settings: +export DICS="/usr/share/stardict/dic/" +export DOAS_ASKPASS="$HOME/.local/bin/dmenupass" +export LESS="R" +export LESS_TERMCAP_mb="$(printf '%b' '')" +export LESS_TERMCAP_md="$(printf '%b' '')" +export LESS_TERMCAP_me="$(printf '%b' '')" +export LESS_TERMCAP_so="$(printf '%b' '')" +export LESS_TERMCAP_se="$(printf '%b' '')" +export LESS_TERMCAP_us="$(printf '%b' '')" +export LESS_TERMCAP_ue="$(printf '%b' '')" +export LESSOPEN="| /usr/bin/highlight -O ansi %s 2>/dev/null" +export QT_QPA_PLATFORMTHEME="qt5ct" # Have QT use gtk3 theme. +export MOZ_USE_XINPUT2=1 # Mozilla smooth scrolling/touchpads. +export AWT_TOOLKIT="MToolkit wmname LG3D" # May have to install wmname +export _JAVA_AWT_WM_NONREPARENTING=1 # Fix for Java applications in dwm +export LIBVA_DRIVER_NAME=nvidia +export __GLX_VENDOR_LIBRARY_NAME=nvidia +export MOZ_DISABLE_RDD_SANDBOX=1 # For HW in Firefox on nvidia gpu +export ELECTRON_OZONE_PLATFORM_HINT=auto # Electron fix +export QT_WAYLAND_DISABLE_WINDOWDECORATION=1 # Disables decorations for QT applications +export FZF_DEFAULT_OPTS="$FZF_DEFAULT_OPTS \ +--layout=reverse --height 40% \ +--color=fg:#e5e9f0,bg:-1,hl:#81a1c1 \ +--color=fg+:#e5e9f0,bg+:#3b4252,hl+:#81a1c1 \ +--color=info:#eacb8a,prompt:#bf6069,pointer:#b48dac \ +--color=marker:#a3be8b,spinner:#b48dac,header:#a3be8b +--cycle +--scroll-off=8 +--tabstop=4 +--preview-window=border-sharp +--highlight-line +--no-ansi +--scrollbar=█ +--wrap-sign= +--bind=ctrl-u:preview-half-page-up +--bind=ctrl-d:preview-half-page-down +--bind=ctrl-l:forward-char +--bind=ctrl-h:backward-char +--bind=resize:refresh-preview +" +export YT_X_FZF_OPTS="$FZF_DEFAULT_OPTS" +export ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=#665c54" +export BAT_THEME="gruvbox-dark" +export GTK_USE_PORTAL=1 +export ENABLE_HDR_WSI=1 +export PROTON_ENABLE_WAYLAND=1 +export WLR_RENDERER=vulkan + +[ ! -f "$XDG_CONFIG_HOME/shell/shortcutrc" ] && setsid -f shortcuts >/dev/null 2>&1 + +# Start graphical server on user's current tty if not already running. +# [ "$(tty)" = "/dev/tty1" ] && ! pidof -s Hyprland >/dev/null 2>&1 && exec dbus-run-session Hyprland +[ "$(tty)" = "/dev/tty1" ] && ! pidof -s sway >/dev/null 2>&1 && exec dbus-run-session sway --unsupported-gpu diff --git a/.config/sway/config b/.config/sway/config @@ -0,0 +1,272 @@ +set $mod Mod4 +# Home row direction keys, like vim +set $left h +set $down j +set $up k +set $right l + +set $term footclient +set $menu mew-run + +# autostart +exec start-pipewire +exec singboxwrap +exec fnott +exec gpg-agent --daemon +exec gnome-keyring-daemon --start --components=secrets +exec foot --server +exec portal +exec mpd +# exec stmpdup +exec swayidle + +output DP-3 resolution 3840x2160@165Hz position 2560 0 scale 1.5 render_bit_depth 10 hdr off +output DP-2 resolution 2560x1440@165Hz position 0 0 scale 1 + +workspace 1 output DP-2 +workspace 2 output DP-3 + +# seat seat0 xcursor_theme hicolor + +# floating window assigments +for_window [window_role = "pop-up"] floating enable +for_window [window_role = "bubble"] floating enable +for_window [window_role = "dialog"] floating enable +for_window [window_type = "dialog"] floating enable +for_window [window_role = "task_dialog"] floating enable +for_window [window_type = "menu"] floating enable +for_window [app_id = "floating"] floating enable +for_window [app_id = "floating_update"] floating enable, resize set width 1000px height 600px +for_window [class = "(?i)pinentry"] floating enable +for_window [title = "Administrator privileges required"] floating enable +for_window [title = "About GNU IceCat"] floating enable +for_window [window_role = "About"] floating enable +for_window [app_id="icecat" title="Library"] floating enable, border pixel 1, sticky enable +for_window [app_id="termfloat"] floating enable +for_window [app_id="termfloat"] resize set height 540 +for_window [app_id="termfloat"] resize set width 960 +for_window [class="^steam$" title="^Friends List$"] floating enable +for_window [class="^steam$" title="Steam - News"] floating enable +for_window [class="^steam$" title=".* - Chat"] floating enable +for_window [class="^steam$" title=".* Settings$"] floating enable +for_window [class="^steam$" title=".* - event started"] floating enable +for_window [class="^steam$" title=".* CD key"] floating enable +for_window [class="^steam$" title="^Steam - Self Updater$"] floating enable +for_window [class="^steam$" title="^Screenshot Uploader$"] floating enable +for_window [class="^steam$" title="^Recordings & Screenshots$"] floating enable +for_window [class="^steam$" title="^Steam Guard - Computer Authorization Required$"] floating enable +for_window [title="^Steam Keyboard$"] floating enable +for_window [app_id="termfilechooser"] floating enable +for_window [app_id="termfilechooser"] resize set height 960 +for_window [app_id="termfilechooser"] resize set width 960 + +for_window [app_id="showmethekey-gtk"] floating enable +for_window [app_id="showmethekey-gtk"] resize set height 160 +for_window [app_id="showmethekey-gtk"] resize set width 1260 +for_window [app_id="showmethekey-gtk"] move down 500 +no_focus [app_id="showmethekey-gtk"] + +floating_modifier $mod normal + +# binds +bindsym $mod+Return exec $term +bindsym $mod+q kill +# bindsym $mod+r exec $term -e yazi +# bindsym $mod+e exec 'sh -c "$term -e neomutt; pkill -RTMIN+12 i3blocks"' +bindsym $mod+d exec $menu +bindsym $mod+Shift+d exec tessen +# bindsym $mod+minus exec 'wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-; kill -44 $(pidof i3blocks)' +bindsym $mod+underscore exec 'wpctl set-volume @DEFAULT_AUDIO_SINK@ 15%-; kill -44 $(pidof i3blocks)' +bindsym $mod+equal exec 'wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+; kill -44 $(pidof i3blocks)' +bindsym $mod+plus exec 'wpctl set-volume @DEFAULT_AUDIO_SINK@ 15%+; kill -44 $(pidof i3blocks)' +bindsym $mod+backspace exec sysact +bindsym $mod+Shift+backspace exec sysact +bindsym $mod+Shift+q exec sysact +bindsym $mod+Shift+c reload +bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit' +bindsym $mod+Print exec dmenurecord +bindsym $mod+Shift+Print exec 'dmenurecord kill' +bindsym $mod+Tab exec chooseprofile +bindsym $mod+grave exec bookmarks +bindsym $mod+Shift+grave exec define +bindsym $mod+Shift+n exec 'sh -c "$term -T newsraft -e newsraft; pkill -RTMIN+6 i3blocks"' +bindsym $mod+m exec $term -e rmpc +bindsym $mod+Shift+m exec 'wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle; kill -44 $(pidof i3blocks)' +bindsym $mod+comma exec mpc prev +bindsym $mod+Shift+comma exec mpc seek 0% +bindsym $mod+period exec mpc next +bindsym $mod+Shift+period exec mpc repeat +bindsym $mod+insert exec wtype $(rg -v '^#' ~/.local/share/larbs/snippets | mew -i -l 50 | cut -d' ' -f1) +bindsym $mod+f1 exec singboxwrap menu +bindsym $mod+f4 exec '$term -e pulsemixer; kill -44 $(pidof i3blocks)' +bindsym $mod+f6 exec torwrap +bindsym $mod+f7 exec td-toggle +bindsym $mod+f8 exec mailup +bindsym $mod+f9 exec mounter +bindsym $mod+f10 exec unmounter +bindsym $mod+f11 exec "mpv --untimed --no-cache --no-osc --no-input-default-bindings --profile=low-latency --input-conf=/dev/null --title=webcam $(ls /dev/video[0,2,4,6,8] | tail -n 1)" +bindsym XF86AudioPlay exec mpc toggle +bindsym $mod+p exec mpc toggle +bindsym $mod+Shift+p exec 'mpc pause; pauseallmpv' +bindsym XF86AudioNext exec mpc next +bindsym XF86AudioPrev exec mpc prev +bindsym $mod+bracketleft exec mpc seek -10 +bindsym $mod+Shift+bracketleft exec mpc seek -60 +bindsym $mod+bracketright exec mpc seek +10 +bindsym $mod+Shift+bracketright exec mpc seek +60 +bindsym --whole-window BTN_EXTRA exec 'wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle; kill -57 $(pidof i3blocks)' +bindsym Print exec 'grim pic-full-$(date '+%y%m%d-%H%M-%S').png' +bindsym Shift+Print exec maimpick +bindsym $mod+Scroll_Lock exec killall showmethekey-gtk || showmethekey-gtk -A +# +# Moving around: +# + # Move your focus around + bindsym $mod+$left focus left + bindsym $mod+$down focus down + bindsym $mod+$up focus up + bindsym $mod+$right focus right + # Or use $mod+[up|down|left|right] + bindsym $mod+Left focus left + bindsym $mod+Down focus down + bindsym $mod+Up focus up + bindsym $mod+Right focus right + + # Move the focused window with the same, but add Shift + bindsym $mod+Shift+$left move left + bindsym $mod+Shift+$down move down + bindsym $mod+Shift+$up move up + bindsym $mod+Shift+$right move right + # Ditto, with arrow keys + bindsym $mod+Shift+Left move left + bindsym $mod+Shift+Down move down + bindsym $mod+Shift+Up move up + bindsym $mod+Shift+Right move right +# +# Workspaces: +# + # Switch to workspace + bindsym $mod+1 workspace number 1 + bindsym $mod+2 workspace number 2 + bindsym $mod+3 workspace number 3 + bindsym $mod+4 workspace number 4 + bindsym $mod+5 workspace number 5 + bindsym $mod+6 workspace number 6 + bindsym $mod+7 workspace number 7 + bindsym $mod+8 workspace number 8 + bindsym $mod+9 workspace number 9 + bindsym $mod+0 workspace number 10 + # Move focused container to workspace + bindsym $mod+Shift+1 move container to workspace number 1 + bindsym $mod+Shift+2 move container to workspace number 2 + bindsym $mod+Shift+3 move container to workspace number 3 + bindsym $mod+Shift+4 move container to workspace number 4 + bindsym $mod+Shift+5 move container to workspace number 5 + bindsym $mod+Shift+6 move container to workspace number 6 + bindsym $mod+Shift+7 move container to workspace number 7 + bindsym $mod+Shift+8 move container to workspace number 8 + bindsym $mod+Shift+9 move container to workspace number 9 + bindsym $mod+Shift+0 move container to workspace number 10 + # Note: workspaces can have any name you want, not just numbers. + # We just use 1-10 as the default. +# +# Layout stuff: +# + # You can "split" the current object of your focus with + # $mod+b or $mod+v, for horizontal and vertical splits + # respectively. + bindsym $mod+b splith + bindsym $mod+v splitv + + # Switch the current container between different layout styles + bindsym $mod+s layout stacking + bindsym $mod+w exec icecat + bindsym $mod+Shift+w layout tabbed + bindsym $mod+e layout toggle split + + bindsym $mod+f fullscreen + + # Toggle the current focus between tiling and floating mode + bindsym $mod+Shift+space floating toggle + + # Swap focus between the tiling area and the floating area + bindsym $mod+space focus mode_toggle + + # Move focus to the parent container + bindsym $mod+a focus parent +# +# Scratchpad: +# + # Sway has a "scratchpad", which is a bag of holding for windows. + # You can send windows there and get them back later. + + # Move the currently focused window to the scratchpad + bindsym $mod+Shift+minus move scratchpad + + # Show the next scratchpad window or hide the focused scratchpad window. + # If there are multiple scratchpad windows, this command cycles through them. + bindsym $mod+minus scratchpad show +# +# Resizing containers: +# +mode "resize" { + # left will shrink the containers width + # right will grow the containers width + # up will shrink the containers height + # down will grow the containers height + bindsym $left resize shrink width 10px + bindsym $down resize grow height 10px + bindsym $up resize shrink height 10px + bindsym $right resize grow width 10px + + # Ditto, with arrow keys + bindsym Left resize shrink width 10px + bindsym Down resize grow height 10px + bindsym Up resize shrink height 10px + bindsym Right resize grow width 10px + + # Return to default mode + bindsym Return mode "default" + bindsym Escape mode "default" +} +bindsym $mod+r mode "resize" +# +# Utilities: +# + # Special keys to adjust volume via PulseAudio + bindsym --locked XF86AudioMute exec pactl set-sink-mute \@DEFAULT_SINK@ toggle + bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5% + bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5% + bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle + + # Special keys to control media via playerctl + bindsym --locked XF86AudioPlay exec mpc toggle + bindsym --locked XF86AudioPause exec mpc toggle + bindsym --locked XF86AudioPrev exec mpc prev + bindsym --locked XF86AudioNext exec mpc next + bindsym --locked XF86AudioStop exec mpc stop + + # Special keys to adjust brightness via brightnessctl + bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%- + bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+ + + +input * { + xkb_layout "us,ru" + xkb_options "grp:win_space_toggle,caps:escape" + accel_profile flat +} + +input "type:keyboard" { + repeat_rate 45 + repeat_delay 300 +} + +bar { + position top + status_command i3blocks + font pango:FreeMono Bold 10 + separator_symbol "" +} + +include /etc/sway/config.d/* diff --git a/.config/swayidle/config b/.config/swayidle/config @@ -0,0 +1,3 @@ +timeout 300 'swaylock -f' +timeout 330 'swaymsg "output * dpms off"' resume 'swaymsg "output * dpms on"' +before-sleep 'swaylock -f' diff --git a/.config/swayimg/config b/.config/swayimg/config @@ -0,0 +1,38 @@ +[general] +overlay = no +[viewer] +window = #000000 +scale = fit +[gallery] +window = #000000 +[list] +all = yes +[keys.viewer] +h = step_left +j = step_down +k = step_up +l = step_right +n = next_file +p = prev_file +w = zoom fit +g = first_file +Shift+g = last_file +Ctrl+x = exec ~/.config/swayimg/key-handler "%" +r = rotate_right +Shift+r = rotate_left +Alt+Shift+k = zoom +20 +Alt+Shift+j = zoom -20 +[keys.gallery] +h = step_left +j = step_down +k = step_up +l = step_right +g = first_file +Shift+g = last_file +[info] +info_timeout = 0 +[info.viewer] +top_left = none +top_right = none +bottom_left = +name +bottom_right = +scale,+frame,+index diff --git a/.config/swayimg/key-handler b/.config/swayimg/key-handler @@ -0,0 +1,35 @@ +#!/bin/sh +file=$1 + [ -z "$selection" ] && selection=$(printf "w - Set as wallpaper\nc - Copy to dir\nm - Move to dir\nr - Rotate 90°\nR - Rotate -90°\nf - Flip horizontal\ny - Copy filename to clipboard\nY - Copy full path to clipboard\nd - Delete\ng - Open in GIMP\ni - Show media info" | + mew -i -l 12 -p "Choose action for selected files:") + action=$(printf "%s" "$selection" | cut -d'-' -f1 | tr -d ' ') + case "$action" in + "w") setbg "$file" & ;; + "c") + [ -z "$destdir" ] && destdir="$(sed "s/#.*$//;/^\s*$/d" ${XDG_CONFIG_HOME:-$HOME/.config}/shell/bm-dirs | awk '{print $2}' | mew -l 20 -i -p "Copy file(s) to where?" | sed "s|~|$HOME|g")" + [ ! -d "$destdir" ] && notify-send "$destdir is not a directory, cancelled." && exit + cp "$file" "$destdir" && notify-send -i "$(readlink -f "$file")" "$file copied to $destdir." & + ;; + "m") + [ -z "$destdir" ] && destdir="$(sed "s/#.*$//;/^\s*$/d" ${XDG_CONFIG_HOME:-$HOME/.config}/shell/bm-dirs | awk '{print $2}' | mew -l 20 -i -p "Move file(s) to where?" | sed "s|~|$HOME|g")" + [ ! -d "$destdir" ] && notify-send "$destdir is not a directory, cancelled." && exit + mv "$file" "$destdir" && notify-send -i "$(readlink -f "$file")" "$file moved to $destdir." & + ;; + "r") + magick "$file" -rotate 90 "$file" ;; + "R") + magick "$file" -rotate -90 "$file" ;; + "f") + magick "$file" -flop "$file" ;; + "y") + printf "%s" "$file" | tr -d '\n' | wl-copy && + notify-send "$file copied to clipboard" & ;; + "Y") + readlink -f "$file" | tr -d '\n' | wl-copy && + notify-send "$(readlink -f "$file") copied to clipboard" & ;; + "d") + [ "$(printf "No\\nYes" | mew -i -p "Really delete $file?")" = "Yes" ] && rm "$file" && notify-send "$file deleted." ;; + "g") ifinstalled gimp && setsid -f gimp "$file" ;; + "i") notify-send "File information" "$(mediainfo "$file" | sed "s/[ ]\+:/:/g;s/: /: <b>/;s/$/<\/b>/" | rg "<b>")" ;; + *) notify-send "No keybind for that key" ;; + esac diff --git a/.config/swaylock/config b/.config/swaylock/config @@ -0,0 +1 @@ +color=#000000 diff --git a/.config/tmux/tmux.conf b/.config/tmux/tmux.conf @@ -0,0 +1,69 @@ +# # List of plugins +# set -g @plugin 'tmux-plugins/tpm' +# set -g @plugin 'tmux-plugins/tmux-sensible' + +# ========== General options ========== +# Neovim told me to set these parameters +set-option -sg escape-time 10 +set-option -g focus-events on +# Tell tmux that foot support true color +set -sa terminal-features 'foot*:sixel:sync:RGB:256:hyperlinks:usstyle:rectfill:focus:mouse' +# Limit scrollback buffer to 100k lines +set -g history-limit 100000 +# needed for large sixels +set -gq input-buffer-size 10485760 +# Automatically renumber windows when a window is closed +set -g renumber-windows on +set -g display-panes-time 60000 +# Update envvars so hyprctl works +set -g update-environment DISPLAY +set -ga update-environment HYPRLAND_INSTANCE_SIGNATURE +set -ga update-environment HYPRLAND_CMD +set -ga update-environment WAYLAND_DISPLAY +set -ga update-environment SWAYSOCK +set -ga update-environment I3SOCK + +set -g base-index 1 +set -g pane-base-index 1 + +# ========== Keybinds ========== +# Enable mouse +set -g mouse on + +# Prefix is Ctrl-a +set -g prefix C-a +bind C-a send-prefix +unbind C-b + +# Vim-like keybinds +set -g mode-keys vi +set -g status-keys vi + +# reload config on r +bind r source-file "~/.config/tmux/tmux.conf" + +# Split pane into two +bind \\ split-window -h -c "#{pane_current_path}" +bind - split-window -v -c "#{pane_current_path}" +unbind '"' +unbind % + +# moving between panes with vim movement keys +bind h select-pane -L +bind j select-pane -D +bind k select-pane -U +bind l select-pane -R + +# Moving between windows with j and k +# If a window to the right of the current window exists, switch to it, otherwise create a new one +bind -r C-k if-shell -F '#{==:#{active_window_index},#{last_window_index}}' 'new-window' 'select-window -n' +# If the current window is the leftmost window, do nothing +bind -r C-j if-shell -F '#{>:#{window_index},0}' 'select-window -p' + +# resize panes with vim movement keys +bind -r H resize-pane -L 1 +bind -r J resize-pane -D 1 +bind -r K resize-pane -U 1 +bind -r L resize-pane -R 1 + +run '~/.config/tmux/plugins/tpm/tpm' diff --git a/.config/user-dirs.dirs b/.config/user-dirs.dirs @@ -0,0 +1 @@ +XDG_DESKTOP_DIR="$HOME/" diff --git a/.config/wget/wgetrc b/.config/wget/wgetrc @@ -0,0 +1 @@ +hsts-file=~/.cache/wget-hsts diff --git a/.config/xdg-desktop-portal-termfilechooser/config b/.config/xdg-desktop-portal-termfilechooser/config @@ -0,0 +1,2 @@ +[filechooser] +env=TERMCMD="footclient -a termfilechooser" diff --git a/.config/xdg-desktop-portal-wlr/config b/.config/xdg-desktop-portal-wlr/config @@ -0,0 +1,3 @@ +[screencast] +chooser_type=dmenu +chooser_cmd=mew diff --git a/.config/xdg-desktop-portal/config b/.config/xdg-desktop-portal/config @@ -0,0 +1,3 @@ +[preferred] +default=wlr;termfilechooser +org.freedesktop.impl.portal.FileChooser=termfilechooser diff --git a/.config/yazi/init.lua b/.config/yazi/init.lua @@ -0,0 +1,4 @@ +require("session"):setup { + sync_yanked = true, +} +require("git"):setup() diff --git a/.config/yazi/plugins/git.yazi/main.lua b/.config/yazi/plugins/git.yazi/main.lua @@ -0,0 +1,261 @@ +--- @since 25.5.31 + +local WINDOWS = ya.target_family() == "windows" + +-- The code of supported git status, +-- also used to determine which status to show for directories when they contain different statuses +-- see `bubble_up` +---@enum CODES +local CODES = { + excluded = 100, -- ignored directory + ignored = 6, -- ignored file + untracked = 5, + modified = 4, + added = 3, + deleted = 2, + updated = 1, + unknown = 0, +} + +local PATTERNS = { + { "!$", CODES.ignored }, + { "?$", CODES.untracked }, + { "[MT]", CODES.modified }, + { "[AC]", CODES.added }, + { "D", CODES.deleted }, + { "U", CODES.updated }, + { "[AD][AD]", CODES.updated }, +} + +---@param line string +---@return CODES, string +local function match(line) + local signs = line:sub(1, 2) + for _, p in ipairs(PATTERNS) do + local path, pattern, code = nil, p[1], p[2] + if signs:find(pattern) then + path = line:sub(4, 4) == '"' and line:sub(5, -2) or line:sub(4) + path = WINDOWS and path:gsub("/", "\\") or path + end + if not path then + elseif path:find("[/\\]$") then + -- Mark the ignored directory as `excluded`, so we can process it further within `propagate_down` + return code == CODES.ignored and CODES.excluded or code, path:sub(1, -2) + else + return code, path + end + ---@diagnostic disable-next-line: missing-return + end +end + +---@param cwd Url +---@return string? +local function root(cwd) + local is_worktree = function(url) + local file, head = io.open(tostring(url)), nil + if file then + head = file:read(8) + file:close() + end + return head == "gitdir: " + end + + repeat + local next = cwd:join(".git") + local cha = fs.cha(next) + if cha and (cha.is_dir or is_worktree(next)) then + return tostring(cwd) + end + cwd = cwd.parent + until not cwd +end + +---@param changed Changes +---@return Changes +local function bubble_up(changed) + local new, empty = {}, Url("") + for path, code in pairs(changed) do + if code ~= CODES.ignored then + local url = Url(path).parent + while url and url ~= empty do + local s = tostring(url) + new[s] = (new[s] or CODES.unknown) > code and new[s] or code + url = url.parent + end + end + end + return new +end + +---@param excluded string[] +---@param cwd Url +---@param repo Url +---@return Changes +local function propagate_down(excluded, cwd, repo) + local new, rel = {}, cwd:strip_prefix(repo) + for _, path in ipairs(excluded) do + if rel:starts_with(path) then + -- If `cwd` is a subdirectory of an excluded directory, also mark it as `excluded` + new[tostring(cwd)] = CODES.excluded + elseif cwd == repo:join(path).parent then + -- If `path` is a direct subdirectory of `cwd`, mark it as `ignored` + new[path] = CODES.ignored + else + -- Skipping, we only care about `cwd` itself and its direct subdirectories for maximum performance + end + end + return new +end + +---@param cwd string +---@param repo string +---@param changed Changes +local add = ya.sync(function(st, cwd, repo, changed) + ---@cast st State + + st.dirs[cwd] = repo + st.repos[repo] = st.repos[repo] or {} + for path, code in pairs(changed) do + if code == CODES.unknown then + st.repos[repo][path] = nil + elseif code == CODES.excluded then + -- Mark the directory with a special value `excluded` so that it can be distinguished during UI rendering + st.dirs[path] = CODES.excluded + else + st.repos[repo][path] = code + end + end + -- TODO: remove this + if ui.render then + ui.render() + else + ya.render() + end +end) + +---@param cwd string +local remove = ya.sync(function(st, cwd) + ---@cast st State + + local repo = st.dirs[cwd] + if not repo then + return + end + + -- TODO: remove this + if ui.render then + ui.render() + else + ya.render() + end + st.dirs[cwd] = nil + if not st.repos[repo] then + return + end + + for _, r in pairs(st.dirs) do + if r == repo then + return + end + end + st.repos[repo] = nil +end) + +---@param st State +---@param opts Options +local function setup(st, opts) + st.dirs = {} + st.repos = {} + + opts = opts or {} + opts.order = opts.order or 1500 + + local t = th.git or {} + local styles = { + [CODES.ignored] = t.ignored and ui.Style(t.ignored) or ui.Style():fg("darkgray"), + [CODES.untracked] = t.untracked and ui.Style(t.untracked) or ui.Style():fg("magenta"), + [CODES.modified] = t.modified and ui.Style(t.modified) or ui.Style():fg("yellow"), + [CODES.added] = t.added and ui.Style(t.added) or ui.Style():fg("green"), + [CODES.deleted] = t.deleted and ui.Style(t.deleted) or ui.Style():fg("red"), + [CODES.updated] = t.updated and ui.Style(t.updated) or ui.Style():fg("yellow"), + } + local signs = { + [CODES.ignored] = t.ignored_sign or "", + [CODES.untracked] = t.untracked_sign or "?", + [CODES.modified] = t.modified_sign or "", + [CODES.added] = t.added_sign or "", + [CODES.deleted] = t.deleted_sign or "", + [CODES.updated] = t.updated_sign or "", + } + + Linemode:children_add(function(self) + local url = self._file.url + local repo = st.dirs[tostring(url.base)] + local code + if repo then + code = repo == CODES.excluded and CODES.ignored or st.repos[repo][tostring(url):sub(#repo + 2)] + end + + if not code or signs[code] == "" then + return "" + elseif self._file.is_hovered then + return ui.Line { " ", signs[code] } + else + return ui.Line { " ", ui.Span(signs[code]):style(styles[code]) } + end + end, opts.order) +end + +---@type UnstableFetcher +local function fetch(_, job) + local cwd = job.files[1].url.base + local repo = root(cwd) + if not repo then + remove(tostring(cwd)) + return true + end + + local paths = {} + for _, file in ipairs(job.files) do + paths[#paths + 1] = tostring(file.url) + end + + -- stylua: ignore + local output, err = Command("git") + :cwd(tostring(cwd)) + :arg({ "--no-optional-locks", "-c", "core.quotePath=", "status", "--porcelain", "-unormal", "--no-renames", "--ignored=matching" }) + :arg(paths) + :stdout(Command.PIPED) + :output() + if not output then + return true, Err("Cannot spawn `git` command, error: %s", err) + end + + local changed, excluded = {}, {} + for line in output.stdout:gmatch("[^\r\n]+") do + local code, path = match(line) + if code == CODES.excluded then + excluded[#excluded + 1] = path + else + changed[path] = code + end + end + + if job.files[1].cha.is_dir then + ya.dict_merge(changed, bubble_up(changed)) + end + ya.dict_merge(changed, propagate_down(excluded, cwd, Url(repo))) + + -- Reset the status of any files that don't appear in the output of `git status` to `unknown`, + -- so that cleaning up outdated statuses from `st.repos` + for _, path in ipairs(paths) do + local s = path:sub(#repo + 2) + changed[s] = changed[s] or CODES.unknown + end + + add(tostring(cwd), repo, changed) + + return false +end + +return { setup = setup, fetch = fetch } diff --git a/.config/yazi/yazi.toml b/.config/yazi/yazi.toml @@ -0,0 +1,12 @@ +[mgr] +show_hidden = true + +[[plugin.prepend_fetchers]] +id = "git" +name = "*" +run = "git" + +[[plugin.prepend_fetchers]] +id = "git" +name = "*/" +run = "git" diff --git a/.config/yt-dlp/config b/.config/yt-dlp/config @@ -0,0 +1 @@ +--mtime diff --git a/.config/zathura/zathurarc b/.config/zathura/zathurarc @@ -0,0 +1,25 @@ +set sandbox none +set statusbar-h-padding 0 +set statusbar-v-padding 0 +set page-padding 1 +set selection-clipboard clipboard +map u scroll half-up +map d scroll half-down +map D toggle_page_mode +map r reload +map R rotate +map K zoom in +map J zoom out +map i recolor +map p print +map g goto top +map [fullscreen] u scroll half-up +map [fullscreen] d scroll half-down +map [fullscreen] D toggle_page_mode +map [fullscreen] r reload +map [fullscreen] R rotate +map [fullscreen] K zoom in +map [fullscreen] J zoom out +map [fullscreen] i recolor +map [fullscreen] p print +map [fullscreen] g goto top diff --git a/.config/zsh/.zshrc b/.config/zsh/.zshrc @@ -0,0 +1,121 @@ +# Enable colors and change prompt: +autoload -U colors && colors # Load colors + +clp(){ + yes | paru -Scc +} + +autoload -Uz vcs_info + +# enable only git +zstyle ':vcs_info:*' enable git + +# setup a hook that runs before every ptompt. +precmd_vcs_info() { vcs_info } +precmd_functions+=( precmd_vcs_info ) + +# add a function to check for untracked files in the directory. +# from https://github.com/zsh-users/zsh/blob/master/Misc/vcs_info-examples +zstyle ':vcs_info:git*+set-message:*' hooks git-untracked +# ++vi-git-untracked(){ + if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) == 'true' ]] && \ + git status --porcelain | grep '??' &> /dev/null ; then + # This will show the marker if there are any untracked files in repo. + # If instead you want to show the marker only if there are untracked + # files in $PWD, use: + #[[ -n $(git ls-files --others --exclude-standard) ]] ; then + hook_com[staged]+='!' # signify new files with a bang + fi +} + +zstyle ':vcs_info:*' check-for-changes true +# zstyle ':vcs_info:git:*' formats " %r/%S %b %m%u%c " +zstyle ':vcs_info:git:*' formats " %{$fg[red]%}%m%u%c%{$fg[white]%}*%{$fg[white]%}%b" + +# format our main prompt for hostname current folder, and permissions. +PROMPT="%{$fg[blue]%}%~" +# PROMPT="%B%{$fg[blue]%}[%{$fg[white]%}%n%{$fg[red]%}@%{$fg[white]%}%m%{$fg[blue]%}] %(?:%{$fg_bold[green]%}➜ :%{$fg_bold[red]%}➜ )%{$fg[cyan]%}%c%{$reset_color%}" +# PROMPT="%{$fg[green]%}%n@%m %~ %{$reset_color%}%#> " +PROMPT+="\$vcs_info_msg_0_" +PROMPT+=" %(?:%{$fg_bold[magenta]%}>:%{$fg_bold[red]%}>)%{$reset_color%} " +setopt autocd # Automatically cd into typed directory. +stty stop undef # Disable ctrl-s to freeze terminal. +setopt interactive_comments +setopt prompt_subst +# History in cache directory: +HISTSIZE=10000000 +SAVEHIST=10000000 +HISTFILE="${XDG_CACHE_HOME:-$HOME/.cache}/zshhistory" +setopt inc_append_history + +# Load aliases and shortcuts if existent. +[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutrc" +[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutenvrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutenvrc" +[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/aliasrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/aliasrc" +[ -f "${XDG_CONFIG_HOME:-$HOME/.config}/shell/zshnameddirrc" ] && source "${XDG_CONFIG_HOME:-$HOME/.config}/shell/zshnameddirrc" + +# Basic auto/tab complete: +autoload -U compinit +zstyle ':completion:*' menu select +zmodload zsh/complist +compinit +_comp_options+=(globdots) # Include hidden files. + +# vi mode +bindkey -v +export KEYTIMEOUT=1 + +# Use vim keys in tab complete menu: +bindkey -M menuselect 'h' vi-backward-char +bindkey -M menuselect 'k' vi-up-line-or-history +bindkey -M menuselect 'l' vi-forward-char +bindkey -M menuselect 'j' vi-down-line-or-history +bindkey -v '^?' backward-delete-char + +# Change cursor shape for different vi modes. +function zle-keymap-select () { + case $KEYMAP in + vicmd) echo -ne '\e[6 q';; + viins|main) echo -ne '\033[0 q';; + esac +} +zle -N zle-keymap-select +zle-line-init() { + zle -K viins # initiate `vi insert` as keymap (can be removed if `bindkey -V` has been set elsewhere) + echo -ne '\033[0 q' +} +zle -N zle-line-init + +function preexec { + print -Pn "\e]0;${(q)1}\e\\" +} + +# Use yazi to switch directories and bind it to ctrl-o +function y() { + local tmp="$(mktemp -t "yazi-cwd.XXXXXX")" cwd + yazi "$@" --cwd-file="$tmp" + if cwd="$(command cat -- "$tmp")" && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then + builtin cd -- "$cwd" + fi + rm -f -- "$tmp" > /dev/null +} + +bindkey -s '^o' '^uy\n' + +bindkey -s '^a' '^ubc -lq\n' + +bindkey -s '^f' '^ucd "$(dirname "$(fzf)")"\n' + +bindkey '^[[P' delete-char + +# Edit line in vim with ctrl-e: +autoload edit-command-line; zle -N edit-command-line +bindkey '^e' edit-command-line +bindkey -M vicmd '^[[P' vi-delete-char +bindkey -M vicmd '^e' edit-command-line +bindkey -M visual '^[[P' vi-delete + +source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh +# Load syntax highlighting; should be last. +source /usr/share/zsh/plugins/fast-syntax-highlighting/fast-syntax-highlighting.plugin.zsh 2>/dev/null diff --git a/.local/bin/bookmarks b/.local/bin/bookmarks @@ -0,0 +1,142 @@ +#!/bin/dash + +URLQUERY_FILE="${HOME}/.local/share/urlquery" +ACTION_MENU="@@" + +CLIPBOARD() { + wl-paste +} + +DMENU() { + mew -i -l "${1}" -p "${2}" +} + +error_notify() { + notify-send "${1}" + exit "1" +} + +ensure_file_exists() { + [ -f "${URLQUERY_FILE}" ] || { + notify-send "${URLQUERY_FILE} does not exist. Creating it now." + printf "SearXNG=https://searx.tiekoetter.com/search?q=\n" > "${URLQUERY_FILE}" + } +} + +get_selection() { + cut -d= -f1 "${URLQUERY_FILE}" | DMENU "${LINE_COUNT}" "Bookmarks" +} + +update_file() { + pattern="${1}" + replacement="${2}" + + sed "/${pattern}/c\\${replacement}" "${URLQUERY_FILE}" > "${URLQUERY_FILE}.tmp" && + mv "${URLQUERY_FILE}.tmp" "${URLQUERY_FILE}" || + error_notify "Failed to update the file." +} + +is_valid_url() { + printf "%s\n" "${1}" | rg -q "^https?://[^[:space:]/?#][^[:space:]]+$" +} + +add_bookmark() { + URL="$(CLIPBOARD)" + + is_valid_url "${URL}" || error_notify "The clipboard content is not a valid URL." + + rg -q "=${URL}$" "${URLQUERY_FILE}" && + notify-send "The URL is already in the list." && return + + NAME="$(printf "" | DMENU "0" "Name")" + + [ -n "${NAME}" ] && printf "%s\n" "${NAME}=${URL}" >> "${URLQUERY_FILE}" && + notify-send "'${NAME}' is bookmarked." +} + +delete_bookmark() { + NAME="$(get_selection)" + + [ -z "${NAME}" ] && error_notify "Failed to delete the bookmark." && return + + sed "/^${NAME}=/d" "${URLQUERY_FILE}" > "${URLQUERY_FILE}.tmp" + mv "${URLQUERY_FILE}.tmp" "${URLQUERY_FILE}" + + [ -s "${URLQUERY_FILE}" ] && rg -q "\S" "${URLQUERY_FILE}" || rm "${URLQUERY_FILE}" + + notify-send "'${NAME}' is deleted." +} + +edit_name() { + OLD_NAME="${1}" + NEW_NAME="$(printf "" | DMENU "0" "New Name")" + + [ -z "${NEW_NAME}" ] && return + + URL="$(rg "^${OLD_NAME}=" "${URLQUERY_FILE}" | cut -d= -f2)" + + update_file "^${OLD_NAME}=" "${NEW_NAME}=${URL}" +} + +edit_url() { + NAME="${1}" + NEW_URL="$(echo "" | DMENU "0" "New URL")" + + [ -z "${NEW_URL}" ] && return + + update_file "^${NAME}=.*" "${NAME}=${NEW_URL}" +} + +edit_bookmark() { + NAME="$(get_selection)" + + [ -z "${NAME}" ] && error_notify "Failed to edit the bookmark." && return + + FIELD="$(printf "Name\nURL\n" | DMENU "2" "Edit")" + + case "${FIELD}" in + "Name")edit_name "${NAME}";; + "URL")edit_url "${NAME}" + esac + + notify-send "'${NAME}' is updated." +} + +open_bookmark() { + URL="$(rg "^${SELECTION}=" "${URLQUERY_FILE}" | cut -d= -f2-)" + + [ -z "${URL}" ] && notify-send "Bookmark not found." && exit "1" + + case "${URL}" in + *"search"* | *"wiki"* | *"packages"* | *"chatgpt"*) QUERY="$(echo "" | DMENU "0" "Search")" + URL="${URL}${QUERY}" + ;; + esac + + "${BROWSER}" "${URL}" || notify-send "Failed to open the URL." +} + +ensure_file_exists + +LINE_COUNT="$(wc -l < "${URLQUERY_FILE}")" + +[ "${LINE_COUNT}" -ge "15" ] && LINE_COUNT="15" + +SELECTION="$(get_selection)" + +[ -z "${SELECTION}" ] && exit + +case "${SELECTION}" in + "${ACTION_MENU}") + ACTION="$(printf "Add\nDelete\nEdit\n" | DMENU "3" "Action")" + + case "${ACTION}" in + "Add") add_bookmark ;; + "Delete") delete_bookmark ;; + "Edit") edit_bookmark ;; + esac + ;; + *) + open_bookmark + ;; +esac diff --git a/.local/bin/booksplit b/.local/bin/booksplit @@ -0,0 +1,43 @@ +#!/bin/sh + +# Requires ffmpeg + +[ ! -f "$2" ] && printf "The first file should be the audio, the second should be the timecodes.\\n" && exit + +echo "Enter the album/book title:"; read -r booktitle +echo "Enter the artist/author:"; read -r author +echo "Enter the publication year:"; read -r year + +inputaudio="$1" +ext="${1##*.}" + +# Get a safe file name from the book. +escbook="$(echo "$booktitle" | iconv -c -f UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g")" + +! mkdir -p "$escbook" && + echo "Do you have write access in this directory?" && + exit 1 + +# Get the total number of tracks from the number of lines. +total="$(wc -l < "$2")" + +cmd="ffmpeg -i \"$inputaudio\" -nostdin -y" + +while read -r x; +do + end="$(echo "$x" | cut -d' ' -f1)" + file="$escbook/$(printf "%.2d" "$track")-$esctitle.$ext" + if [ -n "$start" ]; then + cmd="$cmd -metadata artist=\"$author\" -metadata title=\"$title\" -metadata album=\"$booktitle\" -metadata year=\"$year\" -metadata track=\"$track\" -metadata total=\"$total\" -ss \"$start\" -to \"$end\" -vn -c:a copy \"$file\" " + fi + title="$(echo "$x" | cut -d' ' -f2-)" + esctitle="$(echo "$title" | iconv -c -f UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g")" + track="$((track+1))" + start="$end" +done < "$2" + +# Last track must be added out of the loop. +file="$escbook/$(printf "%.2d" "$track")-$esctitle.$ext" +cmd="$cmd -metadata artist=\"$author\" -metadata title=\"$title\" -metadata album=\"$booktitle\" -metadata year=\"$year\" -metadata track=\"$track\" -ss \"$start\" -vn -c copy \"$file\"" + +eval "$cmd" diff --git a/.local/bin/check_lyrics b/.local/bin/check_lyrics @@ -0,0 +1,21 @@ +#!/bin/sh + +MUSIC_DIR="${1:-.}" + +# Check FLAC files for matching lyrics +fd --glob "*.flac" -t f "$MUSIC_DIR" | while IFS= read -r flac_file; do + base_name="${flac_file%.flac}" + lyrics_file="${base_name}.lrc" + if [ ! -f "$lyrics_file" ]; then + echo "Mismatch: $(basename "$flac_file" .flac) exists, but $(basename "$lyrics_file") is missing" + fi +done + +# Check for orphaned lyrics files +fd --glob "*.lrc" -t f "$MUSIC_DIR" | while IFS= read -r lyrics_file; do + base_name="${lyrics_file%.lrc}" + flac_file="${base_name}.flac" + if [ ! -f "$flac_file" ]; then + echo "Orphaned: $(basename "$lyrics_file" .lrc) exists, but $(basename "$flac_file") is missing" + fi +done diff --git a/.local/bin/chkmd5m b/.local/bin/chkmd5m @@ -0,0 +1,31 @@ +#!/bin/sh +# Compare MD5 hashes of audio streams from multiple files using ffmpeg + +if [ "$#" -lt 2 ]; then + echo "Usage: $0 <file1> <file2> [file3 ...]" + exit 1 +fi + +get_md5() { + ffmpeg -v error -i "$1" -map 0:a:0 -f md5 - 2>/dev/null | cut -d= -f2 +} + +echo "Comparing audio hashes:" +echo + +first_file=$1 +first_hash=$(get_md5 "$first_file") + +echo "$first_file : $first_hash" +shift + +for file in "$@"; do + hash=$(get_md5 "$file") + echo "$file : $hash" + if [ "$hash" = "$first_hash" ]; then + echo "✅ Matches $first_file" + else + echo "❌ Differs from $first_file" + fi + echo +done diff --git a/.local/bin/chooseprofile b/.local/bin/chooseprofile @@ -0,0 +1,12 @@ +#!/bin/sh + +profiles=$(awk -F= '/^\[Profile/ {profile=$1} /Name/ && !/default/ {print $2}' ~/.mozilla/icecat/profiles.ini) + +profile=$(echo "$profiles" | mew -p "Select IceCat Profile" -l 10) + +if [ -z "$profile" ] || ! echo "$profiles" | rg -q "^$profile$"; then + echo "Invalid selection or no profile selected. Exiting." + exit 1 +fi + +swaymsg exec "icecat -P $profile" diff --git a/.local/bin/compiler b/.local/bin/compiler @@ -0,0 +1,42 @@ +#!/bin/sh + +# This script will compile or run another finishing operation on a document. I +# have this script run via vim. + +# Compiles .tex. groff (.mom, .ms), .rmd, .md, .org. Opens .sent files as sent +# presentations. Runs scripts based on extension or shebang. + +file="${1}" +ext="${file##*.}" +dir=${file%/*} +base="${file%.*}" + +cd "${dir}" || exit "1" + +case "${ext}" in + [0-9]) preconv "${file}" | refer -PS -e | groff -mandoc -T pdf > "${base}.pdf" ;; + mom|ms) preconv "${file}" | refer -PS -e | groff -T pdf -m"${ext}" > "${base}.pdf" ;; + c) cc "${file}" -o "${base}" && "${base}" ;; + cob) cobc -x -o "$base" "$file" && "$base" ;; + cpp|cc) g++ "${file}" -o "${base}" && "${base}" ;; + cs) mcs "${file}" && mono "${base}.exe" ;; + go) go run "${file}" ;; + h) doas make install ;; + java) javac -d classes "${file}" && java -cp classes "${base}" ;; + m) octave "${file}" ;; + md) [ -x "$(command -v lowdown)" ] && \ + lowdown --parse-no-intraemph "${file}" -Tms | groff -mpdfmark -ms -kept -T pdf > "${base}.pdf" || \ + [ -x "$(command -v groffdown)" ] && \ + groffdown -i "${file}" | groff -T pdf > "${base}.pdf" || \ + pandoc -t ms --highlight-style="kate" -s -o "${base}.pdf" "${file}" ;; + org) emacs "${file}" --batch -u "${USER}" -f org-latex-export-to-pdf ;; + py) python "${file}" ;; + rink) rink -f "${file}" ;; + [rR]md) Rscript -e "rmarkdown::render('${file}', quiet=TRUE)" ;; + rs) cargo build && cargo run --quiet ;; + sass) sassc -a "${file}" "${base}.css" ;; + scad) openscad -o "${base}.stl" "${file}" ;; + sent) setsid -f sent "${file}" 2> "/dev/null" ;; + tex) latexmk ;; + *) sed -n '/^#!/s/^#!//p; q' "${file}" | xargs -r -I % "${file}" ;; +esac diff --git a/.local/bin/cron/README.md b/.local/bin/cron/README.md @@ -0,0 +1,14 @@ +# Important Note + +These cronjobs have components that require information about your current display to display notifications correctly. + +When you add them as cronjobs, I recommend you precede the command with commands as those below: + +``` +export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u $USER)/bus; export WAYLAND_DISPLAY=wayland-1; . $HOME/.zprofile; then_command_goes_here +``` + +This ensures that notifications will display, wtype/wlrctl commands will function and environmental variables will work as well. + +# Also important +DBUS_SESSION_BUS_ADDRESS variable will always have a random address in the /tmp directory when you are using dinit as your init system. diff --git a/.local/bin/cron/checkup b/.local/bin/cron/checkup @@ -0,0 +1,13 @@ +#!/bin/sh + +# Syncs repositories and downloads updates, meant to be run as a cronjob. + +doas pacman -Syyuw --noconfirm || notify-send "Error downloading updates. + +Check your internet connection, if pacman is already running, or run update manually to see errors." +pkill -RTMIN+8 "${STATUSBAR:-i3blocks}" + +if pacman -Qu | rg -v "\[ignored\]" +then + notify-send "Repository Sync" "Updates available." +fi diff --git a/.local/bin/cron/crontog b/.local/bin/cron/crontog @@ -0,0 +1,6 @@ +#!/bin/sh + +# Toggles all cronjobs off/on. +# Stores disabled crontabs in ~/.config/cronsaved until restored. + +([ -f "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved ] && crontab - < "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && rm "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && notify-send "🕓 Cronjobs re-enabled.") || ( crontab -l > "${XDG_CONFIG_HOME:-$HOME/.config}"/cronsaved && crontab -r && notify-send "🕓 Cronjobs saved and disabled.") diff --git a/.local/bin/cron/mailup b/.local/bin/cron/mailup @@ -0,0 +1,9 @@ +#!/bin/sh + +pgrep -f mailsync && exit + +echo 🔃 > /tmp/mailupdate +pkill -RTMIN+12 "${STATUSBAR:-i3blocks}" +/usr/bin/mailsync +rm -f /tmp/mailupdate +pkill -RTMIN+12 "${STATUSBAR:-i3blocks}" diff --git a/.local/bin/cron/newsup b/.local/bin/cron/newsup @@ -0,0 +1,15 @@ +#!/bin/sh + +# Set as a cron job to check for new RSS entries for newsboat. +# If newsboat is open, sends it an "R" key to refresh. + +export XDG_RUNTIME_DIR=/run/user/1000 +export WAYLAND_DISPLAY=wayland-1 + +pgrep -f newsboat$ && /usr/bin/wlrctl window focus title:newsboat && /usr/bin/wlrctl keyboard type R && exit + +echo "<span color='#7F7F7F'>[updating]</span>" > /tmp/newsupdate +pkill -RTMIN+6 "${STATUSBAR:-i3blocks}" +/usr/bin/newsboat -x reload +rm -f /tmp/newsupdate +pkill -RTMIN+6 "${STATUSBAR:-i3blocks}" diff --git a/.local/bin/define b/.local/bin/define @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +word=${1:-$(xclip -o -selection primary 2>/dev/null || wl-paste 2>/dev/null)} + +# Check for empty word or special characters +[[ -z "$word" || "$word" =~ [\/] ]] && notify-send -h string:bgcolor:#bf616a -t 3000 "Invalid input." && exit 0 + +query=$(curl -s --connect-timeout 5 --max-time 10 "https://api.dictionaryapi.dev/api/v2/entries/en_US/$word") + +# Check for connection error (curl exit status stored in $?) +[ $? -ne 0 ] && notify-send -h string:bgcolor:#bf616a -t 3000 "Connection error." && exit 1 + +# Check for invalid word response +[[ "$query" == *"No Definitions Found"* ]] && notify-send -h string:bgcolor:#bf616a -t 3000 "Invalid word." && exit 0 + +# Show only first 3 definitions +def=$(echo "$query" | jq -r '[.[].meanings[] | {pos: .partOfSpeech, def: .definitions[].definition}] | .[:3].[] | "\n\(.pos). \(.def)"') + +# Requires a notification daemon to be installed +notify-send -t 60000 "$word -" "$def" + + +### MORE OPTIONS :) + +# Show first definition for each part of speech (thanks @morgengabe1 on youtube) +# def=$(echo "$query" | jq -r '.[0].meanings[] | "\(.partOfSpeech): \(.definitions[0].definition)\n"') + +# Show all definitions +# def=$(echo "$query" | jq -r '.[].meanings[] | "\n\(.partOfSpeech). \(.definitions[].definition)"') + +# Regex + rg for just definition, if anyone prefers that to jq +# def=$(rg -Po '"definition":"\K(.*?)(?=")' <<< "$query") + +# bold=$(tput bold) # Print text bold with echo, for visual clarity +# normal=$(tput sgr0) # Reset text to normal +# echo "${bold}Definition of $word" +# echo "${normal}$def" diff --git a/.local/bin/displayselect b/.local/bin/displayselect @@ -0,0 +1,82 @@ +#!/bin/sh + +# A UI for detecting and selecting all displays. Probes xrandr for connected +# displays and lets user select one to use. User may also select "manual +# selection" which opens arandr. + +twoscreen() { # If multi-monitor is selected and there are two screens. + + mirror=$(printf "no\\nyes" | dmenu -i -p "Mirror displays?") + # Mirror displays using native resolution of external display and a scaled + # version for the internal display + if [ "$mirror" = "yes" ]; then + external=$(echo "$screens" | dmenu -i -p "Optimize resolution for:") + internal=$(echo "$screens" | rg -v "$external") + + res_external=$(xrandr --query | sed -n "/^$external/,/\+/p" | \ + tail -n 1 | awk '{print $1}') + res_internal=$(xrandr --query | sed -n "/^$internal/,/\+/p" | \ + tail -n 1 | awk '{print $1}') + + res_ext_x=$(echo "$res_external" | sed 's/x.*//') + res_ext_y=$(echo "$res_external" | sed 's/.*x//') + res_int_x=$(echo "$res_internal" | sed 's/x.*//') + res_int_y=$(echo "$res_internal" | sed 's/.*x//') + + scale_x=$(echo "$res_ext_x / $res_int_x" | bc -l) + scale_y=$(echo "$res_ext_y / $res_int_y" | bc -l) + + xrandr --output "$external" --auto --scale 1.0x1.0 \ + --output "$internal" --auto --same-as "$external" \ + --scale "$scale_x"x"$scale_y" + else + + primary=$(echo "$screens" | dmenu -i -p "Select primary display:") + secondary=$(echo "$screens" | rg -v ^"$primary"$) + direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?") + xrandr --output "$primary" --auto --scale 1.0x1.0 --output "$secondary" --"$direction"-of "$primary" --auto --scale 1.0x1.0 + fi + } + +morescreen() { # If multi-monitor is selected and there are more than two screens. + primary=$(echo "$screens" | dmenu -i -p "Select primary display:") + secondary=$(echo "$screens" | rg -v ^"$primary"$ | dmenu -i -p "Select secondary display:") + direction=$(printf "left\\nright" | dmenu -i -p "What side of $primary should $secondary be on?") + tertiary=$(echo "$screens" | rg -v ^"$primary"$ | rg -v ^"$secondary"$ | dmenu -i -p "Select third display:") + xrandr --output "$primary" --auto --output "$secondary" --"$direction"-of "$primary" --auto --output "$tertiary" --"$(printf "left\\nright" | rg -v "$direction")"-of "$primary" --auto + } + +multimon() { # Multi-monitor handler. + case "$(echo "$screens" | wc -l)" in + 2) twoscreen ;; + *) morescreen ;; + esac ;} + +onescreen() { # If only one output available or chosen. + xrandr --output "$1" --auto --scale 1.0x1.0 $(echo "$allposs" | rg -v "\b$1" | awk '{print "--output", $1, "--off"}' | paste -sd ' ' -) + } + +postrun() { # Stuff to run to clean up. + setbg # Fix background if screen size/arangement has changed. + { killall dunst ; setsid -f dunst ;} >/dev/null 2>&1 # Restart dunst to ensure proper location on screen + } + +# Get all possible displays +allposs=$(xrandr -q | rg "connected") + +# Get all connected screens. +screens=$(echo "$allposs" | awk '/ connected/ {print $1}') + +# If there's only one screen +[ "$(echo "$screens" | wc -l)" -lt 2 ] && + { onescreen "$screens"; postrun; notify-send "💻 Only one screen detected." "Using it in its optimal settings..."; exit ;} + +# Get user choice including multi-monitor and manual selection: +chosen=$(printf "%s\\nmulti-monitor\\nmanual selection" "$screens" | dmenu -i -p "Select display arangement:") && +case "$chosen" in + "manual selection") arandr ; exit ;; + "multi-monitor") multimon ;; + *) onescreen "$chosen" ;; +esac + +postrun diff --git a/.local/bin/dmenuhandler b/.local/bin/dmenuhandler @@ -0,0 +1,21 @@ +#!/bin/sh + +# Feed this script a link and it will give mew +# some choice programs to use to open it. +feed="${1:-$(true | mew -p 'Paste URL or file path')}" + +case "$(printf "copy url\\nswayimg\\nsetbg\\nPDF\\nbrowser\\nlynx\\nvim\\nmpv\\nmpv loop\\nmpv float\\nqueue download\\nqueue yt-dlp\\nqueue yt-dlp audio" | mew -i -p "Open it with?")" in + "copy url") echo "$feed" | wl-copy ;; + mpv) setsid -f mpv -quiet "$feed" >/dev/null 2>&1 ;; + "mpv loop") setsid -f mpv -quiet --loop "$feed" >/dev/null 2>&1 ;; + "mpv float") setsid -f "$TERMINAL" -e mpv --geometry=+0-0 --autofit=30% --title="mpvfloat" "$feed" >/dev/null 2>&1 ;; + "queue yt-dlp") qndl "$feed" >/dev/null 2>&1 ;; + "queue yt-dlp audio") qndl "$feed" 'yt-dlp -o "%(title)s.%(ext)s" -f bestaudio --embed-metadata --restrict-filenames' ;; + "queue download") qndl "$feed" 'curl -LO' >/dev/null 2>&1 ;; + PDF) curl -sL "$feed" > "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && zathura "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;; + swayimg) curl -sL "$feed" > "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && swayimg "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;; + vim) curl -sL "$feed" > "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" && setsid -f "$TERMINAL" -e "$EDITOR" "/tmp/$(echo "$feed" | sed "s|.*/||;s/%20/ /g")" >/dev/null 2>&1 ;; + setbg) curl -L "$feed" > $XDG_CACHE_HOME/pic ; swaybg -i $XDG_CACHE_HOME/pic -m fill & >/dev/null 2>&1 ;; + browser) setsid -f "$BROWSER" "$feed" >/dev/null 2>&1 ;; + lynx) lynx "$feed" >/dev/null 2>&1 ;; +esac diff --git a/.local/bin/dmenumountcifs b/.local/bin/dmenumountcifs @@ -0,0 +1,19 @@ +#!/bin/sh +# Gives a mew prompt to mount unmounted local NAS shares for read/write. +# Requirements - "%wheel ALL=(ALL) NOPASSWD: ALL" +# +# Browse for mDNS/DNS-SD services using the Avahi daemon... +srvname=$(avahi-browse _smb._tcp -t | awk '{print $4}' | mew -i -p "Which NAS?") || exit 1 +notify-send "Searching for network shares..." "Please wait..." +# Choose share disk... +share=$(smbclient -L "$srvname" -N | rg Disk | awk '{print $1}' | mew -i -p "Mount which share?") || exit 1 +# Format URL... +share2mnt=//"$srvname".local/"$share" + +sharemount() { + mounted=$(mount -v | rg "$share2mnt") || ([ ! -d /mnt/"$share" ] && doas mkdir /mnt/"$share") + [ -z "$mounted" ] && doas mount -t cifs "$share2mnt" -o user=nobody,password="",noperm /mnt/"$share" && notify-send "Netshare $share mounted" && exit 0 + notify-send "Netshare $share already mounted"; exit 1 +} + +sharemount diff --git a/.local/bin/dmenupass b/.local/bin/dmenupass @@ -0,0 +1,6 @@ +#!/bin/sh + +# This script is the SUDO_ASKPASS variable, meaning that it will be used as a +# password prompt if needed. + +mew -P -p "password:" <&- diff --git a/.local/bin/dmenurecord b/.local/bin/dmenurecord @@ -0,0 +1,121 @@ +#!/bin/sh + +getdim() { + gpu-screen-recorder --list-capture-options | sed 's/|[0-9]\{1,\}x[0-9]\{1,\}//g' \ + | rofi -dmenu -no-custom -p "Select output: " +} + +updateicon() { \ + echo "$1" > /tmp/recordingicon + pkill -RTMIN+9 "${STATUSBAR:-i3blocks}" +} + +killrecording() { + recpid="$(bat /tmp/recordingpid)" + echo $recpid + kill -2 "$recpid" + rm -f /tmp/recordingpid + updateicon "" + pkill -RTMIN+9 "${STATUSBAR:-i3blocks}" +} + +screencast() { gpu-screen-recorder \ + -w "$(getdim)" \ + -fm content \ + -f 60 \ + -a default_output \ + -a default_input \ + -o "$HOME/screencast-$(date '+%y%m%d-%H%M-%S').mkv" & + echo $! > /tmp/recordingpid + updateicon "󰑋 " +} + +video() { gpu-screen-recorder \ + -w "$(getdim)" \ + -fm content \ + -f 60 \ + -o "$HOME/video-$(date '+%y%m%d-%H%M-%S').mkv" & + echo $! > /tmp/recordingpid + updateicon "󰑋" +} + +webcamhidef() { ffmpeg \ + -f v4l2 \ + -i /dev/video0 \ + -video_size 1920x1080 \ + "$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" & + echo $! > /tmp/recordingpid + updateicon "󰖠" +} + +webcam() { ffmpeg \ + -f v4l2 \ + -i /dev/video0 \ + -video_size 640x480 \ + "$HOME/webcam-$(date '+%y%m%d-%H%M-%S').mkv" & + echo $! > /tmp/recordingpid + updateicon "󰖠" +} + + +audio() { \ + ffmpeg \ + -f alsa -i default \ + -c:a flac \ + "$HOME/audio-$(date '+%y%m%d-%H%M-%S').flac" & + echo $! > /tmp/recordingpid + updateicon "" +} + +replay() { gpu-screen-recorder \ + -w "$(getdim)" \ + -fm content \ + -f 60 \ + -r 60 \ + -c mkv \ + -a default_output \ + -a default_input \ + -o "/mnt/ssd/rndm/clips/replays" & + echo $! > /tmp/recordingpid + updateicon "󰑙 󰑋 " +} + +savereplay() { + recpid="$(bat /tmp/recordingpid)" + echo $recpid + kill -37 "$recpid" + pkill -RTMIN+9 "${STATUSBAR:-i3blocks}" + notify-send "Replay" "Last 60 seconds saved" + exit +} + +askrecording() { \ + choice=$(printf "screencast\\nreplay\\nvideo\\naudio\\nwebcam\\nwebcam (hi-def)" | rofi -dmenu -no-custom -i -p "Select recording style") + case "$choice" in + screencast) screencast;; + replay) replay;; + audio) audio;; + video) video;; + webcam) webcam;; + "webcam (hi-def)") webcamhidef;; + esac +} + +asktoend() { \ + if grep -q "󰑙" /tmp/recordingicon; then + response=$(printf "No\\nYes" | rofi -dmenu -no-custom -i -p "Replay is active. Save it?") && + [ "$response" = "Yes" ] && savereplay + fi + + response=$(printf "No\\nYes" | rofi -dmenu -no-custom -i -p "Recording still active. End recording?") && + [ "$response" = "Yes" ] && killrecording +} + +case "$1" in + screencast) screencast;; + replay) replay;; + audio) audio;; + video) video;; + kill) killrecording;; + *) ([ -f /tmp/recordingpid ] && asktoend && exit) || askrecording;; +esac diff --git a/.local/bin/doas_askpass b/.local/bin/doas_askpass @@ -0,0 +1,47 @@ +#!/usr/bin/expect -- + +# askpass implementation for doas +# example usage: DOAS_ASKPASS="mew -P -p password:" doas_askpass echo working + +# don't mind the man behind the curtain +log_user 0 + +# no command, then nothing to do +if { $argc == 0 } { exit 0 } + +# treat all arguments as command input +set cmd [lrange $argv 0 end]; + +# read askpass from env or fallback to dmenu_pass () +if {[info exists ::env(DOAS_ASKPASS)]} { + set askpass "$::env(DOAS_ASKPASS)" +} else { + set askpass "dmenu_pass password:" +} + +# read password from user +set pwd [exec {*}$askpass] + +# spawn doas operation +spawn doas {*}$cmd + +# send password and execute command +expect "doas*password:" { + send -- "$pwd\r" + expect \r + log_user 1 + expect eof +} + +# get the exit status of the spawned doas command +set status [wait] + +# check exit code (4th element of wait result) +set exit_code [lindex $status 3] + +# exit with 1 if doas failed, else 0 +if { $exit_code != 0 } { + exit 1 +} else { + exit 0 +} diff --git a/.local/bin/getbib b/.local/bin/getbib @@ -0,0 +1,71 @@ +#!/bin/sh + +BIB_FILE="${HOME}/latex/uni.bib" +[ -f "${BIB_FILE}" ] || BIB_FILE="${2:-$(find "${HOME}" -path "${HOME}/.*" \ + -prune -o -type "f" -name "*.bib" -print -quit)}" + +{ [ -f "${BIB_FILE}" ] || [ "${2}" ]; } || { + printf "%s\n" "Create a .bib file or provide as \$2." && exit "1" +} + +filter() { + sed -n -E 's/.*((DOI|doi)((\.(org))?\/?|:? *))([^: ]+[^ .]).*/\6/p; T; q' +} + +fpdf() { + pdf="${1}" + doi="$(pdfinfo "${pdf}" 2> "/dev/null" | filter)" + + [ "${doi}" ] || doi="$(pdftotext -q -l "2" "${pdf}" - 2> "/dev/null" | filter)" + + [ "${doi}" ] || printf "%s\n" "No DOI found for PDF: ${pdf}" >&2 + + printf "%s\n" "${doi}" +} + +arrange() { + sed 's/\}, /\},\n /g + s/, /,\n / + s/ }/\n}/ + s/,\s*pages=/,\n\tpages=/' | + sed '1s/^ *// + 1s/[0-9]*\([0-9]\{2\}\)/\1/ + 1s/_// + 1s/.*/\L&/ + s/.*=/\L&/ + s/=/ = /' +} + +doi2bib() { + doi="${1#doi:}" + url="https://api.crossref.org/works/${doi}/transform/application/x-bibtex" + entry="$(curl -kLsS --no-fail "${url}" | arrange)" + red='\033[0;31m' + reset='\033[0m' + + printf "${red}%s${reset}\n" "${entry}" + + [ "${entry%"${entry#?}"}" != "@" ] && { + printf "%s\n" "Failed to fetch bibtex entry for DOI: ${doi}" + return "1" + } + + rg -iFq "doi = {${doi}}" "${BIB_FILE}" 2> "/dev/null" && { + printf "%s\n" "Bibtex entry for DOI: ${doi} already exists in the file." + } || { + [ -s "${BIB_FILE}" ] && printf "\n" >> "${BIB_FILE}" + printf "%s\n" "${entry}" >> "${BIB_FILE}" + printf "%s\n" "Added bibtex entry for DOI: ${doi}" + } +} + +[ "${1}" ] || { + printf "%s\n" "Give either a pdf file or a DOI or a directory path that has PDFs as an argument." + exit "1" +} + +[ -f "${1}" ] && doi="$(fpdf "${1}")" && doi2bib "${doi}" && exit "0" + +[ -d "${1}" ] && for i in "${1}"/*.pdf; do doi="$(fpdf "${i}")" && doi2bib "${doi}"; done && exit "0" + +doi="$(printf "%s\n" "${1}" | filter)" && doi2bib "${doi}" diff --git a/.local/bin/getcomproot b/.local/bin/getcomproot @@ -0,0 +1,9 @@ +#!/bin/sh + +# A helper script for LaTeX/groff files used by `compiler` and `opout`. +# The user can add the root file of a larger project as a comment as below: +# % root = mainfile.tex +# And the compiler script will run on that instead of the opened file. + +texroot="$(sed -n 's/^\s*%.*root\s*=\s*\(\S\+\).*/\1/p' "${1}")" +[ -f "${texroot}" ] && readlink -f "${texroot}" || exit "1" diff --git a/.local/bin/ifinstalled b/.local/bin/ifinstalled @@ -0,0 +1,12 @@ +#!/bin/sh + +# Some optional functions in LARBS require programs not installed by default. I +# use this little script to check to see if a command exists and if it doesn't +# it informs the user that they need that command to continue. This is used in +# various other scripts for clarity's sake. + +for x in "$@"; do + if ! which "$x" >/dev/null 2>&1 && ! pacman -Qq "$x" >/dev/null 2>&1; then + notify-send "📦 $x" "must be installed for this function." && exit 1 ; + fi +done diff --git a/.local/bin/linkhandler b/.local/bin/linkhandler @@ -0,0 +1,32 @@ +#!/bin/sh + +# Feed script a url or file location. +# If an image, it will view in swayimg, +# if a video or gif, it will view in mpv +# if a music file or pdf, it will download, +# otherwise it opens link in browser. + +if [ -z "$1" ]; then + url="$(wl-paste)" +else + url="$1" +fi + +# Check if the URL is from inv.nadeko.net and adjust it for YouTube +echo "$url" | rg -q 'inv.nadeko.net/watch' +if [ $? -eq 0 ]; then + url="https://www.youtube.com/watch?v=$(echo "$url" | sed 's/.*inv\.nadeko\.net\/watch?v=\([^&]*\)/\1/')" +fi + +case "$url" in + *mkv|*webm|*mp4|*youtube.com/watch*|*youtube.com/playlist*|*youtube.com/v/*|*youtube.com/shorts*|*youtu.be*|*hooktube.com*|*bitchute.com*|*videos.lukesmith.xyz*|*odysee.com*) + setsid -f mpv -quiet "$url" >/dev/null 2>&1 ;; + *png|*jpg|*jpe|*jpeg|*gif|*webp) + curl -sL "$url" > "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" && swayimg "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" >/dev/null 2>&1 & ;; + *pdf|*cbz|*cbr) + curl -sL "$url" > "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" && zathura "/tmp/$(echo "$url" | sed "s/.*\///;s/%20/ /g")" >/dev/null 2>&1 & ;; + *mp3|*flac|*opus|*mp3?source*) + qndl "$url" 'curl -LO' >/dev/null 2>&1 ;; + *) + [ -f "$url" ] && setsid -f "$TERMINAL" -e "$EDITOR" "$url" >/dev/null 2>&1 || setsid -f "$BROWSER" "$url" >/dev/null 2>&1 +esac diff --git a/.local/bin/maimpick b/.local/bin/maimpick @@ -0,0 +1,33 @@ +#!/bin/sh + +# This is bound to Shift+PrintScreen by default, requires grim (also optionally tesseract and swappy). It lets you +# choose the kind of screenshot to take, including copying the image or even +# highlighting an area to copy. + +# variables +output="$(date '+%y%m%d-%H%M-%S').png" +wclip_cmd="wl-copy -t image/png" +ocr_cmd="wl-copy" + +case "$(printf "a selected area\\ncurrent window\\nfull screen\\na selected area (copy)\\ncurrent window (copy)\\nfull screen (copy)\\na selected area (swappy)\\ncurrent window (swappy)\\nfull screen (swappy)\\ncopy selected image to text" | mew -l 10 -i -p "Screenshot which area?")" in + "a selected area") geometry=$(slurp) && sleep 0.2 && grim -g "$geometry" pic-selected-"${output}" && notify-send "📸 maimpick" "Screenshot saved as pic-selected-$output." ;; + "current window") + geometry=$(swaymsg --raw -t get_tree | jq -r '.. | select(type == "object" and .focused == true) | .rect | "\(.x),\(.y) \(.width)x\(.height)"') + sleep 0.2 + grim -g "$geometry" pic-window-"${output}" && notify-send "📸 maimpick" "Screenshot saved as pic-window-$output." ;; + "full screen") geometry=$(slurp -o) && sleep 0.2 && grim -g "$geometry" pic-full-"${output}" && notify-send "📸 maimpick" "Screenshot saved as pic-full-$output." ;; + "a selected area (copy)") geometry=$(slurp) && sleep 0.2 && grim -g "$geometry" - | ${wclip_cmd} && notify-send "📸 maimpick" "Selected area screenshot copied to clipboard." ;; + "current window (copy)") + geometry=$(swaymsg --raw -t get_tree | jq -r '.. | select(type == "object" and .focused == true) | .rect | "\(.x),\(.y) \(.width)x\(.height)"') + sleep 0.2 + grim -g "$geometry" - | ${wclip_cmd} && notify-send "📸 maimpick" "Window screenshot copied to clipboard." ;; + "full screen (copy)") geometry=$(slurp -o) && sleep 0.2 && grim -g "$geometry" - | ${wclip_cmd} && notify-send "📸 maimpick" "Full screen screenshot copied to clipboard.";; + "a selected area (swappy)") geometry=$(slurp) && sleep 0.2 && grim -g "$geometry" - | swappy -f - ;; + "current window (swappy)") + geometry=$(swaymsg --raw -t get_tree | jq -r '.. | select(type == "object" and .focused == true) | .rect | "\(.x),\(.y) \(.width)x\(.height)"') + sleep 0.2 + grim -g "$geometry" - | swappy -f - ;; + "full screen (swappy)") geometry=$(slurp -o) && sleep 0.2 && grim -g "$geometry" - | swappy -f - ;; + "copy selected image to text") tmpfile=$(mktemp /tmp/ocr-XXXXXX.png) && slurp | grim -g - - > "$tmpfile" && tesseract "$tmpfile" - -l eng | ${ocr_cmd} && rm "$tmpfile" && notify-send "📸 maimpick" "Detected text copied to clipboard.\n$(wl-paste)" ;; + *) notify-send "📸 maimpick" "Wrong option." +esac diff --git a/.local/bin/mounter b/.local/bin/mounter @@ -0,0 +1,114 @@ +#!/bin/bash + +IFS=' +' +# Function for escaping cell-phone names. +escape(){ echo "$@" | iconv -cf UTF-8 -t ASCII//TRANSLIT | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed "s/-\+/-/g;s/\(^-\|-\$\)//g" ;} + +# Check for phones. +phones="$(simple-mtpfs -l 2>/dev/null | sed "s/^/📱/")" +mountedphones="$(rg "simple-mtpfs" /etc/mtab)" +# If there are already mounted phones, remove them from the list of mountables. +[ -n "$mountedphones" ] && phones="$(for phone in $phones; do + for mounted in $mountedphones; do + escphone="$(escape "$phone")" + [[ "$mounted" =~ "$escphone" ]] && break 1 + done && continue 1 + echo "$phone" +done)" + +# Check for drives. +lsblkoutput="$(doas lsblk -rpo "uuid,name,type,size,label,mountpoint,fstype")" +# Get all LUKS drives +allluks="$(echo "$lsblkoutput" | rg crypto_LUKS)" +# Get a list of the LUKS drive UUIDs already decrypted. +decrypted="$(find /dev/disk/by-id/dm-uuid-CRYPT-LUKS2-* | sed "s|.*LUKS2-||;s|-.*||")" +# Functioning for formatting drives correctly for mew: +filter() { sed "s/ /:/g" | awk -F':' '$7==""{printf "%s%s (%s) %s\n",$1,$3,$5,$6}' ; } + +# Get only LUKS drives that are not decrypted. +unopenedluks="$(for drive in $allluks; do + uuid="${drive%% *}" + uuid="${uuid//-}" # This is a bashism. + [ -n "$decrypted" ] && for open in $decrypted; do + [ "$uuid" = "$open" ] && break 1 + done && continue 1 + echo "🔒 $drive" +done | filter)" + +# Get all normal, non-encrypted or decrypted partitions that are not mounted. +normalparts="$(echo "$lsblkoutput"| rg -v crypto_LUKS | rg 'part|rom|crypt' | sed "s/^/💾 /" | filter )" + +# Add all to one variable. If no mountable drives found, exit. +alldrives="$(echo "$phones +$unopenedluks +$normalparts" | sed "/^$/d;s/ *$//")" + +# Quit the script if a sequential command fails. +set -e + +test -n "$alldrives" + +# Feed all found drives to mew and get user choice. +chosen="$(echo "$alldrives" | mew -p "Mount which drive?" -i)" + +# Function for prompting user for a mountpoint. +getmount(){ + mp="$(find /mnt /media /mount /home -maxdepth 1 -type d 2>/dev/null | mew -i -p "Mount this drive where?")" + test -n "$mp" + if [ ! -d "$mp" ]; then + mkdiryn=$(printf "No\\nYes" | mew -i -p "$mp does not exist. Create it?") + [ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" || doas_askpass mkdir -p "$mp") + fi +} + +attemptmount(){ + # Attempt to mount without a mountpoint, to see if drive is in fstab. + doas mount "$chosen" || return 1 + notify-send "💾Drive Mounted." "$chosen mounted." + exit +} + +case "$chosen" in + 💾*) + chosen="${chosen%% *}" + chosen="${chosen:1}" # This is a bashism. + parttype="$(echo "$lsblkoutput" | rg "$chosen" | awk '{print $NF}' | tr -d '[:space:]\r\n')" + attemptmount || getmount + case "${parttype##* }" in + vfat) doas mount -t vfat "$chosen" "$mp" -o rw,umask=0000 ;; + btrfs) doas mount "$chosen" "$mp" ;; + ntfs) doas mount -t ntfs3 "$chosen" "$mp" ;; + *) doas mount "$chosen" "$mp" -o uid="$(id -u)",gid="$(id -g)" ;; + esac + notify-send "💾Drive Mounted." "$chosen mounted to $mp." + ;; + + 🔒*) + chosen="${chosen%% *}" + chosen="${chosen:1}" # This is a bashism. + # Number the drive. + while true; do + [ -f "/dev/mapper/usb$num" ] || break + num="$(printf "%02d" "$((num +1))")" + done + + # Decrypt in a terminal window + ${TERMINAL:-st} -a termfloat -e doas cryptsetup open "$chosen" "usb$num" + # Check if now decrypted. + test -b "/dev/mapper/usb$num" + + attemptmount || getmount + doas mount "/dev/mapper/usb$num" "$mp" + notify-send "🔓Decrypted drive Mounted." "$chosen decrypted and mounted to $mp." + ;; + + 📱*) + notify-send "❗Note" "Remember to allow file access on your phone now." + getmount + number="${chosen%%:*}" + number="${chosen:1}" # This is a bashism. + doas_askpass simple-mtpfs -o allow_other -o fsname="simple-mtpfs-$(escape "$chosen")" --device "$number" "$mp" + notify-send "🤖 Android Mounted." "Android device mounted to $mp." + ;; +esac diff --git a/.local/bin/noisereduce b/.local/bin/noisereduce @@ -0,0 +1,81 @@ +#!/usr/bin/sh + +usage () +{ + printf "Usage : noisereduce <input video file> <output video file>\n" + exit +} + +# Tests for requirements +ifinstalled ffmpeg || { echo >&2 "We require 'ffmpeg' but it's not installed."; exit 1; } +ifinstalled sox || { echo >&2 "We require 'ffmpeg' but it's not installed."; exit 1; } + +if [ "$#" -ne 2 ] +then + usage +fi + +if [ ! -e "$1" ] +then + printf "File not found: %s\n" "$1" + exit +fi + +if [ -e "$2" ] +then + printf "File %s already exists, overwrite? [y/N]\n: " "$2" + read -r yn + case $yn in + [Yy]* ) ;; + * ) exit;; + esac +fi + +inBasename=$(basename "$1") +inExt="${inBasename##*.}" + +isVideoStr=$(ffprobe -v warning -show_streams "$1" | rg codec_type=video) +if [ -n "$isVideoStr" ] +then + isVideo=1 + printf "Detected %s as a video file\n" "$inBasename" +else + isVideo=0 + printf "Detected %s as an audio file\n" "$inBasename" +fi + +printf "Sample noise start time [00:00:00]: " +read -r sampleStart +if [ -z "$sampleStart" ] ; then sampleStart="00:00:00"; fi +printf "Sample noise end time [00:00:00.900]: " +read -r sampleEnd +if [ -z "$sampleEnd" ] ; then sampleEnd="00:00:00.900"; fi +printf "Noise reduction amount [0.21]: " +read -r sensitivity +if [ -z "$sensitivity" ] ; then sensitivity="0.21"; fi + + +tmpVidFile="/tmp/noiseclean_tmpvid.$inExt" +tmpAudFile="/tmp/noiseclean_tmpaud.wav" +noiseAudFile="/tmp/noiseclean_noiseaud.wav" +noiseProfFile="/tmp/noiseclean_noise.prof" +tmpAudCleanFile="/tmp/noiseclean_tmpaud-clean.wav" + +printf "Cleaning noise on %s...\n" "$1" + +if [ $isVideo -eq "1" ]; then + ffmpeg -v warning -y -i "$1" -qscale:v 0 -vcodec copy -an "$tmpVidFile" + ffmpeg -v warning -y -i "$1" -qscale:a 0 "$tmpAudFile" +else + cp "$1" "$tmpAudFile" +fi +ffmpeg -v warning -y -i "$1" -vn -ss "$sampleStart" -t "$sampleEnd" "$noiseAudFile" +sox "$noiseAudFile" -n noiseprof "$noiseProfFile" +sox "$tmpAudFile" "$tmpAudCleanFile" noisered "$noiseProfFile" "$sensitivity" +if [ $isVideo -eq "1" ]; then + ffmpeg -v warning -y -i "$tmpAudCleanFile" -i "$tmpVidFile" -vcodec copy -qscale:v 0 -qscale:a 0 "$2" +else + cp "$tmpAudCleanFile" "$2" +fi + +printf "Done" diff --git a/.local/bin/opout b/.local/bin/opout @@ -0,0 +1,13 @@ +#!/bin/sh + +# opout: "open output": A general handler for opening a file's intended output, +# usually the pdf of a compiled document. I find this useful especially +# running from vim. + +basename="${1%.*}" + +case "${*}" in + *.tex|*.sil|*.m[dse]|*.[rR]md|*.mom|*.[0-9]) target="$(getcomproot "$1" || echo "$1")" ; setsid -f xdg-open "${target%.*}".pdf >/dev/null 2>&1 ;; + *.html) setsid -f "$BROWSER" "$basename".html >/dev/null 2>&1 ;; + *.sent) setsid -f sent "$1" >/dev/null 2>&1 ;; +esac diff --git a/.local/bin/pauseallmpv b/.local/bin/pauseallmpv @@ -0,0 +1,10 @@ +#!/bin/sh + +# You might notice all mpv commands are aliased to have this input-ipc-server +# thing. That's just for this particular command, which allows us to pause +# every single one of them with one command! This is bound to super + shift + p +# (with other things) by default and is used in some other places. + +for i in $(ls /tmp/mpvSockets/*); do + echo '{ "command": ["set_property", "pause", true] }' | socat - "$i"; +done diff --git a/.local/bin/peertubetorrent b/.local/bin/peertubetorrent @@ -0,0 +1,9 @@ +#!/bin/sh +# torrent peertube videos, requires the transadd script +# first argument is the video link, second is the quality (360, 480 or 1080) +# 13/07/20 - Arthur Bais + +instance=$(echo "$1" | sed "s|/w.\+||") +vidid=$(echo "$1" | sed "s|.\+/||") +link=$(curl -s "$instance/api/v1/videos/$vidid" | rg -o "$instance/download/torrents/.\{37\}$2.torrent") +transadd "$link" diff --git a/.local/bin/podentr b/.local/bin/podentr @@ -0,0 +1,7 @@ +#!/bin/sh + +# entr command to run `queueandnotify` when newsboat queue is changed + +[ "$(pgrep -x "$(basename "$0")" | wc -l)" -gt 2 ] && exit + +echo "${XDG_DATA_HOME:-$HOME/.local/share}"/newsboat/queue | entr -p queueandnotify 2>/dev/null diff --git a/.local/bin/portal b/.local/bin/portal @@ -0,0 +1,7 @@ +#!/bin/sh +sleep 1 +killall xdg-desktop-portal-wlr +killall xdg-desktop-portal +killall xdg-desktop-portal-termfilechooser +/usr/lib/xdg-desktop-portal-wlr & +/usr/lib/xdg-desktop-portal-termfilechooser -r & diff --git a/.local/bin/qndl b/.local/bin/qndl @@ -0,0 +1,12 @@ +#!/bin/sh + +# $1 is a url; $2 is a command +[ -z "$1" ] && exit +base="$(basename "$1")" +notify-send "⏳ Queuing $base..." +cmd="$2" +[ -z "$cmd" ] && cmd="yt-dlp --embed-metadata -ic" +idnum="$(tsp $cmd "$1")" +realname="$(echo "$base" | sed "s/?\(source\|dest\).*//;s/%20/ /g")" +tsp -D "$idnum" mv "$base" "$realname" +tsp -D "$idnum" notify-send "👍 $realname done." diff --git a/.local/bin/queueandnotify b/.local/bin/queueandnotify @@ -0,0 +1,14 @@ +#!/bin/sh + +# Podboat sucks. This script replaces it. +# It reads the newsboat queue, queuing downloads with taskspooler. +# It also removes the junk from extensions. +queuefile="${XDG_DATA_HOME:-$HOME/.local/share}/newsboat/queue" + +while read -r line; do + [ -z "$line" ] && continue + url="${line%%[ ]*}" + qndl "$url" "curl -LO" +done < "$queuefile" + +echo > "$queuefile" diff --git a/.local/bin/randombg b/.local/bin/randombg @@ -0,0 +1,35 @@ +#!/bin/sh +killall swaybg +PIDFILE="/tmp/randombg.pid" +# Check if the PID file exists and if the process is running +if [ -e "$PIDFILE" ] && kill -0 "$(bat "$PIDFILE")"; then + echo "Another instance of the script is already running." + kill -9 "$(bat $PIDFILE)" # Forcefully kill the old process +fi +echo $$ > "$PIDFILE" + +cleanup() { + [ -n "$OLD_PID" ] && kill "$OLD_PID" 2>/dev/null + rm -f "$PIDFILE" + exit 0 +} +trap cleanup INT TERM EXIT + +sleep_interruptible() { + t=$1 + while [ "$t" -gt 0 ]; do + sleep 1 || return + t=$((t-1)) + done +} + +swaybg -i "$(fd . /mnt/ssd/papes -t f | shuf -n1)" -m fill & +OLD_PID=$! +while true; do + sleep_interruptible 300 + swaybg -i "$(fd . /mnt/ssd/papes -t f | shuf -n1)" -m fill & + NEXT_PID=$! + sleep_interruptible 5 + kill $OLD_PID + OLD_PID=$NEXT_PID +done diff --git a/.local/bin/rssadd b/.local/bin/rssadd @@ -0,0 +1,18 @@ +#!/bin/sh + +if echo "$1" | rg -q "https*://\S\+\.[A-Za-z]\+\S*" ; then + url="$1" +else + url="$(rg -Eom1 '<[^>]+(rel="self"|application/[a-z]+\+xml)[^>]+>' "$1" | + rg -o "https?://[^\" ]")" + + echo "$url" | rg -q "https*://\S\+\.[A-Za-z]\+\S*" || + notify-send "That doesn't look like a full URL." && exit 1 +fi + +RSSFILE="${XDG_CONFIG_HOME:-$HOME/.config}/newsboat/urls" +if awk '{print $1}' "$RSSFILE" | rg "^$url$" >/dev/null; then + notify-send "You already have this RSS feed." +else + echo "$url" >> "$RSSFILE" && notify-send "RSS feed added." +fi diff --git a/.local/bin/rssget b/.local/bin/rssget @@ -0,0 +1,115 @@ +#!/bin/bash + +# Searches the website for RSS feeds and adds them to newsboat url list. Can +# also find hidden RSS feeds on various websites, namely Youtube, Reddit, +# Vimeo, Github, Gitlab and Medium. Gets site url as $1 or (if not present) +# from X clipboard. Gets tags as $2. If it finds more than one feed, calls +# mew for the user to choose which one to add. I have bound it to a keyboard +# shortcut so i copy a site link and easily add its feed to the reader. + +# Inspired by and based on the logic of this extension: +# https://github.com/shevabam/get-rss-feed-url-extension + +# This script requires rssadd to add feeds to the list. + +getlink () { + local url="$1" + feeds="$(curl -s "$url" | rg -x '.*type=.*(rss|rdf|atom).*' | sed 's/ //g')" + url="$(echo $url | sed 's|^\(https://[^/]*/\).*|\1|')" + + for rsspath in $feeds; do + rsspath="$(echo $rsspath | sed -n "s|.*href=['\"]\([^'\"]*\)['\"].*|\1|p")" + if echo "$rsspath" | rg "http" > /dev/null; then + link="$rsspath" + elif echo "$rsspath" | rg "^/" > /dev/null; then + link="$url$(echo $rsspath | sed 's|^/||')" + else + link="$url$rsspath" + fi + echo $link + done +} + +getRedditRss() { + echo "${1%/}.rss" +} + +getYoutubeRss() { + local url="$1" + path=$(echo "$url" | sed -e 's|^http[s]*://||') + case "$path" in + *"/channel/"*) channel_id="$(echo $path | sed -r 's|.*channel/([^/]*).*|\1|')" && feed="https://www.youtube.com/feeds/videos.xml?channel_id=${channel_id}" ;; + *"/c/"*|*"/user/"*) + feed=$(wget -q "$url" -O tmp_rssget_yt \ + && sed -n 's|.*\("rssUrl":"[^"]*\).*|\1|; p' tmp_rssget_yt \ + | rg rssUrl \ + | sed 's|"rssUrl":"||') ;; + esac + echo "$feed" +} + +getVimeoRss() { + local url="$1" + if echo "$url" | rg -q "/videos$"; then + feed_url=$(echo "$url" | sed 's/\/videos$//' | sed 's/\/$/\/rss/') + else + feed_url="${url}/videos/rss" + fi + echo "$feed_url" +} + +getGithubRss () { + local url="${1%/}" + if echo $url | rg "github.com/[^/]*/[a-zA-Z0-9].*" >/dev/null ; then + echo "${url}/commits.atom" + echo "${url}/releases.atom" + echo "${url}/tags.atom" + elif echo $url | rg "github.com/[^/]*(/)" >/dev/null ; then + echo "${url}.atom" + fi +} + +getGitlabRss () { + local url="${1%/}" + echo "${url}.atom" +} + +getMediumRss () { + echo $1 | sed 's|/tag/|/feed/|' +} + + +if [ -n "$1" ] ; then + url="$1" +else + url="$(wl-paste)" + [ -z "$url" ] && echo "usage: $0 url 'tag1 tag2 tag3'" && exit 1 +fi + +declare -a list=() + +yt_regex="^(http(s)?://)?((w){3}\.)?(youtube\.com|invidio\.us|invidious\.flokinet\.to|invidious\.materialio\.us|iv\.datura\.network|invidious\.perennialte\.ch|invidious\.fdn\.fr|invidious\.private\.coffee|invidious\.protokolla\.fi|invidious\.privacyredirect\.com|yt\.artemislena\.eu|yt\.drgnz\.club|invidious\.incogniweb\.net|yewtu\.be|inv\.tux\.pizza|invidious\.reallyaweso\.me|iv\.melmac\.space|inv\.us\.projectsegfau\.lt|inv\.nadeko\.net|invidious\.darkness\.services|invidious\.jing\.rocks|invidious\.privacydev\.net|inv\.in\.projectsegfau\.lt|invidious\.drgns\.space)/(channel|user|c).+" +reddit_regex="^(http(s)?://)?((w){3}\.)?reddit\.com.*" +vimeo_regex="^(http(s)?://)?((w){3}.)?vimeo\.com.*" +if echo $url | rg -x "$yt_regex" >/dev/null ; then + list="$(getYoutubeRss "$url")" +elif echo $url | rg -x "$reddit_regex" >/dev/null ; then + list="$(getRedditRss "$url")" +# vimeo actually works with getlink +elif echo $url | rg "$vimeo_regex" >/dev/null ; then + list="$(getVimeoRss "$url")" +elif echo $url | rg "github.com" >/dev/null ; then + list="$(getGithubRss "$url")" +# gitlab also works with getlink +elif echo $url | rg "gitlab.com/[a-zA-Z0-9].*" >/dev/null ; then + list="$(getGitlabRss "$url")" +elif echo $url | rg "medium.com/tag" >/dev/null ; then + list="$(getMediumRss "$url")" +else + list="$(getlink "$url")" +fi + +[ "$(echo "$list" | wc -l)" -eq 1 ] && chosen_link="$list" || chosen_link=$(printf '%s\n' "${list[@]}" | mew -p "Choose a feed:") +tags="$2" +ifinstalled rssadd && rssadd "$chosen_link" "$tags" +echo "$chosen_link" "$tags" diff --git a/.local/bin/sd b/.local/bin/sd @@ -0,0 +1,22 @@ +#!/bin/sh + +# Open a terminal window in the same directory as the currently active window. + +windowPID=$(swaymsg --raw -t get_tree | jq -r '.. | select(type == "object" and .focused == true) | .pid') +PIDlist=$(pstree -lpATna "$windowPID" | sed -En 's/.*,([0-9]+).*/\1/p' | tac) +for PID in $PIDlist; do + cmdline=$(ps -o args= -p "$PID") + process_group_leader=$(ps -o comm= -p "$(ps -o pgid= -p "$PID" | tr -d ' ')") + cwd=$(readlink /proc/"$PID"/cwd) + # zsh and yazi won't be ignored even if it shows ~ or / + case "$cmdline" in + 'Yazi') continue ;; + "${SHELL##*/}"|'yazi'|'yazi '*) break ;; + esac + # git (and its sub-processes) will show the root of a repository instead of the actual cwd, so they're ignored + [ "$process_group_leader" = 'git' ] || [ ! -d "$cwd" ] && continue + # This is to ignore programs that show ~ or / instead of the actual working directory + [ "$cwd" != "$HOME" ] && [ "$cwd" != '/' ] && break +done +[ "$PWD" != "$cwd" ] && [ -d "$cwd" ] && { cd "$cwd" || exit 1; } +"$TERMINAL" diff --git a/.local/bin/setbg b/.local/bin/setbg @@ -0,0 +1,42 @@ +#!/bin/sh + +# This script does the following: +# Run by itself, set the wallpaper (at X start). +# If given a file, set that as the new wallpaper. +# If given a directory, choose random file in it. +# If wal is installed, also generates a colorscheme. + +# Location of link to wallpaper link. +bgloc="${XDG_DATA_HOME:-$HOME/.local/share}/bg" + +# Configuration files of applications that have their themes changed by pywal. +dunstconf="${XDG_CONFIG_HOME:-$HOME/.config}/dunst/dunstrc" +zathuraconf="${XDG_CONFIG_HOME:-$HOME/.config}/zathura/zathurarc" + +# Give -s as parameter to make notifications silent. +while getopts "s" o; do case "${o}" in + s) silent='1' ;; +esac done + +shift $((OPTIND - 1)) + +trueloc="$(readlink -f "$1")" && +case "$(file --mime-type -b "$trueloc")" in + image/* ) ln -sf "$trueloc" "$bgloc" && [ -z "$silent" ] && notify-send -i "$bgloc" "Changing wallpaper..." ;; + inode/directory ) ln -sf "$(find "$trueloc" -iregex '.*.\(jpg\|jpeg\|png\|gif\)' -type f | shuf -n 1)" "$bgloc" && [ -z "$silent" ] && notify-send -i "$bgloc" "Random Wallpaper chosen." ;; + *) [ -z "$silent" ] && notify-send "🖼️ Error" "Not a valid image or directory." ; exit 1;; +esac + +# If pywal is installed, use it. +if command -v wal >/dev/null 2>&1 ; then + wal -n -i "$(readlink -f $bgloc)" -o "${XDG_CONFIG_HOME:-$HOME/.config}/wal/postrun" >/dev/null 2>&1 +# If pywal is removed, return config files to normal. +else + [ -f "$dunstconf.bak" ] && unlink "$dunstconf" && mv "$dunstconf.bak" "$dunstconf" + [ -f "$zathuraconf.bak" ] && unlink "$zathuraconf" && mv "$zathuraconf.bak" "$zathuraconf" +fi + +killall swaybg +swaybg -i "$bgloc" -m fill & +# If running, dwm hit the key to refresh the color scheme. +# pidof dwm >/dev/null && xdotool key super+F5 diff --git a/.local/bin/shortcuts b/.local/bin/shortcuts @@ -0,0 +1,59 @@ +#!/bin/sh + +bmdirs="${XDG_CONFIG_HOME:-$HOME/.config}/shell/bm-dirs" +bmfiles="${XDG_CONFIG_HOME:-$HOME/.config}/shell/bm-files" + +# Output locations. Unactivated progs should go to /dev/null. +shell_shortcuts="${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutrc" +shell_env_shortcuts="${XDG_CONFIG_HOME:-$HOME/.config}/shell/shortcutenvrc" +zsh_named_dirs="${XDG_CONFIG_HOME:-$HOME/.config}/shell/zshnameddirrc" +lf_shortcuts="/dev/null" +vim_shortcuts="/dev/null" +qute_shortcuts="/dev/null" +fish_shortcuts="/dev/null" +vifm_shortcuts="/dev/null" +yazi_shortcuts="${XDG_CONFIG_HOME:-$HOME/.config}/yazi/keymap.toml" + +# Remove, prepare files +rm -f "$lf_shortcuts" "$qute_shortcuts" "$zsh_named_dirs" "$vim_shortcuts" "$yazi_shortcuts" 2>/dev/null +printf "# vim: filetype=sh\\n" > "$fish_shortcuts" +printf "# vim: filetype=sh\\nalias " > "$shell_shortcuts" +printf "# vim: filetype=sh\\n" > "$shell_env_shortcuts" +printf "\" vim: filetype=vim\\n" > "$vifm_shortcuts" + +# Format the `directories` file in the correct syntax and sent it to all three configs. +eval "echo \"$(bat "$bmdirs")\"" | \ +awk "!/^\s*#/ && !/^\s*\$/ {gsub(\"\\\s*#.*$\",\"\"); + printf(\"%s=\42cd %s && ls -A\42 \\\\\n\",\$1,\$2) >> \"$shell_shortcuts\" ; + printf(\"[ -n \42%s\42 ] && export %s=\42%s\42 \n\",\$1,\$1,\$2) >> \"$shell_env_shortcuts\" ; + printf(\"hash -d %s=%s \n\",\$1,\$2) >> \"$zsh_named_dirs\" ; + printf(\"abbr %s \42cd %s; and ls -A\42\n\",\$1,\$2) >> \"$fish_shortcuts\" ; + printf(\"map g%s :cd %s<CR>\nmap t%s <tab>:cd %s<CR><tab>\nmap M%s <tab>:cd %s<CR><tab>:mo<CR>\nmap Y%s <tab>:cd %s<CR><tab>:co<CR> \n\",\$1,\$2, \$1, \$2, \$1, \$2, \$1, \$2) >> \"$vifm_shortcuts\" ; + printf(\"config.bind(';%s', \42set downloads.location.directory %s ;; hint links download\42) \n\",\$1,\$2) >> \"$qute_shortcuts\" ; + printf(\"map C%s cd \42%s\42 \n\",\$1,\$2) >> \"$lf_shortcuts\" ; + printf(\"cmap ;%s %s\n\",\$1,\$2) >> \"$vim_shortcuts\" ; + split(\$1, a, \"\") + chars = \"\" + for (i = 1; i <= length(a); i++) { + chars = chars sprintf(\"\\\"%s\\\"\", a[i]) + if (i < length(a)) chars = chars \", \" + } + printf(\"[[mgr.prepend_keymap]]\\non = [\\\"g\\\", %s]\\nrun = \\\"cd %s\\\"\\n\\n\", chars, \$2) >> \"$yazi_shortcuts\" }" + +# Format the `files` file in the correct syntax and sent it to both configs. +eval "echo \"$(bat "$bmfiles")\"" | \ +awk "!/^\s*#/ && !/^\s*\$/ {gsub(\"\\\s*#.*$\",\"\"); + printf(\"%s=\42\$EDITOR %s\42 \\\\\n\",\$1,\$2) >> \"$shell_shortcuts\" ; + printf(\"[ -n \42%s\42 ] && export %s=\42%s\42 \n\",\$1,\$1,\$2) >> \"$shell_env_shortcuts\" ; + printf(\"hash -d %s=%s \n\",\$1,\$2) >> \"$zsh_named_dirs\" ; + printf(\"abbr %s \42\$EDITOR %s\42 \n\",\$1,\$2) >> \"$fish_shortcuts\" ; + printf(\"map %s :e %s<CR> \n\",\$1,\$2) >> \"$vifm_shortcuts\" ; + printf(\"map E%s \$\$EDITOR \42%s\42 \n\",\$1,\$2) >> \"$lf_shortcuts\" ; + printf(\"cmap ;%s %s\n\",\$1,\$2) >> \"$vim_shortcuts\" ; + split(\$1, a, \"\") + chars = \"\" + for (i = 1; i <= length(a); i++) { + chars = chars sprintf(\"\\\"%s\\\"\", a[i]) + if (i < length(a)) chars = chars \", \" + } + printf(\"[[mgr.prepend_keymap]]\\non = [\\\"e\\\", %s]\\nrun = 'shell --block \\\"\$EDITOR %s\\\"'\\n\\n\", chars, \$2) >> \"$yazi_shortcuts\" }" diff --git a/.local/bin/singboxwrap b/.local/bin/singboxwrap @@ -0,0 +1,33 @@ +#!/bin/sh + +config_dir="/mnt/ssd/settings/sing-box" +default_path="${XDG_DATA_HOME:-$HOME/.local/share}/singboxcfg" + +input=$1 + +run_singbox () { + [ -z "${1}" ] && exit + sing-box check -c "$1" && { + pidof sing-box >/dev/null && killall sing-box + setsid -f sing-box -c "$1" run + if [ "$input" = menu ]; then + notify-send "sing-box with $(readlink "$1") is running now" + fi + } || notify-send "Config check has failed. Check your configuration at $(readlink $1)" & exit 1 +} + +choose_menu () { + choose="$(fd . $config_dir -d 1 -t f | mew -p "Config to use")" + [ -z "${choose}" ] && exit + ln -sf "$choose" "$default_path" + echo "$default_path" +} + +if [ ! -f "$default_path" ]; then + input="menu" +fi + +case $input in + menu) run_singbox $(choose_menu) ;; + *) run_singbox $default_path ;; +esac diff --git a/.local/bin/slider b/.local/bin/slider @@ -0,0 +1,126 @@ +#!/bin/sh + +# Give a file with images and timecodes and creates a video slideshow of them. +# +# Timecodes must be in format 00:00:00. +# +# Imagemagick and ffmpeg required. + +# Application cache if not stated elsewhere. +cache="${XDG_CACHE_HOME:-$HOME/.cache}/slider" + +while getopts "hvrpi:c:a:o:d:f:t:e:x:s:" o; do case "${o}" in + c) bgc="$OPTARG" ;; + t) fgc="$OPTARG" ;; + f) font="$OPTARG" ;; + i) file="$OPTARG" ;; + a) audio="$OPTARG" ;; + o) outfile="$OPTARG" ;; + d) prepdir="$OPTARG" ;; + r) redo="$OPTARG" ;; + s) ppt="$OPTARG" ;; + e) endtime="$OPTARG" ;; + x) res="$OPTARG" + echo "$res" | rg -qv "^[0-9]\+x[0-9]\+$" && + echo "Resolution must be dimensions separated by a 'x': 1280x720, etc." && + exit 1 ;; + p) echo "Purge old build files in $cache? [y/N]" + read -r confirm + echo "$confirm" | rg -iq "^y$" && rm -rf "$cache" && echo "Done." + exit ;; + v) verbose=True ;; + *) echo "$(basename "$0") usage: + -i input timecode list (required) + -a audio file + -c color of background (use html names, black is default) + -t text color for text slides (white is default) + -s text font size for text slides (150 is default) + -f text font for text slides (sans serif is default) + -o output video file + -e if no audio given, the time in seconds that the last slide will be shown (5 is default) + -x resolution (1920x1080 is default) + -d tmp directory + -r rerun imagemagick commands even if done previously (in case files or background has changed) + -p purge old build files instead of running + -v be verbose" && exit 1 + +esac done + +# Check that the input file looks like it should. +{ head -n 1 "$file" 2>/dev/null | rg -q "^00:00:00 " ;} || { + echo "Give an input file with -i." && + echo "The file should look as this example: + +00:00:00 first_image.jpg +00:00:03 otherdirectory/next_image.jpg +00:00:09 this_image_starts_at_9_seconds.jpg +etc... + +Timecodes and filenames must be separated by Tabs." && + exit 1 + } + +if [ -n "${audio+x}" ]; then + # Check that the audio file looks like an actual audio file. + case "$(file --dereference --brief --mime-type -- "$audio")" in + audio/*) ;; + *) echo "That doesn't look like an audio file."; exit 1 ;; + esac + totseconds="$(date '+%s' -d $(ffmpeg -i "$audio" 2>&1 | awk '/Duration/ {print $2}' | sed s/,//))" +fi + +prepdir="${prepdir:-$cache/$file}" +outfile="${outfile:-$file.mp4}" +prepfile="$prepdir/$file.prep" + +[ -n "${verbose+x}" ] && echo "Preparing images... May take a while depending on the number of files." +mkdir -p "$prepdir" + +{ +while read -r x; +do + # Get the time from the first column. + time="${x%% *}" + seconds="$(date '+%s' -d "$time")" + # Duration is not used on the first looped item. + duration="$((seconds - prevseconds))" + + # Get the filename/text content from the rest. + content="${x#* }" + base="$(basename "$content")" + base="${base%.*}.jpg" + + if [ -f "$content" ]; then + # If images have already been made in a previous run, do not recreate + # them unless -r was given. + { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ] ;} && + magick -size "${res:-1920x1080}" canvas:"${bgc:-black}" -gravity center "$content" -resize 1920x1080 -composite "$prepdir/$base" + else + { [ ! -f "$prepdir/$base" ] || [ -n "${redo+x}" ] ;} && + magick -size "${res:-1920x1080}" -background "${bgc:-black}" -fill "${fgc:-white}" -font "${font:-Sans}" -pointsize "${ppt:-150}" -gravity center label:"$content" "$prepdir/$base" + fi + + # If the first line, do not write yet. + [ "$time" = "00:00:00" ] || echo "file '$prevbase' +duration $duration" + + # Keep the information required for the next file. + prevbase="$base" + prevtime="$time" + prevseconds="$(date '+%s' -d "$prevtime")" +done < "$file" +# Do last file which must be given twice as follows +endtime="$((totseconds-seconds))" +echo "file '$base' +duration ${endtime:-5} +file '$base'" +} > "$prepfile" +if [ -n "${audio+x}" ]; then + ffmpeg -hide_banner -y -f concat -safe 0 -i "$prepfile" -i "$audio" -c:a aac -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile" +else + ffmpeg -hide_banner -y -f concat -safe 0 -i "$prepfile" -vsync vfr -c:v libx264 -pix_fmt yuv420p "$outfile" +fi + +# Might also try: +# -vf "fps=${fps:-24},format=yuv420p" "$outfile" +# but has given some problems. diff --git a/.local/bin/spec b/.local/bin/spec @@ -0,0 +1,23 @@ +#!/bin/sh + +me=$(basename "$0") + +if [ -z "$1" ] ; then + exit 1; +fi + +TEMP=$(mktemp -u -t "${me}" 2>/dev/null || mktemp -u -t "${me}"-XXXXXXXXXX) +TEMPIMG="${TEMP}.png" +TEMPTXT="${TEMP}.txt" + +basename "$1" > "${TEMPTXT}" + +ffmpeg -v quiet -y -i "$1" -filter_complex showspectrumpic=s=2560x1024,drawtext="expansion=none:textfile='${TEMPTXT}':x=(w-tw)/2:y=16:fontcolor='white':fontsize=20" "$TEMPIMG" +exitcode=$? +if [ $exitcode -ne 0 ] ; then + rm "$TEMPTXT" + exit $exitcode +fi + +dummy=$(xdg-open "$TEMPIMG") +rm "$TEMPIMG" "$TEMPTXT" diff --git a/.local/bin/start-pipewire b/.local/bin/start-pipewire @@ -0,0 +1,5 @@ +#!/bin/sh +killall pipewire +killall pipewire-pulse +killall wireplumber +setsid -f pipewire diff --git a/.local/bin/statusbar/sb-microphone b/.local/bin/statusbar/sb-microphone @@ -0,0 +1,26 @@ +#!/bin/sh + +case $BLOCK_BUTTON in + 1) setsid -w -f "$TERMINAL" -e pulsemixer; pkill -RTMIN+23 "${STATUSBAR:-i3blocks}" ;; + 2) wpctl set-mute @DEFAULT_SOURCE@ toggle ;; + 4) wpctl set-volume @DEFAULT_SOURCE@ 1%+ ;; + 5) wpctl set-volume @DEFAULT_SOURCE@ 1%- ;; + 8) setsid -f "$TERMINAL" -e "$EDITOR" "$0" >/dev/null 2>&1 ;; +esac + +vol="$(wpctl get-volume @DEFAULT_AUDIO_SOURCE@)" + +[ "$vol" != "${vol%\[MUTED\]}" ] && echo "<span color='#FF0000'>muted</span>" && exit + +vol="${vol#Volume: }" + +split() { + # For ommiting the . without calling and external program. + IFS=$2 + set -- $1 + printf '%s' "$@" +} + +vol="$(printf "%.0f" "$(split "$vol" ".")")" + +echo "$vol%" diff --git a/.local/bin/statusbar/sb-news b/.local/bin/statusbar/sb-news @@ -0,0 +1,9 @@ +#!/bin/sh + +case $BLOCK_BUTTON in + 1) setsid "$TERMINAL" -T newsboat -e newsboat >/dev/null 2>&1 ;; + 2) setsid -f newsup >/dev/null && exit ;; + 8) setsid -f "$TERMINAL" -e "$EDITOR" "$0" >/dev/null 2>&1 ;; +esac + +cat /tmp/newsupdate 2>/dev/null || echo "$(newsboat -x print-unread | awk '{ if($1>0) print $1}')$(bat "${XDG_CONFIG_HOME:-$HOME/.config}"/newsboat/.update 2>/dev/null)" diff --git a/.local/bin/statusbar/sb-pacpackages b/.local/bin/statusbar/sb-pacpackages @@ -0,0 +1,9 @@ +#!/bin/sh + +case $BLOCK_BUTTON in + 1) setsid -f "$TERMINAL" -e sb-popupgrade >/dev/null 2>&1 ;; + 2) notify-send "$(/usr/bin/pacman -Qu)" ;; + 8) setsid -f "$TERMINAL" -e "$EDITOR" "$0" >/dev/null 2>&1 ;; +esac + +pacman -Qu | rg -Fcv "[ignored]" | sed "s/^//;s/^📦0$//g" diff --git a/.local/bin/statusbar/sb-popupgrade b/.local/bin/statusbar/sb-popupgrade @@ -0,0 +1,9 @@ +#!/bin/sh + +printf "Beginning upgrade.\\n" + +paru +pkill -RTMIN+8 "${STATUSBAR:-i3blocks}" + +printf "\\nUpgrade complete.\\nPress <Enter> to exit window.\\n\\n" +read -r _ diff --git a/.local/bin/sysact b/.local/bin/sysact @@ -0,0 +1,36 @@ +#!/bin/sh + +# A mew wrapper script for system functions. +export WM="sway" +case "$(readlink -f /sbin/init)" in + *systemd*) ctl='systemctl' ;; + *) ctl='loginctl' ;; +esac + +wmpid(){ # This function is needed if there are multiple instances of the window manager. + tree="$(pstree -ps $$)" + tree="${tree#*$WM(}" + echo "${tree%%)*}" +} + +lock(){ + mpc pause + pauseallmpv + wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle + kill -44 $(pidof i3blocks) + swaylock + wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle + kill -44 $(pidof i3blocks) +} + +case "$(printf "🔒 lock\n🚪 leave $WM\n♻️ renew $WM\n🐻 hibernate\n🔃 reboot\n🖥 shutdown\n💤 sleep\n📺 display off" | mew -i -p 'Action: ')" in + '🔒 lock') lock ;; + "🚪 leave $WM") swaymsg exit ;; + "♻️ renew $WM") swaymsg reload ;; + '🐻 hibernate') $ctl hibernate -i ;; + '💤 sleep') $ctl suspend -i ;; + '🔃 reboot') $ctl reboot -i ;; + '🖥️shutdown') $ctl poweroff -i ;; + '📺 display off') xset dpms force off ;; + *) exit 1 ;; +esac diff --git a/.local/bin/tag b/.local/bin/tag @@ -0,0 +1,49 @@ +#!/bin/sh + +err() { echo "Usage: + tag [OPTIONS] file +Options: + -a: artist/author + -t: song/chapter title + -A: album/book title + -n: track/chapter number + -N: total number of tracks/chapters + -d: year of publication + -g: genre + -c: comment +You will be prompted for title, artist, album and track if not given." && exit 1 ;} + +while getopts "a:t:A:n:N:d:g:c:" o; do case "${o}" in + a) artist="${OPTARG}" ;; + t) title="${OPTARG}" ;; + A) album="${OPTARG}" ;; + n) track="${OPTARG}" ;; + N) total="${OPTARG}" ;; + d) date="${OPTARG}" ;; + g) genre="${OPTARG}" ;; + c) comment="${OPTARG}" ;; + *) printf "Invalid option: -%s\\n" "$OPTARG" && err ;; +esac done + +shift $((OPTIND - 1)) + +file="$1" + +temp="$(mktemp -p "$(dirname "$file")")" +trap 'rm -f $temp' HUP INT QUIT TERM PWR EXIT + +[ ! -f "$file" ] && echo 'Provide file to tag.' && err + +[ -z "$title" ] && echo 'Enter a title.' && read -r title +[ -z "$artist" ] && echo 'Enter an artist.' && read -r artist +[ -z "$album" ] && echo 'Enter an album.' && read -r album +[ -z "$track" ] && echo 'Enter a track number.' && read -r track + +cp -f "$file" "$temp" && ffmpeg -i "$temp" -map 0 -y -codec copy \ + -metadata title="$title" \ + -metadata album="$album" \ + -metadata artist="$artist" \ + -metadata track="${track}${total:+/"$total"}" \ + ${date:+-metadata date="$date"} \ + ${genre:+-metadata genre="$genre"} \ + ${comment:+-metadata comment="$comment"} "$file" diff --git a/.local/bin/td-toggle b/.local/bin/td-toggle @@ -0,0 +1,12 @@ +#!/bin/sh + +# If transmission-daemon is running, will ask to kill, else will ask to start. + +if pidof transmission-daemon >/dev/null ; +then + [ "$(printf "No\\nYes" | mew -i -p "Turn off transmission-daemon?")" = "Yes" ] && killall transmission-daemon && notify-send "transmission-daemon disabled." +else + ifinstalled transmission-cli || exit + [ "$(printf "No\\nYes" | mew -i -p "Turn on transmission daemon?")" = "Yes" ] && transmission-daemon && notify-send "transmission-daemon enabled." +fi +sleep 3 && pkill -RTMIN+7 "${STATUSBAR:-i3blocks}" diff --git a/.local/bin/torwrap b/.local/bin/torwrap @@ -0,0 +1,7 @@ +#!/bin/sh + +ifinstalled stig transmission-cli || exit 1 + +! pidof transmission-daemon >/dev/null && transmission-daemon && notify-send "Starting torrent daemon..." + +$TERMINAL -e stig; pkill -RTMIN+7 "${STATUSBAR:-i3blocks}" diff --git a/.local/bin/transadd b/.local/bin/transadd @@ -0,0 +1,9 @@ +#!/bin/sh + +# Mimeapp script for adding torrent to transmission-daemon, but will also start the daemon first if not running. + +# transmission-daemon sometimes fails to take remote requests in its first moments, hence the sleep. + +pidof transmission-daemon >/dev/null || (transmission-daemon && notify-send "Starting transmission daemon..." && sleep 3 && pkill -RTMIN+7 "${STATUSBAR:-i3blocks}") + +transmission-remote -a "$@" && notify-send "🔽 Torrent added." diff --git a/.local/bin/unix b/.local/bin/unix @@ -0,0 +1,26 @@ +#!/bin/sh + +#original artwork by http://www.sanderfocus.nl/#/portfolio/tech-heroes +#converted to shell by #nixers @ irc.unix.chat + +bat << 'eof' + ,_ ,_==▄▂ + , ▂▃▄▄▅▅▅▂▅¾. / / + ▄▆<´ "»▓▓▓%\ / / / / + ,▅7" ´>▓▓▓% / / > / >/% + ▐¶▓ ,»▓▓¾´ /> %/%// / / + ▓▃▅▅▅▃,,▄▅▅▅Æ\// ///>// />/ / + V║«¼.;→ ║<«.,`=// />//%/% / / + //╠<´ -²,)(▓~"-╝/¾/ %/>/ /> + / / / ▐% -./▄▃▄▅▐, /7//;//% / / + / ////`▌▐ %zWv xX▓▇▌//&;% / / + / / / %//%/¾½´▌▃▄▄▄▄▃▃▐¶\/& / + </ /</%//`▓!%▓%╣[38;5;255;╣WY<Y)y&/`\ + / / %/%//</%//\i7; ╠N>)VY>7; \_ UNIX IS VERY SIMPLE IT JUST NEEDS A + / /</ //<///<_/%\▓ V%W%£)XY _/%‾\_, GENIUS TO UNDERSTAND ITS SIMPLICITY + / / //%/_,=--^/%/%%\¾%¶%%} /%%%%%%;\, + %/< /_/ %%%%%;X%%\%%;, _/%%%;, \ + / / %%%%%%;, \%%l%%;// _/%;, dmr + / %%%;, <;\-=-/ / + ;, l +eof diff --git a/.local/bin/unmounter b/.local/bin/unmounter @@ -0,0 +1,25 @@ +#!/bin/sh + +set -e + +mounteddroids="$(rg simple-mtpfs /etc/mtab | awk '{print "📱" $2}')" +lsblkoutput="$(lsblk -nrpo "name,type,size,mountpoint")" +mounteddrives="$(echo "$lsblkoutput" | awk '($2=="part"||$2="crypt")&&$4!~/\/boot|\/home$|SWAP/&&length($4)>1{printf "💾%s (%s)\n",$4,$3}')" + +allunmountable="$(echo "$mounteddroids +$mounteddrives" | sed "/^$/d;s/ *$//")" +test -n "$allunmountable" + +chosen="$(echo "$allunmountable" | mew -i -p "Unmount which drive?")" +chosen="${chosen%% *}" +test -n "$chosen" + +doas umount -l "/${chosen#*/}" +notify-send "Device unmounted." "$chosen has been unmounted." + +# Close the chosen drive if decrypted. +cryptid="$(echo "$lsblkoutput" | rg "/${chosen#*/}$")" +cryptid="${cryptid%% *}" +test -b /dev/mapper/"${cryptid##*/}" +doas_askpass cryptsetup close "$cryptid" +notify-send "🔒Device decryption closed." "Drive is now securely locked again." diff --git a/.local/bin/weath b/.local/bin/weath @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Get the weather on the terminal. You can pass an alternative location as a parameter, +# and/or use the 'cp' option to copy the forecast as plaintext to the clipboard. + +report="${XDG_CACHE_HOME:-$HOME/.cache}/weatherreport" + +if [ "$1" = 'cp' ]; then + # shellcheck disable=SC2015 + [ -z "$2" ] && sed 's/\x1b\[[^m]*m//g' "$report" | wl-copy && + notify-send "Weather forecast for '${LOCATION:-$(head -n 1 "$report" | cut -d' ' -f3-)}' copied to clipboard." || + { data="$(curl -sfm 5 "${WTTRURL:-wttr.in}/$2?T")" && + notify-send "Weather forecast for '$2' copied to clipboard." && + echo "$data" | wl-copy || + notify-send 'Failed to get weather forecast!' 'Check your internet connection and the supplied location.'; } +else + [ -n "$2" ] && + notify-send "Invalid option '$1'! The only valid option is 'cp'." && + exit 1 + + # shellcheck disable=SC2015 + [ -z "$1" ] && less -S "$report" || + data="$(curl -sfm 5 "${WTTRURL:-wttr.in}/$1")" && echo "$data" | less -S || + notify-send 'Failed to get weather forecast!' 'Check your internet connection and the supplied location.' +fi diff --git a/.local/bin/xdg-terminal-exec b/.local/bin/xdg-terminal-exec @@ -0,0 +1,3 @@ +#!/bin/sh + +"$TERMINAL" -e "$@" diff --git a/.local/share/applications/file.desktop b/.local/share/applications/file.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=Application +Name=File Manager +Exec=/usr/bin/footclient -e yazi %u diff --git a/.local/share/applications/img.desktop b/.local/share/applications/img.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=Application +Name=Image viewer +Exec=/usr/bin/swayimg %f diff --git a/.local/share/applications/mail.desktop b/.local/share/applications/mail.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=Application +Name=Mail +Exec=/usr/bin/footclient -e neomutt %u diff --git a/.local/share/applications/pdf.desktop b/.local/share/applications/pdf.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=Application +Name=PDF reader +Exec=/usr/bin/zathura %u diff --git a/.local/share/applications/rss.desktop b/.local/share/applications/rss.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=Application +Name=RSS feed addition +Exec=/usr/bin/env rssadd %U diff --git a/.local/share/applications/text.desktop b/.local/share/applications/text.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=Application +Name=Text editor +Exec=/usr/bin/footclient -e nvim %u diff --git a/.local/share/applications/torrent.desktop b/.local/share/applications/torrent.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=Application +Name=Torrent +Exec=/usr/bin/env transadd %U diff --git a/.local/share/applications/video.desktop b/.local/share/applications/video.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=Application +Name=Video viewer +Exec=/usr/bin/mpv -quiet %f diff --git a/LICENSE b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 2025 awy + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) 2025 awy + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<https://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<https://www.gnu.org/licenses/why-not-lgpl.html>. diff --git a/README.md b/README.md @@ -0,0 +1,9 @@ +Sway Deployment Script +====================== +Installs all needed dependencies +and deploys my dotfiles + +## Install +```sh +doas ./rice.sh +``` diff --git a/aurdeps.txt b/aurdeps.txt @@ -0,0 +1,7 @@ +sing-box +#sing-geoip-rule-set +#sing-geosite-rule-set +gpu-screen-recorder +doasedit-alternative +newsraft +dragon-drop-git diff --git a/dependencies.txt b/dependencies.txt @@ -0,0 +1,131 @@ +# COMPRESSION # +7zip +unrar +unzip + +# SWAY # +sway +swayidle +swaylock +fnott +foot +xdg-desktop-portal-wlr +xdg-desktop-portal-termfilechooser +i3blocks +mew + +# PROGRAMS # +neovim +lynx +wget + +# SHELL & YAZI # +jq +fzf +zsh +zsh-completions +zsh-autosuggestions +zathura +zathura-pdf-mupdf +wl-clipboard +yazi +bat +chafa +bc +eza +ripgrep +fd +git-delta + +# BROWSER # +icecat + +# SOUND # +pipewire +pipewire-pulse +wireplumber +pulsemixer + +# FONTS # +gnu-free-fonts +ttf-nerd-fonts-symbols +noto-fonts-emoji +fontconfig + +# MONITORING # +btop + +# MULTIMEDIA # +ffmpeg +mediainfo +mpv +imagemagick +swayimg +mpc +mpd +rmpc +grim +swappy +slurp +swaybg + +# DATA HOARDING # +yt-dlp +transmission-cli + +# UTILS # +make +gnupg +gnome-keyring +openssh +fakeroot +polkit +fastfetch +libnotify +pkgconf +exfat-utils +tesseract-data-eng +tesseract +mandoc +calcurse +cmake +wlrctl +tllist + +# TESSEN DEPS # +tessen +gopass +which +wtype + +# MUTT-WIZARD DEPS # +neomutt +isync +msmtp +cronie-dinit + +# WAYLAND # +qt6-wayland +qt5-wayland +xorg-xrandr +xorg-xwayland +wayland-protocols + +# STYLING # +gnome-themes-extra +qt5ct +qt6ct + +# DOAS ASKPASS # +expect + +# LANGUAGE SERVERS # +clang +rust-analyzer +vscode-css-languageserver +vscode-json-languageserver +vscode-html-languageserver +yaml-language-server +bash-language-server +shellcheck +lua-language-server diff --git a/rice.sh b/rice.sh @@ -0,0 +1,198 @@ +#!/bin/sh +set -e +WORKDIRECTORY=$PWD + +if [ "$(id -u)" -ne 0 ] + then printf "The script has to be run as root.\n" + exit +fi + +if [ -x "$(command -v doas)" ]; then + PERMUSER=$DOAS_USER + evalcommand="doas -u $PERMUSER" + cp /etc/doas.conf /etc/doas.bak + echo "permit nopass :wheel" > /etc/doas.conf + echo "permit nopass root" >> /etc/doas.conf +else + PERMUSER=$SUDO_USER + evalcommand="sudo -u $PERMUSER" + echo "%wheel ALL=(ALL) NOPASSWD: ALL + Defaults:%wheel,root runcwd=*" >/etc/sudoers.d/temp +fi + +run_as_user() { + $evalcommand "$@" +} + +usermod -aG seat $PERMUSER + +DEPLIST="`sed -e 's/#.*$//' -e '/^$/d' dependencies.txt | tr '\n' ' '`" +pacman -S $DEPLIST --noconfirm --needed + +# run_as_user git submodule update --init --recursive + +run_as_user cp -r "$WORKDIRECTORY"/.config /home/"$PERMUSER" +run_as_user cp -r "$WORKDIRECTORY"/.local /home/"$PERMUSER" +run_as_user cp -a "$WORKDIRECTORY"/.zprofile /home/"$PERMUSER" + +run_as_user mkdir -p /home/"$PERMUSER"/.config/git +run_as_user touch /home/"$PERMUSER"/.config/git/config +run_as_user mkdir -p /home/"$PERMUSER"/.config/npm +run_as_user touch /home/"$PERMUSER"/.config/npm/npmrc +run_as_user mkdir -p /home/"$PERMUSER"/.config/mpd/playlists +run_as_user mkdir -p /home/"$PERMUSER"/.local/share/themes +run_as_user mkdir -p /home/"$PERMUSER"/.local/share/icons + +cd "$WORKDIRECTORY" || exit +run_as_user git clone https://github.com/zdharma-continuum/fast-syntax-highlighting +mkdir -p /usr/share/zsh/plugins +cp -rf fast-syntax-highlighting /usr/share/zsh/plugins +cd "$WORKDIRECTORY" + +run_as_user mkdir -p /home/"$PERMUSER"/.ssh +run_as_user mkdir -p /home/"$PERMUSER"/.gnupg +run_as_user touch /home/"$PERMUSER"/.gnupg/gpg-agent.conf + +cat <<EOL >> /home/$PERMUSER/.gnupg/gpg-agent.conf +enable-ssh-support +pinentry-program /usr/bin/pinentry-gnome3 +default-cache-ttl 34560000 +max-cache-ttl 34560000 +EOL + +cat <<EOL >> /home/$PERMUSER/.config/npm/npmrc +prefix=\${XDG_DATA_HOME}/npm +cache=\${XDG_CACHE_HOME}/npm +init-module=\${XDG_CONFIG_HOME}/npm/config/npm-init.js +logs-dir=\${XDG_STATE_HOME}/npm/logs +EOL + +run_as_user find /home/"$PERMUSER"/.gnupg -type f -exec chmod 600 {} \; +run_as_user find /home/"$PERMUSER"/.gnupg -type d -exec chmod 700 {} \; +run_as_user find /home/"$PERMUSER"/.ssh -type f -exec chmod 600 {} \; +run_as_user find /home/"$PERMUSER"/.ssh -type d -exec chmod 700 {} \; + +chsh -s /bin/zsh "$PERMUSER" + +# Make pacman colorful, concurrent downloads and Pacman eye-candy. +grep -q "ILoveCandy" /etc/pacman.conf || sed -i "/#VerbosePkgLists/a ILoveCandy" /etc/pacman.conf +sed -Ei "s/^#(ParallelDownloads).*/\1 = 5/;/^#Color$/s/#//" /etc/pacman.conf + +# Disable Pacman sandbox since kernel doesn't support landlock (only for custom kernels from my artix script) +# sed -Ei "s/^#(DisableSandbox).*/\1/" /etc/pacman.conf + +# Use all cores for compilation. +sed -i "s/-j2/-j$(nproc)/;/^#MAKEFLAGS/s/^#//" /etc/makepkg.conf + +if [ -x "$(command -v doas)" ]; then + # Use doas for Pacman authentification + sed -i 's/#PACMAN_AUTH=.*$/PACMAN_AUTH=(doas)/' /etc/makepkg.conf +fi + +cat <<EOL >> /usr/share/libalpm/hooks/statusbar.hook +[Trigger] +Operation = Upgrade +Type = Package +Target = * + +[Action] +Description = Updating statusbar... +When = PostTransaction +Exec = /usr/bin/pkill -RTMIN+8 i3blocks +EOL + +cat <<EOL >> /etc/pacman.d/hooks/sing-box.hook +[Trigger] +Type = Package +Operation = Install +Operation = Upgrade +Target = sing-box + +[Action] +Description = Setting caps for sing-box... +When = PostTransaction +Exec = /usr/bin/setcap cap_net_admin=ep /usr/sbin/sing-box +EOL + +run_as_user git clone https://aur.archlinux.org/paru.git +cd paru +run_as_user makepkg -csi --noconfirm +cd "$WORKDIRECTORY" +DEPLIST="`sed -e 's/#.*$//' -e '/^$/d' aurdeps.txt | tr '\n' ' '`" +run_as_user paru --sudo doas -S $DEPLIST --noconfirm +run_as_user dbus-launch gsettings set org.gnome.desktop.interface gtk-theme "Adwaita-dark" +run_as_user dbus-launch gsettings set org.gnome.desktop.wm.preferences theme "Adwaita-dark" +run_as_user dbus-launch gsettings set org.gnome.desktop.wm.preferences button-layout 'appmenu' +run_as_user dbus-launch gsettings set org.gnome.desktop.interface font-name "sans 11" +cd .. +rm -rf swaydots +rm -rf paru +rm -rf go + +dinitctl enable cronie +echo "*/30 * * * * export DBUS_SESSION_BUS_ADDRESS=\$(rg --null-data \"DBUS_SESSION_BUS_ADDRESS\" \"/proc/\$(pgrep -x swaybar)/environ\" | sed 's/DBUS_SESSION_BUS_ADDRESS=//'); /home/$PERMUSER/.local/bin/cron/newsup +*/30 * * * * export DBUS_SESSION_BUS_ADDRESS=\$(rg --null-data \"DBUS_SESSION_BUS_ADDRESS\" \"/proc/\$(pgrep -x swaybar)/environ\" | sed 's/DBUS_SESSION_BUS_ADDRESS=//'); /home/$PERMUSER/.local/bin/cron/checkup +*/5 * * * * export DBUS_SESSION_BUS_ADDRESS=\$(rg --null-data \"DBUS_SESSION_BUS_ADDRESS\" \"/proc/\$(pgrep -x swaybar)/environ\" | sed 's/DBUS_SESSION_BUS_ADDRESS=//'); /home/$PERMUSER/.local/bin/cron/mailup" | run_as_user crontab - + +echo "0 */1 * * * ntpdate pool.ntp.org" | crontab - + +cd /home/"$PERMUSER" +run_as_user git clone https://git.awy.one/autofox +cd autofox +run_as_user ./configure_icecat.sh +cd /home/"$PERMUSER" +rm -rf autofox + +run_as_user git clone https://git.awy.one/statusbar +cd statusbar +run_as_user make +cd /home/"$PERMUSER" +rm -rf statusbar + +# MPV +thumbfastlua_url=https://raw.githubusercontent.com/po5/thumbfast/refs/heads/master/thumbfast.lua +thumbfastconf_url=https://raw.githubusercontent.com/po5/thumbfast/refs/heads/master/thumbfast.conf +sponsorblock_url=https://codeberg.org/jouni/mpv_sponsorblock_minimal/raw/branch/master/sponsorblock_minimal.lua +config_dir="/home/$PERMUSER/.config/mpv" +scriptopts_dir="$config_dir/script-opts" + +run_as_user mkdir -p "$config_dir/scripts" +run_as_user mkdir -p $scriptopts_dir || echo "Couldn't create: $scriptopts_dir" + +# thumbfast +run_as_user curl -Ls -o "$config_dir/scripts/thumbfast.lua" $thumbfastlua_url || echo "Couldn't download: $thumbfastlua_url" +run_as_user curl -Ls -o "$scriptopts_dir/thumbfast.conf" $thumbfastconf_url || echo "Couldn't download: $thumbfastconf_url" +sed -Ei "s/(network).*/\1=yes/" "$scriptopts_dir/thumbfast.conf" + +# sponsorblock +run_as_user curl -Ls -o "$config_dir/scripts/sponsorblock_minimal.lua" $sponsorblock_url || echo "Couldn't download: $sponsorblock_url" + +#uosc +run_as_user wget "https://github.com/tomasklaen/uosc/releases/latest/download/uosc.zip" +run_as_user unzip uosc.zip -d "$config_dir" +run_as_user wget "https://github.com/tomasklaen/uosc/releases/latest/download/uosc.conf" +run_as_user mv uosc.conf "$scriptopts_dir" + +# pam +sed -i '/auth[[:space:]]*include[[:space:]]*system-local-login/a auth optional pam_gnome_keyring.so' /etc/pam.d/login +sed -i '/session[[:space:]]*include[[:space:]]*system-local-login/a session optional pam_gnome_keyring.so auto_start' /etc/pam.d/login +echo "password optional pam_gnome_keyring.so" >> /etc/pam.d/passwd + +yes | run_as_user paru --sudo doas -Scc +rm -rf /home/"$PERMUSER"/.cargo + +if [ -x "$(command -v doas)" ]; then + rm /etc/doas.conf + mv /etc/doas.bak /etc/doas.conf +else + rm /etc/sudoers.d/temp +fi + +chown -R $PERMUSER:wheel /home/$PERMUSER + +# cleanup +rm -rf /home/$PERMUSER/.cache/go-build +rm -f /home/$PERMUSER/.wget-hsts +rm -f /home/"$PERMUSER"/uosc.zip + +echo "Your linux is riced!"