summary refs log tree commit diff
diff options
context:
space:
mode:
authorRobert Günzler <r@gnzler.io>2026-04-08 13:11:48 +0900
committerRobert Günzler <r@gnzler.io>2026-04-08 13:11:48 +0900
commitd4620f3ac119263fee90bd819eadaf84fc8d50b7 (patch)
treef5268e72a1c0037d29acac699e5d3f4c37be0e6b
parent8afe3fa8d88ae56e96f65897935ef508581ac5f7 (diff)
*
Automated-commit-by: dots
Signed-off-by: Robert Günzler <r@gnzler.io>
Diffstat (limited to '')
-rw-r--r--.config/aerc/binds.conf1
-rw-r--r--.config/darkman/config.yaml7
-rw-r--r--.config/fuzzel/colors-dark.ini9
-rw-r--r--.config/fuzzel/colors-light.ini9
-rw-r--r--.config/fuzzel/colors.ini1
-rw-r--r--.config/fuzzel/fuzzel.ini26
-rw-r--r--.config/git/config191
-rw-r--r--.config/git/ignore60
-rw-r--r--.config/git/message2
-rwxr-xr-x.config/git/signoff.sh2
-rw-r--r--.config/git/tig41
-rw-r--r--.config/nvim/after/ftplugin/typst.lua2
-rw-r--r--.config/nvim/after/lsp/tinymist.lua5
-rw-r--r--.config/nvim/lazy-lock.json38
-rw-r--r--.config/nvim/lua/config/diagnostics.lua13
-rw-r--r--.config/nvim/lua/config/filetypes.lua3
-rw-r--r--.config/nvim/lua/config/keymaps.lua9
-rw-r--r--.config/nvim/lua/config/lsp.lua36
-rw-r--r--.config/nvim/lua/plugins/colorizer.lua14
-rw-r--r--.config/nvim/lua/plugins/gentoo.lua25
-rw-r--r--.config/nvim/lua/plugins/git.lua1
-rw-r--r--.config/nvim/lua/plugins/lsp.lua25
-rw-r--r--.config/nvim/lua/plugins/mini.lua2
-rw-r--r--.config/nvim/lua/plugins/treesitter.lua65
-rw-r--r--.config/nvim/lua/plugins/whichkey.lua3
-rwxr-xr-x.config/river/init143
-rw-r--r--.config/waybar/config.jsonc326
-rw-r--r--.config/waybar/menu-network.xml15
-rwxr-xr-x.config/waybar/module-calendar.sh29
-rwxr-xr-x.config/waybar/module-screenrec.sh13
-rwxr-xr-x.config/waybar/module-tailscale.sh33
-rwxr-xr-x.config/waybar/module-timew.sh11
-rw-r--r--.config/waybar/style-dark.css13
-rw-r--r--.config/waybar/style-light.css16
-rw-r--r--.config/waybar/style.css180
35 files changed, 1253 insertions, 116 deletions
diff --git a/.config/aerc/binds.conf b/.config/aerc/binds.conf
index ea31df7..3540547 100644
--- a/.config/aerc/binds.conf
+++ b/.config/aerc/binds.conf
@@ -84,7 +84,6 @@ z0 = :expand-folder<cr>
 zo = :open-thread<cr>
 
 [messages:account=gnzler]
-ga = :cf Archive/2024<cr>
 gs = :clear<cr>:filter -H 'List-ID:lists.sr.ht'<cr>
 gh = :clear<cr>:filter -H 'List-ID:github.com'<cr>
 # / = :ct notmuch<cr>:cf<space>
