summaryrefslogtreecommitdiff
path: root/tw
diff options
context:
space:
mode:
authorTimo Wilken2022-12-30 10:54:17 +0100
committerTimo Wilken2022-12-30 10:54:17 +0100
commit0f308b1c90444f5f0293bf40aa1ef177bc51036c (patch)
treeb052860a66b8bf3b094fa16e4538887003c37ada /tw
parentda1ff8a0816e430d2c035217d80db3be5cc919ab (diff)
Reorganise Guix home declaration into module tree
Diffstat (limited to 'tw')
-rw-r--r--tw/home/files/XCompose161
-rw-r--r--tw/home/files/Xresources13
m---------tw/home/files/catppuccin/dunst0
m---------tw/home/files/catppuccin/emacs0
m---------tw/home/files/catppuccin/kde0
m---------tw/home/files/catppuccin/kitty0
m---------tw/home/files/catppuccin/neomutt0
m---------tw/home/files/catppuccin/newsboat0
m---------tw/home/files/catppuccin/polybar0
m---------tw/home/files/catppuccin/rofi0
m---------tw/home/files/catppuccin/zathura0
-rw-r--r--tw/home/files/cursors.ini4
-rw-r--r--tw/home/files/dunstrc98
-rw-r--r--tw/home/files/emacs-init.el565
-rw-r--r--tw/home/files/emacs-packages/actionlint.el34
-rw-r--r--tw/home/files/emacs-packages/alidist-mode.el162
-rw-r--r--tw/home/files/emacs-packages/bemscript-mode.el92
-rw-r--r--tw/home/files/emacs-packages/environmentd-mode.el46
-rw-r--r--tw/home/files/emacs-packages/ifm-mode.el18
-rw-r--r--tw/home/files/emacs-packages/org-latex-classes.el54
-rw-r--r--tw/home/files/emacs-packages/pam-env-mode.el45
-rw-r--r--tw/home/files/emacs-packages/vcard-mode.el56
-rw-r--r--tw/home/files/gitconfig43
-rw-r--r--tw/home/files/gpg-agent.conf4
-rw-r--r--tw/home/files/gpg.conf142
-rw-r--r--tw/home/files/gtk2.ini20
-rw-r--r--tw/home/files/gtk3.ini16
-rw-r--r--tw/home/files/htoprc59
-rw-r--r--tw/home/files/i3.conf250
-rw-r--r--tw/home/files/khal.conf39
-rw-r--r--tw/home/files/khard.conf22
-rw-r--r--tw/home/files/kitty.conf1337
-rw-r--r--tw/home/files/lesskey33
-rw-r--r--tw/home/files/mailcap10
-rw-r--r--tw/home/files/muttrc173
m---------tw/home/files/neomutt0
-rw-r--r--tw/home/files/newsboat.conf29
-rwxr-xr-xtw/home/files/passmenu91
-rw-r--r--tw/home/files/picom.conf304
-rw-r--r--tw/home/files/polybar.ini235
-rw-r--r--tw/home/files/prompt.zsh128
-rw-r--r--tw/home/files/ranger.conf52
-rw-r--r--tw/home/files/rofi.rasi41
-rwxr-xr-xtw/home/files/sessionmenu29
-rw-r--r--tw/home/files/user-dirs.dirs15
-rw-r--r--tw/home/files/vdirsyncer.conf77
-rw-r--r--tw/home/files/vim-keys.muttrc36
-rwxr-xr-xtw/home/files/volume60
-rw-r--r--tw/home/files/zathurarc32
-rw-r--r--tw/home/files/zshrc168
-rw-r--r--tw/home/lap.scm697
51 files changed, 5490 insertions, 0 deletions
diff --git a/tw/home/files/XCompose b/tw/home/files/XCompose
new file mode 100644
index 00000000..cb73e833
--- /dev/null
+++ b/tw/home/files/XCompose
@@ -0,0 +1,161 @@
+# -*- mode: conf-colon -*-
+## Locale defaults
+include "%S/en_US.UTF-8/Compose"
+# This file apparently doesn't exist for all locales, including en_GB.
+# Most useful things live in en_US, included above.
+include "%L"
+
+## Dashes
+<Multi_key> <minus> <minus> <period> : "–" U2013 # EN DASH
+<Multi_key> <minus> <minus> <space> : "–" U2013 # EN DASH
+<Multi_key> <minus> <n> : "–" U2013 # EN DASH
+<Multi_key> <minus> <minus> <minus> : "—" U2014 # EM DASH
+<Multi_key> <minus> <m> : "—" U2014 # EM DASH
+<Multi_key> <minus> <1> : "—" U2014 # EM DASH
+<Multi_key> <minus> <2> : "⸺" U2E3A # TWO-EM DASH
+<Multi_key> <minus> <3> : "⸻" U2E3B # THREE-EM DASH
+<Multi_key> <minus> <b> : "―" U2015 # HORIZONTAL BAR
+<Multi_key> <minus> <h> <b> : "―" U2015 # HORIZONTAL BAR
+
+## Mathematical operators
+<Multi_key> <minus> <x> : "−" U2212 # MINUS SIGN
+<Multi_key> <minus> <plus> : "−" U2212 # MINUS SIGN
+<Multi_key> <minus> <equal> <equal> : "≡" U2261 # IDENTICAL TO
+<Multi_key> <minus> <equal> <slash> : "≢" U2262 # NOT IDENTICAL TO
+<Multi_key> <asciitilde> <asciitilde> : "≈" U2248 # ALMOST EQUAL TO
+<Multi_key> <asciitilde> <equal> : "≃" similarequal # ASYMPTOTICALLY EQUAL TO
+<Multi_key> <equal> <asciitilde> : "≃" similarequal # ASYMPTOTICALLY EQUAL TO
+<Multi_key> <asciitilde> <less> : "≲" U2272 # LESS-THAN OR EQUIVALENT TO
+<Multi_key> <less> <asciitilde> : "≲" U2272 # LESS-THAN OR EQUIVALENT TO
+<Multi_key> <asciitilde> <greater> : "≳" U2273 # GREATER-THAN OR EQUIVALENT TO
+<Multi_key> <greater> <asciitilde> : "≳" U2273 # GREATER-THAN OR EQUIVALENT TO
+<Multi_key> <less> <slash> <minus> : "↚" U219A # LEFTWARDS ARROW WITH STROKE
+<Multi_key> <minus> <slash> <greater> : "↛" U219B # RIGHTWARDS ARROW WITH STROKE
+<Multi_key> <less> <slash> <greater> : "↮" U21AE # LEFT RIGHT ARROW WITH STROKE
+<Multi_key> <slash> <E> : "∄" U2204 # THERE DOES NOT EXIST
+<Multi_key> <e> <slash> : "∉" U2209 # NOT AN ELEMENT OF
+<Multi_key> <slash> <e> : "∌" U220C # DOES NOT CONTAIN AS MEMBER
+<Multi_key> <slash> <bar> : "∤" U2224 # DOES NOT DIVIDE
+<Multi_key> <bar> <slash> : "∤" U2224 # DOES NOT DIVIDE
+<Multi_key> <backslash> <slash> <slash> : "∦" U2226 # NOT PARALLEL TO
+<Multi_key> <slash> <backslash> <slash> : "∦" U2226 # NOT PARALLEL TO
+<Multi_key> <asciitilde> <slash> : "≁" U2241 # NOT TILDE
+<Multi_key> <slash> <asciitilde> : "≁" U2241 # NOT TILDE
+
+## Symbols
+<Multi_key> <d> <i> : "⌀" U2300 # DIAMETER SIGN
+# Note: also defined as <Multi_key> <N> <o>, but that's hard to remember!
+<Multi_key> <n> <o> : "№" numerosign # NUMERO SIGN
+# <Multi_key> <minus> <O> is already reserved for Ō
+<Multi_key> <O> <minus> : "⊖" U2296 # ○ - CIRCLED MINUS
+# <Multi_key> <period> <O> is already reserved for Ȯ
+<Multi_key> <O> <period> : "⊙" U2299 # ○ - CIRCLED DOT
+
+## Whitespace
+<Multi_key> <space> <b> : " " U00A0 # NO-BREAK SPACE (~)
+<Multi_key> <space> <n> : " " U2002 # EN SPACE (\enspace)
+<Multi_key> <space> <m> : " " U2003 # EM SPACE (\quad)
+<Multi_key> <space> <3> : " " U2004 # THREE-PER-EM SPACE
+<Multi_key> <space> <4> : " " U2005 # FOUR-PER-EM SPACE
+<Multi_key> <space> <6> : " " U2006 # SIX-PER-EM SPACE
+<Multi_key> <space> <f> : " " U2007 # FIGURE SPACE
+<Multi_key> <space> <p> : " " U2008 # PUNCTUATION SPACE
+<Multi_key> <space> <t> : " " U2009 # THIN SPACE (\,)
+<Multi_key> <space> <h> : " " U200A # HAIR SPACE
+<Multi_key> <space> <x> : " " U205F # MEDIUM MATHEMATICAL SPACE (may be used around operators)
+
+## Greek uppercase
+<Multi_key> <g> <A> : "Α" U0391
+<Multi_key> <g> <B> : "Β" U0392
+<Multi_key> <g> <G> : "Γ" U0393
+<Multi_key> <g> <D> : "Δ" U0394
+<Multi_key> <g> <E> : "Ε" U0395
+<Multi_key> <g> <Z> : "Ζ" U0396
+<Multi_key> <g> <H> : "Η" U0397
+<Multi_key> <G> <T> : "Θ" U0398
+<Multi_key> <g> <I> : "Ι" U0399
+<Multi_key> <g> <K> : "Κ" U039A
+<Multi_key> <g> <L> : "Λ" U039B
+<Multi_key> <g> <M> : "Μ" U039C
+<Multi_key> <g> <N> : "Ν" U039D
+<Multi_key> <g> <C> : "Ξ" U039E
+<Multi_key> <g> <O> : "Ο" U039F
+<Multi_key> <g> <P> : "Π" U03A0
+<Multi_key> <g> <R> : "Ρ" U03A1
+<Multi_key> <g> <S> : "Σ" U03A3
+<Multi_key> <g> <T> : "Τ" U03A4
+<Multi_key> <g> <Y> : "Υ" U03A5
+<Multi_key> <g> <F> : "Φ" U03A6
+<Multi_key> <g> <X> : "Χ" U03A7
+<Multi_key> <g> <U> : "Ψ" U03A8
+<Multi_key> <g> <W> : "Ω" U03A9
+
+## Greek lowercase
+<Multi_key> <g> <a> : "α" U03B1
+<Multi_key> <g> <b> : "β" U03B2
+<Multi_key> <g> <g> : "γ" U03B3
+<Multi_key> <g> <d> : "δ" U03B4
+<Multi_key> <g> <e> : "ε" U03B5
+<Multi_key> <g> <z> : "ζ" U03B6
+<Multi_key> <g> <h> : "η" U03B7
+<Multi_key> <G> <t> : "θ" U03B8
+<Multi_key> <g> <i> : "ι" U03B9
+<Multi_key> <g> <k> : "κ" U03BA
+<Multi_key> <g> <l> : "λ" U03BB
+<Multi_key> <g> <m> : "μ" U03BC
+<Multi_key> <g> <n> : "ν" U03BD
+<Multi_key> <g> <c> : "ξ" U03BE
+<Multi_key> <g> <o> : "ο" U03BF
+<Multi_key> <g> <p> : "π" U03C0
+<Multi_key> <g> <r> : "ρ" U03C1
+<Multi_key> <G> <s> : "ς" U03C2
+<Multi_key> <g> <s> : "σ" U03C3
+<Multi_key> <g> <t> : "τ" U03C4
+<Multi_key> <g> <y> : "υ" U03C5
+<Multi_key> <g> <f> : "φ" U03C6
+<Multi_key> <G> <f> : "ɸ" U03D5
+<Multi_key> <g> <x> : "χ" U03C7
+<Multi_key> <g> <u> : "ψ" U03C8
+<Multi_key> <g> <w> : "ω" U03C9
+
+## Weird symbols
+# TODO: give these typeable names!
+<Multi_key> <U2243> <U0338> : "≄" U2244 # NOT ASYMPTOTICALLY EQUAL TO
+<Multi_key> <approximate> <U0338> : "≇" U2247 # NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO
+<Multi_key> <U2248> <U0338> : "≉" U2249 # NOT ALMOST EQUAL TO
+<Multi_key> <U224D> <U0338> : "≭" U226D # NOT EQUIVALENT TO
+<Multi_key> <less> <U0338> : "≮" U226E # NOT LESS-THAN
+<Multi_key> <leftcaret> <U0338> : "≮" U226E # NOT LESS-THAN
+<Multi_key> <greater> <U0338> : "≯" U226F # NOT GREATER-THAN
+<Multi_key> <rightcaret> <U0338> : "≯" U226F # NOT GREATER-THAN
+<Multi_key> <lessthanequal> <U0338> : "≰" U2270 # NEITHER LESS-THAN NOR EQUAL TO
+<Multi_key> <greaterthanequal> <U0338> : "≱" U2271 # NEITHER GREATER-THAN NOR EQUAL TO
+<Multi_key> <U2272> <U0338> : "≴" U2274 # NEITHER LESS-THAN NOR EQUIVALENT TO
+<Multi_key> <U2273> <U0338> : "≵" U2275 # NEITHER GREATER-THAN NOR EQUIVALENT TO
+<Multi_key> <U2276> <U0338> : "≸" U2278 # NEITHER LESS-THAN NOR GREATER-THAN
+<Multi_key> <U2277> <U0338> : "≹" U2279 # NEITHER GREATER-THAN NOR LESS-THAN
+<Multi_key> <U227A> <U0338> : "⊀" U2280 # DOES NOT PRECEDE
+<Multi_key> <U227B> <U0338> : "⊁" U2281 # DOES NOT SUCCEED
+<Multi_key> <includedin> <U0338> : "⊄" U2284 # NOT A SUBSET OF
+<Multi_key> <leftshoe> <U0338> : "⊄" U2284 # NOT A SUBSET OF
+<Multi_key> <includes> <U0338> : "⊅" U2285 # NOT A SUPERSET OF
+<Multi_key> <rightshoe> <U0338> : "⊅" U2285 # NOT A SUPERSET OF
+<Multi_key> <U2286> <U0338> : "⊈" U2288 # NEITHER A SUBSET OF NOR EQUAL TO
+<Multi_key> <U2287> <U0338> : "⊉" U2289 # NEITHER A SUPERSET OF NOR EQUAL TO
+<Multi_key> <righttack> <U0338> : "⊬" U22AC # DOES NOT PROVE
+<Multi_key> <U22A8> <U0338> : "⊭" U22AD # NOT TRUE
+<Multi_key> <U22A9> <U0338> : "⊮" U22AE # DOES NOT FORCE
+<Multi_key> <U22AB> <U0338> : "⊯" U22AF # NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNSTILE
+<Multi_key> <U227C> <U0338> : "⋠" U22E0 # DOES NOT PRECEDE OR EQUAL
+<Multi_key> <U227D> <U0338> : "⋡" U22E1 # DOES NOT SUCCEED OR EQUAL
+<Multi_key> <U2291> <U0338> : "⋢" U22E2 # NOT SQUARE IMAGE OF OR EQUAL TO
+<Multi_key> <U2292> <U0338> : "⋣" U22E3 # NOT SQUARE ORIGINAL OF OR EQUAL TO
+<Multi_key> <U22B2> <U0338> : "⋪" U22EA # NOT NORMAL SUBGROUP OF
+<Multi_key> <U22B3> <U0338> : "⋫" U22EB # DOES NOT CONTAIN AS NORMAL SUBGROUP
+<Multi_key> <U22B4> <U0338> : "⋬" U22EC # NOT NORMAL SUBGROUP OF OR EQUAL TO
+<Multi_key> <U22B5> <U0338> : "⋭" U22ED # DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL
+<Multi_key> <U2ADD> <U0338> : "⫝̸" U2ADC # FORKING
+<Multi_key> <underscore> <U2282> : "⊆" U2286 # _ ⊂ SUBSET OF OR EQUAL TO
+<Multi_key> <U2282> <underscore> : "⊆" U2286 # ⊂ _ SUBSET OF OR EQUAL TO
+<Multi_key> <underscore> <U2283> : "⊇" U2287 # _ ⊃ SUPERSET OF OR EQUAL TO
+<Multi_key> <U2283> <underscore> : "⊇" U2287 # ⊃ _ SUPERSET OF OR EQUAL TO
diff --git a/tw/home/files/Xresources b/tw/home/files/Xresources
new file mode 100644
index 00000000..ba534392
--- /dev/null
+++ b/tw/home/files/Xresources
@@ -0,0 +1,13 @@
+!! ~/.Xresources: X resources and settings
+
+*TkTheme: clam
+
+Xft.antialias: true
+Xft.dpi: 96
+Xft.hinting: true
+Xft.hintstyle: hintfull
+Xft.lcdfilter: lcddefault
+Xft.rgba: rgb
+
+Xcursor.theme: Catppuccin-Mocha-Dark-Cursors
+Xcursor.size: 16
diff --git a/tw/home/files/catppuccin/dunst b/tw/home/files/catppuccin/dunst
new file mode 160000
+Subproject a72991e56338289a9fce941b5df9f0509d2cba0
diff --git a/tw/home/files/catppuccin/emacs b/tw/home/files/catppuccin/emacs
new file mode 160000
+Subproject b4be30de73aa295ab56a20c21dff07fba3dec05
diff --git a/tw/home/files/catppuccin/kde b/tw/home/files/catppuccin/kde
new file mode 160000
+Subproject d95dd080b3e6a064390aceb8af0a6dc9506a40c
diff --git a/tw/home/files/catppuccin/kitty b/tw/home/files/catppuccin/kitty
new file mode 160000
+Subproject 43b08da1e1168aff2edb74105f293a90b4567c4
diff --git a/tw/home/files/catppuccin/neomutt b/tw/home/files/catppuccin/neomutt
new file mode 160000
+Subproject f6ce83da47cc36d5639b0d54e7f5f63cdaf69f1
diff --git a/tw/home/files/catppuccin/newsboat b/tw/home/files/catppuccin/newsboat
new file mode 160000
+Subproject be3d0ee1ba0fc26baf7a47c2aa7032b7541deb0
diff --git a/tw/home/files/catppuccin/polybar b/tw/home/files/catppuccin/polybar
new file mode 160000
+Subproject 9ee66f83335404186ce979bac32fcf3cd047396
diff --git a/tw/home/files/catppuccin/rofi b/tw/home/files/catppuccin/rofi
new file mode 160000
+Subproject 62a78eaf0e12b69b75fdee9def6d333cebcb74a
diff --git a/tw/home/files/catppuccin/zathura b/tw/home/files/catppuccin/zathura
new file mode 160000
+Subproject d85d8750acd0b0247aa10e0653998180391110a
diff --git a/tw/home/files/cursors.ini b/tw/home/files/cursors.ini
new file mode 100644
index 00000000..c4eb9cdb
--- /dev/null
+++ b/tw/home/files/cursors.ini
@@ -0,0 +1,4 @@
+[Icon Theme]
+Name=Default
+Comment=Default Cursor Theme
+Inherits=Catppuccin-Mocha-Dark-Cursors
diff --git a/tw/home/files/dunstrc b/tw/home/files/dunstrc
new file mode 100644
index 00000000..c64d374f
--- /dev/null
+++ b/tw/home/files/dunstrc
@@ -0,0 +1,98 @@
+# See dunst(5) for all configuration options -*- mode: conf -*-
+
+[global]
+ ### Display ###
+
+ # Display notification on focused monitor. Possible modes are:
+ # mouse: follow mouse pointer
+ # keyboard: follow window with keyboard focus
+ # none: don't follow anything
+ #
+ # "keyboard" needs a window manager that exports the
+ # _NET_ACTIVE_WINDOW property.
+ # This should be the case for almost all modern window managers.
+ #
+ # If this option is set to mouse or keyboard, the monitor option
+ # will be ignored.
+ follow = mouse
+
+ ### Geometry ###
+
+ # dynamic width from 0 to 300
+ # width = (0, 300)
+ # constant width of 300
+ width = 300
+
+ # The maximum height of a single notification, excluding the frame.
+ height = 300
+
+ # Offset from the origin
+ # Leave 24pt space for polybar.
+ offset = 16x40
+
+ # Don't remove messages, if the user is idle (no mouse or keyboard input)
+ # for longer than idle_threshold seconds.
+ # Set to 0 to disable.
+ # A client can set the 'transient' hint to bypass this. See the rules
+ # section for how to disable this if necessary
+ idle_threshold = 120
+
+ ### Text ###
+
+ font = Fira Sans 12
+
+ # The format of the message. Possible variables are:
+ # %a appname
+ # %s summary
+ # %b body
+ # %i iconname (including its path)
+ # %I iconname (without its path)
+ # %p progress value if set ([ 0%] to [100%]) or nothing
+ # %n progress value if set without any extra characters
+ # %% Literal %
+ # Markup is allowed
+ # format = "<b>%s</b>\n%b"
+ format = "<span size='x-small'>%a %p</span>\n<b>%s</b>\n%b"
+
+ ### Icons ###
+
+ # Recursive icon lookup. You can set a single theme, instead of having to
+ # define all lookup paths.
+ enable_recursive_icon_lookup = true
+
+ # Set icon theme (only used for recursive icon lookup)
+ # You can also set multiple icon themes, with the leftmost one being used first.
+ # icon_theme = "Adwaita, breeze"
+ icon_theme = "Papirus-Dark, hicolor"
+
+ ### History ###
+
+ # Maximum amount of notifications kept in history
+ history_length = 100
+
+ ### Misc/Advanced ###
+
+ # dmenu path.
+ dmenu = /usr/bin/env rofi -dmenu -p dunst:
+
+ # Browser for opening urls in context menu.
+ browser = /usr/bin/env xdg-open
+
+ ### mouse
+
+ # Defines list of actions for each mouse event
+ # Possible values are:
+ # * none: Don't do anything.
+ # * do_action: Invoke the action determined by the action_name rule. If there is no
+ # such action, open the context menu.
+ # * open_url: If the notification has exactly one url, open it. If there are multiple
+ # ones, open the context menu.
+ # * close_current: Close current notification.
+ # * close_all: Close all notifications.
+ # * context: Open context menu for the notification.
+ # * context_all: Open context menu for all notifications.
+ # These values can be strung together for each mouse event, and
+ # will be executed in sequence.
+ mouse_left_click = close_current
+ mouse_middle_click = do_action, close_current
+ mouse_right_click = close_all
diff --git a/tw/home/files/emacs-init.el b/tw/home/files/emacs-init.el
new file mode 100644
index 00000000..daf6f737
--- /dev/null
+++ b/tw/home/files/emacs-init.el
@@ -0,0 +1,565 @@
+;;; init.el --- Emacs configuration. -*- lexical-binding: t -*-
+;;; Commentary:
+;;; Code:
+
+;; Load settings set through Custom.
+;; (setq custom-file (locate-user-emacs-file "custom.el"))
+;; (when (file-readable-p custom-file)
+;; (load custom-file))
+
+(defun tw/xdg-emacs-subdir (type name)
+ "Get the name of a subdirectory called NAME under $XDG_<TYPE>_HOME/emacs."
+ (expand-file-name
+ (concat "emacs/" (string-trim-right name "/") "/")
+ (pcase type
+ ('cache (or (getenv "XDG_CACHE_HOME") "~/.cache/"))
+ ('config (or (getenv "XDG_CONFIG_HOME") "~/.config/"))
+ ('data (or (getenv "XDG_DATA_HOME") "~/.local/share/"))
+ ;; The following two are Guix/GuixSD extensions.
+ ('log (or (getenv "XDG_LOG_HOME") "~/.local/var/log/"))
+ ('state (or (getenv "XDG_STATE_HOME") "~/.local/var/lib/"))
+ (_ (error "Unknown XDG directory type: %S" type)))))
+
+;; Global/built-in Custom settings
+;; Apply these as early as possible so that e.g. the native-comp files go to the right place.
+(mapc (apply-partially #'apply #'customize-set-variable)
+ `((native-comp-async-report-warnings-errors silent "Don't pop up Warnings buffer for native compilation.")
+ (native-compile-target-directory ,(tw/xdg-emacs-subdir 'cache "eln") "Put native-compiled binaries in the cache.")
+ ;; Emacs GUI customization.
+ (inhibit-startup-screen t "Don't show the startup screen with help links.")
+ (menu-bar-mode nil "Hide the menu bar globally.")
+ (tool-bar-mode nil "Hide the tool bar globally.")
+ (tooltip-mode nil "Show tooltips in the echo area instead.")
+ (max-mini-window-height 3 "Let the echo area grow to a maximum of 3 lines, e.g. when using `eldoc-mode'.")
+ (scroll-up-aggressively 0.0 "Don't recenter the window if the point moves off the page.")
+ (scroll-down-aggressively 0.0 "Don't recenter the window if the point moves off the page.")
+ ;; Indentation.
+ (indent-tabs-mode nil "Always use spaces to indent.")
+ ;; Niceties.
+ (global-hl-line-mode t "Highlight the current line in all buffers.")
+ (column-number-mode t "Show the column number in the statusline.")
+ (electric-pair-mode t "Auto-pair suitable characters like parentheses.")
+ (completion-cycle-threshold 6 "Allow cycling through completions if there are 6 or fewer of them.")))
+
+(defalias 'yes-or-no-p #'y-or-n-p
+ "Always use `y-or-n-p' when asking for confirmation.")
+
+;; Custom modes depending on file names.
+(mapc (apply-partially #'add-to-list 'auto-mode-alist)
+ `((,(rx (or bos "/") "PKGBUILD" eos) . sh-mode)
+ (,(rx ".install" eos) . sh-mode)
+ (,(rx bos "/tmp/neomutt-") . mail-mode)
+ (,(rx "." (1+ anything) "rc" eos) . conf-unix-mode)))
+
+(add-to-list 'magic-mode-alist
+ `(,(rx "#!" (* (not space))
+ (? "env" (+ space) (? "-S" (+ space)))
+ (or "guile" "racket"))
+ . scheme-mode))
+
+(add-hook 'mail-mode-hook #'auto-fill-mode)
+
+;; `use-package' requirements.
+(require 'package)
+(package-initialize t)
+(eval-when-compile
+ (require 'use-package))
+(use-package diminish) ; for using :diminish later
+(use-package bind-key) ; for using :bind later
+
+;; Some packages below have `:commands (...) :demand t'.
+;; We need :commands for the byte-compiler, but we want to load the package immediately.
+
+;; Look and feel
+(set-face-attribute 'default nil :family "Hermit" :height 100)
+
+(use-package autothemer
+ ;; The "catppuccin" theme is linked to the appropriate variant by guix home.
+ :config (load-theme 'catppuccin-mocha t))
+
+(defun tw/get-catppuccin-color (name)
+ "Get the hex code of the Catppuccin color named NAME."
+ (cl-loop for color in (autothemer--theme-colors autothemer-current-theme)
+ when (string= (autothemer--color-name color) name)
+ return (autothemer--color-value color)))
+
+(custom-set-faces
+ `(cursor ((default :background ,(tw/get-catppuccin-color "rosewater"))) t
+ "Make the cursor \"rosewater\", as recommended by Catppuccin upstream, overriding catppuccin/emacs."))
+
+(use-package smart-mode-line
+ :commands (sml/setup) :demand t
+ :custom
+ (sml/no-confirm-load-theme t "Stop Emacs from constantly asking for user confirmation.")
+ (sml/mode-width 'right "Move the minor-mode list to the right of the modeline.")
+ (sml/theme 'respectful "Make `smart-mode-line' blend in with the active theme.")
+ :config (sml/setup))
+
+;; Built-in Emacs stuff.
+(use-package package
+ :custom
+ (package-archives nil "Don't fetch packages from the internet; only get them from Guix."))
+
+(use-package recentf
+ :custom
+ (recentf-max-saved-items 1000 "Save lots of recently-opened files.")
+ (recentf-save-file ,(tw/xdg-emacs-subdir 'data "recentf") "Save recently-opened files here.")
+ (recentf-mode t "Save recently-opened files."))
+
+(use-package savehist
+ :custom
+ (savehist-file ,(tw/xdg-emacs-subdir 'data "history") "Save minibuffer history here.")
+ (savehist-mode t "Save minibuffer history on quit."))
+
+(use-package files
+ :custom
+ (backup-directory-alist `(("." . ,(tw/xdg-emacs-subdir 'data "backup")))
+ "Save all backup files in one place to avoid clutter."))
+
+;; General editor behaviour.
+(use-package ivy
+ :commands (ivy-mode) :demand t
+ :custom
+ (ivy-use-selectable-prompt t "Allow selecting the ivy input as-is.")
+ (ivy-use-virtual-buffers t "Show recentf and bookmarks in buffers list.")
+ :config (ivy-mode +1)
+ :diminish ivy-mode)
+
+(use-package counsel ; extra niceties for `ivy-mode'
+ :after (ivy evil) ; evil for :bind-ing to <leader>
+ :bind (("<leader>SPC" . counsel-M-x) ; <leader><leader> doesn't work
+ ("<leader>fr" . counsel-buffer-or-recentf)
+ :map evil-visual-state-map
+ ("<leader>SPC" . counsel-M-x))
+ :commands (counsel-mode) :demand t
+ :config (counsel-mode +1)
+ :diminish counsel-mode)
+
+(defun tw/counsel-dash-is-help ()
+ "Install `counsel-dash-at-point' as `evil-lookup-func'."
+ (setq-local evil-lookup-func #'counsel-dash-at-point))
+
+(use-package counsel-dash
+ :bind (("<leader>K" . counsel-dash-at-point) ; TODO: just install as `evil-lookup-func'?
+ ("<leader>dK" . counsel-dash)
+ ("<leader>di" . counsel-dash-install-docset)
+ ("<leader>da" . counsel-dash-activate-docset)
+ ("<leader>dd" . counsel-dash-deactivate-docset))
+ :hook (python-mode . tw/counsel-dash-is-help)
+ :custom
+ (counsel-dash-docsets-path (tw/xdg-emacs-subdir 'data "dash-docsets") "Store docsets in the XDG data directory.")
+ (counsel-dash-browser-func 'eww "Open documentation pages using `eww' instead of an external browser.")
+ (counsel-dash-enable-debugging nil "Disable popping up useless warnings."))
+
+(use-package rainbow-mode
+ :after (evil)
+ :bind (("<leader>tR" . rainbow-mode)))
+
+(use-package display-line-numbers
+ ;; Included in Emacs >= 26. Better than `linum-mode'.
+ ;; There is also `global-display-line-numbers-mode', but that also
+ ;; enables line numbers in help windows, which I don't want.
+ :hook (prog-mode conf-mode))
+
+(use-package which-key
+ :commands (which-key-mode) :demand t
+ :config (which-key-mode +1)
+ :diminish which-key-mode)
+
+(use-package undo-tree
+ :after (evil) ; for our :bind-ing
+ :bind (("<leader>U" . undo-tree-visualize))
+ :custom
+ (undo-tree-history-directory-alist
+ `(("." . ,(tw/xdg-emacs-subdir 'data "undo-tree-history")))
+ "Store all `undo-tree' history in a single directory, instead of next to the associated file.")
+ :commands (global-undo-tree-mode)
+ :demand t ; this is required so that the :config stanza is actually run asap despite :bind
+ :config (global-undo-tree-mode +1))
+
+;; IDE-like features.
+(use-package vc
+ :after (which-key evil)
+ :init (which-key-add-key-based-replacements
+ "<leader>g" '("vc" . "Version control")
+ "<leader>gM" '("merge" . "Version control merging"))
+ :bind-keymap ("<leader>g" . vc-prefix-map))
+
+(use-package corfu ; https://github.com/minad/corfu
+ :hook (prog-mode ledger-mode shell-mode eshell-mode)
+ :custom
+ (corfu-auto t "Show completion popup after a few seconds automatically.")
+ :diminish corfu-mode)
+
+(use-package corfu-doc
+ :after (corfu)
+ :hook (corfu-mode)
+ :diminish corfu-doc-mode)
+
+(use-package flyspell
+ :hook mail-mode)
+
+(use-package flymake
+ :after (evil which-key)
+ :hook prog-mode
+ :init (which-key-add-key-based-replacements
+ "<leader>e" '("errors" . "Flymake"))
+ :bind (("<leader>eb" . flymake-start)
+ ("<leader>el" . flymake-show-buffer-diagnostics)
+ ("<leader>ep" . flymake-show-project-diagnostics)
+ ("<leader>e." . display-local-help) ; Show the error message at point in the minibuffer.
+ ; `flymake' also shows it using `eldoc', but documentation
+ ; seems to override error messages.
+ ("<leader>en" . flymake-goto-next-error)
+ ("<leader>eN" . flymake-goto-prev-error))
+ :custom
+ (flymake-suppress-zero-counters nil "Show severity counters even when they are zero."))
+
+(use-package flymake-collection
+ :after (flymake)
+ ;; This needs to be called in `after-init-hook' so that all other
+ ;; packages' `:flymake-hook's are processed before f-c-hook-setup is
+ ;; called. See https://github.com/mohkale/flymake-collection.
+ :hook (after-init . flymake-collection-hook-setup))
+
+;; Language Server Protocol.
+(defun tw/help-is-eldoc (&rest _)
+ "Set up `evil-lookup-func' to display the `eldoc' buffer."
+ (setq-local evil-lookup-func #'eldoc-doc-buffer))
+
+(use-package eglot
+ ;; I have clang (for clangd) and python-lsp-server installed.
+ ;; `:hook' adds `-mode' to the package name, but `eglot-mode' doesn't exist.
+ :hook ((python-mode c-mode c++-mode) . eglot)
+ :custom
+ (eglot-autoshutdown t "Shut down language servers after deleting their last associated buffer.")
+ (eglot-sync-connect 0.1 "Wait for the language server in the background if it takes longer than 100ms.")
+ :config
+ ;; TODO: only run `tw/help-is-eldoc' if `eglot-managed-p' is true.
+ (add-hook 'eglot-managed-mode-hook #'tw/help-is-eldoc))
+
+;; Non-LSP language modes.
+(use-package cmake-mode
+ :mode (rx (or (: (or bos "/") "CMakeLists.txt") ".cmake") eos))
+
+(use-package gnuplot
+ :commands (gnuplot-mode gnuplot-make-buffer)
+ :mode ((rx ".gnuplot" eos) . gnuplot-mode))
+
+(use-package graphviz-dot-mode
+ :mode (rx ".dot" eos)
+ :custom (graphviz-dot-view-command "xdot %s" "Use xdot for previewing graphviz files."))
+
+(use-package hcl-mode
+ :mode (rx "." (or "hcl" "nomad") eos))
+
+(use-package mmm-mode
+ :commands (mmm-mode))
+
+(use-package puppet-mode
+ :mode (rx ".pp" eos))
+
+(use-package python
+ :commands (python-mode)
+ :mode (((rx ".py" (? (or ?\i ?\w)) eos) . python-mode)
+ ((rx ".aurora" eos) . python-mode)))
+
+(use-package rec-mode
+ :mode (rx ".rec" eos))
+
+(use-package sh-script ; built-in
+ :custom (sh-basic-offset 2 "Use 2 spaces for `sh-mode' indents."))
+
+(use-package tcl
+ :mode ((rx ".tcl" eos) . tcl-mode)
+ :magic ((rx "#%Module1.0") . tcl-mode))
+
+(use-package web-mode
+ :mode (rx "." (or "htm" "html" "js" "css" "scss") eos))
+
+(use-package yaml-mode
+ :mode (rx ".y" (? "a") "ml" eos))
+
+(use-package ledger-mode
+ :mode (rx ".journal" eos)
+ :custom
+ (ledger-default-date-format ledger-iso-date-format "Use hledger-style dates.")
+ (ledger-reconcile-default-date-format ledger-iso-date-format "Use hledger-style dates.")
+ (ledger-reconcile-default-commodity "€" "Make euros the default currency.")
+ (ledger-post-account-alignment-column 2 "Use 2-space indents.")
+ (ledger-post-amount-alignment-at :decimal "Align amounts at decimal points/commas.")
+ (ledger-post-amount-alignment-column 52 "Align amounts' decimal points to the 52nd column.")
+ (ledger-highlight-xact-under-point nil "Don't highlight the transaction at point."))
+
+(use-package geiser
+ :after (evil)
+ :commands (geiser geiser-eval-buffer geiser-eval-definition geiser-eval-region geiser-eval-last-sexp)
+ :hook (scheme-mode . geiser-autodoc-mode)
+ :config
+ (evil-define-key '(normal visual) scheme-mode-map
+ (kbd "<localleader>i") #'geiser
+ (kbd "<localleader>eb") #'geiser-eval-buffer
+ (kbd "<localleader>ef") #'geiser-eval-definition
+ (kbd "<localleader>er") #'geiser-eval-region
+ (kbd "<localleader>el") #'geiser-eval-last-sexp)
+ :defines scheme-mode-map)
+
+(use-package geiser-racket
+ :after (geiser))
+(use-package geiser-guile
+ :after (geiser))
+
+;; Org-mode
+(use-package org)
+(use-package ob ; org-babel
+ :after (org))
+
+(use-package outline
+ :commands (outline-mode outline-minor-mode)
+ :custom
+ ;; Mirror the default "C-c @" binding for `outline-minor-mode'.
+ (outline-minor-mode-prefix (kbd "<localleader>@") "Use localleader for `outline-minor-mode' keybindings."))
+
+;; My own custom packages, and stuff that isn't on MELPA.
+;; (use-package actionlint ; TODO: port to flymake
+;; :after (flycheck)
+;; :load-path "include/")
+
+(use-package alidist-mode
+ :after (flymake mmm-mode yaml-mode)
+ :mode (rx (or bos "/") "alidist/" (1+ anything) ".sh" eos)
+ :load-path "include/")
+
+(use-package bemscript-mode
+ :load-path "include/"
+ :mode (rx ".bem" eos))
+
+(use-package ifm-mode
+ :load-path "include/"
+ :mode (rx ".ifm" eos))
+
+(use-package pam-env-mode
+ :load-path "include/"
+ :mode (rx (or bos "/") (or "pam_env.conf" ".pam_environment") eos))
+
+(use-package environmentd-mode
+ :load-path "include/"
+ :mode (rx (or bos "/")
+ (or (: (? "etc/") "environment")
+ (: ".environment.d/" (1+ (not ?\/)) ".conf"))
+ eos))
+
+(use-package org-latex-classes
+ :after (ox-latex)
+ :load-path "include/")
+
+(use-package ob-rec
+ ;; `org-babel' hooks for `rec-mode'
+ :after (org ob rec-mode)
+ :load-path "include/")
+
+(use-package vcard-mode
+ :load-path "include/"
+ :mode (rx "." (or "vcf" "vcard") eos))
+
+;; Vim keybindings.
+(defun tw/switch-to-other-buffer ()
+ "Switch to the last-used buffer."
+ (interactive)
+ (switch-to-buffer (other-buffer)))
+
+(defun tw/new-buffer ()
+ "Open a new, empty buffer."
+ (interactive)
+ (switch-to-buffer (generate-new-buffer "untitled")))
+
+(defun tw/delete-current-buffer-file ()
+ "Ask for confirmation, then delete the file associated with the current buffer."
+ (interactive)
+ (let ((buffer (current-buffer)))
+ (when (yes-or-no-p (concat "Delete `" (buffer-file-name buffer) "'?"))
+ (delete-file (buffer-file-name buffer))
+ (kill-buffer buffer))))
+
+(use-package evil
+ :after (which-key)
+ :commands (evil-mode evil-ex-nohighlight)
+ :init (setq evil-want-keybinding nil) ; evil-collection needs this
+ :custom
+ (evil-undo-system 'undo-tree "Use `undo-tree' for evil's undo-redo function.")
+ (evil-want-minibuffer t "Use evil bindings in the minibuffer too.")
+ (evil-want-C-u-scroll t "Scroll on C-u in normal mode, not `universal-argument'.")
+ (evil-want-C-u-delete t "Delete line on C-u in insert mode, not `universal-argument'.")
+ (evil-want-Y-yank-to-eol t "Yank from point to end-of-line on Y.")
+ :config
+ (evil-mode +1)
+ (evil-set-leader '(normal visual) (kbd "SPC")) ; <leader>
+ (evil-set-leader '(normal visual) (kbd "\\") t) ; <localleader>
+ (evil-define-key '(normal insert visual replace) 'global
+ (kbd "C-s") #'save-buffer)
+ ;; Global major-mode-independent keys should be defined here. Major
+ ;; mode-dependent keys (e.g. for launching a REPL) should go under
+ ;; <localleader> instead. Use `use-package' `:bind' for those.
+ (evil-define-key '(normal visual) 'global
+ ;; These keybindings mirror the default Spacemacs ones because I have
+ ;; muscle memory of those.
+ (kbd "<leader>:") #'eval-expression
+ (kbd "<leader>TAB") #'tw/switch-to-other-buffer
+ (kbd "<leader>bb") #'switch-to-buffer
+ (kbd "<leader>bd") #'kill-current-buffer
+ (kbd "<leader>bn") #'tw/new-buffer
+ (kbd "<leader>br") #'revert-buffer-quick
+ (kbd "<leader>bx") #'kill-buffer-and-window
+ (kbd "<leader>fd") #'tw/delete-current-buffer-file
+ (kbd "<leader>ff") #'find-file
+ (kbd "<leader>fs") #'save-buffer
+ (kbd "<leader>h") help-map
+ (kbd "<leader>sc") #'evil-ex-nohighlight
+ (kbd "<leader>tf") #'auto-fill-mode
+ (kbd "<leader>tl") #'toggle-truncate-lines
+ (kbd "<leader>tn") #'display-line-numbers-mode
+ (kbd "<leader>u") #'universal-argument
+ (kbd "<leader>w") evil-window-map
+ (kbd "<leader>wd") #'evil-window-delete ; analogous to "<leader>bd"
+ (kbd "<leader>wx") #'kill-buffer-and-window) ; analogous to "<leader>bx"
+ (which-key-add-key-based-replacements
+ ;; Names are a `cons' of a short name and a long name.
+ ;; E.g. for <leader>b, "buffer" is shown under "b" in the "<leader>" menu,
+ ;; while "Buffers" is shown as the title in the "<leader>b" menu.
+ "<leader>b" '("buffer" . "Buffers")
+ "<leader>f" '("file" . "Files")
+ "<leader>h" '("help" . "General help and documentation")
+ "<leader>q" '("quit" . "Finish editing the current buffer in emacsclient")
+ "<leader>s" '("search" . "Search operations and options")
+ "<leader>t" '("toggle" . "Toggles and quick settings")
+ "<leader>w" '("window" . "Windows"))
+ :functions (evil-define-key evil-set-leader
+ evil-define-key* evil-window-delete evil-delay)
+ :defines (evil-visual-state-map))
+
+(use-package evil-collection
+ :after (evil)
+ :commands (evil-collection-init) :demand t
+ :config (evil-collection-init))
+
+(use-package evil-org
+ :after (evil org)
+ :hook org-mode
+ :config
+ (evil-define-key '(normal visual) org-mode-map
+ (kbd "<localleader>\\") #'org-ctrl-c-ctrl-c
+ (kbd "<localleader>'") #'org-edit-src-code
+ (kbd "<localleader>e") #'org-export-dispatch)
+ (evil-define-key '(normal visual) org-src-mode-map
+ (kbd "<localleader>'") #'org-edit-src-exit
+ (kbd "<localleader>\\") #'org-edit-src-save
+ (kbd "<localleader>a") #'org-edit-src-abort))
+
+(use-package evil-replace-with-register
+ :after (evil)
+ :commands (evil-replace-with-register-install) :demand t
+ ;; :custom (evil-replace-with-register-key "gR" "Use the default key.")
+ :config (evil-replace-with-register-install))
+
+(use-package evil-commentary
+ :after (evil)
+ :commands (evil-commentary-mode) :demand t
+ :config (evil-commentary-mode +1))
+
+(use-package evil-expat ; for :reverse, :remove, :rename, :colo, :g*, ... ex commands
+ :after (evil))
+
+(use-package evil-surround
+ :after (evil)
+ :commands (global-evil-surround-mode) :demand t
+ :config (global-evil-surround-mode +1))
+
+(use-package evil-smartparens
+ :after (evil smartparens)
+ :hook smartparens-enabled-hook)
+
+(use-package evil-multiedit
+ ;; See: https://github.com/hlissner/evil-multiedit#usage
+ :commands (evil-multiedit-default-keybinds) :demand t
+ :config (evil-multiedit-default-keybinds))
+
+(use-package evil-args
+ :after (evil)
+ :config
+ ;; Bind evil-args text objects only.
+ ;; See https://github.com/wcsmith/evil-args for more bindings.
+ (define-key evil-inner-text-objects-map "a" 'evil-inner-arg)
+ (define-key evil-outer-text-objects-map "a" 'evil-outer-arg))
+
+(use-package evil-numbers
+ :after (evil)
+ :bind (("<leader>+" . evil-numbers/inc-at-pt)
+ ("<leader>-" . evil-numbers/dec-at-pt)))
+
+(use-package evil-goggles ; visual previews for edit operations
+ :after (evil)
+ :commands (evil-goggles-mode evil-goggles-use-diff-faces) :demand t
+ :config
+ (evil-goggles-mode +1)
+ (evil-goggles-use-diff-faces)
+ :diminish evil-goggles-mode)
+
+(use-package evil-traces ; visual previews for :ex commands
+ :after (evil)
+ :commands (evil-traces-mode evil-traces-use-diff-faces) :demand t
+ :config
+ (evil-traces-mode +1)
+ (evil-traces-use-diff-faces)
+ :diminish evil-traces-mode)
+
+(use-package evil-markdown
+ :after (evil markdown-mode)
+ :hook markdown-mode)
+
+(use-package evil-tex
+ :after (evil tex)
+ :hook tex-mode)
+
+(use-package evil-text-object-python
+ :after (evil python)
+ :hook (python-mode . evil-text-object-python-add-bindings))
+
+;; Lisp features
+(use-package aggressive-indent
+ :hook (; scheme-mode
+ emacs-lisp-mode common-lisp-mode))
+
+(defun tw/lisp-evil-setup ()
+ "Set up evil in general `lisp-mode' buffers."
+ (setq-local evil-symbol-word-search t
+ ;; https://github.com/wcsmith/evil-args#customization
+ evil-args-delimiters '(" ")))
+
+;; Sadly, not all Lisp modes derive from `lisp-mode'.
+(add-hook 'lisp-mode-hook #'tw/lisp-evil-setup)
+(add-hook 'lisp-data-mode-hook #'tw/lisp-evil-setup) ; for elisp
+(add-hook 'scheme-mode-hook #'tw/lisp-evil-setup)
+
+;; buffer-locally set `evil-lookup-func' (used on K keys) for
+;; languages where something better than man pages is available
+;; (e.g. `describe-symbol' for elisp).
+(defun tw/elisp-lookup-func ()
+ "Show help in `emacs-lisp-mode' buffers."
+ (let ((sym (symbol-at-point)))
+ (if sym (describe-symbol sym)
+ (call-interactively #'describe-symbol))))
+
+(defun tw/emacs-lisp-evil-setup ()
+ "Set up evil in `emacs-lisp-mode' buffers."
+ (setq-local evil-lookup-func #'tw/elisp-lookup-func))
+
+(add-hook 'emacs-lisp-mode-hook #'tw/emacs-lisp-evil-setup)
+
+(evil-define-key '(normal visual) emacs-lisp-mode-map
+ (kbd "<localleader>eb") #'eval-buffer
+ (kbd "<localleader>ef") #'eval-defun
+ (kbd "<localleader>er") #'eval-region
+ (kbd "<localleader>el") #'eval-last-sexp
+ (kbd "<localleader>ep") #'eval-print-last-sexp)
+
+;;; init.el ends here
diff --git a/tw/home/files/emacs-packages/actionlint.el b/tw/home/files/emacs-packages/actionlint.el
new file mode 100644
index 00000000..3c37e34b
--- /dev/null
+++ b/tw/home/files/emacs-packages/actionlint.el
@@ -0,0 +1,34 @@
+;;; actionlint.el --- Flycheck checker for GitHub Actions.
+;;; Commentary:
+;; GitHub Actions are defined using mostly plain YAML files.
+;; Actionlint is a linter catching GitHub Action-specific mistakes, and also
+;; checks Shell and Python code embedded in Actions (using shellcheck and
+;; pyflakes, respectively).
+;;; Code:
+
+(require 'flycheck)
+
+(defun actionlint-github-workflow-p ()
+ "Does the current buffer contain a GitHub Action?"
+ (string-match-p "\\.github/workflows/[^/]+\\.yml\\'" (buffer-file-name)))
+
+(flycheck-def-executable-var actionlint "actionlint")
+
+(flycheck-define-checker actionlint
+ "A syntax checker and linter for alidist recipes."
+ ;; `flycheck-alidist-executable' automatically overrides the car of the
+ ;; :command list if set and non-nil.
+ :command ("actionlint" "-no-color" "-oneline" source)
+ :error-patterns
+ ((error line-start (file-name) ":" line ":" column ": " (message)
+ " [" (id (minimal-match (one-or-more not-newline))) "]" line-end))
+ ;; Only enable this in actual GitHub Actions, not just any YAML document.
+ :modes (yaml-mode)
+ :predicate actionlint-github-workflow-p
+ ;; Also check the document with YAML checkers, whether or not we have errors.
+ :next-checkers (yaml-ruby yaml-yamllint))
+
+(add-to-list 'flycheck-checkers 'actionlint)
+
+(provide 'actionlint)
+;;; actionlint.el ends here
diff --git a/tw/home/files/emacs-packages/alidist-mode.el b/tw/home/files/emacs-packages/alidist-mode.el
new file mode 100644
index 00000000..0f264e3b
--- /dev/null
+++ b/tw/home/files/emacs-packages/alidist-mode.el
@@ -0,0 +1,162 @@
+;;; alidist-mode.el --- Major mode for alidist recipes -*- lexical-binding: t -*-
+;;; Commentary:
+;;; alidist recipes are shell scripts with a YAML header in front. We
+;;; want both these parts highlighted properly, and to lint the whole
+;;; thing with a custom script that glues together yamllint and
+;;; shellcheck with a few custom checks.
+;;; Code:
+
+(require 'custom)
+(require 'flymake)
+(require 'mmm-mode)
+(require 'mmm-cmds)
+(require 'mmm-vars)
+(require 'sh-script)
+(require 'yaml-mode)
+
+(defgroup alidist-mode nil
+ "Alidist-related options."
+ :group 'languages
+ :prefix "alidist-mode-")
+
+(defcustom alidist-mode-alidistlint-executable "alidistlint"
+ "The alidistlint executable to use. This will be looked up in $PATH."
+ :type '(string)
+ :risky t
+ :group 'alidist-mode)
+
+(defvar alidist-mode--message-regexp
+ (rx bol "-:" ; filename
+ (group (+ digit)) ":" ; line
+ (group (+ digit)) ": " ; column
+ (group (or "note" "warning" "error")) ": " ; type
+ (group (+ not-newline)) eol) ; message
+ "Regular expression matching messages from alidistlint.
+`alidist-flymake' expects the following capturing groups in this
+regexp: (1) line number; (2) column number; (3) error type; (4)
+message.")
+
+(defvar-local alidist-mode--flymake-proc nil
+ "The latest invocation of alidistlint.")
+
+;; See info node: (flymake)An annotated example backend.
+(defun alidist-flymake (report-fn &rest _args)
+ "Run alidistlint and report diagnostics from it using REPORT-FN.
+Any running invocations are killed before running another one."
+ (unless (executable-find alidist-mode-alidistlint-executable)
+ (funcall report-fn :panic
+ :explanation "Cannot find `alidist-mode-alidistlint-executable' program")
+ (error "Cannot find alidistlint executable"))
+
+ ;; Kill previous check, if it's still running.
+ (when (process-live-p alidist-mode--flymake-proc)
+ (kill-process alidist-mode--flymake-proc))
+
+ ;; This needs `lexical-binding'.
+ (let ((source (current-buffer)))
+ (save-restriction
+ (widen)
+ (setq alidist-mode--flymake-proc
+ (make-process
+ :name "alidistlint-flymake" :noquery t :connection-type 'pipe
+ ;; Direct output to a temporary buffer.
+ :buffer (generate-new-buffer " *alidistlint-flymake*")
+ :command (list alidist-mode-alidistlint-executable "-f" "gcc" "-")
+ :sentinel
+ (lambda (proc _event)
+ "Parse diagnostic messages once the process PROC has exited."
+ ;; Check the process has actually exited, not just been suspended.
+ (when (memq (process-status proc) '(exit signal))
+ (unwind-protect
+ ;; Only proceed if we've got the "latest" process.
+ (if (with-current-buffer source (eq proc alidist-mode--flymake-proc))
+ (with-current-buffer (process-buffer proc)
+ (goto-char (point-min))
+ (cl-loop
+ while (search-forward-regexp alidist-mode--message-regexp nil t)
+ for (beg . end) = (flymake-diag-region
+ source
+ (string-to-number (match-string 1))
+ (string-to-number (match-string 2)))
+ for type = (pcase (match-string 3)
+ ("note" :note)
+ ("warning" :warning)
+ ("error" :error)
+ (type (error "Unknown alidistlint error type %s" type)))
+ collect (flymake-make-diagnostic source beg end type (match-string 4))
+ into diags
+ finally (funcall report-fn diags)))
+ (flymake-log :warning "Canceling obsolete check %s" proc))
+ ;; Clean up temporary buffer.
+ (kill-buffer (process-buffer proc)))))))
+ ;; Send the buffer to alidistlint on stdin.
+ (process-send-region alidist-mode--flymake-proc (point-min) (point-max))
+ (process-send-eof alidist-mode--flymake-proc))))
+
+(defvar-local alidist-mode--mmm-refresh-timer nil
+ "An idle timer for the current buffer, to make `mmm-mode' reparse it.")
+(put 'alidist-mode--mmm-refresh-timer 'risky-local-variable t)
+
+(defun alidist-mode--cancel-refresh-timer ()
+ "Cancel and delete the timer that reparses the buffer.
+It is stored in `alidist-mode--mmm-refresh-timer'."
+ (when alidist-mode--mmm-refresh-timer
+ (cancel-timer alidist-mode--mmm-refresh-timer)
+ (setq alidist-mode--mmm-refresh-timer nil)))
+
+(define-derived-mode alidist-mode yaml-mode "alidist"
+ "An outer mode for alidist recipes, handling the metadata."
+ (mmm-mode)
+ ;; `mmm-mode' doesn't refresh its submodes when the buffer changes
+ ;; (e.g. when a *_recipe key is added to the YAML header), so
+ ;; refresh manually when idle.
+ (alidist-mode--cancel-refresh-timer)
+ (add-hook 'kill-buffer-hook #'alidist-mode--cancel-refresh-timer 0 t)
+ (setq alidist-mode--mmm-refresh-timer
+ (run-with-idle-timer
+ 2 t (lambda (original-buffer)
+ (when (eq original-buffer (current-buffer))
+ ;; Silence `mmm-parse-buffer''s annoying message.
+ (let ((inhibit-message t))
+ (mmm-parse-buffer))))
+ ;; Idle timers are global, so make sure we only run the timer
+ ;; in the right buffer. Save the buffer now to enable this,
+ ;; and compare every time the timer ticks over.
+ (current-buffer)))
+ ;; Set up `flymake-mode'.
+ (add-hook 'flymake-diagnostic-functions #'alidist-flymake nil t))
+
+(define-derived-mode alidist-script-mode sh-mode "Script"
+ "A mode for scripts in alidist recipes with some default settings."
+ (sh-set-shell "bash"))
+
+(mmm-add-group
+ 'alidist-recipe
+ '((alidist-main-script
+ :submode alidist-script-mode
+ :face mmm-default-submode-face
+ :front (rx line-start "---\n")
+ :back (rx buffer-end))
+ (alidist-option-script
+ :submode alidist-script-mode
+ :face mmm-default-submode-face
+ ;; Any *_recipe key with a multiline string value is probably a script.
+ :front (rx line-start (* whitespace) (1+ (any alnum ?\_)) "_recipe: |\n")
+ ;; End of YAML header, or another YAML key.
+ :back (rx line-start
+ (or "---\n"
+ (seq (* whitespace) (+ (any alnum ?\_)) ":"
+ (or line-end whitespace)))))))
+
+;; Make `mmm-mode' remember `sh-mode' indentation variables.
+(cl-dolist (var sh-var-list)
+ (cl-pushnew `(,var nil (sh-mode))
+ mmm-save-local-variables :test 'equal))
+
+(mmm-add-mode-ext-class 'alidist-mode nil 'alidist-recipe)
+(add-to-list 'auto-mode-alist
+ (cons (rx (or bot "/") "alidist/" (1+ (not ?\/)) ".sh" eot)
+ #'alidist-mode))
+
+(provide 'alidist-mode)
+;;; alidist-mode.el ends here
diff --git a/tw/home/files/emacs-packages/bemscript-mode.el b/tw/home/files/emacs-packages/bemscript-mode.el
new file mode 100644
index 00000000..f46c858b
--- /dev/null
+++ b/tw/home/files/emacs-packages/bemscript-mode.el
@@ -0,0 +1,92 @@
+;;; bemscript-mode.el --- Syntax highlighting for MERRILL BEMScript files.
+;;; Commentary:
+;;; Based on the MERRILL manual. Some commands may have been missed.
+;;; Code:
+
+(defconst bemscript-mode-keywords
+ '("set" "setsubdomain" "magnetite" "iron" "tm54" "resize" "cubic anisotropy"
+ "uniaxial anisotropy" "cubicrotation" "easy axis" "external field strength"
+ "external field direction" "readmesh" "loadmesh" "readmagnetization"
+ "uniform magnetization" "randomize magnetization" "randomize all moments"
+ "remesh" "conjugategradient" "steepestdescent" "minimize" "energylog"
+ "closelogfile" "writeloopdata" "writemagnetization" "writedemag" "writehyst"
+ "writeboxdata" "appenddemagzone" "magnetizationtopath" "pathtomagnetization"
+ "renewpath" "refinepathto" "writetecplotpath" "readtecplotpath"
+ "readtecplotzone" "keypause" "makeinitialpath" "pathminimize" "pathlogfile"
+ "systemcommand" "pathstructureenergies" "reportenergy" "stop" "end" "loop"
+ "endloop" "define" "addto" "undefine" "generatecubemesh" "zonename")
+ "List of keywords for BEMScript mode. Intended for case folding.")
+
+(defconst bemscript-mode-builtins
+ '("Aex" "CurvatureWeight" "ExchangeCalculator" "K1" "K2" "Ls" "Ms" "mu"
+ "MaxEnergyEvaluations" "MaxPathEvaluations" "MaxRestart" "MaxMeshNumber"
+ "NEBSpring" "PathN" "Zone" "ZoneIncrement")
+ "List of built-in variable names for BEMScript.")
+
+(defconst bemscript-mode-special
+ '("patran" "tecplot" "POINT" "BLOCK" "SD" "muT" "mT" "T" "C")
+ "Variables with special meanings and units in BEMScript.")
+
+;; Available font-lock-*-faces: doc type string builtin comment keyword warning
+;; constant (reference) preprocessor syntactic-function function-name
+;; negation-char variable-name comment-delimiter
+(defconst bemscript-mode-font-lock-defaults
+ `((;; See font-lock-keywords docs. Earlier lines seem to override later ones,
+ ;; except if both have OVERRIDE? t.
+ ;; Format: (REGEXP (GROUPNUM FACENAME OVERRIDE? LAXMATCH?)...)
+ ;; Comments
+ ("!\\(.*\\)" 1 font-lock-comment-face t t)
+ ("!" 0 font-lock-comment-delimiter-face t)
+ ("!!.*$" 0 font-lock-doc-face t)
+
+ ;; Equals signs need spaces around them.
+ ("\\s-=\\s-" 0 font-lock-type-face t) ; there is no "operator" etc face
+ ("=" . font-lock-warning-face)
+
+ ;; Numbers and variables
+ ("\\<[0-9]*\\.?[0-9]+\\(e[-+]?\\)?[0-9]*\\>" . font-lock-constant-face)
+ ("\\(\\<[#%][A-Z_a-z][0-9A-Z_a-z]*\\>\\|\\$[A-Z_a-z][0-9A-Za-z_]*\\$\\)"
+ . font-lock-variable-name-face)
+
+ ;; Preprocessor (&-substitution)
+ (,(concat "\\([^&]\\|^\\)\\(&&\\)*" ; && escapes &
+ "\\(&\\([_a-zA-Z][_a-zA-Z0-9]*\\|{[_a-zA-Z][_a-zA-Z0-9]*}\\)\\)")
+ 3 font-lock-preprocessor-face)
+ (,(concat "\\([^&]\\|^\\)\\(&&\\)*" ; && escapes &
+ "\\(&\\($" ; bare &
+ "\\|[^&{_a-zA-Z]\\|{[^_a-zA-Z]" ; invalid char following & or &{
+ ; invalid name or unclosed {
+ "\\|{[_a-zA-Z][_0-9a-zA-Z]*\\([^_0-9a-zA-Z}]\\|$\\)\\)\\)")
+ 3 font-lock-warning-face t)
+
+ ;; Variable definitions
+ ("\\<\\(loop\\|define\\)\\s-+\\([_a-zA-Z][_a-zA-Z0-9]*\\)\\>"
+ 2 font-lock-function-name-face)
+ ("\\<\\(addto\\|undefine\\)\\s-+\\([_a-zA-Z][_a-zA-Z0-9]*\\)\\>"
+ 2 font-lock-variable-name-face)
+
+ ;; Keywords
+ (,(regexp-opt bemscript-mode-special 'words) . font-lock-string-face)
+ (,(regexp-opt bemscript-mode-keywords 'words) . font-lock-keyword-face)
+ (,(regexp-opt bemscript-mode-builtins 'words) . font-lock-builtin-face))
+
+ ;; KEYWORDS-ONLY: if t, no syntactic fontification (strings and comments)
+ nil
+ ;; CASE-FOLD: if t, make keywords case-insensitive.
+ t)
+ "Font lock settings for BEMScript mode.")
+
+(define-derived-mode bemscript-mode prog-mode "BEMScript"
+ "BEMScript-mode is used for editing MERRILL scripts."
+ (setq comment-start "!"
+ comment-end ""
+ tab-width 2
+ font-lock-defaults bemscript-mode-font-lock-defaults)
+ ;; Only word syntax entries are highlighted; add needed chars.
+ (modify-syntax-entry ?# "w")
+ ;; Strings in BEMScript are not quoted.
+ (modify-syntax-entry ?\" "w"))
+
+(add-to-list 'auto-mode-alist '("\\.bem\\'" . bemscript-mode))
+(provide 'bemscript-mode)
+;;; bemscript-mode.el ends here
diff --git a/tw/home/files/emacs-packages/environmentd-mode.el b/tw/home/files/emacs-packages/environmentd-mode.el
new file mode 100644
index 00000000..4bb8812e
--- /dev/null
+++ b/tw/home/files/emacs-packages/environmentd-mode.el
@@ -0,0 +1,46 @@
+;;; environmentd-mode.el --- Major mode for environment.d(5) files.
+
+;;; Commentary:
+
+;; This major mode font-locks files including /etc/environment and
+;; ~/.config/environment.d/*.conf. Their format is specified by the
+;; environment.d(5) man page.
+
+;;; Code:
+
+(defconst environmentd-mode/font-lock-defaults
+ '((("^[[:blank:]]+[^[:blank:]]+" . font-lock-warning-face) ; stray leading whitespace
+ ("^#+[[:blank:]]*" . font-lock-comment-delimiter-face)
+ ("^#+[[:blank:]]*\\(.*\\)$" 1 font-lock-comment-face)
+ ("\\\\[$\\]" . font-lock-string-face) ; escaped $ \
+ ("^\\([A-Za-z_][A-Za-z0-9_]*\\)\\(=\\)"
+ (1 font-lock-variable-name-face)
+ (2 font-lock-keyword-face))
+ ("\\(\\${\\)\\([A-Za-z_][A-Za-z0-9_]*\\)\\(:[+-]\\)[^}]*\\(}\\)"
+ (1 font-lock-keyword-face)
+ (2 font-lock-variable-name-face)
+ (3 font-lock-keyword-face)
+ (4 font-lock-keyword-face)) ; ${X:-default}-variable references
+ ("\\(\\${\\)\\([A-Za-z_][A-Za-z0-9_]*\\)\\(}\\)"
+ (1 font-lock-keyword-face)
+ (2 font-lock-variable-name-face)
+ (3 font-lock-keyword-face)) ; ${X}-variable references
+ ("\\(\\$\\)\\([A-Za-z_][A-Za-z0-9_]*\\)"
+ (1 font-lock-keyword-face)
+ (2 font-lock-variable-name-face))) ; $X-variable references
+ t nil ((?\' . "w") (?\" . "w")))
+ "Font lock settings for Environment.d mode. See `font-lock-defaults' for documentation.")
+
+(define-derived-mode environmentd-mode prog-mode "Environment.d"
+ "Environment.d mode is used for environment.d(5) files."
+ (setq-local comment-start "#"
+ comment-start-skip "#"
+ comment-end ""
+ font-lock-defaults environmentd-mode/font-lock-defaults))
+
+(add-to-list 'auto-mode-alist
+ '("/environment\\.d/[^/]+\\.conf\\'\\|\\`/etc/environment\\'"
+ . environmentd-mode))
+
+(provide 'environmentd-mode)
+;;; environmentd-mode.el ends here
diff --git a/tw/home/files/emacs-packages/ifm-mode.el b/tw/home/files/emacs-packages/ifm-mode.el
new file mode 100644
index 00000000..7416588b
--- /dev/null
+++ b/tw/home/files/emacs-packages/ifm-mode.el
@@ -0,0 +1,18 @@
+(define-generic-mode 'ifm-mode
+ '("#")
+ '("title" "map" "require" "room" "join" "to" "dir" "exit" "go" "oneway"
+ "tag" "from" "link" "nolink" "item" "in" "note" "score" "need" "after"
+ "before" "leave" "all" "except" "cmd" "length" "start" "finish" "nodrop"
+ "nopath" "style" "hidden" "keep" "with" "until" "ignore" "give" "lost"
+ "do" "get" "drop" "until" "safe" "ignore" "goto" "endstyle")
+ '(("\\<\\(\\(north\\|south\\)\\(east\\|west\\)?\\|[ns][ew]?\\|east\\|west\\|[ew]\\)\\>"
+ . 'font-lock-builtin-face)
+ ("\\<\\([du]\\|down\\|up\\|in\\|out\\|last\\|it\\|them\\)\\>"
+ . 'font-lock-builtin-face)
+ ("\\<[0-9]+" . 'font-lock-constant-face)
+ ("\\<[_a-zA-Z][_0-9A-Za-z]*\\>" . 'font-lock-variable-name-face))
+ '("\\.ifm\\'")
+ nil
+ "A mode for interactive fiction manager files")
+
+(provide 'ifm-mode)
diff --git a/tw/home/files/emacs-packages/org-latex-classes.el b/tw/home/files/emacs-packages/org-latex-classes.el
new file mode 100644
index 00000000..90d13341
--- /dev/null
+++ b/tw/home/files/emacs-packages/org-latex-classes.el
@@ -0,0 +1,54 @@
+;;; org-latex-classes.el --- LaTeX documentclass definitions for org-mode.
+;;; Commentary:
+;;; Code:
+(require 'ox-latex)
+
+(defun tw/latex-section-commands (name)
+ "Create a pair of section commands like (\"\\NAME{%s}\" . \"\\NAME*{%s}\")."
+ (cons (format "\\%s{%%s}" name) (format "\\%s*{%%s}" name)))
+
+(defconst tw/latex-part (tw/latex-section-commands "part"))
+(defconst tw/latex-chapter (tw/latex-section-commands "chapter"))
+(defconst tw/latex-section-and-below
+ (mapcar #'tw/latex-section-commands
+ '("section" "subsection" "subsubsection" "paragraph" "subparagraph")))
+
+(setq org-latex-classes
+ `(("paperlike" "\\documentclass{paperlike}"
+ . ,tw/latex-section-and-below)
+
+ ("examtext" "\\documentclass{examtext}"
+ . ,tw/latex-section-and-below)
+
+ ("minutes" "\\documentclass{minutes}"
+ . ,tw/latex-section-and-below)
+
+ ("mapreport" "\\documentclass{mapreport}"
+ ,tw/latex-chapter . ,tw/latex-section-and-below)
+
+ ("pt3report" "\\documentclass{pt3report}"
+ ,tw/latex-chapter . ,tw/latex-section-and-below)
+
+ ("article" "\\documentclass{article}"
+ . ,tw/latex-section-and-below)
+
+ ("scrartcl" "\\documentclass{scrartcl}"
+ . ,tw/latex-section-and-below)
+
+ ("report" "\\documentclass{report}"
+ ,tw/latex-part ,tw/latex-chapter . ,tw/latex-section-and-below)
+
+ ("report-noparts" "\\documentclass{report}"
+ ,tw/latex-chapter . ,tw/latex-section-and-below)
+
+ ("book" "\\documentclass{book}"
+ ,tw/latex-part ,tw/latex-chapter . ,tw/latex-section-and-below)
+
+ ("book-noparts" "\\documentclass{book}"
+ ,tw/latex-chapter . ,tw/latex-section-and-below)
+
+ ("checklist" "\\documentclass{checklist}"
+ . ,tw/latex-section-and-below)))
+
+(provide 'org-latex-classes)
+;;; org-latex-classes.el ends here
diff --git a/tw/home/files/emacs-packages/pam-env-mode.el b/tw/home/files/emacs-packages/pam-env-mode.el
new file mode 100644
index 00000000..75b0bf94
--- /dev/null
+++ b/tw/home/files/emacs-packages/pam-env-mode.el
@@ -0,0 +1,45 @@
+;;; pam-env.el --- Major mode for pam_env.conf(5) files.
+
+;;; Commentary:
+
+;; This major mode font-locks files including ~/.pam_environment and
+;; /etc/security/pam_env.conf, but notably not /etc/environment. Their format is
+;; specified by the pam_env.conf(5) man page.
+
+;; TODO: Only apply font-lock-variable-name-face to variable declarations if
+;; the previous line didn't end with a backslash. The following case didn't
+;; work (some declarations that should've been font-locked weren't):
+;; '("\\(?:^$\\|[^\\\\]\\)[\r\n]\\([^[:blank:]]+\\)"
+;; 1 font-lock-variable-name-face keep)
+
+;; pam_env does not support escaped double quotes ("). Single-quoted strings are
+;; not used as string delimiters. We can only match against word chars in
+;; `pam-env-mode/font-lock-defaults', so make double quotes word chars.
+
+;;; Code:
+
+(defconst pam-env-mode/font-lock-defaults
+ '((("^#+" . font-lock-comment-delimiter-face)
+ ("^#+[[:blank:]]*\\(.*\\)$" 1 font-lock-comment-face)
+ ("\\\\[@$\\]" . font-lock-string-face) ; escaped $ @ \
+ ("@{[^}]+}" . font-lock-builtin-face) ; @{}-variable references
+ ("\\${[^}]+}" . font-lock-variable-name-face) ; ${}-variable references
+ ("\"[^\"]*\"" 0 font-lock-string-face keep) ; double-quoted strings; escaped " not supported
+ ("\\<\\(DEFAULT\\|OVERRIDE\\)=" . font-lock-keyword-face) ; DEFAULT= and OVERRIDE=
+ ("^[^[:blank:]]+" . font-lock-variable-name-face) ; variable declarations
+ ("[[:blank:]]+[^[:blank:]]+" . font-lock-warning-face)) ; stray whitespace
+ t nil ((?\' . "w") (?\" . "w")))
+ "Font lock settings for PAM-Environment mode. See `font-lock-defaults' for documentation.")
+
+(define-derived-mode pam-env-mode prog-mode "PAM-Environment"
+ "PAM-environment mode is used for pam_env.conf(5) files."
+ (set (make-local-variable 'comment-start) "#")
+ (set (make-local-variable 'comment-start-skip) "^#+[[:blank:]]*")
+ (set (make-local-variable 'comment-end) "")
+ (set (make-local-variable 'font-lock-defaults) pam-env-mode/font-lock-defaults))
+
+(let ((regexp "\\(\\`\\|/\\)\\(pam_env\\.conf\\|\\.pam_environment\\)\\'"))
+ (add-to-list 'auto-mode-alist `(,regexp . pam-env-mode)))
+
+(provide 'pam-env-mode)
+;;; pam-env.el ends here
diff --git a/tw/home/files/emacs-packages/vcard-mode.el b/tw/home/files/emacs-packages/vcard-mode.el
new file mode 100644
index 00000000..a932477a
--- /dev/null
+++ b/tw/home/files/emacs-packages/vcard-mode.el
@@ -0,0 +1,56 @@
+;;; vcard-mode.el --- Major mode for vCard files.
+
+;; Copyright (C) 2012 Desmond O. Chang
+
+;; Author: Desmond O. Chang <dochang@gmail.com>
+;; Version: 0.1.0
+;; Keywords: files
+
+;; 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 <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package provides a major mode to edit vCard files.
+
+;; To install it, put this file under your load path. Then add the
+;; following to your .emacs file:
+
+;; (require 'vcard-mode)
+
+;; Or if you don't want to load it until editing a vCard file:
+
+;; (autoload 'vcard-mode "vcard-mode" "Major mode for vCard files" t)
+;; (add-to-list 'auto-mode-alist '("\\.vc\\(f\\|ard\\)\\'" . vcard-mode))
+
+;;; Code:
+
+(require 'generic)
+
+(defun vcard-mode-init ()
+ (set (make-local-variable 'paragraph-start) "BEGIN:VCARD"))
+
+;;;###autoload
+(define-generic-mode vcard-mode
+ '()
+ nil
+ '(("^BEGIN:VCARD" . font-lock-function-name-face)
+ (";[^:\n]+:" . font-lock-type-face)
+ ("^\\([^;:\n]+\\):?" . font-lock-keyword-face))
+ '("\\.\\(vcf\\|vcard\\)\\'")
+ '(vcard-mode-init)
+ "Generic mode for vCard files.")
+
+(provide 'vcard-mode)
+
+;;; vcard-mode.el ends here
diff --git a/tw/home/files/gitconfig b/tw/home/files/gitconfig
new file mode 100644
index 00000000..1f6c49f1
--- /dev/null
+++ b/tw/home/files/gitconfig
@@ -0,0 +1,43 @@
+# This is Git's per-user configuration file.
+[user]
+ name = Timo Wilken
+ email = timo.21.wilken@gmail.com
+ signingkey = 53EC3C06856883DD92355BC22FC78504681F69B0
+[commit]
+ gpgsign = true
+[url "https://"]
+ insteadOf = git://
+[url "ssh://git@gitlab.cern.ch:7999/"]
+ insteadOf = https://gitlab.cern.ch/
+[url "ssh://git@ssh.github.com/"]
+ insteadOf = gh:
+[gui]
+ fontui = -family \"Fira Sans\" -size 10 -weight normal -slant roman -underline 0 -overstrike 0
+ fontdiff = -family \"Hermit\" -size 10 -weight normal -slant roman -underline 0 -overstrike 0
+ tabsize = 4
+[merge]
+ summary = true
+ conflictstyle = diff3
+[color]
+ ui = auto
+[pull]
+ rebase = false
+ ff = only
+[alias]
+ glog = log --decorate --graph --all --oneline
+ plog = log --decorate --graph --all --oneline --pretty=tformat:\"%C(yellow)%h %Cgreen%as %Cblue%<(10,trunc)%an%Cred%d%Creset %s\"
+[init]
+ defaultBranch = master
+[advice]
+ detachedHead = false
+# https://sw.kovidgoyal.net/kitty/kittens/diff/
+[diff]
+ tool = kitty
+ guitool = kitty.gui
+[difftool]
+ prompt = false
+ trustExitCode = true
+[difftool "kitty"]
+ cmd = kitty +kitten diff $LOCAL $REMOTE
+[difftool "kitty.gui"]
+ cmd = kitty kitty +kitten diff $LOCAL $REMOTE \ No newline at end of file
diff --git a/tw/home/files/gpg-agent.conf b/tw/home/files/gpg-agent.conf
new file mode 100644
index 00000000..6f476443
--- /dev/null
+++ b/tw/home/files/gpg-agent.conf
@@ -0,0 +1,4 @@
+pinentry-program pinentry-rofi
+# Needed if spawning lots of parallel gpg --decrypt processes.
+# https://dev.gnupg.org/T3530
+auto-expand-secmem
diff --git a/tw/home/files/gpg.conf b/tw/home/files/gpg.conf
new file mode 100644
index 00000000..0d1d77a7
--- /dev/null
+++ b/tw/home/files/gpg.conf
@@ -0,0 +1,142 @@
+# Options for GnuPG
+# Copyright 1998-2003, 2010 Free Software Foundation, Inc.
+# Copyright 1998-2003, 2010 Werner Koch
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This file is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Unless you specify which option file to use (with the command line
+# option "--options filename"), GnuPG uses the file ~/.gnupg/gpg.conf
+# by default.
+#
+# An options file can contain any long options which are available in
+# GnuPG. If the first non white space character of a line is a '#',
+# this line is ignored. Empty lines are also ignored.
+#
+# See the gpg man page for a list of options.
+
+
+# If you have more than 1 secret key in your keyring, you may want to
+# uncomment the following option and set your preferred keyid.
+
+#default-key 621CC013
+
+
+# If you do not pass a recipient to gpg, it will ask for one. Using
+# this option you can encrypt to a default key. Key validation will
+# not be done in this case. The second form uses the default key as
+# default recipient.
+
+#default-recipient some-user-id
+#default-recipient-self
+
+
+# Group names may be defined like this:
+# group mynames = paige 0x12345678 joe patti
+#
+# Any time "mynames" is a recipient (-r or --recipient), it will be
+# expanded to the names "paige", "joe", and "patti", and the key ID
+# "0x12345678". Note there is only one level of expansion - you
+# cannot make an group that points to another group. Note also that
+# if there are spaces in the recipient name, this will appear as two
+# recipients. In these cases it is better to use the key ID.
+
+#group mynames = paige 0x12345678 joe patti
+
+
+# GnuPG can automatically locate and retrieve keys as needed using
+# this option. This happens when encrypting to an email address (in
+# the "user@@example.com" form) and there are no keys matching
+# "user@example.com" in the local keyring. This option takes any
+# number mechanisms which are tried in the given order. The default
+# is "--auto-key-locate local" to search for keys only in the local
+# key database. Uncomment the next line to locate a missing key using
+# two DNS based mechanisms.
+
+#auto-key-locate local,pka,dane
+
+
+# Common options for keyserver functions:
+# (Note that the --keyserver option has been moved to dirmngr.conf)
+#
+# include-disabled = when searching, include keys marked as "disabled"
+# on the keyserver (not all keyservers support this).
+#
+# no-include-revoked = when searching, do not include keys marked as
+# "revoked" on the keyserver.
+#
+# verbose = show more information as the keys are fetched.
+# Can be used more than once to increase the amount
+# of information shown.
+#
+# auto-key-retrieve = automatically fetch keys as needed from the keyserver
+# when verifying signatures or when importing keys that
+# have been revoked by a revocation key that is not
+# present on the keyring.
+#
+# no-include-attributes = do not include attribute IDs (aka "photo IDs")
+# when sending keys to the keyserver.
+
+#keyserver-options auto-key-retrieve
+
+
+# Uncomment this line to display photo user IDs in key listings and
+# when a signature from a key with a photo is verified.
+
+#show-photos
+
+
+# Use this program to display photo user IDs
+#
+# %i is expanded to a temporary file that contains the photo.
+# %I is the same as %i, but the file isn't deleted afterwards by GnuPG.
+# %k is expanded to the key ID of the key.
+# %K is expanded to the long OpenPGP key ID of the key.
+# %t is expanded to the extension of the image (e.g. "jpg").
+# %T is expanded to the MIME type of the image (e.g. "image/jpeg").
+# %f is expanded to the fingerprint of the key.
+# %% is %, of course.
+#
+# If %i or %I are not present, then the photo is supplied to the
+# viewer on standard input. If your platform supports it, standard
+# input is the best way to do this as it avoids the time and effort in
+# generating and then cleaning up a secure temp file.
+#
+# The default program is "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin"
+# On Mac OS X and Windows, the default is to use your regular JPEG image
+# viewer.
+#
+# Some other viewers:
+# photo-viewer "qiv %i"
+# photo-viewer "ee %i"
+# photo-viewer "display -title 'KeyID 0x%k'"
+#
+# This one saves a copy of the photo ID in your home directory:
+# photo-viewer "cat > ~/photoid-for-key-%k.%t"
+#
+# Use your MIME handler to view photos:
+# photo-viewer "metamail -q -d -b -c %T -s 'KeyID 0x%k' -f GnuPG"
+
+
+# Because some mailers change lines starting with "From " to ">From "
+# it is good to handle such lines in a special way when creating
+# cleartext signatures; all other PGP versions do it this way too.
+# To enable full OpenPGP compliance you may want to use this option.
+
+#no-escape-from-lines
+
+
+# Uncomment the following option to get rid of the copyright notice
+
+#no-greeting
+
+default-key 2FC78504681F69B0
+
+keyserver hkp://pool.sks-keyservers.net
+use-agent
+group Friends = DB3DB428A6E2D6DFDAC4531369C6F5CF9FB85A52
diff --git a/tw/home/files/gtk2.ini b/tw/home/files/gtk2.ini
new file mode 100644
index 00000000..6af8200a
--- /dev/null
+++ b/tw/home/files/gtk2.ini
@@ -0,0 +1,20 @@
+gtk-theme-name="Catppuccin-Dark"
+gtk-icon-theme-name="Papirus-Dark"
+gtk-font-name="Fira Sans, 10"
+gtk-cursor-theme-name="Catppuccin-Mocha-Dark-Cursors"
+gtk-cursor-theme-size=0
+gtk-toolbar-style=GTK_TOOLBAR_BOTH_HORIZ
+gtk-toolbar-icon-size=GTK_ICON_SIZE_MENU
+gtk-button-images=1
+gtk-menu-images=1
+gtk-enable-event-sounds=1
+gtk-enable-input-feedback-sounds=1
+gtk-xft-antialias=1
+gtk-xft-hinting=1
+gtk-xft-hintstyle="hintslight"
+gtk-xft-rgba="rgb"
+#gtk-modules="appmenu-gtk-module"
+gtk-enable-animations=1
+gtk-primary-button-warps-slider=0
+# Use Qt UI conventions, I think (at least this setting is from KDE).
+gtk-alternative-button-order = 1
diff --git a/tw/home/files/gtk3.ini b/tw/home/files/gtk3.ini
new file mode 100644
index 00000000..fff5946b
--- /dev/null
+++ b/tw/home/files/gtk3.ini
@@ -0,0 +1,16 @@
+[Settings]
+gtk-theme-name=Catppuccin-Dark
+gtk-icon-theme-name=Papirus-Dark
+gtk-font-name=Fira Sans 10
+gtk-cursor-theme-name=Catppuccin-Mocha-Dark-Cursors
+gtk-cursor-theme-size=0
+gtk-toolbar-style=GTK_TOOLBAR_BOTH_HORIZ
+gtk-toolbar-icon-size=GTK_ICON_SIZE_MENU
+gtk-button-images=1
+gtk-menu-images=1
+gtk-enable-event-sounds=1
+gtk-enable-input-feedback-sounds=1
+gtk-xft-antialias=1
+gtk-xft-hinting=1
+gtk-xft-hintstyle=hintslight
+gtk-xft-rgba=rgb
diff --git a/tw/home/files/htoprc b/tw/home/files/htoprc
new file mode 100644
index 00000000..f47f1c8a
--- /dev/null
+++ b/tw/home/files/htoprc
@@ -0,0 +1,59 @@
+# Beware! This file is rewritten by htop when settings are changed in the interface.
+# The parser is also very primitive, and not human-friendly.
+htop_version=3.2.0
+config_reader_min_version=3
+fields=0 48 17 18 38 39 40 2 46 47 49 1
+hide_kernel_threads=1
+hide_userland_threads=1
+shadow_other_users=0
+show_thread_names=1
+show_program_path=0
+highlight_base_name=1
+highlight_deleted_exe=1
+highlight_megabytes=1
+highlight_threads=1
+highlight_changes=0
+highlight_changes_delay_secs=5
+find_comm_in_cmdline=1
+strip_exe_from_cmdline=1
+show_merged_command=0
+header_margin=1
+screen_tabs=0
+detailed_cpu_time=1
+cpu_count_from_one=0
+show_cpu_usage=1
+show_cpu_frequency=1
+update_process_names=1
+account_guest_in_cpu_meter=0
+color_scheme=0
+enable_mouse=1
+delay=15
+hide_function_bar=1
+header_layout=two_50_50
+column_meters_0=LeftCPUs Memory Swap
+column_meter_modes_0=1 1 1
+column_meters_1=RightCPUs Tasks LoadAverage Uptime
+column_meter_modes_1=1 2 2 2
+tree_view=1
+sort_key=46
+tree_sort_key=0
+sort_direction=-1
+tree_sort_direction=1
+tree_view_always_by_pid=1
+all_branches_collapsed=0
+screen:Main=PID USER PRIORITY NICE M_VIRT M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command
+.sort_key=PERCENT_CPU
+.tree_sort_key=PID
+.tree_view=1
+.tree_view_always_by_pid=1
+.sort_direction=-1
+.tree_sort_direction=1
+.all_branches_collapsed=0
+screen:I/O=PID USER EXE IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE
+.sort_key=IO_RATE
+.tree_sort_key=PID
+.tree_view=0
+.tree_view_always_by_pid=0
+.sort_direction=-1
+.tree_sort_direction=1
+.all_branches_collapsed=0
diff --git a/tw/home/files/i3.conf b/tw/home/files/i3.conf
new file mode 100644
index 00000000..43e4cead
--- /dev/null
+++ b/tw/home/files/i3.conf
@@ -0,0 +1,250 @@
+#
+# i3 config file (v4)
+#
+# Please see http://i3wm.org/docs/userguide.html for a complete reference!
+# Use xev to see keycodes for special keys.
+
+# Appearance and Styling {{{
+# Font for window titles. Will also be used by the bar unless a different font
+# is used in the bar {} block below.
+font pango:Fira Sans 10
+
+title_align center
+default_border pixel 3
+default_floating_border pixel 3
+hide_edge_borders smart
+gaps inner 15
+gaps outer 0
+smart_gaps on
+
+# Catppuccin colours
+# Foregrounds
+set $c_fg #cdd6f4
+set $c_inactive #7f849c
+# Backgrounds
+set $c_bg #11111b
+set $c_active #585b70
+set $c_urgent #f9e2af
+
+# class border background text indicator child_border
+client.focused $c_active $c_active $c_fg $c_active $c_active
+client.focused_inactive $c_bg $c_bg $c_fg $c_bg $c_bg
+client.unfocused $c_bg $c_bg $c_inactive $c_bg $c_bg
+client.urgent $c_urgent $c_urgent $c_fg $c_urgent $c_urgent
+client.placeholder $c_bg $c_bg $c_inactive $c_bg $c_bg
+client.background $c_bg
+# }}}
+
+# Basic Configuration {{{
+# use the Super key as $mod
+set $mod Mod4
+
+# Use Mouse+$mod to drag floating windows to their wanted position
+floating_modifier $mod
+
+# essential applications
+bindsym $mod+Return exec kitty "--directory=$(xcwd)"
+bindsym $mod+i exec icecat --new-window
+bindsym $mod+shift+i exec icecat --new-tab "$(xclip -out)"
+bindsym $mod+e exec $EDITOR
+# Use ASYNC_EDITOR so I can continue using ranger while the editor
+# remains open in a separate window.
+bindsym $mod+slash exec EDITOR=$ASYNC_EDITOR kitty ranger "$(xcwd)"
+bindsym $mod+semicolon exec --no-startup-id nheko
+bindsym $mod+shift+colon exec --no-startup-id kitty neomutt
+bindsym $mod+y exec kitty pulsemixer
+
+# volume
+bindsym XF86AudioRaiseVolume exec --no-startup-id volume +5
+bindsym XF86AudioLowerVolume exec --no-startup-id volume -5
+bindsym XF86AudioMute exec --no-startup-id volume toggle-mute
+
+# notifications
+bindsym $mod+n exec dunstctl close
+bindsym $mod+shift+n exec dunstctl history-pop
+bindsym $mod+period exec dunstctl context
+
+# screen locking
+bindsym $mod+shift+slash exec screenlock
+
+# kill focused window
+bindsym $mod+shift+q kill
+
+# rofi menus
+bindsym $mod+d exec rofi -show combi
+bindsym $mod+shift+e exec rofi -show session:sessionmenu
+# When selecting a history line in rofi-calc, it is printed; copy it so we can use it elsewhere.
+bindsym $mod+q exec rofi -show calc | xclip -quiet -i -rmlastnl -selection clipboard
+bindsym $mod+p exec passmenu --type-all
+bindsym $mod+shift+p exec passmenu --type-pass
+bindsym $mod+o exec passmenu --type-otp
+bindsym $mod+comma exec rofi-colors.sh
+bindsym $mod+shift+Return exec rofi -show ssh
+# }}}
+
+# Basic Movement {{{
+# change focus
+bindsym $mod+h focus left
+bindsym $mod+j focus down
+bindsym $mod+k focus up
+bindsym $mod+l focus right
+bindsym $mod+Left focus left
+bindsym $mod+Down focus down
+bindsym $mod+Up focus up
+bindsym $mod+Right focus right
+
+# move focused window
+bindsym $mod+shift+h move left
+bindsym $mod+shift+j move down
+bindsym $mod+shift+k move up
+bindsym $mod+shift+l move right
+bindsym $mod+shift+Left move left
+bindsym $mod+shift+Down move down
+bindsym $mod+shift+Up move up
+bindsym $mod+shift+Right move right
+
+bindsym $mod+a focus parent
+bindsym $mod+shift+a focus child
+# }}}
+
+# Layout {{{
+bindsym $mod+b split h
+bindsym $mod+v split v
+
+# enter fullscreen mode for the focused container
+bindsym $mod+f fullscreen toggle
+bindsym $mod+shift+f floating enable, resize set 1920 1080, move position 0 0
+
+# change container layout (stacked, tabbed, toggle split)
+bindsym $mod+s layout stacking
+bindsym $mod+w layout tabbed
+bindsym $mod+x layout toggle split
+
+bindsym $mod+shift+space floating toggle
+# change focus between tiling / floating windows
+bindsym $mod+space focus mode_toggle
+
+# scratchpad
+bindsym $mod+numbersign scratchpad show
+bindsym $mod+shift+numbersign move to scratchpad
+bindsym $mod+apostrophe sticky disable, floating disable
+bindsym $mod+shift+apostrophe sticky enable, floating enable
+
+# move workspaces
+bindsym $mod+shift+comma move workspace to output left
+bindsym $mod+shift+period move workspace to output right
+# }}}
+
+# Default Workspaces {{{
+# switch to workspace
+bindsym $mod+Escape workspace back_and_forth
+bindsym $mod+1 workspace 1
+bindsym $mod+2 workspace 2
+bindsym $mod+3 workspace 3
+bindsym $mod+4 workspace 4
+bindsym $mod+5 workspace 5
+bindsym $mod+6 workspace 6
+bindsym $mod+7 workspace 7
+bindsym $mod+8 workspace 8
+bindsym $mod+9 workspace 9
+bindsym $mod+0 workspace 10
+bindsym $mod+parenleft workspace prev
+bindsym $mod+parenright workspace next
+
+# move focused container to workspace
+bindsym $mod+shift+Escape move container to workspace back_and_forth
+bindsym $mod+shift+1 move container to workspace 1
+bindsym $mod+shift+2 move container to workspace 2
+bindsym $mod+shift+3 move container to workspace 3
+bindsym $mod+shift+4 move container to workspace 4
+bindsym $mod+shift+5 move container to workspace 5
+bindsym $mod+shift+6 move container to workspace 6
+bindsym $mod+shift+7 move container to workspace 7
+bindsym $mod+shift+8 move container to workspace 8
+bindsym $mod+shift+9 move container to workspace 9
+bindsym $mod+shift+0 move container to workspace 10
+bindsym $mod+shift+parenleft move container to workspace prev
+bindsym $mod+shift+parenright move container to workspace next
+# }}}
+
+# Custom Workspaces {{{
+set $ws_terminal 0:>_
+bindsym $mod+grave workspace "$ws_terminal"
+bindsym $mod+shift+grave move container to workspace "$ws_terminal"
+
+set $ws_coding 50:🖊️
+bindsym $mod+c workspace "$ws_coding"
+bindsym $mod+shift+c move container to workspace "$ws_coding"
+
+set $ws_games 60:🎮
+bindsym $mod+g workspace "$ws_games"
+bindsym $mod+shift+g move container to workspace "$ws_games"
+
+set $ws_music 80:🎵
+bindsym $mod+u workspace "$ws_music"
+bindsym $mod+shift+u move container to workspace "$ws_music"
+
+set $ws_messaging 90:💬
+bindsym $mod+m workspace "$ws_messaging"
+bindsym $mod+shift+m move container to workspace "$ws_messaging"
+
+assign [class="^Franz$"] "$ws_messaging"
+assign [window_role="^weechat$"] "$ws_messaging"
+assign [window_role="^mutt$"] "$ws_messaging"
+assign [class="^evolution-initial$"] "$ws_messaging"
+assign [class="^kontact$"] "$ws_messaging"
+
+# NOTE: no_focus will also be ignored for the first window on a workspace as
+# there shouldn’t be a reason to not focus the window in this case. This allows
+# for better usability in combination with workspace_layout. (From i3 docs)
+#no_focus [class="^Franz$"]
+#no_focus [window_role="^weechat$"]
+#no_focus [window_role="^mutt$"]
+#no_focus [class="^evolution-initial$"]
+
+## PlayOnLinux
+for_window [title="PlayOnLinux"] floating enable
+
+## Plasma/KDE: https://ryanlue.com/posts/2019-06-13-kde-i3
+# Don’t treat Plasma pop-ups as full-sized windows
+for_window [class="plasmashell"] floating enable
+# Don’t spawn an empty window for the Plasma Desktop
+for_window [title="Desktop — Plasma"] move scratchpad
+# Don’t let notifications and non-interactive pop-up windows steal focus
+#no_focus [class="plasmashell" window_type="on_screen_display"]
+# https://github.com/heckelson/i3-and-kde-plasma
+# Move notifications to top-right corner.
+#for_window [class="plasmashell" window_type="notification"] move up 400, move right 750, no_focus
+# Notifications appear in the centre of the screen. According to
+# https://old.reddit.com/r/i3wm/comments/bw1yfs/kde_notifications_appearing_in_the_centre_of/,
+# setting notifications to appear in the top-left corner gets placement right.
+no_focus [class="plasmashell" window_type="notification"]
+
+for_window [class="^Pyneedle$"] floating enable
+for_window [instance="^emacs-initial$"] floating enable
+for_window [class="^Spotify$"] move container to workspace "$ws_music"
+for_window [class="^Pidgin$"] move container to workspace "$ws_messaging"
+# }}}
+
+# Modes {{{
+mode "resize" {
+ bindsym h resize shrink width 10 px or 10 ppt
+ bindsym j resize grow height 10 px or 10 ppt
+ bindsym k resize shrink height 10 px or 10 ppt
+ bindsym l resize grow width 10 px or 10 ppt
+
+ bindsym shift+h resize shrink width 5 px or 5 ppt
+ bindsym shift+j resize grow height 5 px or 5 ppt
+ bindsym shift+k resize shrink height 5 px or 5 ppt
+ bindsym shift+l resize grow width 5 px or 5 ppt
+
+ bindsym left resize shrink width 10 px or 10 ppt
+ bindsym down resize grow height 10 px or 10 ppt
+ bindsym up resize shrink height 10 px or 10 ppt
+ bindsym right resize grow width 10 px or 10 ppt
+
+ bindsym Return mode "default"
+ bindsym Escape mode "default"
+}
+bindsym $mod+r mode "resize"
+# }}}
diff --git a/tw/home/files/khal.conf b/tw/home/files/khal.conf
new file mode 100644
index 00000000..73bb5c19
--- /dev/null
+++ b/tw/home/files/khal.conf
@@ -0,0 +1,39 @@
+# https://lostpackets.de/khal/configure.html
+
+[default]
+default_calendar = Personal
+highlight_event_days = True
+show_all_days = True
+timedelta = 7d
+
+[highlight_days]
+default_color = dark gray
+method = foreground
+
+[locale]
+timeformat = %H:%M
+dateformat = %d/%m
+longdateformat = %d/%m/%Y
+datetimeformat = %d/%m %H:%M
+longdatetimeformat = %d/%m/%Y %H:%M
+weeknumbers = off
+
+[view]
+monthdisplay = firstfullweek
+
+[calendars]
+[[birthdays]]
+type = birthdays
+path = ~/.local/share/vdirsyncer/main-contacts/contacts
+color = dark red
+# Prefer other events' colours when colouring days.
+priority = -1
+
+[[indico]]
+path = ~/.local/share/vdirsyncer/indico
+color = yellow
+readonly = True
+
+[[calendars]]
+type = discover
+path = ~/.local/share/vdirsyncer/calendars/imported-*ics
diff --git a/tw/home/files/khard.conf b/tw/home/files/khard.conf
new file mode 100644
index 00000000..a3cfe41a
--- /dev/null
+++ b/tw/home/files/khard.conf
@@ -0,0 +1,22 @@
+# https://khard.readthedocs.io/en/latest/man/khard.conf.html
+
+[addressbooks]
+[[main]]
+path = ~/.local/share/vdirsyncer/main-contacts/contacts
+[[sandbox]]
+path = ~/.local/share/vdirsyncer/sandbox-contacts/sandbox-contacts
+
+[general]
+default_action = list
+merge_editor = vimdiff
+
+[contact table]
+display = formatted_name
+sort = last_name
+group_by_addressbook = yes
+localize_dates = yes
+show_nicknames = yes
+show_kinds = yes
+show_uids = no
+preferred_phone_number_type = pref, cell, home
+preferred_email_address_type = pref, home, work
diff --git a/tw/home/files/kitty.conf b/tw/home/files/kitty.conf
new file mode 100644
index 00000000..014e70f6
--- /dev/null
+++ b/tw/home/files/kitty.conf
@@ -0,0 +1,1337 @@
+# vim:fileencoding=utf-8:ft=conf:foldmethod=marker
+
+#: Fonts {{{
+
+#: kitty has very powerful font management. You can configure
+#: individual font faces and even specify special fonts for particular
+#: characters.
+
+font_family Hermit
+# bold_font auto
+# italic_font auto
+# bold_italic_font auto
+
+#: You can specify different fonts for the bold/italic/bold-italic
+#: variants. To get a full list of supported fonts use the `kitty
+#: list-fonts` command. By default they are derived automatically, by
+#: the OSes font system. Setting them manually is useful for font
+#: families that have many weight variants like Book, Medium, Thick,
+#: etc. For example::
+
+#: font_family Operator Mono Book
+#: bold_font Operator Mono Medium
+#: italic_font Operator Mono Book Italic
+#: bold_italic_font Operator Mono Medium Italic
+
+font_size 10.0
+
+#: Font size (in pts)
+
+# force_ltr no
+
+#: kitty does not support BIDI (bidirectional text), however, for RTL
+#: scripts, words are automatically displayed in RTL. That is to say,
+#: in an RTL script, the words "HELLO WORLD" display in kitty as
+#: "WORLD HELLO", and if you try to select a substring of an RTL-
+#: shaped string, you will get the character that would be there had
+#: the the string been LTR. For example, assuming the Hebrew word
+#: ירושלים, selecting the character that on the screen appears to be ם
+#: actually writes into the selection buffer the character י.
+
+#: kitty's default behavior is useful in conjunction with a filter to
+#: reverse the word order, however, if you wish to manipulate RTL
+#: glyphs, it can be very challenging to work with, so this option is
+#: provided to turn it off. Furthermore, this option can be used with
+#: the command line program GNU FriBidi
+#: <https://github.com/fribidi/fribidi#executable> to get BIDI
+#: support, because it will force kitty to always treat the text as
+#: LTR, which FriBidi expects for terminals.
+
+# adjust_line_height 0
+# adjust_column_width 0
+
+#: Change the size of each character cell kitty renders. You can use
+#: either numbers, which are interpreted as pixels or percentages
+#: (number followed by %), which are interpreted as percentages of the
+#: unmodified values. You can use negative pixels or percentages less
+#: than 100% to reduce sizes (but this might cause rendering
+#: artifacts).
+
+# symbol_map U+E0A0-U+E0A3,U+E0C0-U+E0C7 PowerlineSymbols
+
+#: Map the specified unicode codepoints to a particular font. Useful
+#: if you need special rendering for some symbols, such as for
+#: Powerline. Avoids the need for patched fonts. Each unicode code
+#: point is specified in the form U+<code point in hexadecimal>. You
+#: can specify multiple code points, separated by commas and ranges
+#: separated by hyphens. symbol_map itself can be specified multiple
+#: times. Syntax is::
+
+#: symbol_map codepoints Font Family Name
+
+# disable_ligatures never
+
+#: Choose how you want to handle multi-character ligatures. The
+#: default is to always render them. You can tell kitty to not render
+#: them when the cursor is over them by using cursor to make editing
+#: easier, or have kitty never render them at all by using always, if
+#: you don't like them. The ligature strategy can be set per-window
+#: either using the kitty remote control facility or by defining
+#: shortcuts for it in kitty.conf, for example::
+
+#: map alt+1 disable_ligatures_in active always
+#: map alt+2 disable_ligatures_in all never
+#: map alt+3 disable_ligatures_in tab cursor
+
+#: Note that this refers to programming ligatures, typically
+#: implemented using the calt OpenType feature. For disabling general
+#: ligatures, use the font_features setting.
+
+# font_features none
+
+#: Choose exactly which OpenType features to enable or disable. This
+#: is useful as some fonts might have features worthwhile in a
+#: terminal. For example, Fira Code Retina includes a discretionary
+#: feature, zero, which in that font changes the appearance of the
+#: zero (0), to make it more easily distinguishable from Ø. Fira Code
+#: Retina also includes other discretionary features known as
+#: Stylistic Sets which have the tags ss01 through ss20.
+
+#: Note that this code is indexed by PostScript name, and not the font
+#: family. This allows you to define very precise feature settings;
+#: e.g. you can disable a feature in the italic font but not in the
+#: regular font.
+
+#: On Linux, these are read from the FontConfig database first and
+#: then this, setting is applied, so they can be configured in a
+#: single, central place.
+
+#: To get the PostScript name for a font, use kitty + list-fonts
+#: --psnames:
+
+#: .. code-block:: sh
+
+#: $ kitty + list-fonts --psnames | grep Fira
+#: Fira Code
+#: Fira Code Bold (FiraCode-Bold)
+#: Fira Code Light (FiraCode-Light)
+#: Fira Code Medium (FiraCode-Medium)
+#: Fira Code Regular (FiraCode-Regular)
+#: Fira Code Retina (FiraCode-Retina)
+
+#: The part in brackets is the PostScript name.
+
+#: Enable alternate zero and oldstyle numerals::
+
+#: font_features FiraCode-Retina +zero +onum
+
+#: Enable only alternate zero::
+
+#: font_features FiraCode-Retina +zero
+
+#: Disable the normal ligatures, but keep the calt feature which (in
+#: this font) breaks up monotony::
+
+#: font_features TT2020StyleB-Regular -liga +calt
+
+#: In conjunction with force_ltr, you may want to disable Arabic
+#: shaping entirely, and only look at their isolated forms if they
+#: show up in a document. You can do this with e.g.::
+
+#: font_features UnifontMedium +isol -medi -fina -init
+
+# box_drawing_scale 0.001, 1, 1.5, 2
+
+#: Change the sizes of the lines used for the box drawing unicode
+#: characters These values are in pts. They will be scaled by the
+#: monitor DPI to arrive at a pixel value. There must be four values
+#: corresponding to thin, normal, thick, and very thick lines.
+
+#: }}}
+
+#: Cursor customization {{{
+
+# cursor #cccccc
+
+#: Default cursor color
+
+# cursor_text_color #111111
+
+#: Choose the color of text under the cursor. If you want it rendered
+#: with the background color of the cell underneath instead, use the
+#: special keyword: background
+
+# cursor_shape block
+
+#: The cursor shape can be one of (block, beam, underline)
+
+# cursor_beam_thickness 1.5
+
+#: Defines the thickness of the beam cursor (in pts)
+
+# cursor_underline_thickness 2.0
+
+#: Defines the thickness of the underline cursor (in pts)
+
+# cursor_blink_interval -1
+
+#: The interval (in seconds) at which to blink the cursor. Set to zero
+#: to disable blinking. Negative values mean use system default. Note
+#: that numbers smaller than repaint_delay will be limited to
+#: repaint_delay.
+
+# cursor_stop_blinking_after 15.0
+
+#: Stop blinking cursor after the specified number of seconds of
+#: keyboard inactivity. Set to zero to never stop blinking.
+
+#: }}}
+
+#: Scrollback {{{
+
+scrollback_lines 100000
+
+#: Number of lines of history to keep in memory for scrolling back.
+#: Memory is allocated on demand. Negative numbers are (effectively)
+#: infinite scrollback. Note that using very large scrollback is not
+#: recommended as it can slow down performance of the terminal and
+#: also use large amounts of RAM. Instead, consider using
+#: scrollback_pager_history_size.
+
+# scrollback_pager less --chop-long-lines --RAW-CONTROL-CHARS +INPUT_LINE_NUMBER
+
+#: Program with which to view scrollback in a new window. The
+#: scrollback buffer is passed as STDIN to this program. If you change
+#: it, make sure the program you use can handle ANSI escape sequences
+#: for colors and text formatting. INPUT_LINE_NUMBER in the command
+#: line above will be replaced by an integer representing which line
+#: should be at the top of the screen. Similarly CURSOR_LINE and
+#: CURSOR_COLUMN will be replaced by the current cursor position.
+
+scrollback_pager_history_size 1024
+
+#: Separate scrollback history size, used only for browsing the
+#: scrollback buffer (in MB). This separate buffer is not available
+#: for interactive scrolling but will be piped to the pager program
+#: when viewing scrollback buffer in a separate window. The current
+#: implementation stores the data in UTF-8, so approximatively 10000
+#: lines per megabyte at 100 chars per line, for pure ASCII text,
+#: unformatted text. A value of zero or less disables this feature.
+#: The maximum allowed size is 4GB.
+
+scrollback_fill_enlarged_window yes
+
+#: Fill new space with lines from the scrollback buffer after
+#: enlarging a window.
+
+# wheel_scroll_multiplier 5.0
+
+#: Modify the amount scrolled by the mouse wheel. Note this is only
+#: used for low precision scrolling devices, not for high precision
+#: scrolling on platforms such as macOS and Wayland. Use negative
+#: numbers to change scroll direction.
+
+# touch_scroll_multiplier 1.0
+
+#: Modify the amount scrolled by a touchpad. Note this is only used
+#: for high precision scrolling devices on platforms such as macOS and
+#: Wayland. Use negative numbers to change scroll direction.
+
+#: }}}
+
+#: Mouse {{{
+
+# mouse_hide_wait 3.0
+
+#: Hide mouse cursor after the specified number of seconds of the
+#: mouse not being used. Set to zero to disable mouse cursor hiding.
+#: Set to a negative value to hide the mouse cursor immediately when
+#: typing text. Disabled by default on macOS as getting it to work
+#: robustly with the ever-changing sea of bugs that is Cocoa is too
+#: much effort.
+
+# url_color #0087bd
+# url_style curly
+
+#: The color and style for highlighting URLs on mouse-over. url_style
+#: can be one of: none, single, double, curly
+
+open_url_modifiers shift
+# open_url_modifiers kitty_mod
+
+#: The modifier keys to press when clicking with the mouse on URLs to
+#: open the URL
+
+# open_url_with default
+
+#: The program with which to open URLs that are clicked on. The
+#: special value default means to use the operating system's default
+#: URL handler.
+
+# url_prefixes http https file ftp gemini irc gopher mailto news git
+
+#: The set of URL prefixes to look for when detecting a URL under the
+#: mouse cursor.
+
+# detect_urls yes
+
+#: Detect URLs under the mouse. Detected URLs are highlighted with an
+#: underline and the mouse cursor becomes a hand over them. Even if
+#: this option is disabled, URLs are still clickable.
+
+# copy_on_select no
+
+#: Copy to clipboard or a private buffer on select. With this set to
+#: clipboard, simply selecting text with the mouse will cause the text
+#: to be copied to clipboard. Useful on platforms such as macOS that
+#: do not have the concept of primary selections. You can instead
+#: specify a name such as a1 to copy to a private kitty buffer
+#: instead. Map a shortcut with the paste_from_buffer action to paste
+#: from this private buffer. For example::
+
+#: map cmd+shift+v paste_from_buffer a1
+
+#: Note that copying to the clipboard is a security risk, as all
+#: programs, including websites open in your browser can read the
+#: contents of the system clipboard.
+
+# strip_trailing_spaces never
+
+#: Remove spaces at the end of lines when copying to clipboard. A
+#: value of smart will do it when using normal selections, but not
+#: rectangle selections. always will always do it.
+
+# rectangle_select_modifiers ctrl+alt
+
+#: The modifiers to use rectangular selection (i.e. to select text in
+#: a rectangular block with the mouse)
+
+# terminal_select_modifiers shift
+
+#: The modifiers to override mouse selection even when a terminal
+#: application has grabbed the mouse
+
+# select_by_word_characters @-./_~?&=%+#
+
+#: Characters considered part of a word when double clicking. In
+#: addition to these characters any character that is marked as an
+#: alphanumeric character in the unicode database will be matched.
+
+# click_interval -1.0
+
+#: The interval between successive clicks to detect double/triple
+#: clicks (in seconds). Negative numbers will use the system default
+#: instead, if available, or fallback to 0.5.
+
+focus_follows_mouse yes
+
+#: Set the active window to the window under the mouse when moving the
+#: mouse around
+
+# pointer_shape_when_grabbed arrow
+
+#: The shape of the mouse pointer when the program running in the
+#: terminal grabs the mouse. Valid values are: arrow, beam and hand
+
+# default_pointer_shape beam
+
+#: The default shape of the mouse pointer. Valid values are: arrow,
+#: beam and hand
+
+# pointer_shape_when_dragging beam
+
+#: The default shape of the mouse pointer when dragging across text.
+#: Valid values are: arrow, beam and hand
+
+#: }}}
+
+#: Performance tuning {{{
+
+# repaint_delay 10
+
+#: Delay (in milliseconds) between screen updates. Decreasing it,
+#: increases frames-per-second (FPS) at the cost of more CPU usage.
+#: The default value yields ~100 FPS which is more than sufficient for
+#: most uses. Note that to actually achieve 100 FPS you have to either
+#: set sync_to_monitor to no or use a monitor with a high refresh
+#: rate. Also, to minimize latency when there is pending input to be
+#: processed, repaint_delay is ignored.
+
+# input_delay 3
+
+#: Delay (in milliseconds) before input from the program running in
+#: the terminal is processed. Note that decreasing it will increase
+#: responsiveness, but also increase CPU usage and might cause flicker
+#: in full screen programs that redraw the entire screen on each loop,
+#: because kitty is so fast that partial screen updates will be drawn.
+
+# sync_to_monitor yes
+
+#: Sync screen updates to the refresh rate of the monitor. This
+#: prevents tearing (https://en.wikipedia.org/wiki/Screen_tearing)
+#: when scrolling. However, it limits the rendering speed to the
+#: refresh rate of your monitor. With a very high speed mouse/high
+#: keyboard repeat rate, you may notice some slight input latency. If
+#: so, set this to no.
+
+#: }}}
+
+#: Terminal bell {{{
+
+# enable_audio_bell yes
+
+#: Enable/disable the audio bell. Useful in environments that require
+#: silence.
+
+# visual_bell_duration 0.0
+
+#: Visual bell duration. Flash the screen when a bell occurs for the
+#: specified number of seconds. Set to zero to disable.
+
+# window_alert_on_bell yes
+
+#: Request window attention on bell. Makes the dock icon bounce on
+#: macOS or the taskbar flash on linux.
+
+# bell_on_tab yes
+
+#: Show a bell symbol on the tab if a bell occurs in one of the
+#: windows in the tab and the window is not the currently focused
+#: window
+
+# command_on_bell none
+
+#: Program to run when a bell occurs.
+
+#: }}}
+
+#: Window layout {{{
+
+# remember_window_size yes
+# initial_window_width 640
+# initial_window_height 400
+
+#: If enabled, the window size will be remembered so that new
+#: instances of kitty will have the same size as the previous
+#: instance. If disabled, the window will initially have size
+#: configured by initial_window_width/height, in pixels. You can use a
+#: suffix of "c" on the width/height values to have them interpreted
+#: as number of cells instead of pixels.
+
+# enabled_layouts *
+
+#: The enabled window layouts. A comma separated list of layout names.
+#: The special value all means all layouts. The first listed layout
+#: will be used as the startup layout. Default configuration is all
+#: layouts in alphabetical order. For a list of available layouts, see
+#: the https://sw.kovidgoyal.net/kitty/index.html#layouts.
+
+# window_resize_step_cells 2
+# window_resize_step_lines 2
+
+#: The step size (in units of cell width/cell height) to use when
+#: resizing windows. The cells value is used for horizontal resizing
+#: and the lines value for vertical resizing.
+
+# window_border_width 0.5pt
+
+#: The width of window borders. Can be either in pixels (px) or pts
+#: (pt). Values in pts will be rounded to the nearest number of pixels
+#: based on screen resolution. If not specified the unit is assumed to
+#: be pts. Note that borders are displayed only when more than one
+#: window is visible. They are meant to separate multiple windows.
+
+# draw_minimal_borders yes
+
+#: Draw only the minimum borders needed. This means that only the
+#: minimum needed borders for inactive windows are drawn. That is only
+#: the borders that separate the inactive window from a neighbor. Note
+#: that setting a non-zero window margin overrides this and causes all
+#: borders to be drawn.
+
+# window_margin_width 0
+
+#: The window margin (in pts) (blank area outside the border). A
+#: single value sets all four sides. Two values set the vertical and
+#: horizontal sides. Three values set top, horizontal and bottom. Four
+#: values set top, right, bottom and left.
+
+# single_window_margin_width -1
+
+#: The window margin (in pts) to use when only a single window is
+#: visible. Negative values will cause the value of
+#: window_margin_width to be used instead. A single value sets all
+#: four sides. Two values set the vertical and horizontal sides. Three
+#: values set top, horizontal and bottom. Four values set top, right,
+#: bottom and left.
+
+# window_padding_width 0
+
+#: The window padding (in pts) (blank area between the text and the
+#: window border). A single value sets all four sides. Two values set
+#: the vertical and horizontal sides. Three values set top, horizontal
+#: and bottom. Four values set top, right, bottom and left.
+
+# placement_strategy center
+
+#: When the window size is not an exact multiple of the cell size, the
+#: cell area of the terminal window will have some extra padding on
+#: the sides. You can control how that padding is distributed with
+#: this option. Using a value of center means the cell area will be
+#: placed centrally. A value of top-left means the padding will be on
+#: only the bottom and right edges.
+
+# active_border_color #00ff00
+
+#: The color for the border of the active window. Set this to none to
+#: not draw borders around the active window.
+
+# inactive_border_color #cccccc
+
+#: The color for the border of inactive windows
+
+# bell_border_color #ff5a00
+
+#: The color for the border of inactive windows in which a bell has
+#: occurred
+
+# inactive_text_alpha 1.0
+
+#: Fade the text in inactive windows by the specified amount (a number
+#: between zero and one, with zero being fully faded).
+
+# hide_window_decorations no
+
+#: Hide the window decorations (title-bar and window borders) with
+#: yes. On macOS, titlebar-only can be used to only hide the titlebar.
+#: Whether this works and exactly what effect it has depends on the
+#: window manager/operating system.
+
+# resize_debounce_time 0.1
+
+#: The time (in seconds) to wait before redrawing the screen when a
+#: resize event is received. On platforms such as macOS, where the
+#: operating system sends events corresponding to the start and end of
+#: a resize, this number is ignored.
+
+# resize_draw_strategy static
+
+#: Choose how kitty draws a window while a resize is in progress. A
+#: value of static means draw the current window contents, mostly
+#: unchanged. A value of scale means draw the current window contents
+#: scaled. A value of blank means draw a blank window. A value of size
+#: means show the window size in cells.
+
+# resize_in_steps no
+
+#: Resize the OS window in steps as large as the cells, instead of
+#: with the usual pixel accuracy. Combined with an
+#: initial_window_width and initial_window_height in number of cells,
+#: this option can be used to keep the margins as small as possible
+#: when resizing the OS window. Note that this does not currently work
+#: on Wayland.
+
+# confirm_os_window_close 0
+
+#: Ask for confirmation when closing an OS window or a tab that has at
+#: least this number of kitty windows in it. A value of zero disables
+#: confirmation. This confirmation also applies to requests to quit
+#: the entire application (all OS windows, via the quit action).
+
+#: }}}
+
+#: Tab bar {{{
+
+# tab_bar_edge bottom
+
+#: Which edge to show the tab bar on, top or bottom
+
+# tab_bar_margin_width 0.0
+
+#: The margin to the left and right of the tab bar (in pts)
+
+# tab_bar_style fade
+
+#: The tab bar style, can be one of: fade, separator, powerline, or
+#: hidden. In the fade style, each tab's edges fade into the
+#: background color, in the separator style, tabs are separated by a
+#: configurable separator, and the powerline shows the tabs as a
+#: continuous line. If you use the hidden style, you might want to
+#: create a mapping for the select_tab action which presents you with
+#: a list of tabs and allows for easy switching to a tab.
+
+# tab_bar_min_tabs 2
+
+#: The minimum number of tabs that must exist before the tab bar is
+#: shown
+
+# tab_switch_strategy previous
+
+#: The algorithm to use when switching to a tab when the current tab
+#: is closed. The default of previous will switch to the last used
+#: tab. A value of left will switch to the tab to the left of the
+#: closed tab. A value of right will switch to the tab to the right of
+#: the closed tab. A value of last will switch to the right-most tab.
+
+# tab_fade 0.25 0.5 0.75 1
+
+#: Control how each tab fades into the background when using fade for
+#: the tab_bar_style. Each number is an alpha (between zero and one)
+#: that controls how much the corresponding cell fades into the
+#: background, with zero being no fade and one being full fade. You
+#: can change the number of cells used by adding/removing entries to
+#: this list.
+
+# tab_separator " ┇"
+
+#: The separator between tabs in the tab bar when using separator as
+#: the tab_bar_style.
+
+# tab_powerline_style angled
+
+#: The powerline separator style between tabs in the tab bar when
+#: using powerline as the tab_bar_style, can be one of: angled,
+#: slanted, or round.
+
+# tab_activity_symbol none
+
+#: Some text or a unicode symbol to show on the tab if a window in the
+#: tab that does not have focus has some activity.
+
+# tab_title_template "{title}"
+
+#: A template to render the tab title. The default just renders the
+#: title. If you wish to include the tab-index as well, use something
+#: like: {index}: {title}. Useful if you have shortcuts mapped for
+#: goto_tab N. In addition you can use {layout_name} for the current
+#: layout name and {num_windows} for the number of windows in the tab.
+#: Note that formatting is done by Python's string formatting
+#: machinery, so you can use, for instance, {layout_name[:2].upper()}
+#: to show only the first two letters of the layout name, upper-cased.
+#: If you want to style the text, you can use styling directives, for
+#: example: {fmt.fg.red}red{fmt.fg.default}normal{fmt.bg._00FF00}green
+#: bg{fmt.bg.normal}. Similarly, for bold and italic:
+#: {fmt.bold}bold{fmt.nobold}normal{fmt.italic}italic{fmt.noitalic}.
+
+# active_tab_title_template none
+
+#: Template to use for active tabs, if not specified falls back to
+#: tab_title_template.
+
+# active_tab_foreground #000
+# active_tab_background #eee
+# active_tab_font_style bold-italic
+# inactive_tab_foreground #444
+# inactive_tab_background #999
+# inactive_tab_font_style normal
+
+#: Tab bar colors and styles
+
+# tab_bar_background none
+
+#: Background color for the tab bar. Defaults to using the terminal
+#: background color.
+
+#: }}}
+
+#: Color scheme {{{
+
+# foreground #dddddd
+# background #000000
+
+#: The foreground and background colors
+
+# background_opacity 1.0
+
+#: The opacity of the background. A number between 0 and 1, where 1 is
+#: opaque and 0 is fully transparent. This will only work if
+#: supported by the OS (for instance, when using a compositor under
+#: X11). Note that it only sets the background color's opacity in
+#: cells that have the same background color as the default terminal
+#: background. This is so that things like the status bar in vim,
+#: powerline prompts, etc. still look good. But it means that if you
+#: use a color theme with a background color in your editor, it will
+#: not be rendered as transparent. Instead you should change the
+#: default background color in your kitty config and not use a
+#: background color in the editor color scheme. Or use the escape
+#: codes to set the terminals default colors in a shell script to
+#: launch your editor. Be aware that using a value less than 1.0 is a
+#: (possibly significant) performance hit. If you want to dynamically
+#: change transparency of windows set dynamic_background_opacity to
+#: yes (this is off by default as it has a performance cost)
+
+# background_image none
+
+#: Path to a background image. Must be in PNG format.
+
+# background_image_layout tiled
+
+#: Whether to tile or scale the background image.
+
+# background_image_linear no
+
+#: When background image is scaled, whether linear interpolation
+#: should be used.
+
+# dynamic_background_opacity no
+
+#: Allow changing of the background_opacity dynamically, using either
+#: keyboard shortcuts (increase_background_opacity and
+#: decrease_background_opacity) or the remote control facility.
+
+# background_tint 0.0
+
+#: How much to tint the background image by the background color. The
+#: tint is applied only under the text area, not margin/borders. Makes
+#: it easier to read the text. Tinting is done using the current
+#: background color for each window. This setting applies only if
+#: background_opacity is set and transparent windows are supported or
+#: background_image is set.
+
+# dim_opacity 0.75
+
+#: How much to dim text that has the DIM/FAINT attribute set. One
+#: means no dimming and zero means fully dimmed (i.e. invisible).
+
+# selection_foreground #000000
+
+#: The foreground for text selected with the mouse. A value of none
+#: means to leave the color unchanged.
+
+# selection_background #fffacd
+
+#: The background for text selected with the mouse.
+
+
+#: The 256 terminal colors. There are 8 basic colors, each color has a
+#: dull and bright version, for the first 16 colors. You can set the
+#: remaining 240 colors as color16 to color255.
+
+# color0 #000000
+# color8 #767676
+
+#: black
+
+# color1 #cc0403
+# color9 #f2201f
+
+#: red
+
+# color2 #19cb00
+# color10 #23fd00
+
+#: green
+
+# color3 #cecb00
+# color11 #fffd00
+
+#: yellow
+
+# color4 #0d73cc
+# color12 #1a8fff
+
+#: blue
+
+# color5 #cb1ed1
+# color13 #fd28ff
+
+#: magenta
+
+# color6 #0dcdcd
+# color14 #14ffff
+
+#: cyan
+
+# color7 #dddddd
+# color15 #ffffff
+
+#: white
+
+# mark1_foreground black
+
+#: Color for marks of type 1
+
+# mark1_background #98d3cb
+
+#: Color for marks of type 1 (light steel blue)
+
+# mark2_foreground black
+
+#: Color for marks of type 2
+
+# mark2_background #f2dcd3
+
+#: Color for marks of type 1 (beige)
+
+# mark3_foreground black
+
+#: Color for marks of type 3
+
+# mark3_background #f274bc
+
+#: Color for marks of type 1 (violet)
+
+#: }}}
+
+#: Advanced {{{
+
+# shell .
+
+#: The shell program to execute. The default value of . means to use
+#: whatever shell is set as the default shell for the current user.
+#: Note that on macOS if you change this, you might need to add
+#: --login to ensure that the shell starts in interactive mode and
+#: reads its startup rc files.
+
+# editor .
+
+#: The console editor to use when editing the kitty config file or
+#: similar tasks. A value of . means to use the environment variables
+#: VISUAL and EDITOR in that order. Note that this environment
+#: variable has to be set not just in your shell startup scripts but
+#: system-wide, otherwise kitty will not see it.
+
+# close_on_child_death no
+
+#: Close the window when the child process (shell) exits. If no (the
+#: default), the terminal will remain open when the child exits as
+#: long as there are still processes outputting to the terminal (for
+#: example disowned or backgrounded processes). If yes, the window
+#: will close as soon as the child process exits. Note that setting it
+#: to yes means that any background processes still using the terminal
+#: can fail silently because their stdout/stderr/stdin no longer work.
+
+# allow_remote_control no
+
+#: Allow other programs to control kitty. If you turn this on other
+#: programs can control all aspects of kitty, including sending text
+#: to kitty windows, opening new windows, closing windows, reading the
+#: content of windows, etc. Note that this even works over ssh
+#: connections. You can chose to either allow any program running
+#: within kitty to control it, with yes or only programs that connect
+#: to the socket specified with the kitty --listen-on command line
+#: option, if you use the value socket-only. The latter is useful if
+#: you want to prevent programs running on a remote computer over ssh
+#: from controlling kitty.
+
+# listen_on none
+
+#: Tell kitty to listen to the specified unix/tcp socket for remote
+#: control connections. Note that this will apply to all kitty
+#: instances. It can be overridden by the kitty --listen-on command
+#: line flag. This option accepts only UNIX sockets, such as
+#: unix:${TEMP}/mykitty or (on Linux) unix:@mykitty. Environment
+#: variables are expanded. If {kitty_pid} is present then it is
+#: replaced by the PID of the kitty process, otherwise the PID of the
+#: kitty process is appended to the value, with a hyphen. This option
+#: is ignored unless you also set allow_remote_control to enable
+#: remote control. See the help for kitty --listen-on for more
+#: details.
+
+# env
+
+#: Specify environment variables to set in all child processes. Note
+#: that environment variables are expanded recursively, so if you
+#: use::
+
+#: env MYVAR1=a
+#: env MYVAR2=${MYVAR1}/${HOME}/b
+
+#: The value of MYVAR2 will be a/<path to home directory>/b.
+
+# update_check_interval 0.0
+
+#: Periodically check if an update to kitty is available. If an update
+#: is found a system notification is displayed informing you of the
+#: available update. The default is to check every 24 hrs, set to zero
+#: to disable.
+
+# startup_session none
+
+#: Path to a session file to use for all kitty instances. Can be
+#: overridden by using the kitty --session command line option for
+#: individual instances. See
+#: https://sw.kovidgoyal.net/kitty/index.html#sessions in the kitty
+#: documentation for details. Note that relative paths are interpreted
+#: with respect to the kitty config directory. Environment variables
+#: in the path are expanded.
+
+# clipboard_control write-clipboard write-primary
+
+#: Allow programs running in kitty to read and write from the
+#: clipboard. You can control exactly which actions are allowed. The
+#: set of possible actions is: write-clipboard read-clipboard write-
+#: primary read-primary. You can additionally specify no-append to
+#: disable kitty's protocol extension for clipboard concatenation. The
+#: default is to allow writing to the clipboard and primary selection
+#: with concatenation enabled. Note that enabling the read
+#: functionality is a security risk as it means that any program, even
+#: one running on a remote server via SSH can read your clipboard.
+
+# allow_hyperlinks yes
+
+#: Process hyperlink (OSC 8) escape sequences. If disabled OSC 8
+#: escape sequences are ignored. Otherwise they become clickable
+#: links, that you can click by holding down ctrl+shift and clicking
+#: with the mouse. The special value of ``ask`` means that kitty will
+#: ask before opening the link.
+
+# term xterm-kitty
+
+#: The value of the TERM environment variable to set. Changing this
+#: can break many terminal programs, only change it if you know what
+#: you are doing, not because you read some advice on Stack Overflow
+#: to change it. The TERM variable is used by various programs to get
+#: information about the capabilities and behavior of the terminal. If
+#: you change it, depending on what programs you run, and how
+#: different the terminal you are changing it to is, various things
+#: from key-presses, to colors, to various advanced features may not
+#: work.
+
+#: }}}
+
+#: OS specific tweaks {{{
+
+# wayland_titlebar_color system
+
+#: Change the color of the kitty window's titlebar on Wayland systems
+#: with client side window decorations such as GNOME. A value of
+#: system means to use the default system color, a value of background
+#: means to use the background color of the currently active window
+#: and finally you can use an arbitrary color, such as #12af59 or red.
+
+# macos_titlebar_color system
+
+#: Change the color of the kitty window's titlebar on macOS. A value
+#: of system means to use the default system color, a value of
+#: background means to use the background color of the currently
+#: active window and finally you can use an arbitrary color, such as
+#: #12af59 or red. WARNING: This option works by using a hack, as
+#: there is no proper Cocoa API for it. It sets the background color
+#: of the entire window and makes the titlebar transparent. As such it
+#: is incompatible with background_opacity. If you want to use both,
+#: you are probably better off just hiding the titlebar with
+#: hide_window_decorations.
+
+# macos_option_as_alt no
+
+#: Use the option key as an alt key. With this set to no, kitty will
+#: use the macOS native Option+Key = unicode character behavior. This
+#: will break any Alt+key keyboard shortcuts in your terminal
+#: programs, but you can use the macOS unicode input technique. You
+#: can use the values: left, right, or both to use only the left,
+#: right or both Option keys as Alt, instead.
+
+# macos_hide_from_tasks no
+
+#: Hide the kitty window from running tasks (⌘+Tab) on macOS.
+
+# macos_quit_when_last_window_closed no
+
+#: Have kitty quit when all the top-level windows are closed. By
+#: default, kitty will stay running, even with no open windows, as is
+#: the expected behavior on macOS.
+
+# macos_window_resizable yes
+
+#: Disable this if you want kitty top-level (OS) windows to not be
+#: resizable on macOS.
+
+# macos_thicken_font 0
+
+#: Draw an extra border around the font with the given width, to
+#: increase legibility at small font sizes. For example, a value of
+#: 0.75 will result in rendering that looks similar to sub-pixel
+#: antialiasing at common font sizes.
+
+# macos_traditional_fullscreen no
+
+#: Use the traditional full-screen transition, that is faster, but
+#: less pretty.
+
+# macos_show_window_title_in all
+
+#: Show or hide the window title in the macOS window or menu-bar. A
+#: value of window will show the title of the currently active window
+#: at the top of the macOS window. A value of menubar will show the
+#: title of the currently active window in the macOS menu-bar, making
+#: use of otherwise wasted space. all will show the title everywhere
+#: and none hides the title in the window and the menu-bar.
+
+# macos_custom_beam_cursor no
+
+#: Enable/disable custom mouse cursor for macOS that is easier to see
+#: on both light and dark backgrounds. WARNING: this might make your
+#: mouse cursor invisible on dual GPU machines.
+
+# linux_display_server auto
+
+#: Choose between Wayland and X11 backends. By default, an appropriate
+#: backend based on the system state is chosen automatically. Set it
+#: to x11 or wayland to force the choice.
+
+#: }}}
+
+#: Keyboard shortcuts {{{
+
+#: Keys are identified simply by their lowercase unicode characters.
+#: For example: ``a`` for the A key, ``[`` for the left square bracket
+#: key, etc. For functional keys, such as ``Enter or Escape`` the
+#: names are present at https://sw.kovidgoyal.net/kitty/keyboard-
+#: protocol.html#functional-key-definitions. For a list of modifier
+#: names, see: GLFW mods
+#: <https://www.glfw.org/docs/latest/group__mods.html>
+
+#: On Linux you can also use XKB key names to bind keys that are not
+#: supported by GLFW. See XKB keys
+#: <https://github.com/xkbcommon/libxkbcommon/blob/master/xkbcommon/xkbcommon-
+#: keysyms.h> for a list of key names. The name to use is the part
+#: after the XKB_KEY_ prefix. Note that you can only use an XKB key
+#: name for keys that are not known as GLFW keys.
+
+#: Finally, you can use raw system key codes to map keys, again only
+#: for keys that are not known as GLFW keys. To see the system key
+#: code for a key, start kitty with the kitty --debug-keyboard option.
+#: Then kitty will output some debug text for every key event. In that
+#: text look for ``native_code`` the value of that becomes the key
+#: name in the shortcut. For example:
+
+#: .. code-block:: none
+
+#: on_key_input: glfw key: 65 native_code: 0x61 action: PRESS mods: 0x0 text: 'a'
+
+#: Here, the key name for the A key is 0x61 and you can use it with::
+
+#: map ctrl+0x61 something
+
+#: to map ctrl+a to something.
+
+#: You can use the special action no_op to unmap a keyboard shortcut
+#: that is assigned in the default configuration::
+
+#: map kitty_mod+space no_op
+
+#: You can combine multiple actions to be triggered by a single
+#: shortcut, using the syntax below::
+
+#: map key combine <separator> action1 <separator> action2 <separator> action3 ...
+
+#: For example::
+
+#: map kitty_mod+e combine : new_window : next_layout
+
+#: this will create a new window and switch to the next available
+#: layout
+
+#: You can use multi-key shortcuts using the syntax shown below::
+
+#: map key1>key2>key3 action
+
+#: For example::
+
+#: map ctrl+f>2 set_font_size 20
+
+# kitty_mod ctrl+shift
+
+#: The value of kitty_mod is used as the modifier for all default
+#: shortcuts, you can change it in your kitty.conf to change the
+#: modifiers for all the default shortcuts.
+
+# clear_all_shortcuts no
+
+#: You can have kitty remove all shortcut definition seen up to this
+#: point. Useful, for instance, to remove the default shortcuts.
+
+# kitten_alias hints hints --hints-offset=0
+
+#: You can create aliases for kitten names, this allows overriding the
+#: defaults for kitten options and can also be used to shorten
+#: repeated mappings of the same kitten with a specific group of
+#: options. For example, the above alias changes the default value of
+#: kitty +kitten hints --hints-offset to zero for all mappings,
+#: including the builtin ones.
+
+#: Clipboard {{{
+
+# map kitty_mod+c copy_to_clipboard
+
+#: There is also a copy_or_interrupt action that can be optionally
+#: mapped to Ctrl+c. It will copy only if there is a selection and
+#: send an interrupt otherwise. Similarly, copy_and_clear_or_interrupt
+#: will copy and clear the selection or send an interrupt if there is
+#: no selection.
+
+# map kitty_mod+v paste_from_clipboard
+# map kitty_mod+s paste_from_selection
+# map shift+insert paste_from_selection
+# map kitty_mod+o pass_selection_to_program
+
+#: You can also pass the contents of the current selection to any
+#: program using pass_selection_to_program. By default, the system's
+#: open program is used, but you can specify your own, the selection
+#: will be passed as a command line argument to the program, for
+#: example::
+
+#: map kitty_mod+o pass_selection_to_program firefox
+
+#: You can pass the current selection to a terminal program running in
+#: a new kitty window, by using the @selection placeholder::
+
+#: map kitty_mod+y new_window less @selection
+
+#: }}}
+
+#: Scrolling {{{
+
+# map kitty_mod+up scroll_line_up
+# map kitty_mod+k scroll_line_up
+# map kitty_mod+down scroll_line_down
+# map kitty_mod+j scroll_line_down
+map shift+page_up scroll_page_up
+map shift+page_down scroll_page_down
+map shift+home scroll_home
+map shift+end scroll_end
+# map kitty_mod+h show_scrollback
+
+#: You can pipe the contents of the current screen + history buffer as
+#: STDIN to an arbitrary program using the ``launch`` function. For
+#: example, the following opens the scrollback buffer in less in an
+#: overlay window::
+
+#: map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting --type=overlay less +G -R
+
+#: For more details on piping screen and buffer contents to external
+#: programs, see launch.
+
+#: }}}
+
+#: Window management {{{
+
+# map kitty_mod+enter new_window
+
+#: You can open a new window running an arbitrary program, for
+#: example::
+
+#: map kitty_mod+y launch mutt
+
+#: You can open a new window with the current working directory set to
+#: the working directory of the current window using::
+
+#: map ctrl+alt+enter launch --cwd=current
+
+#: You can open a new window that is allowed to control kitty via the
+#: kitty remote control facility by prefixing the command line with @.
+#: Any programs running in that window will be allowed to control
+#: kitty. For example::
+
+#: map ctrl+enter launch --allow-remote-control some_program
+
+#: You can open a new window next to the currently active window or as
+#: the first window, with::
+
+#: map ctrl+n launch --location=neighbor some_program
+#: map ctrl+f launch --location=first some_program
+
+#: For more details, see launch.
+
+# map kitty_mod+n new_os_window
+
+#: Works like new_window above, except that it opens a top level OS
+#: kitty window. In particular you can use new_os_window_with_cwd to
+#: open a window with the current working directory.
+
+# map kitty_mod+w close_window
+# map kitty_mod+] next_window
+# map kitty_mod+[ previous_window
+# map kitty_mod+f move_window_forward
+# map kitty_mod+b move_window_backward
+# map kitty_mod+` move_window_to_top
+# map kitty_mod+r start_resizing_window
+# map kitty_mod+1 first_window
+# map kitty_mod+2 second_window
+# map kitty_mod+3 third_window
+# map kitty_mod+4 fourth_window
+# map kitty_mod+5 fifth_window
+# map kitty_mod+6 sixth_window
+# map kitty_mod+7 seventh_window
+# map kitty_mod+8 eighth_window
+# map kitty_mod+9 ninth_window
+# map kitty_mod+0 tenth_window
+#: }}}
+
+#: Tab management {{{
+
+# map kitty_mod+right next_tab
+# map kitty_mod+left previous_tab
+# map kitty_mod+t new_tab
+# map kitty_mod+q close_tab
+# map kitty_mod+. move_tab_forward
+# map kitty_mod+, move_tab_backward
+# map kitty_mod+alt+t set_tab_title
+
+#: You can also create shortcuts to go to specific tabs, with 1 being
+#: the first tab, 2 the second tab and -1 being the previously active
+#: tab, and any number larger than the last tab being the last tab::
+
+#: map ctrl+alt+1 goto_tab 1
+#: map ctrl+alt+2 goto_tab 2
+
+#: Just as with new_window above, you can also pass the name of
+#: arbitrary commands to run when using new_tab and use
+#: new_tab_with_cwd. Finally, if you want the new tab to open next to
+#: the current tab rather than at the end of the tabs list, use::
+
+#: map ctrl+t new_tab !neighbor [optional cmd to run]
+#: }}}
+
+#: Layout management {{{
+
+# map kitty_mod+l next_layout
+
+#: You can also create shortcuts to switch to specific layouts::
+
+#: map ctrl+alt+t goto_layout tall
+#: map ctrl+alt+s goto_layout stack
+
+#: Similarly, to switch back to the previous layout::
+
+#: map ctrl+alt+p last_used_layout
+#: }}}
+
+#: Font sizes {{{
+
+#: You can change the font size for all top-level kitty OS windows at
+#: a time or only the current one.
+
+# map kitty_mod+equal change_font_size all +2.0
+# map kitty_mod+plus change_font_size all +2.0
+# map kitty_mod+kp_add change_font_size all +2.0
+# map kitty_mod+minus change_font_size all -2.0
+# map kitty_mod+kp_subtract change_font_size all -2.0
+# map kitty_mod+backspace change_font_size all 0
+
+#: To setup shortcuts for specific font sizes::
+
+#: map kitty_mod+f6 change_font_size all 10.0
+
+#: To setup shortcuts to change only the current OS window's font
+#: size::
+
+#: map kitty_mod+f6 change_font_size current 10.0
+#: }}}
+
+#: Select and act on visible text {{{
+
+#: Use the hints kitten to select text and either pass it to an
+#: external program or insert it into the terminal or copy it to the
+#: clipboard.
+
+# map kitty_mod+e kitten hints
+
+#: Open a currently visible URL using the keyboard. The program used
+#: to open the URL is specified in open_url_with.
+
+# map kitty_mod+p>f kitten hints --type path --program -
+
+#: Select a path/filename and insert it into the terminal. Useful, for
+#: instance to run git commands on a filename output from a previous
+#: git command.
+
+# map kitty_mod+p>shift+f kitten hints --type path
+
+#: Select a path/filename and open it with the default open program.
+
+# map kitty_mod+p>l kitten hints --type line --program -
+
+#: Select a line of text and insert it into the terminal. Use for the
+#: output of things like: ls -1
+
+# map kitty_mod+p>w kitten hints --type word --program -
+
+#: Select words and insert into terminal.
+
+# map kitty_mod+p>h kitten hints --type hash --program -
+
+#: Select something that looks like a hash and insert it into the
+#: terminal. Useful with git, which uses sha1 hashes to identify
+#: commits
+
+# map kitty_mod+p>n kitten hints --type linenum
+
+#: Select something that looks like filename:linenum and open it in
+#: vim at the specified line number.
+
+# map kitty_mod+p>y kitten hints --type hyperlink
+
+#: Select a hyperlink (i.e. a URL that has been marked as such by the
+#: terminal program, for example, by ls --hyperlink=auto).
+
+
+#: The hints kitten has many more modes of operation that you can map
+#: to different shortcuts. For a full description see kittens/hints.
+#: }}}
+
+#: Miscellaneous {{{
+
+# map kitty_mod+f11 toggle_fullscreen
+# map kitty_mod+f10 toggle_maximized
+# map kitty_mod+u kitten unicode_input
+# map kitty_mod+f2 edit_config_file
+# map kitty_mod+escape kitty_shell window
+
+#: Open the kitty shell in a new window/tab/overlay/os_window to
+#: control kitty using commands.
+
+# map kitty_mod+a>m set_background_opacity +0.1
+# map kitty_mod+a>l set_background_opacity -0.1
+# map kitty_mod+a>1 set_background_opacity 1
+# map kitty_mod+a>d set_background_opacity default
+# map kitty_mod+delete clear_terminal reset active
+
+#: You can create shortcuts to clear/reset the terminal. For example::
+
+#: # Reset the terminal
+#: map kitty_mod+f9 clear_terminal reset active
+#: # Clear the terminal screen by erasing all contents
+#: map kitty_mod+f10 clear_terminal clear active
+#: # Clear the terminal scrollback by erasing it
+#: map kitty_mod+f11 clear_terminal scrollback active
+#: # Scroll the contents of the screen into the scrollback
+#: map kitty_mod+f12 clear_terminal scroll active
+
+#: If you want to operate on all windows instead of just the current
+#: one, use all instead of active.
+
+#: It is also possible to remap Ctrl+L to both scroll the current
+#: screen contents into the scrollback buffer and clear the screen,
+#: instead of just clearing the screen::
+
+#: map ctrl+l combine : clear_terminal scroll active : send_text normal,application \x0c
+
+
+#: You can tell kitty to send arbitrary (UTF-8) encoded text to the
+#: client program when pressing specified shortcut keys. For example::
+
+#: map ctrl+alt+a send_text all Special text
+
+#: This will send "Special text" when you press the ctrl+alt+a key
+#: combination. The text to be sent is a python string literal so you
+#: can use escapes like \x1b to send control codes or \u21fb to send
+#: unicode characters (or you can just input the unicode characters
+#: directly as UTF-8 text). The first argument to send_text is the
+#: keyboard modes in which to activate the shortcut. The possible
+#: values are normal or application or kitty or a comma separated
+#: combination of them. The special keyword all means all modes. The
+#: modes normal and application refer to the DECCKM cursor key mode
+#: for terminals, and kitty refers to the special kitty extended
+#: keyboard protocol.
+
+#: Another example, that outputs a word and then moves the cursor to
+#: the start of the line (same as pressing the Home key)::
+
+#: map ctrl+alt+a send_text normal Word\x1b[H
+#: map ctrl+alt+a send_text application Word\x1bOH
+
+#: }}}
+
+# }}}
diff --git a/tw/home/files/lesskey b/tw/home/files/lesskey
new file mode 100644
index 00000000..8793a77c
--- /dev/null
+++ b/tw/home/files/lesskey
@@ -0,0 +1,33 @@
+# -*- mode: conf-space -*-
+# No compilation is necessary any more in recent less versions.
+
+#command
+{ prev-file
+} next-file
+N reverse-search
+n repeat-search
+^h back-screen
+
+#env
+LESS=--ignore-case --quit-if-one-screen --save-marks --no-histdups --RAW-CONTROL-CHARS --LONG-PROMPT --chop-long-lines
+LESSOPEN=|src-hilite-lesspipe.sh %s
+LESSHISTFILE=$XDG_DATA_HOME/less/history
+LESSHISTSIZE=10000
+# *? setting is shared between LESS{,UTF}BINFMT, with the latter's setting taking priority
+LESSBINFMT=*k%02X
+LESSUTFBINFMT=U+%04lX
+# Use pretty colours for bold/underline/... -- mostly in `man'
+# https://wiki.archlinux.org/index.php/Color_output_in_console#less
+# Note: these values are verbatim as less doesn't support escaping
+# characters in lesskey (I think).
+# blink -> italic white-on-red
+LESS_TERMCAP_mb=
+# bold -> bold blue
+LESS_TERMCAP_md=
+LESS_TERMCAP_me=
+# reverse video -> reverse, bright yellow background
+LESS_TERMCAP_so=
+LESS_TERMCAP_se=
+# underline -> italic green
+LESS_TERMCAP_us=
+LESS_TERMCAP_ue=
diff --git a/tw/home/files/mailcap b/tw/home/files/mailcap
new file mode 100644
index 00000000..904ce065
--- /dev/null
+++ b/tw/home/files/mailcap
@@ -0,0 +1,10 @@
+# mailcap -*- conf-space -*-
+# When using `auto_view <mimetype>` in muttrc, a command for that MIME type is
+# chosen, only considering those with copiousoutput set. Its output is displayed
+# in place of the attachment directly in the message view.
+
+text/html; lynx -display_charset=UTF-8 -dump %s; copiousoutput; nametemplate=%s.html
+text/calendar; khal printics %s; copiousoutput
+image/*; kitty +kitten icat %s; copiousoutput
+audio/*; mpv --no-video %s
+application/pdf; zathura %s
diff --git a/tw/home/files/muttrc b/tw/home/files/muttrc
new file mode 100644
index 00000000..470b1f2b
--- /dev/null
+++ b/tw/home/files/muttrc
@@ -0,0 +1,173 @@
+# -*- mode: conf-space; -*-
+# Colour scheme
+source `echo "\$XDG_CONFIG_HOME/mutt/catppuccin.muttrc"`
+color index color2 default ~O # unread, but not new, messages are green, like new messages
+
+# Vim-like keybindings
+source ~/.guix-home/profile/share/doc/neomutt/vim-keys/vim-keys.rc
+# I have a Return key, not an Enter key, and I want to use it to display the
+# message, not reply to it.
+bind index '<Return>' display-message
+bind index '<Enter>' list-reply
+# group-chat-reply = reply to all recipients preserving To/Cc.
+bind index a group-chat-reply
+bind pager a group-chat-reply
+
+set abort_nosubject = ask-yes
+set abort_unmodified = ask-yes
+set auto_edit edit_headers fast_reply reverse_name noreverse_real_name
+set sig_dashes sig_on_top
+set pager_stop pager_index_lines=10 pager_context=2 pager_skip_quoted_context=3
+
+set allow_ansi # needed for catimg and the like
+unset help # hide keybind hints in top row
+# Don't ask for confirmation.
+set quit
+unset confirmappend
+
+# Use khard for contact info.
+set query_command = "khard email --parsable"
+
+set sort = "reverse-date" # newest first, threads grouped
+set use_threads = "threads"
+unset mark_old # always keep unread messages as "new"
+set tilde nomarkers
+set sleep_time = 0 # no delay when switching mailboxes to display info
+set auto_tag # commands automatically apply to tagged messages (if any), without having to press ";"
+set delete # auto-purge deleted messages when syncing
+
+# show IMAP mailboxes in the sidebar
+set imap_check_subscribed
+
+set sidebar_visible sidebar_format = "%B%* %?F? %F!?%?N? %N?"
+set sidebar_folder_indent sidebar_short_path
+
+set index_format = "%4C %Z %[%_d %b %H:%M] %-15.15L %s%* %?l?%4l&%4c?"
+set status_format = " %r %f (%?M?%M/?%m#%?n? %nN?%?o? %oO?%?d? %dD?%?F? %FF?%?t? %t*?%?p? %pP?%?b? %bE?%?l? %l?) %> %s/%S %P "
+set sidebar_format = "%D%* %!%?N? %N?"
+set attach_format = "%u%D%I %t%4n %T%.40d%> %.9m/%.12M, %.6e%?C?, %C?, %4s "
+
+set mail_check_stats
+set pgp_decrypt_command = "gpg --status-fd=2 %?p?--passphrase-fd 0? --no-verbose --quiet --batch --output - %f"
+set smime_encrypt_self smime_default_key = 681F69B0
+unset arrow_cursor
+set ssl_force_tls
+
+set new_mail_command = "if [ %u -gt 0 ]; then dunstify -a neomutt -i mail-unread 'New mail' 'You have %u unread messages.'; fi"
+
+set history = 1000
+set history_file = `echo "\$XDG_DATA_HOME/neomutt/history"`
+set save_history = 1000
+
+# The $XDG_DATA_HOME/neomutt directory must exist, else neomutt will
+# silently fail to save the certificate file.
+set certificate_file = `echo "\$XDG_DATA_HOME/neomutt/certificates"`
+set debug_file = `echo "\${XDG_LOG_HOME:-$XDG_DATA_HOME/neomutt}/neomutt-debug.log"`
+
+set smime_certificates = `echo "\$XDG_DATA_HOME/neomutt/smime/certificates"`
+set smime_keys = `echo "\$XDG_DATA_HOME/neomutt/smime/keys"`
+
+# For using both, header and body caching, $header_cache and
+# $message_cachedir can be safely set to the same value.
+set message_cachedir = `echo "\$XDG_CACHE_HOME/neomutt/"`
+set header_cache = `echo "\$XDG_CACHE_HOME/neomutt/"`
+
+# I don't care about these headers; hide them by default.
+ignore x-mailer user-agent
+
+alternative_order text/plain text/enriched text/html text image
+auto_view text/html text/calendar image/jpeg image/png
+
+set my_mythic = "imaps://oncilla.mythic-beasts.com/"
+account-hook "$my_mythic" 'unmailboxes *'
+account-hook "$my_mythic" 'set folder = "$my_mythic"'
+account-hook "$my_mythic" 'set smtp_url = "smtps://smtp-auth.mythic-beasts.com"'
+account-hook "$my_mythic" 'set imap_user = "timo@twilken.net"'
+account-hook "$my_mythic" 'set smtp_user = "timo@twilken.net"'
+account-hook "$my_mythic" 'set imap_pass = `pass www/mythic-beasts/email/timo | head -1`'
+account-hook "$my_mythic" 'set smtp_pass = `pass www/mythic-beasts/email/timo | head -1`'
+account-hook "$my_mythic" 'set spool_file = +INBOX'
+account-hook "$my_mythic" 'set postponed = +Drafts'
+account-hook "$my_mythic" 'set record = +Sent'
+account-hook "$my_mythic" 'set trash = +Rubbish'
+account-hook "$my_mythic" 'set from = "$imap_user"'
+account-hook "$my_mythic" 'reset ssl_use_tlsv1 ssl_use_tlsv1_1 smtp_authenticators imap_authenticators imap_oauth_refresh_command'
+
+set my_cern = "imaps://imap.cern.ch/"
+account-hook "$my_cern" 'unmailboxes *'
+account-hook "$my_cern" 'set folder = "$my_cern"'
+account-hook "$my_cern" 'set smtp_url = "smtp://smtp.cern.ch:587"' # we need to rely on STARTTLS
+account-hook "$my_cern" 'set imap_user = "twilken"'
+account-hook "$my_cern" 'set smtp_user = "twilken"'
+account-hook "$my_cern" 'set imap_pass = `pass cern/sso | head -1`'
+account-hook "$my_cern" 'set smtp_pass = `pass cern/sso | head -1`'
+account-hook "$my_cern" 'set smtp_authenticators = "login"' # only LOGIN supported, not even PLAIN
+account-hook "$my_cern" "alternates '^timo\.wilken@cern\.ch$' '^twilken@cern\.ch$'"
+account-hook "$my_cern" 'set spool_file = +INBOX'
+account-hook "$my_cern" 'set postponed = +Drafts'
+account-hook "$my_cern" 'set record = "+Sent Items"'
+account-hook "$my_cern" 'set trash = "+Deleted Items"'
+account-hook "$my_cern" 'set from = "$imap_user"'
+account-hook "$my_cern" 'set ssl_use_tlsv1 ssl_use_tlsv1_1' # imap.cern.ch needs this; make sure to reset for other accounts!
+account-hook "$my_cern" 'reset imap_authenticators imap_oauth_refresh_command'
+
+set my_gmail = "imaps://imap.gmail.com/"
+account-hook "$my_gmail" 'unmailboxes *'
+account-hook "$my_gmail" 'set folder = "$my_gmail"'
+account-hook "$my_gmail" 'set smtp_url = "smtps://smtp.gmail.com"'
+account-hook "$my_gmail" 'set imap_user = "timo.21.wilken@gmail.com"'
+account-hook "$my_gmail" 'set smtp_user = "timo.21.wilken@gmail.com"'
+account-hook "$my_gmail" 'set imap_pass = `pass www/google/app-passwords/mutt | head -1`'
+account-hook "$my_gmail" 'set smtp_pass = `pass www/google/app-passwords/mutt | head -1`'
+account-hook "$my_gmail" 'set spool_file = +INBOX'
+account-hook "$my_gmail" 'set postponed = "+[Gmail]/Drafts"'
+account-hook "$my_gmail" 'set record = "+[Gmail]/Sent Mail"'
+account-hook "$my_gmail" 'set trash = "+[Gmail]/Bin"'
+account-hook "$my_gmail" 'set from = "$imap_user"'
+account-hook "$my_gmail" 'reset ssl_use_tlsv1 ssl_use_tlsv1_1 smtp_authenticators imap_authenticators imap_oauth_refresh_command'
+
+# ExOl needs OAUTH now!
+# https://neomutt.org/guide/optionalfeatures.html#oauth
+set my_cantab = "imaps://tw466@cantab.ac.uk@outlook.office365.com/"
+account-hook "$my_cantab" 'unmailboxes *'
+account-hook "$my_cantab" 'set folder = "$my_cantab"'
+account-hook "$my_cantab" 'set smtp_url = "smtp://smtp-mail.outlook.com:587"'
+account-hook "$my_cantab" 'set imap_user = "tw466@cantab.ac.uk"'
+account-hook "$my_cantab" 'set smtp_user = "tw466@cantab.ac.uk"'
+account-hook "$my_cantab" 'set imap_pass = `pass cambridge/raven | head -1`'
+account-hook "$my_cantab" 'set smtp_pass = `pass cambridge/raven | head -1`'
+account-hook "$my_cantab" 'set spool_file = +INBOX'
+account-hook "$my_cantab" 'set postponed = +Drafts'
+account-hook "$my_cantab" 'set record = "+Sent Items"'
+account-hook "$my_cantab" 'set trash = "+Deleted Items"'
+account-hook "$my_cantab" 'set from = "$imap_user"'
+account-hook "$my_cantab" 'reset ssl_use_tlsv1 ssl_use_tlsv1_1 smtp_authenticators'
+account-hook "$my_cantab" 'set imap_authenticators = "xoauth2"'
+account-hook "$my_cantab" 'set imap_oauth_refresh_command = "mutt_oauth2.py --encryption-pipe=\"gpg --encrypt --recipient timo@twilken.net\" $XDG_DATA_HOME/neomutt/cantab.gpg"'
+
+set my_outlook = "imaps://timo_wilken@live.co.uk@outlook.office365.com/"
+account-hook "$my_outlook" 'unmailboxes *'
+account-hook "$my_outlook" 'set folder = "$my_outlook"'
+account-hook "$my_outlook" 'set smtp_url = "smtp://smtp-mail.outlook.com:587"'
+account-hook "$my_outlook" 'set imap_user = "timo_wilken@live.co.uk"'
+account-hook "$my_outlook" 'set smtp_user = "timo_wilken@live.co.uk"'
+account-hook "$my_outlook" 'set imap_pass = `pass www/microsoft | head -1`'
+account-hook "$my_outlook" 'set smtp_pass = `pass www/microsoft | head -1`'
+account-hook "$my_outlook" 'set spool_file = +INBOX'
+account-hook "$my_outlook" 'set postponed = +Drafts'
+account-hook "$my_outlook" 'set record = +Sent'
+account-hook "$my_outlook" 'set trash = +Deleted'
+account-hook "$my_outlook" 'set from = "$imap_user"'
+account-hook "$my_outlook" 'reset ssl_use_tlsv1 ssl_use_tlsv1_1 smtp_authenticators'
+account-hook "$my_outlook" 'set imap_authenticators = "xoauth2"'
+account-hook "$my_outlook" 'set imap_oauth_refresh_command = "mutt_oauth2.py --encryption-pipe=\"gpg --encrypt --recipient timo@twilken.net\" $XDG_DATA_HOME/neomutt/outlook.gpg"'
+
+macro index,pager gm "<sync-mailbox><enter-command>set folder=$my_mythic<enter><change-folder>+INBOX<enter>" "Switch to Mythic Beasts account"
+macro index,pager gG "<sync-mailbox><enter-command>set folder=$my_gmail<enter><change-folder>+INBOX<enter>" "Switch to Gmail account"
+macro index,pager gw "<sync-mailbox><enter-command>set folder=$my_cern<enter><change-folder>+INBOX<enter>" "Switch to CERN account"
+macro index,pager gc "<sync-mailbox><enter-command>set folder=$my_cantab<enter><change-folder>+INBOX<enter>" "Switch to Cantab account"
+macro index,pager go "<sync-mailbox><enter-command>set folder=$my_outlook<enter><change-folder>+INBOX<enter>" "Switch to Outlook account"
+
+# Open the Mythic inbox by default.
+set folder = "$my_mythic"
+set spool_file = "+INBOX"
diff --git a/tw/home/files/neomutt b/tw/home/files/neomutt
new file mode 160000
+Subproject 34816020295ace8f4b94a4da4c89367354457f4
diff --git a/tw/home/files/newsboat.conf b/tw/home/files/newsboat.conf
new file mode 100644
index 00000000..7959e138
--- /dev/null
+++ b/tw/home/files/newsboat.conf
@@ -0,0 +1,29 @@
+# newsboat config -*- conf-space -*-
+# https://newsboat.org/releases/2.29/docs/newsboat.html#_newsboat_configuration_commands
+# Vim-like keys.
+bind-key j down
+bind-key k up
+bind-key J next-feed articlelist
+bind-key K prev-feed articlelist
+
+# Reload feeds on startup, in parallel.
+auto-reload yes
+reload-threads 100
+
+text-width 72 # wrap rendered HTML at 72 cols (or terminal width, if smaller)
+scrolloff 2 # show 2 lines above/below selected line
+show-keymap-hint no
+show-title-bar yes
+datetime-format "%e %b %Y" # e.g. " 9 Dec 2022"
+notify-program "dunstify -a newsboat -i newsboat Newsboat"
+notify-format "%d new articles (%n unread articles, %f unread feeds)"
+
+# Sync with Nextcloud News.
+urls-source "ocnews"
+ocnews-url "https://cloud.wilkenfamily.de/"
+ocnews-login "timo"
+ocnews-passwordeval "pass www/nextcloud/timo | head -1"
+ocnews-flag-star "s"
+
+# Catppuccin theme.
+include ~/.config/newsboat/config.catppuccin
diff --git a/tw/home/files/passmenu b/tw/home/files/passmenu
new file mode 100755
index 00000000..abeb98fb
--- /dev/null
+++ b/tw/home/files/passmenu
@@ -0,0 +1,91 @@
+#!/bin/sh -e
+
+usage() {
+ cat << EOF
+$(basename "$0") [-c | -p | -a]
+
+ -c, --clip copy the user-selected password to the clipboard
+ -p, --type-pass auto-type the user-selected password only
+ -a, --type-all auto-type the user-selected username <tab> password
+ -h, --help show this help message and exit
+ -v, --version show the program version number and exit
+
+Later options override conflicting earlier ones.
+If no option is given, -c/--clip is the default.
+GNU-style combination of short options (e.g. -pa) is not supported.
+
+Abnormal exit codes:
+ 1 no password selected by user
+ 2 invalid command-line argument
+ 3 internal error
+
+(C) 2019-2022 Timo Wilken; MIT Licence.
+Adapted from https://git.zx2c4.com/password-store/tree/contrib/dmenu/passmenu.
+EOF
+}
+
+version() {
+ # Changelog:
+ # 1.0.0 (2019-??-??) initial version
+ # 1.0.1 (2022-07-17) script formatting; throw away passwords asap
+ # 1.0.2 (2022-10-??) remove fix_xdotool; this should be done on login
+ echo "$(basename "$0") 1.0.2"
+}
+
+password_names() {
+ find "${PASSWORD_STORE_DIR-$HOME/.password-store}" -name '*.gpg' -type f \
+ -printf '%P\n' | sed 's/\.gpg$//'
+}
+
+extract_key() {
+ sed -rn "s/^$1:\\s+(.+)\$/\\1/p"
+}
+
+type_stdin() {
+ tr -d '\n' | xdotool getactivewindow type --clearmodifiers --file -
+}
+
+## Command-line arguments
+mode=clip
+while [ $# -gt 0 ]; do
+ case "$1" in
+ -o|--type-otp) mode=type-otp; shift;;
+ -p|--type-pass) mode=type-pass; shift;;
+ -a|--type-all) mode=type-all; shift;;
+ -c|--clip) mode=clip; shift;;
+ -h|--help) usage; exit;;
+ -v|--version) version; exit;;
+
+ *)
+ echo "$(basename "$0"): unknown option: $1" >&2
+ usage >&2
+ exit 2;;
+ esac
+done
+
+## Password selection menu
+password_name=$(password_names | rofi -dmenu -i -p Password -no-custom)
+[ -n "$password_name" ] || exit 1
+
+## Password typing
+case "$mode" in
+ clip)
+ # Suppress "copied ... to clipboard" notice on stdout.
+ pass show --clip "$password_name" > /dev/null;;
+
+ type-otp)
+ pass otp "$password_name" | type_stdin;;
+
+ type-*)
+ entry=$(pass show "$password_name")
+ if [ "$mode" = type-all ]; then
+ echo "$entry" | extract_key username | type_stdin
+ xdotool getactivewindow key Tab
+ fi
+ echo "$entry" | head -1 | type_stdin
+ unset entry;;
+
+ *)
+ echo "$(basename "$0"): internal error: unknown mode: $mode" >&2
+ exit 3;;
+esac
diff --git a/tw/home/files/picom.conf b/tw/home/files/picom.conf
new file mode 100644
index 00000000..a7d86571
--- /dev/null
+++ b/tw/home/files/picom.conf
@@ -0,0 +1,304 @@
+#
+# ~/.config/picom.conf -- Picom compositor configuration
+#
+
+## Backend
+
+# Backend to use: "xrender" or "glx".
+# GLX backend is typically much faster but depends on a sane driver.
+backend: "glx";
+
+## GLX backend
+
+glx-no-stencil: true;
+
+# GLX backend: Copy unmodified regions from front buffer instead of redrawing
+# them all. My tests with nvidia-drivers show a 10% decrease in performance
+# when the whole screen is modified, but a 20% increase when only 1/4 is. My
+# tests on nouveau show terrible slowdown.
+glx-copy-from-front: false;
+
+# GLX backend: Avoid rebinding pixmap on window damage. Probably could improve
+# performance on rapid window content changes, but is known to break things on
+# some drivers (LLVMpipe). Recommended if it works.
+glx-no-rebind-pixmap: true;
+
+## Shadows
+
+# Enabled client-side shadows on windows.
+shadow: false;
+# The blur radius for shadows. (default 12)
+shadow-radius: 25;
+# The left offset for shadows. (default -15)
+shadow-offset-x: -25;
+# The top offset for shadows. (default -15)
+shadow-offset-y: -25;
+# The translucency for shadows. (default .75)
+shadow-opacity: .3;
+
+# Set if you want different colour shadows
+shadow-red: 0.03;
+shadow-green: 0.03;
+shadow-blue: 0.04;
+
+# The shadow exclude options are helpful if you have shadows enabled. Due to
+# the way compton draws its shadows, certain applications will have visual
+# glitches (most applications are fine, only apps that do weird things with
+# xshapes or argb are affected). This list includes all the affected apps I
+# found in my testing. The "! name~=''" part excludes shadows on any "Unknown"
+# windows, this prevents a visual glitch with the XFWM alt tab switcher.
+# From "man picom", Format of Conditions: WM_CLASS = class_i, class_g
+shadow-exclude: [
+# "! name~=''",
+# "name = 'Notification'",
+# "name = 'Plank'",
+# "name = 'Docky'",
+# "name = 'Kupfer'",
+# "name = 'xfce4-notifyd'",
+# "name *= 'VLC'",
+ "name *= 'mpv'",
+ "class_g = 'ffplay'",
+# "name *= 'compton'",
+# "name *= 'Chromium'",
+# "name *= 'Chrome'",
+ "class_g ?= 'Firefox'",
+# "class_g = 'Conky'",
+ "_NET_WM_STATE@:32a *= '_NET_WM_STATE_STICKY'",
+ "_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'",
+ "_GTK_FRAME_EXTENTS@:c",
+ "class_g ?= 'ulauncher'",
+ "class_g ?= 'tray'",
+ "class_g ?= 'i3-frame'",
+# "class_g ?= 'Notify-osd'",
+# "class_g ?= 'Cairo-dock'",
+# "class_g ?= 'Xfce4-notifyd'",
+# "class_g ?= 'Xfce4-power-manager'"
+ "class_g ?= 'Xfce4-screenshooter'",
+ "class_g ?= 'peek'"
+];
+
+# Avoid drawing shadow on all shaped windows (c.f. --detect-rounded-corners)
+shadow-ignore-shaped: false;
+
+## Opacity
+
+inactive-opacity: 1;
+active-opacity: 1;
+frame-opacity: 1;
+inactive-opacity-override: false;
+
+# Dim inactive windows. (0.0 - 1.0)
+#inactive-dim = 0.1;
+# Do not let dimness adjust based on window opacity.
+#inactive-dim-fixed = true;
+
+# Blur background of transparent windows. Bad performance with X Render
+# backend. GLX backend is preferred.
+blur-method: "dual_kawase";
+blur-strength: 7;
+#blur-method: "kernel";
+# generate kernel using compton-convgen.py --dump-compton gaussian 15 -f sigma=15
+#blur-kernel: "...";
+blur-background: true;
+# Blur background of opaque windows with transparent frames as well.
+blur-background-frame: true;
+# Do not let blur radius adjust based on window opacity.
+blur-background-fixed: true;
+blur-background-exclude: [
+ #"window_type = 'dock'",
+ #"window_type = 'desktop'",
+ "window_type = 'dnd'",
+ "window_type = 'dropdown_menu'",
+ "window_type = 'popup_menu'",
+ #"class_g = 'Rofi'"
+ "class_g ?= 'Xfce4-screenshooter'",
+ # for xfce4-screenshooter triggered from its xfce4-panel button
+ "class_g ?= 'Wrapper-2.0'",
+ "class_g ?= 'peek'"
+];
+
+## Rounded corners
+#corner-radius: 16;
+#rounded-corners-exclude: [
+# "window_type = 'dock'",
+# "window_type = 'desktop'",
+# "class_g ?= 'plasmashell'",
+# "class_g ?= 'krunner'",
+# "class_g ?= 'i3-frame'"
+#];
+
+## Fading
+
+# Fade windows during opacity changes.
+fading: true;
+# The time between steps in a fade in milliseconds. (default 10).
+fade-delta: 5;
+# Opacity change between steps while fading in. (default 0.028).
+fade-in-step: 0.03;
+# Opacity change between steps while fading out. (default 0.03).
+fade-out-step: 0.03;
+# Fade windows in/out when opening/closing
+no-fading-openclose: false;
+
+# Specify a list of conditions of windows that should not be faded.
+fade-exclude: [];
+
+## Other
+
+# Try to detect WM windows and mark them as active.
+mark-wmwin-focused: true;
+# Mark all non-WM but override-redirect windows active (e.g. menus).
+mark-ovredir-focused: true;
+# Use EWMH _NET_WM_ACTIVE_WINDOW to determine which window is focused instead
+# of using FocusIn/Out events. Usually more reliable but depends on a
+# EWMH-compliant WM.
+use-ewmh-active-win: true;
+# Detect rounded corners and treat them as rectangular when
+# --shadow-ignore-shaped is on.
+detect-rounded-corners: true;
+
+# Detect _NET_WM_OPACITY on client windows, useful for window managers not
+# passing _NET_WM_OPACITY of client windows to frame windows. This prevents
+# opacity being ignored for some apps. For example without this enabled my
+# xfce4-notifyd is 100% opacity no matter what.
+detect-client-opacity: true;
+
+# Toggle VSync. See also: --vsync drm/opengl/opengl-oml.
+vsync: true;
+
+# Enable DBE painting mode, intended to use with VSync to (hopefully) eliminate
+# tearing. Reported to have no effect, though.
+dbe: true;
+
+# Unredirect all windows if a full-screen opaque window is detected, to
+# maximize performance for full-screen windows, like games. Known to cause
+# flickering when redirecting/unredirecting windows. paint-on-overlay may make
+# the flickering less obvious.
+unredir-if-possible: true;
+
+# Specify a list of conditions of windows that should always be considered focused.
+focus-exclude: [];
+
+# Use WM_TRANSIENT_FOR to group windows, and consider windows in the same group
+# focused at the same time.
+detect-transient: true;
+# Use WM_CLIENT_LEADER to group windows, and consider windows in the same group
+# focused at the same time. WM_TRANSIENT_FOR has higher priority if
+# --detect-transient is enabled, too.
+detect-client-leader: true;
+
+opacity-rule: [
+ "100:name *?= 'Call'",
+ "100:name *?= 'Conky'",
+ "100:class_g = 'Darktable'",
+ "50:class_g = 'Dmenu'",
+ "100:name *?= 'Event'",
+ "100:class_g = 'Firefox'",
+ "100:class_g = 'GIMP'",
+ "100:name *?= 'Image'",
+ "100:class_g = 'Lazpaint'",
+ "100:class_g = 'Midori'",
+ "100:name *?= 'Minitube'",
+ "83:class_g = 'Mousepad'",
+ "100:name *?= 'MuseScore'",
+ "90:name *?= 'Page Info'",
+ "100:name *?= 'Pale Moon'",
+ "100:name *?= 'Screenshot'",
+ "100:class_g = 'Viewnior'",
+ "100:name *?= 'VirtualBox'",
+ "100:name *?= 'VLC'",
+ "100:class_g = 'mpv'",
+ "100:name *?= 'Write'",
+ "0:_NET_WM_STATE@:32a *= '_NET_WM_STATE_HIDDEN'",
+ "100:_NET_WM_STATE@:32a *= '_NET_WM_STATE_STICKY'"
+];
+
+## Window type settings
+
+wintypes: {
+ # WINDOW_TYPE is one of the 15 window types defined in the EWMH standard:
+ # https://specifications.freedesktop.org/wm-spec/latest/ar01s05.html#idm45550357242464
+ # fade: Fade the particular type of windows.
+ # shadow: Give those windows shadow
+ # opacity: Default opacity for the type of windows.
+ # focus: Controls whether the window of this type is to be always considered
+ # focused. (By default, all window types except "normal" and "dialog" has
+ # this on.)
+ # full-shadow: Controls whether shadow is drawn under the parts of the
+ # window that you normally won't be able to see. Useful when the window
+ # has parts of it transparent, and you want shadows in those areas.
+ # redir-ignore: Controls whether this type of windows should cause screen to
+ # become redirected again after been unredirected. If you have
+ # --unredir-if-possible set, and doesn't want certain window to cause
+ # unnecessary screen redirection, you can set this to true.
+
+ normal: {
+ opacity: 1;
+ };
+
+ dialog: {
+ opacity: 1;
+ };
+
+ # On-screen notification
+ notify: {
+ opacity: 1;
+ };
+
+ tooltip: {
+ opacity: .85;
+ };
+
+ # Desktop window with icons etc.
+ desktop: {
+ };
+
+ # Persistent dock, usually on top of all other windows
+ dock: {
+ opacity: 1;
+ };
+
+ # Window representing something being dragged
+ dnd: {
+ opacity: .85;
+ shadow: true;
+ full-shadow: true;
+ };
+
+ # Menu "torn off" from another window
+ menu: {
+ opacity: 1;
+ };
+
+ # Toolbar "torn off" from another window
+ toolbar: {
+ opacity: 1;
+ };
+
+ # A menu from a right click
+ popup_menu: {
+ opacity: 1;
+ };
+
+ # A menu from a menu bar
+ dropdown_menu: {
+ opacity: 1;
+ };
+
+ # The pop-up/drop-down from a combo box
+ combo: {
+ opacity: 1;
+ };
+
+ # A starting application's splash screen
+ splash: {
+ opacity: 1;
+ };
+
+ # A small persistent utility window, such as a palette or toolbox
+ utility: {
+ };
+
+ unknown: {
+ };
+};
diff --git a/tw/home/files/polybar.ini b/tw/home/files/polybar.ini
new file mode 100644
index 00000000..4243cb95
--- /dev/null
+++ b/tw/home/files/polybar.ini
@@ -0,0 +1,235 @@
+; -*- mode: conf-windows; -*-
+
+[global/wm]
+include-file = catppuccin.ini
+
+[colors]
+background = ${colors.base}
+background-alt = ${colors.surface0}
+primary = ${colors.blue}
+alert = ${colors.yellow}
+disabled = ${colors.overlay2}
+empty-bar = ${colors.surface2}
+
+[settings]
+screenchange-reload = true
+
+[bar/primary]
+monitor = ${env:POLYBAR_MONITOR}
+width = 100%
+height = 24pt
+radius = 0
+line-size = 3pt
+line-color = ${colors.primary}
+border-size = 0
+padding-left = 0
+padding-right = 2
+module-margin = 2
+
+background = ${colors.background}
+foreground = ${colors.text}
+
+font-0 = Fira Sans:size=11;2
+; Some workspace names have emojis in them.
+font-1 = Openmoji:size=11;2
+; Hermit is needed for Unicode block chars.
+font-2 = Hermit:size=10;1
+
+separator = ·
+separator-foreground = ${colors.disabled}
+
+modules-left = i3 xwindow
+modules-right = memory cpu temp xkeyboard pulseaudio dunst wlan eth battery date
+tray-position = right
+
+enable-ipc = true
+
+cursor-click = pointer
+cursor-scroll = ns-resize
+
+[module/i3]
+type = internal/i3
+pin-workspaces = false
+show-urgent = true
+strip-wsnumbers = true
+index-sort = true
+label-mode-foreground = ${colors.alert}
+label-mode-background = ${colors.background-alt}
+label-mode-padding = 2
+label-focused-overline = ${colors.primary}
+label-focused-padding = 1
+label-urgent-foreground = ${colors.alert}
+label-urgent-background = ${colors.background-alt}
+label-urgent-padding = 1
+label-unfocused-padding = 1
+label-visible-padding = 1
+
+[module/xwindow]
+type = internal/xwindow
+label = %title%
+label-maxlen = 120
+
+[module/pulseaudio]
+type = internal/pulseaudio
+format-volume = <ramp-volume> <label-volume>
+label-volume = %percentage%%
+label-muted = 🔇
+label-muted-foreground = ${colors.disabled}
+
+ramp-volume-0 = 🔈
+ramp-volume-1 = 🔉
+ramp-volume-2 = 🔊
+ramp-volume-0-foreground = ${colors.primary}
+ramp-volume-1-foreground = ${colors.primary}
+ramp-volume-2-foreground = ${colors.primary}
+
+[module/xkeyboard]
+type = internal/xkeyboard
+blacklist-0 = num lock
+; hide xkeyboard module unless Caps Lock is pressed
+label-layout =
+label-indicator-padding = 1
+label-indicator-margin = 0
+label-indicator-foreground = ${colors.alert}
+label-indicator-background = ${colors.background-alt}
+
+[module/memory]
+type = internal/memory
+interval = 1
+format = <label> <ramp-used>
+format-prefix = "RAM "
+format-prefix-foreground = ${colors.primary}
+format-warn-prefix = "RAM "
+format-warn-prefix-foreground = ${colors.primary}
+label = %used%
+label-warn = %free% left
+label-warn-foreground = ${colors.alert}
+label-warn-background = ${colors.background-alt}
+
+ramp-used-0 = ▁
+ramp-used-1 = ▂
+ramp-used-2 = ▃
+ramp-used-3 = ▄
+ramp-used-4 = ▅
+ramp-used-5 = ▆
+ramp-used-6 = ▇
+ramp-used-7 = █
+ramp-used-0-foreground = ${colors.empty-bar}
+ramp-used-1-foreground = ${colors.primary}
+ramp-used-2-foreground = ${colors.primary}
+ramp-used-3-foreground = ${colors.primary}
+ramp-used-4-foreground = ${colors.primary}
+ramp-used-5-foreground = ${colors.alert}
+ramp-used-6-foreground = ${colors.alert}
+ramp-used-7-foreground = ${colors.alert}
+
+[module/cpu]
+type = internal/cpu
+interval = 1
+format = <ramp-coreload>
+format-prefix = "CPU "
+format-prefix-foreground = ${colors.primary}
+
+; Spacing (number of spaces, pixels, points) between individual per-core ramps
+ramp-coreload-spacing = 1
+ramp-coreload-0 = ▁
+ramp-coreload-1 = ▂
+ramp-coreload-2 = ▃
+ramp-coreload-3 = ▄
+ramp-coreload-4 = ▅
+ramp-coreload-5 = ▆
+ramp-coreload-6 = ▇
+ramp-coreload-7 = █
+ramp-coreload-0-foreground = ${colors.empty-bar}
+ramp-coreload-1-foreground = ${colors.primary}
+ramp-coreload-2-foreground = ${colors.primary}
+ramp-coreload-3-foreground = ${colors.primary}
+ramp-coreload-4-foreground = ${colors.primary}
+ramp-coreload-5-foreground = ${colors.primary}
+ramp-coreload-6-foreground = ${colors.alert}
+ramp-coreload-7-foreground = ${colors.alert}
+
+[module/temp]
+type = internal/temperature
+; head /sys/class/thermal/thermal_zone*/type
+thermal-zone = 1
+format-prefix = "🌡 "
+format-prefix-foreground = ${colors.primary}
+format-warn-prefix = "🌡 "
+format-warn-prefix-foreground = ${colors.primary}
+label-warn-foreground = ${colors.alert}
+label-warn-background = ${colors.background-alt}
+
+[network-base]
+type = internal/network
+interval = 5
+format-connected = <label-connected>
+format-packetloss = <animation-packetloss> <label-connected>
+; Hide completely if disconnected.
+format-disconnected =
+
+animation-packetloss-0 = ⚠
+animation-packetloss-0-foreground = ${colors.alert}
+animation-packetloss-0-background = ${colors.background-alt}
+animation-packetloss-1 = 📶
+animation-packetloss-1-foreground = ${colors.alert}
+animation-packetloss-1-background = ${colors.background-alt}
+; Framerate in milliseconds
+animation-packetloss-framerate = 500
+
+[module/wlan]
+inherit = network-base
+interface-type = wireless
+format-connected = <ramp-signal> <label-connected>
+label-connected = %essid%
+; label-connected-foreground = ${colors.green}
+
+ramp-signal-0 = 📶
+ramp-signal-1 = 📶
+ramp-signal-2 = 📶
+ramp-signal-0-foreground = ${colors.red}
+ramp-signal-1-foreground = ${colors.green}
+ramp-signal-2-foreground = ${colors.green}
+
+[module/eth]
+inherit = network-base
+interface-type = wired
+label-connected = %linkspeed%
+format-connected-prefix = "🌐 "
+format-connected-prefix-foreground = ${colors.green}
+
+[module/battery]
+type = internal/battery
+low-at = 25
+time-format = %-Hh%Mm
+format-discharging = <ramp-capacity> <label-discharging>
+label-discharging = %percentage%% %time% %consumption%W
+format-charging = <label-charging>
+format-charging-prefix = "🔌 "
+format-charging-prefix-foreground = ${colors.green}
+label-charging = %percentage%% %time%
+format-full =
+
+ramp-capacity-0 = 🔋
+ramp-capacity-1 = 🔋
+ramp-capacity-2 = 🔋
+ramp-capacity-0-foreground = ${colors.red}
+ramp-capacity-1-foreground = ${colors.yellow}
+ramp-capacity-2-foreground = ${colors.green}
+
+[module/date]
+type = internal/date
+interval = 1
+date = %a %e %b
+time = %H:%M
+date-alt = %Y-%m-%d
+time-alt = %H:%M:%S
+label = %date%, %time%
+
+[module/dunst]
+type = custom/script
+; Only show a "slience" emoji when notifications are paused; nothing otherwise.
+exec = "case $(dunstctl is-paused) in false) echo;; true) echo '🔕';; esac"
+format = <label>
+format-foreground = ${colors.alert}
+format-background = ${colors.background-alt}
diff --git a/tw/home/files/prompt.zsh b/tw/home/files/prompt.zsh
new file mode 100644
index 00000000..15e2d49b
--- /dev/null
+++ b/tw/home/files/prompt.zsh
@@ -0,0 +1,128 @@
+# fast prompt theme
+# MIT License
+# Based on lean prompt theme by Miek Gieben: https://github.com/miekg/lean
+# Based on Pure by Sindre Sorhus: https://github.com/sindresorhus/pure
+
+: ${PROMPT_FAST_NOTITLE=0} ${PROMPT_FAST_VIMODE=y} ${PROMPT_FAST_VIMODE_FORMAT=%S} ${PROMPT_FAST_CMD_MAX_EXEC_TIME=5}
+
+prompt_fast_help () {
+ cat << EOF
+This is a one line prompt that tries to stay out of your face. It utilizes the
+left side prompt for most information, like the CWD. When the exit code of a
+process isn't zero the prompt turns red and the exit code is displayed. If a
+process takes more than 5 (default) seconds to run the total running time is
+shown in the next prompt.
+
+Configuration:
+
+PROMPT_FAST_VIMODE: used to determine whether or not to display indicator (default on)
+PROMPT_FAST_VIMODE_FORMAT: Defaults to "%S"
+PROMPT_FAST_NOTITLE: used to determine whether or not to set the title, set to 0
+ by default. Set it to your own condition, make it 1 when you don't want the
+ title set.
+PROMPT_FAST_CMD_MAX_EXEC_TIME: if a commands takes more than this many seconds,
+ show how long it took
+EOF
+}
+
+# fastest possible way to check if repo is dirty
+prompt_fast_git_dirty () {
+ # check if we're in a git repo
+ git rev-parse --is-inside-work-tree &>/dev/null &&
+ # check if it's dirty
+ [ -n "$(git status --porcelain --ignore-submodules -uno 2>/dev/null | head -n 1)" ] &&
+ echo ' +'
+}
+
+# displays the exec time of the last command if set threshold was exceeded
+prompt_fast_cmd_exec_time () {
+ [ -n "$cmd_timestamp" ] || return
+ integer elapsed=$EPOCHSECONDS-$cmd_timestamp
+ ((elapsed > PROMPT_FAST_CMD_MAX_EXEC_TIME)) || return
+ # turns seconds into human readable time, 165392 => 1d 21h 56m 32s
+ ((elapsed > 60 * 60 * 24)) && print -f '%dd ' $((elapsed / 60 / 60 / 24))
+ ((elapsed > 60 * 60)) && print -f '%dh ' $((elapsed / 60 / 60 % 24))
+ ((elapsed > 60)) && print -f '%dm ' $((elapsed / 60 % 60))
+ ((elapsed > 0)) && print -f '%ds ' $((elapsed % 60))
+}
+
+prompt_fast_set_title () {
+ # shows the current tty and dir and executed command in the title when a process is active
+ print -Pn '\e]0;'
+ tr -dc '[:print:]' <<< "$1"
+ print -Pn ' - %~$prompt_fast_title_host\a'
+}
+
+prompt_fast_preexec () {
+ cmd_timestamp=$EPOCHSECONDS
+ ((PROMPT_FAST_NOTITLE != 1)) && prompt_fast_set_title "$1"
+}
+
+prompt_fast_pwd () {
+ local fast_path="$(print -Pn '%~')"
+ if (($#fast_path / $COLUMNS.0 * 100 > ${PROMPT_FAST_PATH_PERCENT:-33})); then
+ print -Pn '%-1~/…/%2/'
+ else
+ print "$fast_path"
+ fi
+}
+
+prompt_fast_precmd () {
+ ((PROMPT_FAST_NOTITLE != 1)) && print -Pn "\\e]0;%~$prompt_fast_title_host\a"
+
+ vcs_info
+
+ local a j jobs=()
+ for a j (${(kv)jobstates}); do
+ i=${${(@s,:,)j}[2]}
+ jobs+=($a${i//[^+-]/})
+ done
+ # print with [ ] and space separated
+ local prompt_fast_jobs=${jobs:+%F{8}[${(j: :)jobs}] }
+ local vimode=${${KEYMAP/vicmd/${PROMPT_FAST_VIMODE:+$PROMPT_FAST_VIMODE_FORMAT}}/(main|viins)/}
+
+ setopt promptsubst
+ local vcs_info_str='$vcs_info_msg_0_' # avoid https://github.com/njhartwell/pw3nage
+ local genv_short=${GUIX_ENVIRONMENT##*/}
+ genv_short=${genv_short:0:5}
+ local italic=$'\e[03m'
+ PROMPT="${TMUX+%F{green\}t }${GUIX_ENVIRONMENT+%F{11\}${genv_short}%u }$prompt_fast_jobs$prompt_fast_host%F{blue}$(prompt_fast_pwd)%F{8}$vcs_info_str$(prompt_fast_git_dirty) %F{yellow}$(prompt_fast_cmd_exec_time)%(?..%B%F{red}%?%f%b )%(?.%F{blue}.%B%F{red})$vimode%#%s%u%f%k%b "
+
+ unset cmd_timestamp # reset value since `preexec` isn't always triggered
+}
+
+zle-keymap-select () {
+ prompt_fast_precmd
+ zle reset-prompt
+}
+
+prompt_fast_setup () {
+ prompt_opts=(cr percent sp subst)
+
+ #zmodload zsh/datetime # for strftime -- appears unused
+ autoload -Uz add-zsh-hook
+ autoload -Uz vcs_info
+
+ [ -z "$PROMPT_FAST_VIMODE" ] && zle -N zle-keymap-select
+
+ add-zsh-hook precmd prompt_fast_precmd
+ add-zsh-hook preexec prompt_fast_preexec
+
+ zstyle ':vcs_info:*' enable git
+ zstyle ':vcs_info:git*' formats ' %b'
+ zstyle ':vcs_info:git*' actionformats ' %b|%a'
+
+ if [ -n "$SSH_CONNECTION" ]; then
+ prompt_fast_title_host=' - %n@%m'
+ prompt_fast_host='%F{yellow}%n@%m:'
+ fi
+
+ return 0
+}
+
+if [ "$TERM" = dumb ]; then
+ # Emacs Tramp needs a simple prompt when connecting over SSH.
+ PS1='$ '
+else
+ prompt_fast_setup "$@"
+fi
diff --git a/tw/home/files/ranger.conf b/tw/home/files/ranger.conf
new file mode 100644
index 00000000..dd5d4b0b
--- /dev/null
+++ b/tw/home/files/ranger.conf
@@ -0,0 +1,52 @@
+# Be aware of version control systems and display information.
+set vcs_aware true
+
+# Use one of the supported image preview protocols
+set preview_images true
+
+# Set the preview image method. Supported methods:
+#
+# * w3m (default):
+# Preview images in full color with the external command "w3mimgpreview"?
+# This requires the console web browser "w3m" and a supported terminal.
+# It has been successfully tested with "xterm" and "urxvt" without tmux.
+#
+# * terminology:
+# Previews images in full color in the terminology terminal emulator.
+# Supports a wide variety of formats, even vector graphics like svg.
+#
+# * urxvt:
+# Preview images in full color using urxvt image backgrounds. This
+# requires using urxvt compiled with pixbuf support.
+#
+# * urxvt-full:
+# The same as urxvt but utilizing not only the preview pane but the
+# whole terminal window.
+#
+# * kitty:
+# Preview images in full color using kitty image protocol.
+# Requires python PIL or pillow library.
+# If ranger does not share the local filesystem with kitty
+# the transfer method is changed to encode the whole image;
+# while slower, this allows remote previews,
+# for example during an ssh session.
+# Tmux is unsupported.
+#
+# * ueberzug:
+# Preview images in full color with the external command "ueberzug".
+# Images are shown by using a child window.
+# Only for users who run X11 in GNU/Linux.
+set preview_images_method kitty
+
+# Use a unicode "..." character to mark cut-off filenames?
+set unicode_ellipsis true
+
+# Abbreviate $HOME with ~ in the titlebar (first line) of ranger?
+set tilde_in_titlebar true
+
+# Changes case sensitivity for the cd command tab completion
+set cd_tab_case insensitive
+
+# Use fuzzy tab completion with the "cd" command. For example,
+# ":cd /u/lo/b<tab>" expands to ":cd /usr/local/bin".
+set cd_tab_fuzzy true
diff --git a/tw/home/files/rofi.rasi b/tw/home/files/rofi.rasi
new file mode 100644
index 00000000..83363c14
--- /dev/null
+++ b/tw/home/files/rofi.rasi
@@ -0,0 +1,41 @@
+/* -*- mode: css -*- */
+configuration {
+ modi: "combi,windowcd,calc";
+ combi-modi: "window,drun,session:sessionmenu,ssh,run";
+ terminal: "rofi-sensible-terminal";
+ icon-theme: "Papirus-Dark";
+ drun-show-actions: true; /* show actions defined in .desktop files as well */
+ combi-display-format: "{text}"; /* hide {mode} prefix */
+ show-icons: true;
+ disable-history: false;
+ case-sensitive: false;
+ cycle: true;
+ matching: "normal";
+ scroll-method: 0;
+ /* Needed by Catppuccin theme: */
+ sidebar-mode: true;
+ hide-scrollbar: true;
+ location: 0;
+ /* End of theme-related config. */
+ display-window: "Windows";
+ display-windowcd: "Workspace";
+ display-run: "Run";
+ display-ssh: "SSH";
+ display-drun: "Desktop";
+ display-combi: "Combined";
+ display-calc: "Calculator";
+ display-session: "Session";
+ display-keys: "Keys";
+ display-filebrowser: "Files";
+ display-emoji: "Emoji";
+ timeout {
+ action: "kb-cancel";
+ delay: 0;
+ }
+ filebrowser {
+ directories-first: true;
+ sorting-method: "name";
+ }
+}
+
+@theme "catppuccin"
diff --git a/tw/home/files/sessionmenu b/tw/home/files/sessionmenu
new file mode 100755
index 00000000..552c2b3c
--- /dev/null
+++ b/tw/home/files/sessionmenu
@@ -0,0 +1,29 @@
+#!/usr/bin/env -S guile --no-auto-compile
+!#
+(use-modules (ice-9 format)
+ (ice-9 match)
+ (ice-9 ports))
+
+(define (call-command/stderr . command)
+ "Call COMMAND using `system*', but redirect all output to stderr."
+ (with-output-to-port (current-error-port)
+ (lambda () (apply system* command))))
+
+(match (command-line)
+ ((_ "quit")
+ (exit 0))
+ ((_ "Reload i3 configuration")
+ (call-command/stderr "i3-msg" "reload"))
+ ((_ "Log out")
+ (call-command/stderr "i3-msg" "exit"))
+ ((_ "Shutdown")
+ (call-command/stderr "sudo" "-n" "/run/current-system/profile/sbin/shutdown"))
+ ((_ "Reboot")
+ (call-command/stderr "sudo" "-n" "/run/current-system/profile/sbin/reboot"))
+ (_
+ (format #t "\0~a\x1f~a~%~{~a\0icon\x1f~a~%~}"
+ "prompt" "Session"
+ '("Reload i3 configuration" "reload"
+ "Log out" "system-log-out"
+ "Shutdown" "system-shutdown"
+ "Reboot" "system-reboot"))))
diff --git a/tw/home/files/user-dirs.dirs b/tw/home/files/user-dirs.dirs
new file mode 100644
index 00000000..0db0cae3
--- /dev/null
+++ b/tw/home/files/user-dirs.dirs
@@ -0,0 +1,15 @@
+# This file is written by xdg-user-dirs-update
+# If you want to change or add directories, just edit the line you're
+# interested in. All local changes will be retained on the next run.
+# Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped
+# homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an
+# absolute path. No other format is supported.
+#
+XDG_DESKTOP_DIR="$HOME/"
+XDG_DOWNLOAD_DIR="$HOME/downloads"
+XDG_TEMPLATES_DIR="$HOME/"
+XDG_PUBLICSHARE_DIR="$HOME/"
+XDG_DOCUMENTS_DIR="$HOME/documents"
+XDG_MUSIC_DIR="$HOME/music"
+XDG_PICTURES_DIR="$HOME/pictures"
+XDG_VIDEOS_DIR="$HOME/videos"
diff --git a/tw/home/files/vdirsyncer.conf b/tw/home/files/vdirsyncer.conf
new file mode 100644
index 00000000..5dac2686
--- /dev/null
+++ b/tw/home/files/vdirsyncer.conf
@@ -0,0 +1,77 @@
+[general]
+status_path = "~/.local/share/vdirsyncer/status"
+
+# Main contact list from Nextcloud.
+[pair main_contacts]
+a = "nextcloud_main_contacts"
+b = "local_main_contacts"
+metadata = ["displayname", "description"]
+collections = ["from a", "from b"]
+conflict_resolution = "a wins"
+
+[storage local_main_contacts]
+type = "filesystem"
+path = "~/.local/share/vdirsyncer/main-contacts"
+fileext = ".vcf"
+
+[storage nextcloud_main_contacts]
+type = "carddav"
+url = "https://cloud.wilkenfamily.de/remote.php/dav/addressbooks/users/timo/contacts/"
+username.fetch = ["shell", "pass www/nextcloud/timo | awk '/^username: /{print $2}'"]
+password.fetch = ["shell", "pass www/nextcloud/timo | head -1"]
+
+# "Sandbox Contacts" from Nextcloud, mostly for WhatsApp.
+[pair sandbox_contacts]
+a = "nextcloud_sandbox_contacts"
+b = "local_sandbox_contacts"
+metadata = ["displayname", "description"]
+collections = ["from a", "from b"]
+conflict_resolution = "a wins"
+
+[storage local_sandbox_contacts]
+type = "filesystem"
+path = "~/.local/share/vdirsyncer/sandbox-contacts"
+fileext = ".vcf"
+
+[storage nextcloud_sandbox_contacts]
+type = "carddav"
+url = "https://cloud.wilkenfamily.de/remote.php/dav/addressbooks/users/timo/sandbox-contacts/"
+username.fetch = ["shell", "pass www/nextcloud/timo | awk '/^username: /{print $2}'"]
+password.fetch = ["shell", "pass www/nextcloud/timo | head -1"]
+
+# Calendar from Nextcloud.
+[pair calendars]
+a = "nextcloud_calendars"
+b = "local_calendars"
+metadata = ["color", "displayname", "description", "order"]
+collections = ["from a", "from b"]
+conflict_resolution = "a wins"
+
+[storage local_calendars]
+type = "filesystem"
+path = "~/.local/share/vdirsyncer/calendars"
+fileext = ".ics"
+
+[storage nextcloud_calendars]
+type = "caldav"
+url = "https://cloud.wilkenfamily.de/remote.php/dav/principals/users/timo/"
+username.fetch = ["shell", "pass www/nextcloud/timo | awk '/^username: /{print $2}'"]
+password.fetch = ["shell", "pass www/nextcloud/timo | head -1"]
+
+# Work Indico calendar (read-only).
+[pair indico]
+a = "indico_calendar"
+b = "local_indico_calendar"
+metadata = []
+collections = null
+conflict_resolution = "a wins"
+
+[storage local_indico_calendar]
+type = "filesystem"
+path = "~/.local/share/vdirsyncer/indico"
+fileext = ".ics"
+
+[storage indico_calendar]
+type = "http"
+# The URL contains an access token, so get it from pass.
+url.fetch = ["shell", "~/.guix-home/profile/bin/pass cern/indico-calendar-url | head -1"]
diff --git a/tw/home/files/vim-keys.muttrc b/tw/home/files/vim-keys.muttrc
new file mode 100644
index 00000000..a995a931
--- /dev/null
+++ b/tw/home/files/vim-keys.muttrc
@@ -0,0 +1,36 @@
+# Moving around
+bind attach,browser,index g noop
+bind attach,browser,index gg first-entry
+bind attach,browser,index G last-entry
+bind pager g noop
+bind pager gg top
+bind pager G bottom
+#bind pager k previous-line
+#bind pager j next-line
+
+# Scrolling
+bind attach,browser,pager,index \CF next-page
+bind attach,browser,pager,index \CB previous-page
+bind attach,browser,pager,index \Cu half-up
+bind attach,browser,pager,index \Cd half-down
+bind browser,pager \Ce next-line
+bind browser,pager \Cy previous-line
+bind index \Ce next-line
+bind index \Cy previous-line
+
+bind pager,index d noop
+bind pager,index dd delete-message
+
+# Mail & Reply
+bind index \Cl list-reply
+
+# Threads
+bind browser,pager,index N search-opposite
+bind pager,index dT delete-thread
+bind pager,index dt delete-subthread
+bind pager,index gt next-thread
+bind pager,index gT previous-thread
+bind index za collapse-thread
+bind index zA collapse-all # Missing :folddisable/foldenable
+
+# vim:ft=muttrc:
diff --git a/tw/home/files/volume b/tw/home/files/volume
new file mode 100755
index 00000000..96398553
--- /dev/null
+++ b/tw/home/files/volume
@@ -0,0 +1,60 @@
+#!/usr/bin/env -S guile --no-auto-compile
+!#
+(use-modules (ice-9 format)
+ (ice-9 match)
+ (ice-9 popen)
+ (ice-9 regex)
+ (ice-9 textual-ports))
+
+(define (read-from-pactl . args)
+ (let* ((port (apply open-pipe* OPEN_READ "pactl" args))
+ (output (get-string-all port)))
+ (close-pipe port)
+ (string-trim-right output #\newline)))
+
+(define %default-sink
+ (read-from-pactl "get-default-sink"))
+
+(define* (sink-muted? #:optional (sink %default-sink))
+ (match (read-from-pactl "get-sink-mute" sink)
+ ("Mute: yes" #t)
+ ("Mute: no" #f)
+ (output (error "Unknown `pactl get-sink-mute' output" output))))
+
+(define* (sink-volume #:optional (sink %default-sink))
+ (match-let ((`#(,match (,start . ,end))
+ (string-match "[0-9]+%" (read-from-pactl "get-sink-volume" sink))))
+ (string->number (substring match start (1- end))))) ; trim trailing "%"
+
+(define* (set-sink-mute! #:optional (state "toggle") (sink %default-sink))
+ (status:exit-val
+ (system* "pactl" "set-sink-mute" sink state)))
+
+(define* (increment-sink-volume! increment-percent #:optional (sink %default-sink))
+ (status:exit-val
+ (system* "pactl" "set-sink-volume" sink
+ ;; A percentage with a sign is an increment.
+ (format #f "~@d%" increment-percent))))
+
+(define* (notify #:optional (muted? (sink-muted?)) (volume (sink-volume)))
+ (status:exit-val
+ (system* "dunstify" "-a" "volume" "-u" "low"
+ "-i" (cond (muted? "audio-volume-muted")
+ ((< volume 50) "audio-volume-low")
+ (#t "audio-volume-high"))
+ "-h" "string:x-canonical-private-synchronous:volume"
+ "-h" (format #f "int:value:~d" (if muted? 0 volume))
+ (if muted? "Volume muted" "Volume"))))
+
+(match (cdr (program-arguments))
+ (() 0) ; notify only
+ (("mute") (set-sink-mute! "true"))
+ (("unmute") (set-sink-mute! "false"))
+ (("toggle-mute") (set-sink-mute! "toggle"))
+ (((? string->number increment-percent))
+ (and (set-sink-mute! "false")
+ (increment-sink-volume!
+ (string->number increment-percent))))
+ (args (error "Could not parse command-line arguments:" args)))
+
+(notify)
diff --git a/tw/home/files/zathurarc b/tw/home/files/zathurarc
new file mode 100644
index 00000000..06a9351f
--- /dev/null
+++ b/tw/home/files/zathurarc
@@ -0,0 +1,32 @@
+# see man zathurarc -*- mode: conf-space -*-
+
+# [c]ommand line, [s]tatus bar, [v]ertical/[h]orizontal scrollbar
+set guioptions s
+set recolor true
+set recolor-keephue false
+set recolor-reverse-video true
+set render-loading true
+
+set scroll-hstep -1
+set scroll-step 40
+set scroll-full-overlap 0.0
+set scroll-page-aware true
+
+set window-title-basename true
+set window-title-home-tilde true
+set window-title-page false
+set statusbar-basename false
+set statusbar-home-tilde true
+
+set selection-clipboard clipboard
+set selection-notification true
+
+# Completion
+set show-directories true
+set show-hidden true
+set show-recent 10
+
+# Theme
+set font "Hermit 10"
+set page-padding 0 # px between pages
+include catppuccin
diff --git a/tw/home/files/zshrc b/tw/home/files/zshrc
new file mode 100644
index 00000000..49804126
--- /dev/null
+++ b/tw/home/files/zshrc
@@ -0,0 +1,168 @@
+# Zsh configuration -*- sh -*-
+
+# Load functions from Guix profile, where packages like
+# zsh-completions are installed.
+if [ -d ~/.guix-home/profile/share/zsh/site-functions ]; then
+ fpath+=(~/.guix-home/profile/share/zsh/site-functions)
+fi
+
+zmodload zsh/attr
+zmodload -F zsh/cap b:cap
+zmodload zsh/datetime
+zmodload zsh/system
+zmodload zsh/terminfo
+
+setopt extendedglob interactivecomments
+
+# So we don't have to call tput.
+autoload -Uz colors
+colors
+
+# GPG-agent setup
+GPG_TTY=$(tty) && export GPG_TTY
+
+## Key bindings
+# vi-like key mappings
+bindkey -v
+
+bindkey '^[[A' up-line-or-history # up
+bindkey '^[[B' down-line-or-history # down
+
+bindkey '^[[1;5A' up-line-or-history # C-up
+bindkey '^[[1;5B' down-line-or-history # C-down
+bindkey '^[[1;5C' vi-forward-word # C-right
+bindkey '^[[1;5D' vi-backward-word # C-left
+
+bindkey '^[[H' vi-beginning-of-line # home
+bindkey '^[[F' vi-end-of-line # end
+
+## History setup
+# HISTSIZE should be set through Guix's home environment service.
+HISTSIZE=10000000
+HISTFILE=${XDG_DATA_HOME:-$HOME/.local/share}/zsh/history
+SAVEHIST=$HISTSIZE
+setopt appendhistory sharehistory incappendhistory extendedhistory histverify histreduceblanks
+
+## Completion setup
+
+# don't need to run 'hash -r' after commands are added/removed
+zstyle ':completion:*' rehash true
+
+# enable completion list colours
+# http://linuxshellaccount.blogspot.com/2008/12/color-completion-using-zsh-modules-on.html
+zstyle ':completion:*:*:*:*:hosts' list-colors "=*=${fg[blue]}"
+zstyle ':completion:*:*:*:*:users' list-colors "=*=${fg[green]}=${fg[red]}"
+
+# formatting and messages
+# http://www.masterzen.fr/2009/04/19/in-love-with-zsh-part-one/
+zstyle ':completion:*' verbose yes
+zstyle ':completion:*:descriptions' format "${fg[green]}%B--- %d%b"
+zstyle ':completion:*:messages' format '%d'
+zstyle ':completion:*:warnings' format "${fg[red]}%BNo matches for:%b %d"
+zstyle ':completion:*:corrections' format "${fg[yellow]}%B%d (%e char)%b"
+
+# https://stackoverflow.com/a/24237590
+#zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'
+
+# (mostly) from compinstall
+zstyle ':completion:*' auto-description '(param: %d)'
+zstyle ':completion:*' completer _expand _complete _ignored _approximate
+zstyle ':completion:*' expand prefix suffix
+zstyle ':completion:*' file-sort name
+zstyle ':completion:*' group-name ''
+zstyle ':completion:*' ignore-parents parent pwd directory
+zstyle ':completion:*' insert-unambiguous false
+eval "$(dircolors)" # set LS_COLORS
+zstyle ':completion:*' list-colors "${(s.:.)LS_COLORS}"
+zstyle ':completion:*' list-suffixes true
+zstyle ':completion:*' matcher-list '' 'm:{[:lower:]}={[:upper:]}' 'r:|[._-]=** r:|=**' 'l:|=* r:|=*'
+zstyle ':completion:*' max-errors 3
+zstyle ':completion:*' original true
+zstyle ':completion:*' verbose true
+
+autoload -Uz compinit
+if mkdir -p "${XDG_CACHE_HOME:-$HOME/.cache}/zsh"; then
+ compinit -d "${XDG_CACHE_HOME:-$HOME/.cache}/zsh/zcompdump-$ZSH_VERSION"
+else
+ # Don't use dump file.
+ compinit -D
+fi
+
+## Aliases
+# Use ASYNC_EDITOR so I can continue using the shell while the editor
+# remains open in a separate window.
+alias em="$ASYNC_EDITOR"
+alias se='sudo -e'
+alias ls='\ls --color=auto -h'
+alias la='\ls --color=auto -hA'
+alias grep='\grep --color=auto'
+alias diff='\diff -s --color=auto'
+alias cdiff='\diff -s --color=always'
+alias ipy='ipython3 --autoindent --automagic --pprint --no-banner --no-confirm-exit --term-title --autocall=1 --colors=Neutral'
+alias rot13='caesar 13'
+alias wget='\wget --hsts-file="${XDG_CACHE_HOME:-$HOME/.cache}/wget-hsts"'
+alias aurora="REQUESTS_CA_BUNDLE=${XDG_CONFIG_HOME:-$HOME/.config}/cern-ca-bundle.crt \aurora"
+alias aurora_admin="REQUESTS_CA_BUNDLE=${XDG_CONFIG_HOME:-$HOME/.config}/cern-ca-bundle.crt \aurora_admin"
+
+# Git aliases
+alias ga='git add'
+alias gb='git branch'
+alias gc='git commit'
+alias gd='git diff'
+alias gds='git diff --staged'
+alias gf='git fetch'
+alias gfa='git fetch --all --prune --tags'
+alias gk='git checkout'
+alias gkb='git checkout -b'
+alias gg='git glog'
+alias gl='git pull'
+alias gp='git push'
+alias gr='git rebase'
+alias gri='git rebase --interactive'
+alias gs='git status'
+
+if recsel --version 2>/dev/null | grep -qFx 'recsel (GNU recutils) 1.8'; then
+ # Recutils 1.8 has a bug when TMPDIR is on a different mount point, see
+ # https://lists.gnu.org/archive/html/bug-recutils/2019-08/msg00001.html.
+ for _recutil in rec2csv recdel recfix recfmt recinf recins recsel recset; do
+ alias "$_recutil=TMPDIR=. $_recutil"
+ done
+ unset _recutil
+fi
+
+ppscm () {
+ guile -c "(use-modules (ice-9 pretty-print)) (with-input-from-file \"$1\" (compose pretty-print read))" |
+ source-highlight -s scheme -f esc | $PAGER
+}
+
+## Plugins
+load_plugin () {
+ local try_path
+ for try_path in {/usr,"$GUIX_ENVIRONMENT","$HOME/.guix-profile","$HOME/.guix-home/profile"}"/share/zsh/plugins/$1/$1"{.plugin,}.zsh; do
+ [ -r "$try_path" ] && . "$try_path" && return 0
+ done
+ return 1
+}
+
+if load_plugin zsh-autosuggestions; then
+ bindkey '^ ' autosuggest-accept
+ bindkey -M vicmd '^I' autosuggest-accept
+ ZSH_AUTOSUGGEST_STRATEGY=(history completion)
+ ZSH_AUTOSUGGEST_BUFFER_MAX_SIZE=20
+ ZSH_AUTOSUGGEST_USE_ASYNC=1
+ ZSH_AUTOSUGGEST_MANUAL_REBIND=1
+fi
+
+if load_plugin zsh-history-substring-search; then
+ bindkey '^[[A' history-substring-search-up
+ bindkey '^[[B' history-substring-search-down
+ bindkey -M emacs '^P' history-substring-search-up
+ bindkey -M emacs '^N' history-substring-search-down
+ bindkey -M vicmd 'k' history-substring-search-up
+ bindkey -M vicmd 'j' history-substring-search-down
+fi
+
+load_plugin zsh-autopair
+
+load_plugin fast-syntax-highlighting ||
+ load_plugin zsh-syntax-highlighting
diff --git a/tw/home/lap.scm b/tw/home/lap.scm
new file mode 100644
index 00000000..0c400242
--- /dev/null
+++ b/tw/home/lap.scm
@@ -0,0 +1,697 @@
+;; This "home-environment" file can be passed to 'guix home reconfigure'
+;; to reproduce the content of your profile. This is "symbolic": it only
+;; specifies package names. To reproduce the exact same profile, you also
+;; need to capture the channels being used, as returned by "guix describe".
+;; See the "Replicating Guix" section in the manual.
+
+;; See also, for some tips:
+;; https://github.com/alezost/shepherd-config/blob/master/init.scm
+
+;; TODO: Integrate upstream (ctp) configs into ~/guix-home using git submodules:
+;; https://github.com/catppuccin/grub ; TODO: make this system-wide
+;; https://github.com/catppuccin/tty ; system-wide
+;; https://github.com/catppuccin/papirus-folders
+;; https://github.com/catppuccin/Kvantum
+;; https://github.com/catppuccin/fzf
+;; https://github.com/catppuccin/obs
+;; https://github.com/catppuccin/insomnia
+;; https://github.com/catppuccin/firefox ; for icecat
+
+;; Manual installation needed?
+;; https://github.com/catppuccin/dark-reader
+;; https://github.com/catppuccin/github
+
+(define-module (tw home lap)
+ #:use-module (gnu home)
+ #:use-module (gnu home services)
+ #:use-module (gnu home services desktop)
+ #:use-module (gnu home services guix)
+ #:use-module (gnu home services mcron)
+ #:use-module (gnu home services pm)
+ #:use-module (gnu home services shells)
+ #:use-module (gnu home services shepherd)
+ #:use-module (gnu home services ssh)
+ #:use-module (gnu packages)
+ #:use-module (gnu services)
+ #:use-module (gnu services shepherd)
+ #:use-module (guix channels)
+ #:use-module (guix modules)
+ #:use-module (guix gexp))
+
+(fluid-set! read-eval? #t) ; allow #. read expansions in this file
+
+;; One of "latte" (light theme), "frappe", "macchiato", "mocha" (dark
+;; themes); ordered brightest to darkest.
+;; Set and use this at read time so that `local-file' gets a literal
+;; argument. Anything else confuses it and causes it to search
+;; relative to the working directory, not this file's directory.
+#.(define catppuccin-theme-variant "mocha")
+
+;; "Base" colour from the active Catppuccin theme (see
+;; https://github.com/catppuccin/catppuccin).
+(define catppuccin-background-color "1e1e2e")
+
+(define* (package-binary specification #:optional (binary specification))
+ "Get the path of BINARY inside the package referred to by SPECIFICATION."
+ (file-append (specification->package specification)
+ (string-append "/bin/" binary)))
+
+;; See also: `file-join' in (gnu home services ssh).
+(define (combined-text-file name . files)
+ "A file which is the concatenation of the contents of other files."
+ ;; We need to use #$output for `computed-file' to work.
+ ;; Of course this isn't documented anywhere!
+ (computed-file name
+ (with-imported-modules (source-module-closure
+ '((guix build utils))) ; for `dump-port'
+ #~(begin
+ (use-modules (guix build utils))
+ (call-with-output-file #$output
+ (lambda (oport)
+ (for-each (lambda (in-file)
+ (call-with-input-file in-file
+ (lambda (iport)
+ (dump-port iport oport))))
+ '#$files)))))))
+
+(define (text-file/substitutions name input-file . substitutions)
+ "A file containing another file's content with regexp substitutions.
+Each item in SUBSTITUTIONS is a `cons' of a regular expression and a
+replacement spec (to which `regexp-substitute/global' is applied)."
+ (computed-file name
+ #~(begin
+ (use-modules (ice-9 regex)
+ (ice-9 textual-ports))
+ (call-with-output-file #$output
+ (lambda (oport)
+ (call-with-input-file #$input-file
+ (lambda (iport)
+ (let loop ((output (get-string-all iport))
+ (substitutions '#$substitutions))
+ (if (null? substitutions)
+ (put-string oport output)
+ (loop (call-with-output-string
+ (lambda (sport)
+ (apply regexp-substitute/global
+ sport
+ (caar substitutions)
+ output
+ (cdar substitutions))))
+ (cdr substitutions)))))))))))
+
+(define gui-packages
+ '(;; i3 and Xorg. i3 itself must be installed system-wide for gdm to pick it up.
+ ;; acpilight is a drop-in xbacklight replacement, as xbacklight doesn't work on my system.
+ "acpilight" "arandr" "blueman" "dunst" "gnupg" "gimp" "hsetroot"
+ "inkscape" "icecat" "kdeconnect" "kitty" "libreoffice" "mpv" "nheko"
+ "password-store" "pass-otp" "polybar" "rofi" "rofi-calc" "signal-desktop"
+ "simple-scan" "transmission-remote-gtk" "xdg-utils" "xdot" "xclip" "xdotool"
+ "xdpyinfo" "xev" "xfd" "xfontsel" "xinput" "xkill" "xprop" "xrandr" "xrdb"
+ "xsel" "xset" "xwininfo" "zoom" "zathura" "zathura-ps" "zathura-pdf-poppler"
+ "dconf" "dconf-editor" ; required for config by blueman, cozy, ...
+ ;; "gnome-keyring" ; installed system-wide; see system-configuration.scm
+ ;; "geoclue" ; for redshift -- installed system-wide
+ ;; Games
+ "0ad" "freeciv" "warzone2100" "widelands" ; "pioneer"
+ ;; Fonts
+ "font-hermit" "font-inconsolata" "font-fira-code" "font-fira-sans"
+ "font-libertinus" "font-openmoji"
+ ;; Theming
+ "papirus-icon-theme"
+ ;; My own packages -- needs GUIX_PACKAGE_PATH to be set; see below.
+ "catppuccin-gtk-theme"
+ "catppuccin-mocha-dark-cursors"
+ "simutrans"))
+
+(define (polybar-service monitor)
+ (shepherd-service
+ (documentation (string-append "Polybar desktop bar for monitor" monitor "."))
+ (provision (list (string->symbol (string-append "polybar-" monitor))))
+ (start #~(make-forkexec-constructor
+ (list #$(package-binary "polybar"))
+ #:environment-variables
+ (cons #$(string-append "POLYBAR_MONITOR=" monitor)
+ (default-environment-variables))))
+ (stop #~(make-kill-destructor))))
+
+(define gui-services
+ (list
+ ;; Batsignal: battery level notifications.
+ (service home-batsignal-service-type
+ (home-batsignal-configuration
+ (batteries '("BAT0"))))
+
+ ;; Redshift: make the screen turn redder at night.
+ (service home-redshift-service-type
+ (home-redshift-configuration
+ ;; See info '(guix)Desktop Home Services'.
+ (location-provider 'manual)
+ ;; Approximate location
+ (latitude 46.0)
+ (longitude 6.0)
+ ;; (location-provider 'geoclue2) ; currently waits forever for a location -- not sure why geoclue doesn't work
+ ;; (daytime-temperature 6500) ; default 6500
+ ;; (nighttime-temperature 4500) ; default 4500
+ (daytime-brightness 1.0)
+ (nighttime-brightness 0.7)
+ (extra-content "fade=0"))) ; with fade=1, restarting redshift causes flickering for a few secs
+
+ ;; The dbus service doesn't seem to be added automatically.
+ (service home-dbus-service-type (home-dbus-configuration))
+
+ (simple-service
+ 'gui-services home-shepherd-service-type
+ (list
+ (shepherd-service
+ (documentation "KDE connect applet.")
+ (provision '(kdeconnect-applet))
+ (requirement '(kdeconnectd))
+ (start #~(make-forkexec-constructor
+ (list #$(package-binary "kdeconnect" "kdeconnect-indicator"))))
+ (stop #~(make-kill-destructor)))
+ (shepherd-service
+ (documentation "KDE connect daemon.")
+ (provision '(kdeconnectd))
+ (start #~(make-forkexec-constructor
+ (list #$(file-append (specification->package "kdeconnect")
+ (string-append "/libexec/kdeconnectd")))))
+ (stop #~(make-kill-destructor)))
+
+ (shepherd-service
+ (documentation "Blueman applet; provides a GUI for connection to bluetooth devices.")
+ (provision '(blueman-applet))
+ (start #~(make-forkexec-constructor
+ (list #$(package-binary "blueman" "blueman-applet"))))
+ (stop #~(make-kill-destructor)))
+
+ (polybar-service "eDP-1")
+ (polybar-service "HDMI-1-1")
+
+ (shepherd-service
+ (documentation "Dunst notification daemon; displays desktop notifications.")
+ (provision '(dunst))
+ (start #~(make-forkexec-constructor
+ (list #$(package-binary "dunst"))))
+ (stop #~(make-kill-destructor)))
+
+ (shepherd-service
+ (documentation "Picom compositor; enables transparent windows in X.")
+ (provision '(picom))
+ (start #~(make-forkexec-constructor
+ (list #$(package-binary "picom") "--experimental-backends"
+ "--config" #$(local-file "files/picom.conf"))))
+ (stop #~(make-kill-destructor)))
+
+ (shepherd-service
+ (documentation "Source Xresources on login.")
+ (provision '(xrdb))
+ (one-shot? #t)
+ (start #~(lambda _
+ (invoke #$(package-binary "xrdb") "-merge"
+ (string-append (getenv "XDG_CONFIG_HOME") "/X11/Xresources")))))
+
+ ;; By default, xdotool gets most of "#@\|~()<>[]{} wrong. Make
+ ;; it use the correct keymap by re-setting the same one again.
+ (shepherd-service
+ (documentation "Fix X keyboard map on login; passmenu needs this.")
+ (provision '(fix-xdotool))
+ (one-shot? #t)
+ (start #~(lambda _
+ (use-modules (ice-9 rdelim)
+ (ice-9 regex)
+ (ice-9 popen))
+ (let ((port (open-pipe* OPEN_READ #$(package-binary "setxkbmap") "-query")))
+ (let loop ((line (read-line port)))
+ (unless (eof-object? line)
+ (let ((mtch (string-match "^layout:[[:space:]]*" line)))
+ (if mtch
+ (system* #$(package-binary "setxkbmap") (match:suffix mtch))
+ (loop (read-line port))))))))))
+
+ (shepherd-service
+ (documentation "Set up X displays on login.")
+ (provision '(xorg-setup))
+ (one-shot? #t)
+ (start #~(lambda _
+ (invoke #$(package-binary "numlockx") "on")
+ ;; Turn off the monitors if there is no input for 10 minutes.
+ (invoke #$(package-binary "xset") "dpms" "600" "600" "600")
+ (invoke #$(package-binary "xrandr")
+ "--output" "eDP-1" "--auto"
+ ;; Don't use --auto for this monitor. That
+ ;; configures it at 60 Hz, which causes
+ ;; it to briefly turn off every few minutes.
+ "--output" "HDMI-1-1" "--mode" "2560x1440" "--rate" "120.00"
+ "--right-of" "eDP-1")
+ ;; Set the desktop background picture. Hopefully doing this just after
+ ;; xrandr works and sets it for both screens.
+ (invoke #$(package-binary "hsetroot") "-cover"
+ (string-append (getenv "HOME")
+ "/pictures/Backgrounds/greece/IMG_20181201_104748_DRO.jpg")))))))
+
+ ;; Configuration files for GUI programs in $XDG_CONFIG_HOME.
+ (simple-service
+ 'gui-config home-xdg-configuration-files-service-type
+ `(("dunst/dunstrc" ,(local-file "files/dunstrc"))
+ ("dunst/dunstrc.d/50-catppuccin.conf"
+ ,(local-file #.(string-append "files/catppuccin/dunst/src/"
+ catppuccin-theme-variant ".conf")
+ "dunst-theme.conf"))
+ ("emacs/include" ,(local-file "files/emacs-packages" #:recursive? #t))
+ ("emacs/init.el" ,(local-file "files/emacs-init.el"))
+ (#.(string-append "emacs/catppuccin-" catppuccin-theme-variant "-theme.el")
+ ,(local-file #.(string-append "files/catppuccin/emacs/catppuccin-"
+ catppuccin-theme-variant "-theme.el")))
+ ("gtk-2.0/gtkrc" ,(local-file "files/gtk2.ini"))
+ ("gtk-3.0/settings.ini" ,(local-file "files/gtk3.ini"))
+ ("i3/config" ,(local-file "files/i3.conf"))
+ ("kdeglobals" ; TODO: this works for some programs (e.g. kdeconnect-app),
+ ; but not for others (e.g. nheko, kdeconnect-settings)...
+ ,(local-file #.(let ((variant (string-upcase catppuccin-theme-variant 0 1)))
+ (string-append "files/catppuccin/kde/" variant
+ "/Catppuccin" variant ".colors"))))
+ ("kitty/diff.conf"
+ ,(local-file #.(string-append "files/catppuccin/kitty/diff-"
+ catppuccin-theme-variant ".conf")
+ "kitty-diff-theme.conf"))
+ ("kitty/kitty.conf"
+ ,(combined-text-file "kitty.conf"
+ (local-file "files/kitty.conf")
+ (local-file #.(string-append "files/catppuccin/kitty/"
+ catppuccin-theme-variant ".conf")
+ "kitty-theme.conf")))
+ ("polybar/config.ini" ,(local-file "files/polybar.ini"))
+ ("polybar/catppuccin.ini"
+ ,(local-file #.(string-append "files/catppuccin/polybar/themes/"
+ catppuccin-theme-variant ".ini")))
+ ("rofi/config.rasi" ,(local-file "files/rofi.rasi"))
+ ("rofi/themes/catppuccin.rasi"
+ ,(text-file/substitutions "catppuccin.rasi"
+ (local-file #.(string-append "files/catppuccin/rofi/basic/.local/share/rofi/themes/catppuccin-"
+ catppuccin-theme-variant ".rasi"))
+ '("JetBrainsMono Nerd Font 14" pre "Fira Sans 12" post) ; default font
+ '("border-col: #[0-9a-f]*;" pre "border-col: #585b70;" post))) ; i3 border colour
+ ("zathura/zathurarc" ,(local-file "files/zathurarc"))
+ ("zathura/catppuccin"
+ ,(local-file #.(string-append "files/catppuccin/zathura/src/catppuccin-"
+ catppuccin-theme-variant)))))
+
+ (simple-service
+ 'gui-files home-files-service-type
+ `((".icons/default/index.theme" ,(local-file "files/cursors.ini"))))
+
+ (simple-service
+ 'gui-environment-variables home-environment-variables-service-type
+ `(("TERMINAL" . "kitty")
+ ("_JAVA_OPTIONS" .
+ ,(string-append
+ "$_JAVA_OPTIONS${_JAVA_OPTIONS:+ }-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true "
+ "-Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel "
+ "-Dswing.crossplatformlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel"))
+ ;; Smooth trackpad scrolling in Firefox/Icecat.
+ ;; https://wiki.archlinux.org/index.php/Firefox/Tweaks#Pixel-perfect_trackpad_scrolling
+ ("MOZ_USE_XINPUT2" . "1")))
+
+ (simple-service
+ 'gui-scripts home-files-service-type
+ `(;; https://sw.kovidgoyal.net/kitty/kittens/diff/
+ (".local/bin/kdiff" ; show a diff
+ ,(program-file "kdiff" #~(apply execl #$(package-binary "kitty") "kitty"
+ "+kitten" "diff" (cdr (command-line)))))
+ (".local/bin/icat" ; kitty's "catimg" equivalent
+ ,(program-file "icat" #~(apply execl #$(package-binary "kitty") "kitty"
+ "+kitten" "icat" (cdr (command-line)))))
+ (".local/bin/screenlock"
+ ,(program-file
+ "screenlock"
+ #~(begin ; Wrapper around i3lock to turn off the screen and pause notifications.
+ (system* #$(package-binary "dunst" "dunstctl") "set-paused" "true")
+ (system* #$(package-binary "xset") "dpms" "0" "0" "5")
+ ;; We mustn't use `package-binary' here, as we have to pick up the
+ ;; setuid binary for i3lock installed by the system config.
+ (system* "i3lock" "-nc" #$catppuccin-background-color)
+ (system* #$(package-binary "xset") "dpms" "0" "0" "0")
+ (system* #$(package-binary "dunst" "dunstctl") "set-paused" "false"))))
+ ;; With #:recursive? #t, Guix keeps the files' permission bits, i.e. makes them executable.
+ (".local/bin/sessionmenu" ,(local-file "files/sessionmenu" #:recursive? #t)) ; rofi logout/reboot menu
+ (".local/bin/passmenu" ,(local-file "files/passmenu" #:recursive? #t)) ; rofi passwords menu
+ (".local/bin/volume" ,(local-file "files/volume" #:recursive? #t)))))) ; set volume on key press
+
+(home-environment
+ (packages
+ ;; These packages will show up in the home profile, under ~/.guix-home/profile.
+ `(;; Install only bind-utils like dig, not the full suite.
+ (,(specification->package "bind") "utils")
+ ,@(specifications->packages gui-packages)
+ ,@(specifications->packages
+ '(;; CLI tools
+ "adb" "beets" "curl" "dos2unix" "fdupes" "file" "fzf" "git"
+ "gnuplot" "graphviz" "hledger" "imagemagick" "jq" "lesspipe"
+ "nvme-cli" "openssh" "powertop" "pulsemixer" "pv" "python"
+ "python-ipython" "recutils" "rsync" "sbcl" "smartmontools"
+ "source-highlight" "tk" "tmux" "tree" "xxd" "zip" "unzip"
+ "get-iplayer" "ffmpeg" "atomicparsley" "yt-dlp"
+ "neomutt" "mailcap" "lynx" ; mail (lynx for HTML mail)
+ "newsboat" "vdirsyncer" "khal" "khard"
+
+ ;; Ranger can do code highlighting using python-pygments and
+ ;; image previews in kitty using python-pillow.
+ "ranger" "python-pygments" "python-pillow" "mediainfo" "python-pdftotext"
+ "xcwd" ; my own package
+
+ ;; Work
+ "s3cmd" "python-alibuild" ; "python-alidistlint"
+
+ ;; Shell
+ "zsh" "zsh-autosuggestions" "zsh-syntax-highlighting" "zsh-autopair"
+ "zsh-completions" ; my own package
+
+ ;; Development & language servers
+ "make"
+ "shellcheck"
+ "clang" ; for clangd
+ "python-lsp-server"
+ ;; Supported OotB by eglot, but not packaged by guix:
+ ;; https://github.com/mads-hartmann/bash-language-server
+ ;; https://github.com/regen100/cmake-language-server
+ ;; https://github.com/hrsh7th/vscode-langservers-extracted ; {html,css,json}-languageserver
+ ;; https://github.com/golang/tools/tree/master/gopls ; maybe?
+ ;; https://github.com/artempyanykh/marksman ; Markdown
+ ;; https://github.com/jeapostrophe/racket-langserver
+ ;; https://github.com/astoff/digestif ; (La)TeX
+ ;; https://github.com/redhat-developer/yaml-language-server
+ ;; Needs eglot config + not packaged (from lsp-mode):
+ ;; https://github.com/graphql/graphiql/tree/main/packages/graphql-language-service-cli#readme
+ ;; https://github.com/haskell/haskell-language-server / https://github.com/haskell/ghcide
+ ;; https://github.com/eclipse/lemminx
+
+ ;; Emacs
+ "emacs"
+ "emacs-use-package"
+ "emacs-eglot"
+ "emacs-counsel"
+ "emacs-counsel-dash" "sqlite" ; emacs-counsel-dash requires the sqlite3 binary
+ "emacs-ivy"
+ ;; "emacs-company" "emacs-company-quickhelp" "emacs-company-posframe"
+ "emacs-corfu" "emacs-corfu-doc"
+ "emacs-autothemer" ; for catppuccin/emacs
+ "emacs-undo-tree"
+ "emacs-aggressive-indent"
+ "emacs-which-key"
+ "emacs-smart-mode-line"
+ "emacs-diminish"
+ "emacs-rainbow-mode"
+ "emacs-guix"
+ ;; TODO: "emacs-editorconfig" "emacs-sly" "emacs-sly-macrostep"
+ "emacs-org" ; "emacs-org-modern" theme?
+
+ "emacs-evil"
+ "emacs-evil-collection"
+ "emacs-evil-expat" ; for :reverse, :remove, :rename, :colo, :g*, ... ex commands
+ "emacs-evil-surround"
+ ;; "emacs-evil-owl" ; tests failing
+ "emacs-evil-args"
+ "emacs-evil-numbers"
+ "emacs-evil-multiedit"
+ "emacs-evil-goggles"
+ "emacs-evil-traces"
+ "emacs-evil-commentary"
+ "emacs-evil-replace-with-register"
+ "emacs-evil-org"
+ "emacs-evil-markdown"
+ "emacs-evil-tex"
+ "emacs-evil-text-object-python"
+
+ "emacs-geiser" "emacs-geiser-racket" "emacs-geiser-guile"
+ ;; "emacs-macrostep-geiser"
+ "emacs-flymake-collection" ; "emacs-flymake-flycheck" ; if needed
+ "emacs-cmake-mode" "emacs-gnuplot" "emacs-graphviz-dot-mode"
+ "emacs-hcl-mode" "emacs-ledger-mode" "emacs-mmm-mode"
+ "emacs-puppet-mode" "emacs-rec-mode" "emacs-web-mode"
+ "emacs-yaml-mode"))))
+
+ ;; To search for available home services, run 'guix home search KEYWORD'.
+ (services
+ (cons*
+ ;; User shepherd services.
+ (simple-service
+ 'terminal-services home-shepherd-service-type
+ (list
+ (shepherd-service
+ (documentation "Emacs server; connect using emacsclient.")
+ (provision '(emacs))
+ (start #~(make-forkexec-constructor
+ (list #$(package-binary "emacs") "--fg-daemon")))
+ (stop #~(make-kill-destructor)))
+ (shepherd-service
+ (documentation "GPG agent; caches key passwords.")
+ (provision '(gpg-agent))
+ (start #~(lambda _
+ (invoke #$(package-binary "gnupg" "gpg-agent")
+ "--daemon" "--no-detach")))
+ (stop #~(lambda _
+ (invoke "gpg-connect-agent" "killagent" "/bye"))))))
+
+ (simple-service
+ 'terminal-cronjobs home-mcron-service-type
+ (list #~(job "15 */2 * * *" ; every two hours at HH:15
+ (string-append #$(package-binary "vdirsyncer") " metasync"))
+ #~(job "0,30 * * * *" ; every half hour
+ (string-append #$(package-binary "vdirsyncer") " sync"))))
+
+ ;; Configuration files for terminal-only programs in $XDG_CONFIG_HOME.
+ (service
+ home-xdg-configuration-files-service-type
+ `(;; All alibuild needs is an empty file.
+ ("alibuild/disable-analytics"
+ ,(plain-file "alibuild-disable-analytics" ""))
+ ("git/config" ,(local-file "files/gitconfig"))
+ ("htop/htoprc" ,(local-file "files/htoprc"))
+ ("khal/config" ,(local-file "files/khal.conf"))
+ ("khard/khard.conf" ,(local-file "files/khard.conf"))
+ ("lesskey" ,(local-file "files/lesskey"))
+ ("mutt/muttrc" ,(local-file "files/muttrc"))
+ ("mutt/catppuccin.muttrc"
+ ,(local-file #.(if (string=? catppuccin-theme-variant "latte")
+ "files/catppuccin/neomutt/latte-neomuttrc"
+ "files/catppuccin/neomutt/neomuttrc")))
+ ("newsboat/config" ,(local-file "files/newsboat.conf"))
+ ("newsboat/config.catppuccin"
+ ,(local-file #.(if (string=? catppuccin-theme-variant "latte")
+ "files/catppuccin/newsboat/themes/latte"
+ "files/catppuccin/newsboat/themes/dark")
+ "newsboat-theme.conf"))
+ ("ranger/rc.conf" ,(local-file "files/ranger.conf"))
+ ("user-dirs.locale" ,(plain-file "user-dirs.locale" "C")) ; Not sure if this is needed. Arch has it.
+ ("user-dirs.dirs" ,(local-file "files/user-dirs.dirs"))
+ ("vdirsyncer/config" ,(local-file "files/vdirsyncer.conf"))
+ ("X11/XCompose" ,(local-file "files/XCompose")) ; see also: $XCOMPOSEFILE variable
+ ("X11/Xresources" ,(local-file "files/Xresources"))))
+
+ (simple-service
+ 'terminal-files home-files-service-type
+ `((".mailcap" ,(local-file "files/mailcap"))
+ ;; The file from git main is newer than the one bundled with the
+ ;; packaged neomutt version and contains a few fixes.
+ (".local/bin/mutt_oauth2.py"
+ ,(local-file "files/neomutt/contrib/oauth2/mutt_oauth2.py" #:recursive? #t))
+ ;; GnuPG config files must be in ~/.local/share/gnupg, not ~/.config,
+ ;; so we can't use `home-xdg-configuration-files-service-type'.
+ (".local/share/gnupg/gpg.conf" ,(local-file "files/gpg.conf"))
+ (".local/share/gnupg/gpg-agent.conf"
+ ,(mixed-text-file "gpg-agent.conf" "\
+pinentry-program " (package-binary "pinentry-rofi") "
+# Needed if spawning lots of parallel gpg --decrypt processes. https://dev.gnupg.org/T3530
+auto-expand-secmem
+"))))
+
+ (service home-zsh-service-type
+ (home-zsh-configuration
+ (zshrc (list (local-file "files/zshrc")
+ (local-file "files/prompt.zsh")))))
+
+ (simple-service
+ 'terminal-environment-variables home-environment-variables-service-type
+ `(;; Path to my own package definitions. If invoking `guix home'
+ ;; afresh, this needs to be set manually to find these packages.
+ ("GUIX_PACKAGE_PATH" . "$HOME/src/packages/guix")
+
+ ;; Prepend my own binaries to $PATH. These should probably all
+ ;; be managed through `home-files-service-type'.
+ ("PATH" . "$HOME/.local/bin${PATH:+:}$PATH")
+
+ ;; Default terminal-related applications
+ ("EDITOR" . "emacsclient -qc")
+ ;; Tell emacsclient to return immediately after opening the
+ ;; file. I can't put this in $EDITOR as many programs expect
+ ;; $EDITOR to exit only when the user is done editing.
+ ("ASYNC_EDITOR" . "emacsclient -qcn")
+ ("PAGER" . "less")
+ ;; Guix force-overrides $LESS by default, so force-force it to do what I want instead.
+ ;; `less' reads the `lesskey' file configured above.
+ ("GUIX_PAGER" . "env -u LESS less")
+ ;; To make LESS_TERMCAP_* variables (set in lesskey) apply to man pages in kitty.
+ ("GROFF_NO_SGR" . "1")
+
+ ;; Shell history -- primarily for zsh, but Emacs' eshell uses this too
+ ("HISTSIZE" . "10000000")
+
+ ;; ("NVIM_TUI_ENABLE_CURSOR_SHAPE" . "1")
+ ("LEDGER_FILE" . "$HOME/sync/ledger/ledger.journal")
+ ("GTAGSLABEL" . "pygments")
+
+ ;; Disable at-spi-dbus-launcher accessibility service.
+ ("NO_AT_BRIDGE" . "1")
+
+ ;; Auto-compilation is annoying and creates a bunch of files that are never cleaned up.
+ ("GUILE_AUTO_COMPILE" . "0")
+
+ ;; For some reason, Guix doesn't seem to add these paths automatically.
+ ("GUILE_LOAD_PATH" .
+ ,(string-append
+ "$GUIX_PACKAGE_PATH:"
+ "$XDG_CONFIG_HOME/guix/current/share/guile/site/3.0"
+ "${GUILE_LOAD_PATH:+:}$GUILE_LOAD_PATH"))
+ ("GUILE_LOAD_COMPILED_PATH" .
+ ,(string-append
+ "$XDG_CONFIG_HOME/guix/current/lib/guile/3.0/site-ccache:"
+ "$XDG_CONFIG_HOME/guix/current/share/guile/site/3.0"
+ "${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_LOAD_COMPILED_PATH"))))
+
+ ;; XDG basedir spec compliance for various programs
+ ;; See: https://wiki.archlinux.org/index.php/XDG_Base_Directory for a list of programs.
+ ;; The `home-xdg-base-directories' service (enabled by default) sets $XDG_* variables for us.
+ (simple-service
+ 'xdg-spec-compliance home-environment-variables-service-type
+ '(("ANDROID_EMULATOR_HOME" . "$XDG_DATA_HOME/android-emulator")
+ ("ASPELL_CONF" . "per-conf $XDG_CONFIG_HOME/aspell/aspell.conf; home-dir $XDG_DATA_HOME/aspell")
+ ("BUP_DIR" . "$XDG_DATA_HOME/bup")
+ ("CARGO_HOME" . "$XDG_DATA_HOME/cargo")
+ ("DSHGROUP_PATH" . "$XDG_DATA_HOME/dsh/group:/etc/dsh/group")
+ ("ELECTRUMDIR" . "$XDG_DATA_HOME/electrum")
+ ("FG_HOME" . "$XDG_DATA_HOME/fgfs")
+ ("GETIPLAYERUSERPREFS" . "$XDG_DATA_HOME/get_iplayer")
+ ("GNUPGHOME" . "$XDG_DATA_HOME/gnupg")
+ ("GTK2_RC_FILES" . "$XDG_CONFIG_HOME/gtk-2.0/gtkrc")
+ ("ICEAUTHORITY" . "$XDG_CACHE_HOME/ICEauthority")
+ ("INPUTRC" . "$XDG_CONFIG_HOME/readline/inputrc")
+ ("IPYTHONDIR" . "$XDG_CONFIG_HOME/ipython")
+ ("JUPYTER_CONFIG_DIR" . "$XDG_CONFIG_HOME/jupyter")
+ ;; KONAN_DATA_DIR=~/.konan by default; grows to multiple GiB.
+ ;; https://discuss.kotlinlang.org/t/change-konan-folder-location/18309
+ ("KONAN_DATA_DIR" . "$XDG_CACHE_HOME/konan")
+ ("NPM_CONFIG_USERCONFIG" . "$XDG_CONFIG_HOME/npm/npmrc")
+ ("PASSWORD_STORE_DIR" . "$XDG_DATA_HOME/password-store")
+ ("PLTUSERHOME" . "$XDG_DATA_HOME/racket")
+ ("PYLINTHOME" . "$XDG_CACHE_HOME/pylint")
+ ("PYLINTRC" . "$XDG_CONFIG_HOME/pylint/pylintrc")
+ ("RECOLL_CONFDIR" . "$XDG_CONFIG_HOME/recoll")
+ ("RLWRAP_HOME" . "$XDG_DATA_HOME/rlwrap")
+ ("STACK_ROOT" . "$XDG_DATA_HOME/stack")
+ ("TMUX_TMPDIR" . "$XDG_RUNTIME_DIR")
+ ("WEECHAT_HOME" . "$XDG_CONFIG_HOME/weechat")
+ ("XCOMPOSECACHE" . "$XDG_CACHE_HOME/X11/XCompose")
+ ("XCOMPOSEFILE" . "$XDG_CONFIG_HOME/X11/XCompose")
+ ("ZDOTDIR" . "$XDG_CONFIG_HOME/zsh")
+ ("_JAVA_OPTIONS" .
+ "$_JAVA_OPTIONS${_JAVA_OPTIONS:+ }-Djava.util.prefs.userRoot=$XDG_CONFIG_HOME/java")))
+
+ (service
+ home-openssh-service-type
+ (home-openssh-configuration
+ (hosts
+ (let ((my-hosts/ports
+ '(("vin.twilken.net" . 50022)
+ ("vin.wg" . 50022)
+ ("pi3.twilken.net" . 51022)
+ ("pi3.wg" . 51022)
+ ("lud.twilken.net" . 22022)
+ ("lud.wg" . 22)
+ ("matrix.twilken.net" . 22022)))
+ (git-hosts
+ '("github.com" "ssh.github.com" "bitbucket.org" "gitlab.cern.ch"))
+ (cern-ci-hosts/users
+ '(("alimonitor.cern.ch" . "alibuild")
+ ("alinsure.cern.ch" . "alibuild")
+ ("alibuildmac*.cern.ch" . "alibuild")
+ ("aido*osx*.cern.ch" . "alibuild")
+ ("alibuild*.cern.ch" . "root")
+ ("alissandra*.cern.ch" . "root")
+ ("alimesos*.cern.ch" . "root")
+ ("alientest*.cern.ch" . "root")
+ ("aliflow*.cern.ch" . "root")
+ ("alijenkins*.cern.ch" . "root"))))
+
+ (define (cern-extra-content delegate-kerberos-credentials?)
+ (string-append "\
+# Kerberos authentication
+GSSAPIAuthentication yes
+GSSAPIDelegateCredentials " (if delegate-kerberos-credentials? "yes" "no") "
+PreferredAuthentications gssapi-keyex,gssapi-with-mic,publickey,password,keyboard-interactive
+ProxyJump \"twilken@lxplus.cern.ch\"
+"))
+
+ ;; Earlier rules take precedence over later ones.
+ `(,(openssh-host (name "*.srcf.net") (user "tw466"))
+ ,(openssh-host (name "*.fritz.box")
+ (extra-content "ProxyJump lud.twilken.net"))
+ ,@(map (lambda (host port)
+ (openssh-host (name host) (port port) (user "timo")))
+ (map car my-hosts/ports)
+ (map cdr my-hosts/ports))
+ ,@(map (lambda (host) (openssh-host (name host) (user "git"))) git-hosts)
+ ;; BitBucket apparently only supports ssh-rsa.
+ ,(openssh-host (name "bitbucket.org")
+ (host-key-algorithms '("+ssh-rsa"))
+ (accepted-key-types '("+ssh-rsa")))
+ ,(openssh-host (name "gitlab.cern.ch")
+ (port 7999)
+ (extra-content "ProxyJump none")) ; no jump needed
+ ;; Avoid ProxyJump loops.
+ ,(openssh-host (name "lxplus.cern.ch")
+ (extra-content "ProxyJump none"))
+ ,(openssh-host (name "twilkendesktop.cern.ch")
+ (port 22022)
+ (forward-x11? #t)
+ (extra-content (cern-extra-content #t)))
+ ,@(map (lambda (host user)
+ (openssh-host (name host)
+ (user user)
+ (identity-file "~/.local/share/ssh-keys/alicern_id_rsa")))
+ (map car cern-ci-hosts/users)
+ (map cdr cern-ci-hosts/users))
+ ,(openssh-host (name "*.cern.ch")
+ (user "twilken")
+ (identity-file "~/.local/share/ssh-keys/cern_id_rsa")
+ (extra-content (cern-extra-content #f)))
+ ;; Default SSH key. This isn't in ~/.ssh as `home-openssh-service-type'
+ ;; manages that and might delete keys there.
+ ,(openssh-host (name "*")
+ (identity-file "~/.local/share/ssh-keys/id_rsa")
+ ;; Remote servers probably don't know about xterm-kitty.
+ (extra-content "SetEnv TERM=xterm-256color")))))))
+
+ (simple-service ; this can't be a `service' as that would remove the 'guix channel
+ 'nonfree-channels home-channels-service-type
+ (list
+ ;; Nonguix is also needed system-wide for non-free drivers!
+ (channel
+ (name 'nonguix)
+ (url "https://gitlab.com/nonguix/nonguix")
+ ;; Enable signature verification:
+ (introduction
+ (make-channel-introduction
+ "897c1a470da759236cc11798f4e0a5f7d4d59fbc"
+ (openpgp-fingerprint
+ "2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5"))))
+ (channel
+ (name 'guix-gaming-games)
+ (url "https://gitlab.com/guix-gaming-channels/games")
+ ;; Enable signature verification:
+ (introduction
+ (make-channel-introduction
+ "c23d64f1b8cc086659f8781b27ab6c7314c5cca5"
+ (openpgp-fingerprint
+ "50F3 3E2E 5B0C 3D90 0424 ABE8 9BDC F497 A4BB CC7F"))))))
+
+ gui-services)))