diff --git a/.config/darkman/config.yaml b/.config/darkman/config.yaml
new file mode 100644
index 0000000..6271b4b
--- /dev/null
+++ b/.config/darkman/config.yaml
@@ -0,0 +1,7 @@
+## berlin
+# lat: 52.52
+# lng: 13.40
+# tokyo
+lat: 35.68
+lng: 139.76
+portal: true
diff --git a/.config/fuzzel/colors-dark.ini b/.config/fuzzel/colors-dark.ini
new file mode 100644
index 0000000..ef7b3b0
--- /dev/null
+++ b/.config/fuzzel/colors-dark.ini
@@ -0,0 +1,9 @@
+[colors]
+background=000000fe
+text=feeeeeff
+selection=70649aaf
+border=70649aaf
+selection-text=bbbbbbff
+prompt=70649aaf
+placeholder=70649aaf
+input=bbbbbbff
diff --git a/.config/fuzzel/colors-light.ini b/.config/fuzzel/colors-light.ini
new file mode 100644
index 0000000..70a78b7
--- /dev/null
+++ b/.config/fuzzel/colors-light.ini
@@ -0,0 +1,9 @@
+[colors]
+background=feeeeefe
+text=080808ff
+selection=70649aaf
+border=70649aaf
+selection-text=feeeeeff
+prompt=70649aaf
+placeholder=70649aaf
+input=080808ff
diff --git a/.config/fuzzel/colors.ini b/.config/fuzzel/colors.ini
new file mode 100644
index 0000000..81841e2
--- /dev/null
+++ b/.config/fuzzel/colors.ini
@@ -0,0 +1 @@
+include=~/.config/fuzzel/colors-light.ini
diff --git a/.config/fuzzel/fuzzel.ini b/.config/fuzzel/fuzzel.ini
new file mode 100644
index 0000000..a6befa0
--- /dev/null
+++ b/.config/fuzzel/fuzzel.ini
@@ -0,0 +1,26 @@
+include=~/.config/fuzzel/colors.ini
+
+[main]
+font=DejaVu Sans Mono, Symbols Nerd Font Mono, Noto Color Emoji
+fields=filename,name,generic,exec
+show-actions=yes
+minimal-lines=yes
+width=40
+vertical-pad=4
+horizontal-pad=8
+inner-pad=4
+keyboard-focus=on-demand
+exit-on-keyboard-focus-loss=yes
+use-bold=no
+hide-prompt=no
+prompt="󰈲 "
+terminal=foot -a '{cmd}' -T '{cmd}' {cmd}
+dpi-aware=no
+# gamma-correct=yes
+match-counter=yes
+match-mode=fzf
+scaling-filter=lanczos3
+
+[border]
+width=2
+radius=0
diff --git a/.config/git/config b/.config/git/config
new file mode 100644
index 0000000..458280d
--- /dev/null
+++ b/.config/git/config
@@ -0,0 +1,191 @@
+[user]
+    name = Robert Günzler
+    email = r@gnzler.io
+    ; signingkey = AF6AB8C8B10436B9F61293632143BD2AAB94BCC4
+    signingkey = ~/.ssh/id_ed25519.pub
+
+[gpg]
+    format = ssh
+
+[sendemail]
+    sendmailCmd = ~/bin/msmtpq --set-from-header=on
+    envelopeSender = auto
+    annotate = true
+    multiEdit = true
+    confirm = always
+
+[credential "smtp.mailfence.com"]
+    username = robert@gnzler.de
+    helper = !sh -c 'rbw get 68b0ea26-733a-43cd-b8f8-e8ecc98de072 | xargs printf password=%s\\n'
+
+[credential "git.sr.ht"]
+    username = robertgzr
+    helper = !sh -c 'rbw get c7e750ea-6713-4254-90e7-6e077b552ffa | xargs printf password=%s\\n'
+
+[credential "github.com"]
+    username = robertgzr
+    helper = !sh -c 'rbw get f94a873d-9d72-412e-a46d-53e4cd19a291 | xargs printf password=%s\\n'
+
+[core]
+    excludesfile = $HOME/.config/git/ignore
+    filemode = false
+    pager = less -F
+
+[init]
+    defaultBranchName = master
+    # templateDir = ~/.dotfiles/git/templates
+
+[interactive]
+    diffFilter="diff-highlight | less -FRX --tabs=4"
+
+[log]
+    date = iso-strict-local
+
+[format]
+    signoff = true
+    coverLetter = false
+
+[column]
+    ui = auto
+
+[commit]
+    ; template = ~/.config/git/message
+    gpgSign = true
+    verbose = true
+
+[alias]
+    a = add --patch
+    amend = ci --amend --reuse-message=HEAD --edit
+    br = branch
+    ci = commit --trailer=signoff
+    co = checkout
+    cp = cherry-pick
+    dl = diff --stat
+    ds = diff --staged
+    fix = "!f() { local c=${1:-$(git fuzzy commits || :)}; test -n \"$c\" && git fixup $c && git ri $c^; }; f"
+    fixup = "!f() { local c=${1:-$(git fuzzy commits || :)}; test -n \"$c\" && git commit --fixup=$c; }; f"
+    grep = grep --break --heading --line-number
+    l = !tig
+    ls = log --graph --decorate --stat
+    mt = mergetool
+    pushf = push --force-with-lease
+    authors = shortlog -nse
+    reviewers = shortlog -nse --group=trailer:reviewed-by
+    ri = rebase --autostash --autosquash -i # -x 'git hook run pre-commit'
+    squash = "!f() { local c=${1:-$(git fuzzy commits || :)}; test -n \"$c\" && git commit --squash=$c; }; f"
+    st = status -sb
+    sw = switch
+    tig = !tig
+
+[apply]
+    # Detect whitespace errors when applying a patch
+    whitespace = fix
+
+[diff]
+    # Detect copies as well as renames
+    renames = copies
+    renameLimit = 3210
+    tool = difftastic
+
+[diff "bin"]
+    # Use `hexdump` to diff binary files
+    textconv = hexdump -v -C
+    renames = copies
+    tool = vimdiff
+
+[difftool]
+    prompt = false
+
+[pager]
+    difftool = true
+
+[fetch]
+    prune = true
+
+[submodule]
+    recurse = true
+
+[pull]
+    rebase = true
+    autostash = true
+
+[grep]
+    lineNumber = true
+
+[merge]
+    log = true
+    defaultToUpstream = true
+    tool = nvimdiff
+
+[mergetool "nvimdiff"]
+    conflictstyle = diff3
+    prompt = false
+
+[rerere]
+    enabled = false
+
+[push]
+    followTags = true
+    autoSetupRemote = true
+
+[tag]
+    gpgsign = true
+
+[safe]
+    directory = /var/tmp/portage/*
+    directory = /var/cache/distfiles/git3-src/*
+
+[trailer "signoff"]
+    key = "Signed-off-by"
+    cmd = ~/.config/git/signoff.sh $(git config user.email)
+    ifmissing = add
+    ifexists = replace
+    where = end
+[trailer "changetype"]
+    key = "Change-type"
+    where = start
+[trailer "connects"]
+    key = "Connects-to"
+    where = start
+[trailer "depends"]
+    key = "Depends-on"
+    where = start
+[trailer "changelog"]
+    key = "Changelog-entry"
+    where = start
+[trailer "coauthor"]
+    key = "Co-authored-by"
+    where = start
+[trailer "reviewed"]
+    key = "Reviewed-by"
+    where = start
+[trailer "tested"]
+    key = "Tested-by"
+    where = start
+[trailer "fixes"]
+    key = "Fixes"
+    where = start
+[trailer "refs"]
+    key = "Refs"
+    where = start
+
+
+[include]
+    path = tig
+
+[includeIf "gitdir:~/src/unikraft/**"]
+    path = ~/src/unikraft/.gitconfig
+
+
+# url rewrites
+; [url "git@github.com:"]
+;     insteadOf = "https://github.com/"
+[url "git@git.sr.ht:"]
+    insteadOf = "https://git.sr.ht/"
+; [url "git@bitbucket.org:"]
+;     insteadOf = "https://bitbucket.org/"
+; [url "git@gitlab.com:"]
+;     insteadOf = "https://gitlab.com/"
+
+
+; vim: expandtab shiftwidth=4
diff --git a/.config/git/ignore b/.config/git/ignore
new file mode 100644
index 0000000..33b0186
--- /dev/null
+++ b/.config/git/ignore
@@ -0,0 +1,60 @@
+# Random
+*~
+._*
+Thumbs.db
+.DS_Store*
+Desktop.ini
+.idea
+ehthumbs.db
+Icon*
+Session.vim
+
+# external
+.Spotlight-V100
+.Trashes
+
+# compiled
+*.o
+*.out
+*.pyc
+*.so
+*.exe
+*.class
+*.dll
+bin/
+tags
+
+# logs and db
+*.log
+*.sql
+*.sqlite
+*.db
+
+# packages
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# web stuff
+node_modules
+bower_components
+
+# personal stuff
+.todo.md
+.org.md
+
+# clangd
+.cache/
+
+# cargo
+target/debug/build/
+target/release/build/
+
+# zig
+zig-out/
+.zig-cache/
diff --git a/.config/git/message b/.config/git/message
new file mode 100644
index 0000000..d6c62b9
--- /dev/null
+++ b/.config/git/message
@@ -0,0 +1,2 @@
+
+Signed-off-by: Robert Günzler <r@gnzler.io>
diff --git a/.config/git/signoff.sh b/.config/git/signoff.sh
new file mode 100755
index 0000000..9a0f3f6
--- /dev/null
+++ b/.config/git/signoff.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+printf '%s <%s>' "$(git config user.name)" "$1"
diff --git a/.config/git/tig b/.config/git/tig
new file mode 100644
index 0000000..3ab5af7
--- /dev/null
+++ b/.config/git/tig
@@ -0,0 +1,41 @@
+[tig "color"]
+    cursor = default default bold reverse
+    title-focus = black blue
+    title-blur = black white
+
+[tig "bind"]
+    generic = y none
+    generic = yy !@wl-copy -n "%(commit)"
+    generic = I !@fyi -u critical -a tig -- 'tig: you should use yy'
+
+    generic = G move-last-line
+    generic = V view-pager
+    ; generic = <Ctrl-n> find-next
+    ; generic = <Ctrl-p> find-prev
+    ; generic = <Ctrl-l> scroll-right
+    ; generic = <Ctrl-h> scroll-left
+    ; generic = <Ctrl-g> refresh
+    generic = ~ :goto %(commit)^
+
+    main = g none
+    main = gg move-first-line
+    main = G move-last-line
+    main = <esc>g :toggle commit-title-graph
+
+    diff = g none
+    diff = gg move-first-line
+    diff = G move-last-line
+    diff = n :/^@@
+    diff = p :?^@@
+
+    stage = n :/^@@
+    stage = p :?^@@
+
+    pager = n :/^@@
+    pager = p :?^@@
+
+    log = n :/^commit
+    log = p :?^commit
+    log = I !@wl-copy -n "%(commit)"
+
+; vim: ft=gitconfig expandtab shiftwidth=4
diff --git a/.config/nvim/after/ftplugin/typst.lua b/.config/nvim/after/ftplugin/typst.lua
index e444bd5..a201c00 100644
--- a/.config/nvim/after/ftplugin/typst.lua
+++ b/.config/nvim/after/ftplugin/typst.lua
@@ -1,6 +1,6 @@
 vim.api.nvim_buf_create_user_command(0, 'OpenPdf', function()
   local path = vim.api.nvim_buf_get_name(0)
-  vim.system({ 'xdg-open', path:gsub('%.typ$', '.pdf') })
+  vim.system({ 'gio', 'open', path:gsub('%.typ$', '.pdf') })
 end, { desc = 'typst: open pdf' })
 
 vim.keymap.set('n', '<leader>o', '<cmd>OpenPdf<cr>', { desc = 'typst: open pdf', buffer = 0 })
diff --git a/.config/nvim/after/lsp/tinymist.lua b/.config/nvim/after/lsp/tinymist.lua
index 77f0c66..d832f57 100644
--- a/.config/nvim/after/lsp/tinymist.lua
+++ b/.config/nvim/after/lsp/tinymist.lua
@@ -1,7 +1,8 @@
 return {
   settings = {
     exportPdf = "onSave",
-    -- semanticTokens = "disabled",
-    formatterMode = "typstyle",
+    lint = {
+      enabled = true,
+    },
   }
 }
diff --git a/.config/nvim/lazy-lock.json b/.config/nvim/lazy-lock.json
index 8ee25fe..16e407a 100644
--- a/.config/nvim/lazy-lock.json
+++ b/.config/nvim/lazy-lock.json
@@ -1,30 +1,32 @@
 {
-  "LuaSnip": { "branch": "master", "commit": "5a1e39223db9a0498024a77b8441169d260c8c25" },
-  "blink.cmp": { "branch": "main", "commit": "4b18c32adef2898f95cdef6192cbd5796c1a332d" },
+  "LuaSnip": { "branch": "master", "commit": "642b0c595e11608b4c18219e93b88d7637af27bc" },
+  "blink.cmp": { "branch": "main", "commit": "78336bc89ee5365633bcf754d93df01678b5c08f" },
   "calcium.nvim": { "branch": "main", "commit": "7a7d3740083fa5669e1a0f25d133705624a23f0d" },
   "friendly-snippets": { "branch": "main", "commit": "6cd7280adead7f586db6fccbd15d2cac7e2188b9" },
-  "fzf-lua": { "branch": "main", "commit": "fb8c50ba62a0daa433b7ac2b78834f318322b879" },
+  "fzf-lua": { "branch": "main", "commit": "9f0432fdd7825ab163520045831a40b6df82ea28" },
   "gentoo-syntax": { "branch": "master", "commit": "1b3a81ec62e5c843245dc431be0fe072add7eca8" },
   "git-dev.nvim": { "branch": "master", "commit": "90c1395ca37a38228e287d0a843835a5a39ce553" },
   "gitgraph.nvim": { "branch": "main", "commit": "c16daa7d7dd597caf9085644c009cfa80b75db8e" },
-  "gitlinker.nvim": { "branch": "master", "commit": "bbe2a1254fc8fce21f3bbf9a020266a1c49799f7" },
-  "gitsigns.nvim": { "branch": "main", "commit": "31217271a7314c343606acb4072a94a039a19fb5" },
+  "gitlinker.nvim": { "branch": "master", "commit": "d7adb5e4ba5bab4fd443a3f4c46af3f4c864685d" },
+  "gitsigns.nvim": { "branch": "main", "commit": "0d797daee85366bc242580e352a4f62d67557b84" },
   "guess-indent.nvim": { "branch": "main", "commit": "84a4987ff36798c2fc1169cbaff67960aed9776f" },
-  "indent-blankline.nvim": { "branch": "master", "commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba" },
+  "haunt.nvim": { "branch": "main", "commit": "666d83868bac8b38f1dc765d9da6eca4d92c826f" },
+  "indent-blankline.nvim": { "branch": "master", "commit": "d28a3f70721c79e3c5f6693057ae929f3d9c0a03" },
+  "koda.nvim": { "branch": "main", "commit": "a560a332ccc1eb2caacb280a390213bb9f37b3cb" },
   "lazy.nvim": { "branch": "main", "commit": "306a05526ada86a7b30af95c5cc81ffba93fef97" },
   "markdown.nvim": { "branch": "master", "commit": "093be911d90de03877f3009de36081c1a6242d51" },
-  "mini.ai": { "branch": "main", "commit": "9eae720f2b20f6ad28cbfa0ddc524e10dc2c3201" },
-  "mini.align": { "branch": "main", "commit": "6d8a6ca3a0fd313938374abccb50cad026229db3" },
-  "mini.icons": { "branch": "main", "commit": "efc85e42262cd0c9e1fdbf806c25cb0be6de115c" },
-  "mini.operators": { "branch": "main", "commit": "c68010bb5498d418e056704710f71cd8d6fa227b" },
-  "mini.surround": { "branch": "main", "commit": "d648a5601e1c48f175b07d10eba141da338a0a2a" },
-  "nvim-colorizer.lua": { "branch": "master", "commit": "338409dd8a6ed74767bad3eb5269f1b903ffb3cf" },
-  "nvim-lspconfig": { "branch": "master", "commit": "5bfcc89fd155b4ffc02d18ab3b7d19c2d4e246a7" },
-  "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" },
-  "nvim-treesitter-refactor": { "branch": "master", "commit": "9cc0d22becf72e18808208cd0ce85032a2b19c6f" },
-  "nvim-treesitter-textobjects": { "branch": "master", "commit": "5ca4aaa6efdcc59be46b95a3e876300cfead05ef" },
-  "oil.nvim": { "branch": "master", "commit": "f55b25e493a7df76371cfadd0ded5004cb9cd48a" },
+  "mini.ai": { "branch": "main", "commit": "43eb2074843950a3a25aae56a5f41362ec043bfa" },
+  "mini.align": { "branch": "main", "commit": "6f3c69cd57134ad8fbf1e85dcdc90cb37a8c75c8" },
+  "mini.icons": { "branch": "main", "commit": "7fdae2443a0e2910015ca39ad74b50524ee682d3" },
+  "mini.operators": { "branch": "main", "commit": "fccb5e3ef0cc319b217ed9c7db1a4672dd6b766c" },
+  "mini.surround": { "branch": "main", "commit": "2715e04bea3ec9244f15b421dc5b18c0fe326210" },
+  "none-ls-shellcheck.nvim": { "branch": "main", "commit": "0f84461241e76e376a95fb7391deac82dc3efdbf" },
+  "none-ls.nvim": { "branch": "main", "commit": "7f9301e416533b5d74e2fb3b1ce5059eeaed748b" },
+  "nvim-colorizer.lua": { "branch": "master", "commit": "c8c2c19436d33165ec34fa90c975bc0a739fe3fb" },
+  "nvim-lspconfig": { "branch": "master", "commit": "0203a9608d63eda57679b01e69f33a7b4c34b0d1" },
+  "nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
+  "oil.nvim": { "branch": "master", "commit": "0fcc83805ad11cf714a949c98c605ed717e0b83e" },
   "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" },
-  "render-markdown": { "branch": "main", "commit": "48b4175dbca8439d30c1f52231cbe5a712c8f9d9" },
+  "render-markdown": { "branch": "main", "commit": "4ae2f2e8e8c66d070f33cfb57cb6f867e3baf5d9" },
   "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
 }
diff --git a/.config/nvim/lua/config/diagnostics.lua b/.config/nvim/lua/config/diagnostics.lua
index 6314b83..158e3e4 100644
--- a/.config/nvim/lua/config/diagnostics.lua
+++ b/.config/nvim/lua/config/diagnostics.lua
@@ -23,7 +23,9 @@ vim.diagnostic.config({
   underline = {
     severity = { min = vim.diagnostic.severity.WARN },
   },
-  float = false,
+  float = {
+    source = true,
+  },
   virtual_text = false,
   virtual_lines = false,
 })
@@ -34,16 +36,7 @@ vim.api.nvim_set_hl(0, 'DiagnosticUnderlineWarn', commonhl)
 vim.api.nvim_set_hl(0, 'DiagnosticUnderlineInfo', commonhl)
 vim.api.nvim_set_hl(0, 'DiagnosticUnderlineHint', commonhl)
 
-vim.keymap.set('n', '[d', function()
-  vim.diagnostic.jump({ count = 1, float = true })
-end, { desc = 'diagnostic: next' })
-
-vim.keymap.set('n', ']d', function()
-  vim.diagnostic.jump({ count = -1, float = true })
-end, { desc = 'diagnostic: prev' })
-
 vim.keymap.set('n', 'DD', vim.diagnostic.open_float, { desc = 'diagnostic: current line' })
-
 vim.keymap.set('n', '<leader>d', '<nop>', { desc = "diagnostics" })
 vim.keymap.set('n', '<leader>dd', vim.diagnostic.open_float, { desc = 'diagnostic: current line' })
 vim.keymap.set('n', '<leader>dv', function()
diff --git a/.config/nvim/lua/config/filetypes.lua b/.config/nvim/lua/config/filetypes.lua
index f1428d2..f3657db 100644
--- a/.config/nvim/lua/config/filetypes.lua
+++ b/.config/nvim/lua/config/filetypes.lua
@@ -1,5 +1,6 @@
 vim.filetype.add({
   pattern = {
+    ['^/etc/portage/package.*/$'] = 'oil',
     ['todo.txt'] = 'todotxt',
-  }
+  },
 })
diff --git a/.config/nvim/lua/config/keymaps.lua b/.config/nvim/lua/config/keymaps.lua
index 9c00f7b..a93b8fd 100644
--- a/.config/nvim/lua/config/keymaps.lua
+++ b/.config/nvim/lua/config/keymaps.lua
@@ -18,10 +18,10 @@ map(
   { desc = 'Open file under cursor' }
 )
 
-vim.api.nvim_create_autocmd({'BufEnter', 'BufWinEnter'}, {
+vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWinEnter' }, {
   group = vim.api.nvim_create_augroup('bufclose', {}),
   callback = function()
-    if vim.list_contains({'help', 'nofile', 'quickfix'}, vim.bo.buftype) then
+    if vim.list_contains({ 'help', 'nofile', 'quickfix' }, vim.bo.buftype) then
       map('', 'q', '<cmd>close<cr>', { desc = 'Close buffer', buffer = 0 })
     end
   end,
@@ -34,3 +34,8 @@ map('n', '<leader><leader>x', function()
 end, { desc = 'source current file' })
 
 map('n', '<leader>a', '<cmd>edit #<cr>', { desc = 'open alternative file' })
+
+-- yank buffer name
+map('n', ';yb', [[<cmd>let @+ = expand('%')<cr>]], { desc = 'yank buffer name' })
+map('n', ';yn', [[<cmd>let @+ = expand('%:t')<cr>]], { desc = 'yank filename' })
+map('n', ';yp', [[<cmd>let @+ = expand('%:p')<cr>]], { desc = 'yank path' })
diff --git a/.config/nvim/lua/config/lsp.lua b/.config/nvim/lua/config/lsp.lua
index 282a3dc..b168014 100644
--- a/.config/nvim/lua/config/lsp.lua
+++ b/.config/nvim/lua/config/lsp.lua
@@ -1,4 +1,6 @@
-if not vim.lsp.enable then return end
+if not vim.lsp.enable then
+  return
+end
 
 local lsp = vim.lsp
 
@@ -7,6 +9,8 @@ lsp.enable({
   'ccls',
   'clangd',
   'gopls',
+  'harper_ls',
+  'pylsp',
   'rust_analyzer',
   'superhtml',
   'tinymist',
@@ -14,17 +18,21 @@ lsp.enable({
   'zls',
 })
 
-local capabilities = nil
+local capabilities = {}
 if pcall(require, 'blink.cmp') then
   capabilities = require('blink.cmp').get_lsp_capabilities()
 end
 
 lsp.config('*', {
-  root_markers = {'.git'},
-  capabilities = capabilities
+  root_markers = { '.git' },
+  capabilities = vim.tbl_deep_extend('error', capabilities, {
+    textDocument = {
+      semanticTokens = { multilineTokenSupport = true },
+    },
+  }),
 })
 
-vim.api.nvim_create_autocmd({'LspAttach'}, {
+vim.api.nvim_create_autocmd({ 'LspAttach' }, {
   group = vim.api.nvim_create_augroup('lspattach', {}),
   callback = function(args)
     local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
@@ -36,7 +44,7 @@ vim.api.nvim_create_autocmd({'LspAttach'}, {
     vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, { desc = 'goto declaration' })
     vim.keymap.set('n', 'gT', vim.lsp.buf.type_definition, { desc = 'goto type definition' })
     vim.keymap.set('n', 'gR', vim.lsp.buf.references, { desc = 'show references' })
-    vim.keymap.set('n', 'ga', vim.lsp.buf.code_action, { desc = 'show code actions' })
+    vim.keymap.set({ 'n', 'v' }, 'ga', vim.lsp.buf.code_action, { desc = 'show code actions' })
     vim.keymap.set('n', 'grr', vim.lsp.buf.rename, { desc = 'rename' })
 
     if client:supports_method('textDocument/implementation') then
@@ -44,20 +52,24 @@ vim.api.nvim_create_autocmd({'LspAttach'}, {
     end
 
     if client:supports_method('textDocument/completion') then
-      vim.lsp.completion.enable(true, client.id, args.buf, {autotrigger = false})
+      vim.lsp.completion.enable(true, client.id, args.buf, { autotrigger = false })
     end
 
     if client:supports_method('textDocument/formatting') then
-    vim.keymap.set('n', '<leader>f', vim.lsp.buf.format, { desc = 'format' })
+      vim.keymap.set('n', '<leader>f', vim.lsp.buf.format, { desc = 'format' })
     end
 
-    if not client:supports_method('textDocument/willSaveWaitUntil')
-        and client:supports_method('textDocument/formatting') then
-      vim.api.nvim_create_autocmd({'BufWritePre'}, {
+    if
+      not client:supports_method('textDocument/willSaveWaitUntil')
+      and client:supports_method('textDocument/formatting')
+    then
+      vim.api.nvim_create_autocmd({ 'BufWritePre' }, {
         group = vim.api.nvim_create_augroup('lspattach', {}),
         buffer = args.buf,
         callback = function()
-          if vim.b.nofmt then return end
+          if vim.b.nofmt then
+            return
+          end
           vim.lsp.buf.format({ bufnr = args.buf, id = client.id, timeout_ms = 1000 })
         end,
       })
diff --git a/.config/nvim/lua/plugins/colorizer.lua b/.config/nvim/lua/plugins/colorizer.lua
index 2608ae6..c949058 100644
--- a/.config/nvim/lua/plugins/colorizer.lua
+++ b/.config/nvim/lua/plugins/colorizer.lua
@@ -4,12 +4,14 @@ return {
     -- 'norcalli/nvim-colorizer.lua',
     lazy = false,
     opts = {
-      name = true,
-      RRGGBB = true,
-      RRGGBBAA = true,
-      rgb_fn = true,
-      hsl_fn = true,
-      mode = 'background',
+      options = {
+        display = {
+          mode = 'background',
+        },
+        parsers = {
+          css = true,
+        },
+      },
     },
   },
 }
diff --git a/.config/nvim/lua/plugins/gentoo.lua b/.config/nvim/lua/plugins/gentoo.lua
index 470e6de..15ef3aa 100644
--- a/.config/nvim/lua/plugins/gentoo.lua
+++ b/.config/nvim/lua/plugins/gentoo.lua
@@ -1,15 +1,18 @@
 return {
   {
-    'gentoo/gentoo-syntax',
-    -- ft = { 'ebuild' },
-    -- init = function()
-    --   vim.api.nvim_create_autocmd('LspAttach', {
-    --     callback = function(args)
-    --       if vim.opt.filetype == 'ebuild' then
-    --         vim.lsp.stop_client(vim.lsp.get_clients({ bufnr = args.bufnr }))
-    --       end
-    --     end,
-    --   })
-    -- end,
+    'https://github.com/gentoo/gentoo-syntax',
+    lazy = false,
+    init = function()
+      vim.api.nvim_create_autocmd({ 'BufEnter' }, {
+        pattern = { '*/portage/package.*/' },
+        group = vim.api.nvim_create_augroup('gentoo-syntax-oil', {}),
+        callback = function(args)
+          -- HACK: this should not be necessary
+          vim.defer_fn(function()
+            vim.opt_local.filetype = 'oil'
+          end, 50)
+        end,
+      })
+    end,
   },
 }
diff --git a/.config/nvim/lua/plugins/git.lua b/.config/nvim/lua/plugins/git.lua
index 04f0c9f..d1e3f7b 100644
--- a/.config/nvim/lua/plugins/git.lua
+++ b/.config/nvim/lua/plugins/git.lua
@@ -1,6 +1,7 @@
 return {
   {
     'lewis6991/gitsigns.nvim',
+    lazy = false,
     opts = {
       debug_mode = false,
     },
diff --git a/.config/nvim/lua/plugins/lsp.lua b/.config/nvim/lua/plugins/lsp.lua
index aebf3c4..311f1bc 100644
--- a/.config/nvim/lua/plugins/lsp.lua
+++ b/.config/nvim/lua/plugins/lsp.lua
@@ -2,5 +2,28 @@ return {
   {
     'neovim/nvim-lspconfig',
     version = '^2.0',
-  }
+  },
+  {
+    'nvimtools/none-ls.nvim',
+    dependencies = {
+      'nvim-lua/plenary.nvim',
+      'gbprod/none-ls-shellcheck.nvim',
+    },
+    config = function()
+      local nls = require('null-ls')
+      nls.setup({
+        sources = {
+          -- nls.builtins.code_actions.impl,
+          -- nls.builtins.completions.spell,
+
+          nls.builtins.formatting.stylua,
+          nls.builtins.formatting.shfmt,
+          nls.builtins.formatting.buf,
+
+          require("none-ls-shellcheck.diagnostics"),
+          require("none-ls-shellcheck.code_actions"),
+        },
+      })
+    end,
+  },
 }
diff --git a/.config/nvim/lua/plugins/mini.lua b/.config/nvim/lua/plugins/mini.lua
index 74cd962..fb4c9a6 100644
--- a/.config/nvim/lua/plugins/mini.lua
+++ b/.config/nvim/lua/plugins/mini.lua
@@ -2,7 +2,7 @@ return {
   {
     'echasnovski/mini.align',
     version = false,
-    lazy = true,
+    lazy = false,
     opts = {},
   },
   {
diff --git a/.config/nvim/lua/plugins/treesitter.lua b/.config/nvim/lua/plugins/treesitter.lua
index e064a8b..0978eb7 100644
--- a/.config/nvim/lua/plugins/treesitter.lua
+++ b/.config/nvim/lua/plugins/treesitter.lua
@@ -1,19 +1,26 @@
 return {
   {
     'nvim-treesitter/nvim-treesitter',
-    branch = 'master',
+    branch = 'main',
     lazy = false,
-    dependencies = {
-      { 'nvim-treesitter/nvim-treesitter-refactor', lazy = false },
-      { 'nvim-treesitter/nvim-treesitter-textobjects', lazy = false },
-      -- { 'nvim-treesitter/playground', cmd = { 'TSPlayground' } },
-    },
     build = ':TSUpdate',
     config = function(_, opts)
-      require('nvim-treesitter.configs').setup(opts)
-
-      -- use bash parser for gentoo ebuilds
-      vim.treesitter.language.register('bash', 'ebuild')
+      -- enable indent
+      vim.api.nvim_create_autocmd('FileType', {
+        callback = function()
+          pcall(vim.treesitter.start)
+          vim.bo.indentexpr = [[v:lua.require'nvim-treesitter'.indentexpr()]]
+        end,
+      })
+      -- auto-install
+      local already_installed = require('nvim-treesitter').get_installed()
+      local parsers_installable = vim
+        .iter(opts.ensure_installed)
+        :filter(function(parser)
+          return not vim.tbl_contains(already_installed, parser)
+        end)
+        :totable()
+      require('nvim-treesitter').install(parsers_installable)
     end,
     opts = {
       ensure_installed = {
@@ -31,7 +38,6 @@ return {
         'html',
         'javascript',
         'json',
-        'jsonc',
         'lua',
         'make',
         'markdown',
@@ -51,43 +57,6 @@ return {
         'yaml',
         'zig',
       },
-      highlight = {
-        enable = true,
-        additional_vim_regex_highlighting = { 'markdown' },
-      },
-      indent = {
-        enable = true,
-      },
-      refactor = {
-        navigation = {
-          enable = true,
-        },
-        smart_rename = {
-          enable = true,
-        },
-      },
-      textobjects = {
-        select = {
-          enable = true,
-          keymaps = {
-            ['ib'] = '@block.inner',
-            ['ab'] = '@block.outer',
-            -- ['ic'] = '@conditional.inner',
-            -- ['ac'] = '@conditional.outer',
-            ['if'] = '@function.inner',
-            ['af'] = '@function.outer',
-            ['il'] = '@loop.inner',
-            ['al'] = '@loop.outer',
-            ['is'] = '@scopename.inner',
-            ['as'] = '@scopename.outer',
-            ['ic'] = '@comment.inner',
-            ['ac'] = '@comment.outer',
-          },
-          selection_mode = {
-            ['@comment.outer'] = 'V',
-          },
-        },
-      },
     },
   },
 }
diff --git a/.config/nvim/lua/plugins/whichkey.lua b/.config/nvim/lua/plugins/whichkey.lua
index dc21eda..cb89df4 100644
--- a/.config/nvim/lua/plugins/whichkey.lua
+++ b/.config/nvim/lua/plugins/whichkey.lua
@@ -3,8 +3,7 @@ return {
     'folke/which-key.nvim',
     event = 'VeryLazy',
     opts = {
-      sort = { 'alphanum' },
-      icons = { separator = '→ ', group = '🞱 ' },
+      icons = { separator = '→ ', group = ' +' },
     },
   },
 }
diff --git a/.config/river/init b/.config/river/init
new file mode 100755
index 0000000..ae976d7
--- /dev/null
+++ b/.config/river/init
@@ -0,0 +1,143 @@
+#!/bin/sh
+
+# start desktop runlevel
+riverctl spawn 'openrc -U desktop'
+
+# ::start style
+riverctl background-color 0x080808ff
+riverctl border-color-focused 0x70649aff
+riverctl border-color-unfocused 0x080808af
+riverctl border-color-urgent 0xcb1b45ff
+riverctl border-width 3
+# ::end style
+
+# rules
+riverctl rule-add -app-id '*' ssd
+riverctl rule-add -app-id 'librewolf' -title 'Picture-in-Picture' float
+riverctl rule-add -app-id 'workfox' ssd
+riverctl rule-add -app-id 'foot-float' float
+riverctl rule-add -app-id 'mpv' float
+riverctl rule-add -app-id 'nm-connection-editor' float
+
+# keymaps: wm
+riverctl map normal Super+Shift E spawn 'openrc -U default && riverctl exit'
+riverctl map normal Super+Shift Q close
+riverctl map normal Super+Shift Return zoom
+riverctl map normal Super F toggle-fullscreen
+riverctl map normal Super+Shift Space toggle-float
+
+riverctl map normal Super J focus-view next
+riverctl map normal Super K focus-view previous
+riverctl map normal Super L focus-view right
+riverctl map normal Super H focus-view left
+riverctl map normal Super+Shift J swap next
+riverctl map normal Super+Shift K swap previous
+riverctl map normal Super+Shift L swap right
+riverctl map normal Super+Shift H swap left
+riverctl map normal Super Backspace focus-previous-tags
+riverctl map normal Super+Shift Backspace move-previous-tags
+riverctl map normal Super Period focus-output next
+riverctl map normal Super Comma focus-output previous
+riverctl map normal Super+Shift Period send-to-output next
+riverctl map normal Super+Shift Comma send-to-output previous
+all_tags=$(((1 << 32) - 1))
+riverctl map normal Super 0 set-focused-tags $all_tags
+riverctl map normal Super+Shift 0 set-view-tags $all_tags
+for i in $(seq 1 9); do
+	tags=$((1 << (i - 1)))
+	# tag management
+	riverctl map normal Super "$i" set-focused-tags "$tags"
+	riverctl map normal Super+Shift "$i" set-view-tags "$tags"
+	riverctl map normal Super+Control "$i" toggle-focused-tags "$tags"
+	riverctl map normal Super+Shift+Control "$i" toggle-view-tags "$tags"
+done
+riverctl map-pointer normal Super BTN_LEFT move-view
+riverctl map-pointer normal Super BTN_RIGHT resize-view
+
+# keymaps: apps
+riverctl map normal Super Return spawn 'footclient'
+riverctl map normal Super+Control Return spawn 'foot -a foot-float -W 120x40'
+riverctl map normal Super D spawn 'fuzzel'
+riverctl map normal Super Z spawn 'footclient -- sleep .4 && zk e'
+riverctl map normal Super+Shift S spawn '~/bin/grimshot'
+
+# keymaps: cmd mode
+riverctl declare-mode cmd
+riverctl map normal Super Space enter-mode cmd
+riverctl map cmd None Escape enter-mode normal
+riverctl map cmd None Space spawn "riverctl enter-mode normal; menm"
+riverctl map cmd None Backspace spawn "riverctl enter-mode normal; susm"
+riverctl map cmd None A spawn "riverctl enter-mode normal; audm"
+riverctl map cmd None B spawn "riverctl enter-mode normal; blum"
+riverctl map cmd None C spawn "riverctl enter-mode normal; netm"
+riverctl map cmd None D spawn "riverctl enter-mode normal; dskm"
+riverctl map cmd None F spawn "riverctl enter-mode normal; fm"
+riverctl map cmd None K spawn "riverctl enter-mode normal; keym"
+riverctl map cmd None N spawn "riverctl enter-mode normal; ntfm"
+riverctl map cmd None P spawn "riverctl enter-mode normal; pasm"
+riverctl map cmd None R spawn "riverctl enter-mode normal; svcm"
+riverctl map cmd None S spawn "riverctl enter-mode normal; snim"
+riverctl map cmd None T spawn "riverctl enter-mode normal; flatpak run com.odnoyko.valot --compact"
+riverctl map cmd None V spawn "riverctl enter-mode normal; vpnm"
+riverctl map cmd None X spawn "riverctl enter-mode normal; fuzzel --list-executables-in-path"
+riverctl map cmd Shift C spawn "riverctl enter-mode normal; calm"
+
+# keymaps: misc
+# riverctl map-switch normal lid close spawn 'waylock -init-color 0x005469 & playerctl pause'
+riverctl map-switch normal lid close spawn 'fyi todo lock'
+
+riverctl map normal Super B spawn 'pkill -USR1 waybar'
+riverctl map normal Super+Alt K spawn 'mpv ~/pictures/fwkeyboard.jpg'
+
+riverctl map normal Super C spawn 'fyi todo xf86copy'
+riverctl map normal Super X spawn 'fyi todo xf86cut'
+riverctl map normal Super V spawn 'fyi todo xf86paste'
+
+# keymaps: fn/media keys
+riverctl map normal None XF86AudioRaiseVolume spawn 'wpctl set-volume --limit=1.0 @DEFAULT_AUDIO_SINK@ 5%+'
+riverctl map normal None XF86AudioLowerVolume spawn 'wpctl set-volume --limit=1.0 @DEFAULT_AUDIO_SINK@ 5%-'
+riverctl map normal None XF86AudioMute spawn 'wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle'
+riverctl map normal None XF86AudioMicMute spawn 'wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle'
+riverctl map normal None XF86AudioPlay spawn 'playerctl -p playerctld play-pause'
+riverctl map normal None XF86AudioPrev spawn 'playerctl -p playerctld previous'
+riverctl map normal None XF86AudioNext spawn 'playerctl -p playerctld next'
+riverctl map normal None XF86MonBrightnessUp spawn 'brightnessctl set +5%'
+riverctl map normal None XF86MonBrightnessDown spawn 'brightnessctl set 5%-'
+
+# input
+riverctl keyboard-layout -options caps:super,eurosign:5 -variant mac us
+riverctl set-repeat 70 200
+riverctl set-cursor-warp on-output-change
+riverctl focus-follows-cursor normal
+riverctl hide-cursor timeout 0
+riverctl hide-cursor when-typing disabled
+riverctl xcursor-theme Adwaita 16
+
+riverctl input 'pointer-*' natural-scroll enabled
+riverctl input 'pointer-*' pointer-accel 0
+riverctl input 'pointer-*' tap enabled
+riverctl input 'pointer-*' accel-profile adaptive
+riverctl input 'pointer-*' disable-while-typing enabled
+riverctl input '*Kensington_Slimblade_Trackball' natural-scroll disabled
+riverctl input '*Kensington_Slimblade_Trackball' pointer-accel 1
+
+# spawn a terminal
+riverctl spawn foot
+
+# wm
+riverctl default-layout rivercarro
+
+riverctl map normal Super Equal send-layout-cmd rivercarro 'main-ratio +0.02'
+riverctl map normal Super Minus send-layout-cmd rivercarro 'main-ratio -0.02'
+riverctl map normal Super+Alt H send-layout-cmd rivercarro 'main-location left'
+riverctl map normal Super+Alt K send-layout-cmd rivercarro 'main-location top'
+riverctl map normal Super+Alt J send-layout-cmd rivercarro 'main-location bottom'
+riverctl map normal Super+Alt L send-layout-cmd rivercarro 'main-location right'
+riverctl map normal Super+Alt Space send-layout-cmd rivercarro 'main-location monocle'
+
+exec /usr/bin/rivercarro \
+	-inner-gaps 3 \
+	-outer-gaps 3 \
+	-main-location left \
+	-main-ratio 0.6 \
+	-main-count 1
diff --git a/.config/waybar/config.jsonc b/.config/waybar/config.jsonc
new file mode 100644
index 0000000..eac00c8
--- /dev/null
+++ b/.config/waybar/config.jsonc
@@ -0,0 +1,326 @@
+{
+  "position": "top",
+  "spacing": 0,
+  "reload_style_on_change": true,
+
+  // ------------------------------------------------------------------------ //
+
+  "modules-left": [
+    "custom/launcher",
+    "river/mode",
+    "river/tags",
+    "river/window",
+  ],
+
+  "custom/launcher": {
+    "format": "",
+    "tooltip": false,
+    "on-click": "riverctl spawn fuzzel",
+  },
+
+  "river/mode": {},
+
+  "river/tags": {
+    "format": ":{}",
+    "num-tags": 9,
+    "hide-vacant": true,
+  },
+
+  "river/window": {
+    "expand": true,
+    "max-length": 50,
+  },
+
+  // ------------------------------------------------------------------------ //
+
+  "modules-center": [
+    "privacy",
+    "custom/screenrec",
+    "clock#local",
+    "clock#utc",
+    "custom/calendar",
+    "custom/timew",
+  ],
+
+  "privacy": {
+    "icon-size": 9,
+    "modules": [
+      { "type": "screenshare" },
+      { "type": "audio-in" },
+    ],
+  },
+
+  "custom/screenrec": {
+    "format": "{icon}",
+    "format-icons": {
+      "running": "󰹑"
+    },
+    "exec": "~/.config/waybar/module-screenrec.sh",
+    "return-type": "json",
+    "interval": "once",
+    "signal": 1,
+  },
+
+  "clock#local": {
+    "format": "<i>{:%a</i> %d %b <b>%H:%M}</b>",
+    "tooltip-format": "TZ: {:%Z %z}\n\n<tt>{calendar}</tt>",
+    "locale": "",
+    "calendar": {
+      "mode": "year",
+      "mode-mon-col": 3,
+      "weeks-pos": "",
+      "on-scroll": 1,
+      "format": {
+        "today": "<span color='red'><b><u>{}</u></b></span>",
+      },
+    },
+    "actions": {
+      "on-click-right": "mode",
+    },
+  },
+
+  "clock#utc": {
+    "format": "<small>{:%Z</small> %H:%M}",
+    "format-alt": "<small>{:%Z %z</small> %a %b %d %H:%M}",
+    "tooltip-format": "{tz_list}",
+    "timezones": [
+      "Etc/UTC",
+
+      "America/Los_Angeles",
+      "Europe/Berlin",
+      "Europe/Istanbul",
+      "Asia/Kolkata",
+      "Asia/Tokyo",
+    ],
+    "actions": {
+      "on-scroll-up": "tz_up",
+      "on-scroll-down": "tz_down",
+    },
+  },
+
+  "custom/calendar": {
+    "exec": "~/.config/waybar/module-calendar.sh",
+    "return-type": "json",
+    "interval": 10800,
+    "on-click": "calm",
+  },
+
+  "custom/timew": {
+    "exec": "~/.config/waybar/module-timew.sh",
+    "return-type": "json",
+    "interval": 900,
+  },
+
+  // ------------------------------------------------------------------------ //
+
+  "modules-right": [
+    "mpris",
+    "tray",
+    "group/tailscale",
+    "group/net",
+    "bluetooth",
+    "group/sys",
+    "idle_inhibitor",
+    "backlight",
+    "pulseaudio",
+    "power-profiles-daemon",
+    "upower",
+  ],
+
+  "mpris": {
+    "max-length": 50,
+    "format": "{player_icon} {dynamic}",
+    "format-paused": "{status_icon} <i>{dynamic}</i>",
+    "dynamic-order": ["artist", "title", "length"],
+    "player-icons": {
+      "default": "󰲸",
+      "firefox": "",
+      "librewolf": "",
+      "spotify": "󰓇",
+      "mpv": "",
+    },
+    "status-icons": {
+      "paused": "",
+      "playing": "",
+      "stopped": "",
+    }
+  },
+
+  "tray": {
+    "icon-size": 13,
+    "spacing": 2
+  },
+
+  "group/net": {
+    "orientation": "inherit",
+    "drawer": {
+      "transition-duration": 0,
+      "click-to-reveal": true,
+    },
+    "modules": [
+      "network#primary",
+      "network#bandwidth",
+    ],
+  },
+
+  "network#primary": {
+    "format-wifi": "󰖩{signalStrength}%",
+    "format-ethernet": "󰈀",
+    "format-disconnected": "󰌙",
+    //  SSID:      {essid}
+    //             {bssid}
+    // 󱄙 Signal:    {signaldBm} dBm
+    //  Frequency: {frequency} MHz
+    //
+    // 󰛳 Interface: {ifname}
+    // 󰩟 IP:        {ipaddr}
+    // 󱂇 Gateway:   {gwaddr}/{cidr}
+    "tooltip-format-wifi": " SSID:      {essid}\n            {bssid}\n󱄙 Signal:    {signaldBm} dBm\n Frequency: {frequency} MHz\n\n󰛳 Interface: {ifname}\n󰩟 IP:        {ipaddr}\n󱂇 Gateway:   {gwaddr}/{cidr}",
+    "tooltip-format-ethernet": "󰛳 Interface: {ifname}\n󰩟 IP:        {ipaddr}\n󱂇 Gateway:   {gwaddr}/{cidr}",
+    "menu": "on-click-right",
+    "menu-file": "~/.config/waybar/menu-network.xml",
+    "menu-actions": {
+      "open-netm": "netm",
+      "airplane-mode": "fyi todo",
+    },
+    "on-click": "netm",
+  },
+
+  "network#bandwidth": {
+    "format": "󱦲{bandwidthUpBytes} 󱦳{bandwidthDownBytes}",
+  },
+
+  "group/tailscale": {
+    "orientation": "inherit",
+    "modules": [
+      "network#tailscale",
+      "custom/tailscale",
+    ],
+  },
+
+  "network#tailscale": {
+    "interface": "tailscale0",
+    "format": "󱗼",
+    "format-disconnected": "",
+    "format-disabled": "",
+    "format-linked": "",
+    "tooltip-format": "{ifname}: {bandwidthUpBits} {bandwidthDownBits}",
+    "tooltip-format-disconnected": "disconnected",
+    "on-click": "vpnm",
+  },
+
+  "custom/tailscale": {
+    "format": "{icon}",
+    "format-icons": {
+      "exit-node": "󰞀",
+      "work": "work",
+    },
+    "exec": "~/.config/waybar/module-tailscale.sh",
+    "return-type": "json",
+    "signal": 2,
+    "on-click": "vpnm",
+  },
+
+  "bluetooth": {
+    "format-on": "󰂯",
+    "format-off": "󰂲",
+    "format-disabled": "󰂲",
+    "format-no-controller": "󰂲",
+    "format-connected": "󰂱",
+    "format-connected-battery": "󰂱{icon}",
+    "format-icons": ["󰂎","󱊡","󱊢","󱊣"],
+    "tooltip-format": "{device_enumerate}",
+    "tooltip-format-enumerate-connected": "{device_alias} {device_address}",
+    "tooltip-format-enumerate-connected-battery": "{device_alias} {device_address} {device_battery_percentage}%",
+    "on-click": "blum"
+  },
+
+  "group/sys": {
+    "orientation": "inherit",
+    "drawer": {
+      "transition-duration": 0,
+      "click-to-reveal": true,
+    },
+    "modules": [
+      "load",
+      "cpu",
+      "memory",
+      "temperature",
+    ],
+  },
+
+  "load": {
+    "format": "{load1} {load5} {load15}",
+    "on-right-click": "foot -a foot-float -W 120x40 -- btop"
+  },
+
+  "cpu": {
+    "format": "cpu:{usage}%",
+    "format-icons": ["󰍛","󰍛","󰍛","󰍛","󰀦"],
+  },
+
+  "memory": {
+    "format": "mem:{percentage}%",
+    "format-icons": ["󰘚"],
+  },
+
+  "temperature": {
+    "critical-threshold": 100,
+    "format": "{icon}{temperatureC}℃",
+    "format-icons": ["󱃃", "󰔏", "󰸁", "󱜜"]
+  },
+
+  "idle_inhibitor": {
+    "format": "{icon}",
+    "format-icons": {
+      "activated": "󰅶",
+      "deactivated": "󰾪"
+    }
+  },
+
+  "backlight": {
+    "device": "auto",
+    "format": "󰃠{percent}%",
+    "actions": {
+      "on-scroll-up": "brightnessctl set 5%+",
+      "on-scroll-down": "brightnessctl set 5%-",
+    },
+    "reverse-scrolling": true,
+  },
+
+  "pulseaudio": {
+    "format": "{icon}{volume}%",
+    "format-muted": "󰖁{volume}%",
+    "format-icons": {
+      "headphones": "󰋋",
+      "headset": "󰋎",
+      "phone": "󰏲",
+      "portable": "󱀞",
+      "default": ["󰕿", "󰖀", "󰕾"]
+    },
+    "reverse-scrolling": true,
+    "on-click": "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle",
+    "on-click-right": "audm",
+  },
+
+  "power-profiles-daemon": {
+    "format": "{icon}",
+    "tooltip-format": "Power profile: {profile}\nDriver: {driver}",
+    "tooltip": true,
+    "format-icons": {
+      "default": "",
+      "performance": "󰚌",
+      "balanced": "󰫕",
+      "power-saver": ""
+    }
+  },
+
+  "upower": {
+    "format": "{percentage} {time}",
+    "format-alt": "{percentage}",
+    "icon-size": 9,
+    "hide-if-empty": true,
+    "tooltip": true,
+  },
+}
+// vim: et sw=2
diff --git a/.config/waybar/menu-network.xml b/.config/waybar/menu-network.xml
new file mode 100644
index 0000000..11b78f7
--- /dev/null
+++ b/.config/waybar/menu-network.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+    <object class="GtkMenu" id="menu">
+        <child>
+            <object class="GtkMenuItem" id="open-netm">
+                <property name="label">Open netm</property>
+            </object>
+        </child>
+        <child>
+            <object class="GtkMenuItem" id="airplane-mode">
+                <property name="label">Toggle airplane mode</property>
+            </object>
+        </child>
+    </object>
+</interface>
diff --git a/.config/waybar/module-calendar.sh b/.config/waybar/module-calendar.sh
new file mode 100755
index 0000000..6b20e2f
--- /dev/null
+++ b/.config/waybar/module-calendar.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+set -eu
+[ -n "${DEBUG:-}" ] && set -x
+
+shopt -s extglob
+
+update() {
+	local today tn
+	local text
+
+	# get events
+	today=$(~/src/calsync/ics2calendartxt ~/calendars/**/**/* 2>/dev/null |
+		grep -I "$(date -I)")
+	[ -z "$today" ] && return
+
+	tn=$(wc -l <<<"$today")
+
+	# 	text="$today"
+	# 	text="${text##"$(date +'%Y-%m-%d w%V %a ')"}"
+	# 	text="${text##+([^ ]) }"
+
+	text="󰃭 $tn"
+
+	jq -nrc '{text:$text,tooltip:$tip}' \
+		--arg tip "$today" \
+		--arg text "$text"
+}
+
+update || echo '{}'
diff --git a/.config/waybar/module-screenrec.sh b/.config/waybar/module-screenrec.sh
new file mode 100755
index 0000000..b98733c
--- /dev/null
+++ b/.config/waybar/module-screenrec.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+set -eu
+[ -n "${DEBUG:-}" ] && set -x
+
+update() {
+	if ! pgrep -x wl-screenrec >/dev/null; then
+		echo '{}'
+	else
+		echo '{"class":"running","alt":"running"}'
+	fi
+}
+
+update
diff --git a/.config/waybar/module-tailscale.sh b/.config/waybar/module-tailscale.sh
new file mode 100755
index 0000000..53b0409
--- /dev/null
+++ b/.config/waybar/module-tailscale.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+set -eu
+[ -n "${DEBUG:-}" ] && set -x
+
+update() {
+	local ts_acct ts_ip
+	local class text alt
+
+	if tailscale status | grep -q stopped; then
+		return 1
+	fi
+
+	ts_ip=$(tailscale ip -1 || return 1)
+	ts_acct=$(tailscale switch --list 2>/dev/null | awk '/\*$/{ print gensub(/^.* ([^ ]+)\*$/, "\\1", "1") }')
+
+	alt="$ts_acct"
+	class="$ts_acct"
+	# only display account name when not "main"
+	[ "$ts_acct" = "main" ] || text=$ts_acct
+
+	if tailscale exit-node list | grep -q selected; then
+		alt="exit-node"
+		class="exit-node"
+	fi
+
+	jq -nrc '{text:$text,tooltip:$tip,class:$class,alt:$alt}' \
+		--arg tip "${ts_ip:?}" \
+		--arg class "${class:?}" \
+		--arg alt "${alt:?}" \
+		--arg text "${text:-}"
+}
+
+update || echo '{}'
diff --git a/.config/waybar/module-timew.sh b/.config/waybar/module-timew.sh
new file mode 100755
index 0000000..6b3df10
--- /dev/null
+++ b/.config/waybar/module-timew.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+set -eu
+[ -n "${DEBUG:-}" ] && set -x
+
+shopt -s extglob
+
+update() {
+	timew waybar
+}
+
+update || echo '{}'
diff --git a/.config/waybar/style-dark.css b/.config/waybar/style-dark.css
new file mode 100644
index 0000000..5fd324c
--- /dev/null
+++ b/.config/waybar/style-dark.css
@@ -0,0 +1,13 @@
+/* ::start style */
+@define-color bk #101010;
+@define-color wh #b0b0b0;
+@define-color ac #70649a;
+@define-color ur #ff5733;
+@define-color gr #86cd82;
+@define-color ye #d9ba73;
+@define-color bl #5abfb5;
+@define-color fg @wh;
+@define-color bg @bk;
+@define-color di shade(@fg, 0.5);
+/* ::end style */
+@import url("style.css");
diff --git a/.config/waybar/style-light.css b/.config/waybar/style-light.css
new file mode 100644
index 0000000..be6995d
--- /dev/null
+++ b/.config/waybar/style-light.css
@@ -0,0 +1,16 @@
+/* ::start style */
+@define-color bk #101010;
+@define-color wh #faf9f5;
+@define-color ac #70649a;
+@define-color ur #ca0043;
+@define-color gr #86cd82;
+@define-color ye #926200;
+@define-color bl #007d7d;
+@define-color fg @bk;
+@define-color bg @wh;
+@define-color di shade(@bg, 0.4);
+/* ::end style */
+@import url("style.css");
+#tags button.focused label {
+  color: @bg;
+}
diff --git a/.config/waybar/style.css b/.config/waybar/style.css
new file mode 100644
index 0000000..7e22eb3
--- /dev/null
+++ b/.config/waybar/style.css
@@ -0,0 +1,180 @@
+* {
+  font-family: DejaVu Sans Mono, Symbols Nerd Font Mono, monospace;
+}
+* {
+  border: none;
+  border-radius: 0;
+  font-size: 7pt;
+  min-height: 0;
+  border: none;
+  box-shadow: none;
+  margin: 0;
+  padding: 0;
+  background: transparent;
+  color: @fg;
+  transition: none;
+  text-shadow: none;
+  box-shadow: none;
+  border: none;
+  -gtk-icon-shadow: none;
+  -gtk-icon-effect: none;
+}
+
+window#waybar {
+  background: alpha(@bg, 0.99);
+}
+
+.module {
+  padding: 0 3px;
+  padding-bottom: 1px;
+}
+label.module {
+  color: @di;
+}
+
+tooltip, menu {
+  background: alpha(@bg, 0.99);
+  border: 1px solid shade(@fg, 0.8);
+}
+tooltip label {
+  color: shade(@fg, 0.8);
+}
+menuitem {
+  border: 1px solid shade(@fg, 0.8);
+  border-top: none;
+  border-bottom: none;
+  padding: 3px 0;
+}
+menuitem label {
+  color: @fg;
+}
+menuitem:hover {
+  background: @fg;
+  font-weight: bold;
+}
+menuitem:hover label {
+  color: @bg;
+}
+
+
+#custom-launcher {
+  margin-left: 5px;
+  color: @fg;
+}
+
+#mode {
+  background: @ac;
+  color: white;
+}
+#mode.normal {
+  font-size: 0;
+  min-height: 0;
+  min-width: 0;
+  padding: 0;
+  margin: 0;
+  border: 0;
+  opacity: 0;
+}
+
+#tags button {
+  min-width: unset;
+  padding: 0 3px;
+}
+#tags label {
+  color: @di;
+}
+#tags button:hover {
+  background: @ac;
+}
+#tags button.occupied label {
+  color: @fg;
+}
+#tags button.focused {
+  font-weight: bold;
+  background: @ac;
+}
+#tags button.focused:not(.occupied) {
+  background: shade(@ac, 0.75);
+}
+#tags button.focused:not(.occupied) label {
+  color: @bg;
+}
+#tags button.urgent {
+  background: @ur;
+}
+#tags button.urgent label {
+  color: @bg;
+}
+
+#privacy-item {
+  padding: 0 3px;
+}
+
+#privacy-item,
+#custom-screenrec.running,
+#custom-screenrec.running {
+  color: @bk;
+  background: @ye;
+}
+
+#clock.local {
+  color: @fg;
+}
+
+#mpris.paused {
+  color: shade(@di, 1.5);
+  font-style: italic;
+}
+
+#network.tailscale {
+  color: @fg;
+}
+#custom-tailscale.exit-node,
+#custom-tailscale.work {
+  color: @bl;
+  box-shadow: inset 0 -1px @bl;
+  /* color: @bk; */
+  /* background: @bl; */
+  font-weight: bold;
+}
+
+#bluetooth.connected {
+  box-shadow: inset 0 -1px @bl;
+  color: @bl;
+}
+
+#idle_inhibitor.activated {
+  background: @fg;
+  color: @bg;
+}
+
+#pulseaudio.bluetooth {
+  color: @bl;
+}
+#pulseaudio.muted {
+  color: @ur;
+  box-shadow: inset 0 -1px @ur;
+}
+
+#power-profiles-daemon.power-saver {
+  color: @gr;
+  box-shadow: inset 0 -1px @gr;
+}
+#power-profiles-daemon.performance {
+  color: @bk;
+  background: @ye;
+}
+
+#upower * {
+}
+#upower.charging {
+  box-shadow: inset 0 -1px @gr;
+  background: unset;
+}
+#upower.empty {
+  background: @ur;
+}
+
+/*
+vim: et sw=2:
+*/