Introduction

These are my dotfiles. Please see ../dotfiles/.emacs.d/index.html for my Emacs configuration, this file contains non-Emacs stuff.

General information about this file

  • org-babel-tangle to tangle all files.
  • C-u org-babel-tangle to only tangle current file.
  • This file uses «this» syntax in source code blocks to embed noweb code instead of <<that>> syntax. This allows me to use noweb inside bash blocks without interfering with its syntax highlighter. See Postamble.
  • You'll want to make sure some of the files should tangle with executable permission. For that:

    (add-hook 'org-babel-post-tangle-hook 'executable-make-buffer-file-executable-if-script-p)
    
  • Also some configuration files are tangled based on a condition. Here are the convenience condition functions:

    (defun when-darwin (file-path)
      (if (eq system-type 'darwin)
        file-path "no"))
    
    (defun when-linux (file-path)
      (if (eq system-type 'gnu/linux)
          file-path "no"))
    
    (cl-defun when-on (&key linux darwin)
      (pcase system-type
        ('darwin darwin)
        ('gnu/linux linux)
        (_ "no")))
    
  • Here is the code that puts them together, this block is executed during the startup of this file. See Postamble (at the end of this file) where this code-block is called for execution.

    
    (defun when-darwin (file-path)
      (if (eq system-type 'darwin)
        file-path "no"))
    
    (defun when-linux (file-path)
      (if (eq system-type 'gnu/linux)
          file-path "no"))
    
    (cl-defun when-on (&key linux darwin)
      (pcase system-type
        ('darwin darwin)
        ('gnu/linux linux)
        (_ "no")))
    
    when-on
    

Supplementary functions

You can run arbitrary shell commands inside a code block like this: «sh("which git")». This can be useful where an output of a command needs to be statically placed in an exported file.

(let (output status)
  (with-temp-buffer
    (setq status (call-process-shell-command code nil (current-buffer)))
    (setq output (string-trim (buffer-substring-no-properties (point-min) (point-max)))))
  (if (or (not (eq status 0))
          (eq (length output) 0))
      default
    output))

…and because I use «sh("which X")» a lot, I also have this:

(string-trim (shell-command-to-string (format "which '%s'" binary)))

The following is just the babel version of (im-when-on ...) function defined above. It helps you insert the text to a file based on current operating system.

(im-when-on :darwin darwin :linux linux)

And this is for getting values of elisp variables:

(symbol-value (intern var))

Programming languages

R

options(repos = c(CRAN = "https://cran.rstudio.com"))

Guile

Just activate readline.

(use-modules (ice-9 readline))
(activate-readline)

Javascript

Install global packages to user-local.

prefix=${HOME}/.npm-packages

macOS

General configuration

# Disable gatekeeper, allows you to install apps from unidentified developers
sudo spctl --master-disable

Start applications at boot

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>${name}</string>

    <key>ProgramArguments</key>
    <array>
      ${args}
    </array>

    <key>KeepAlive</key>
    <true/>

    <key>RunAtLoad</key>
    <true/>

    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>${HOME}/.nix-profile/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
    </dict>
</dict>
</plist>
(replace-regexp-in-string
 "\\${[a-zA-Z]+}"
 (lambda (substr)
   (pcase substr
     ("${name}" (format "net.isamert.%s" name))
     ("${args}" (replace-regexp-in-string
                 "${HOME}"
                 (expand-file-name "~")
                 (string-join (mapcar (lambda (it) (format "<string>%s</string>" it)) args) "\n") t t))
     ("${HOME}" (expand-file-name "~"))
     (_ "???")))
 (save-excursion
   (org-babel-goto-named-src-block "mac-launchagent-template")
   (org-element-property :value (org-element-at-point)))
 t t)

Application specific shortcuts

Here is a great tool for exporting and importing your application specific bindings in Keyboard Shortcuts settings page. I was hesitant to use this feature because it was not easy to replicate but with this tool it's quite convenient.

# Installation
curl -L -o ~/.local/bin/mac-kb-exporter.php https://gist.githubusercontent.com/miclf/bf4b0cb6de9ead726197db7ed3d937b5/raw/a135140b52014273d59567f24983ded99e30ac2d/macos_keyboard_shortcuts_exporter_importer.php
chmod +x ~/.local/bin/mac-kb-exporter.php

# Usage
# mac-kb-exporter.php save ~/.config/mac-application-shortcuts.json
# mac-kb-exporter.php load ~/.config/mac-application-shortcuts.json
{
    "org.mozilla.firefox": {
        "Close Tab": "^W",
        "Close Window": "⌘W",
        "Find Again": "^G",
        "Find in Page...": "^F",
        "History": "^H",
        "Bookmarks": "^B",
        "New Tab": "^T"
    },
    "com.google.Chrome": {
        "Find...": "^F",
        "New Tab": "^T",
        "Open Location...": "^L"
    }
}

aerospace

I used to use yabai but my main pain point was the virtual desktops. Aerospace fixes it completely and it's more responsive.

brew install --cask nikitabobko/tap/aerospace

# From guide: https://nikitabobko.github.io/AeroSpace/guide

# This helps with expose functionality
defaults write com.apple.dock expose-group-apps -bool true && killall Dock

# This helps generally
defaults write com.apple.spaces spans-displays -bool true && killall SystemUIServer
# https://nikitabobko.github.io/AeroSpace/guide
# https://nikitabobko.github.io/AeroSpace/commands

after-login-command = []
after-startup-command = []
start-at-login = true
enable-normalization-flatten-containers = true
enable-normalization-opposite-orientation-for-nested-containers = true
accordion-padding = 30
default-root-container-layout = 'tiles'
default-root-container-orientation = 'auto'
key-mapping.preset = 'qwerty'

# Mouse lazily follows focused monitor (default in i3)
on-focused-monitor-changed = ['move-mouse monitor-lazy-center']
# Mouse lazily follows any focus (window or workspace)
#on-focus-changed = ['move-mouse window-lazy-center']

[gaps]
inner.horizontal = 0
inner.vertical =   0
outer.left =       0
outer.bottom =     0
outer.top =        0
outer.right =      0

[workspace-to-monitor-force-assignment]
#  When a second monitor is connected, assign workspaces 1-5 to it,
# regardless of whether it's set as the system's main or secondary
# display. This ensures the external monitor effectively serves as the
# main workspace without extra configuration. Additionally, by not
# setting it as the "main" monitor in system settings, the top bar
# remains hidden on the external display as I would like it to
# be. Workspaces 1-5 on the external monitor provide main-monitor
# functionality while keeping system UI elements on the primary
# screen.
1 = ['secondary', 'main', 'built-in']
2 = ['secondary', 'main', 'built-in']
3 = ['secondary', 'main', 'built-in']
4 = ['secondary', 'main', 'built-in']
5 = ['secondary', 'main', 'built-in']
6 = ['built-in', 'secondary']
7 = ['built-in', 'secondary']
8 = ['built-in', 'secondary']
9 = ['built-in', 'secondary']
0 = ['built-in', 'secondary']

# See https://nikitabobko.github.io/AeroSpace/guide#exec-env-vars
[exec]
inherit-env-vars = true

[exec.env-vars]
PATH = '/opt/homebrew/bin:/opt/homebrew/sbin:${HOME}/.bin:${PATH}'

[mode.main.binding]
cmd-period = 'focus-monitor right'
cmd-comma = 'focus-monitor left'
cmd-shift-period = 'move-node-to-monitor right'
cmd-shift-comma = 'move-node-to-monitor left'

cmd-r = 'mode resize'
cmd-f = 'fullscreen'

cmd-e = 'layout v_accordion h_accordion'
cmd-y = 'layout tiles horizontal vertical'
cmd-t = 'layout floating tiling'

cmd-h = 'focus --boundaries-action stop left'
cmd-j = 'focus --boundaries-action stop down'
cmd-k = 'focus --boundaries-action stop up'
cmd-l = 'focus --boundaries-action stop right'

cmd-shift-h = 'move left'
cmd-shift-j = 'move down'
cmd-shift-k = 'move up'
cmd-shift-l = 'move right'

cmd-1 = 'workspace 1'
cmd-2 = 'workspace 2'
cmd-3 = 'workspace 3'
cmd-4 = 'workspace 4'
cmd-5 = 'workspace 5'
cmd-6 = 'workspace 6'
cmd-7 = 'workspace 7'
cmd-8 = 'workspace 8'
cmd-9 = 'workspace 9'
cmd-0 = 'workspace 0'

cmd-shift-1 = 'move-node-to-workspace 1'
cmd-shift-2 = 'move-node-to-workspace 2'
cmd-shift-3 = 'move-node-to-workspace 3'
cmd-shift-4 = 'move-node-to-workspace 4'
cmd-shift-5 = 'move-node-to-workspace 5'
cmd-shift-6 = 'move-node-to-workspace 6'
cmd-shift-7 = 'move-node-to-workspace 7'
cmd-shift-8 = 'move-node-to-workspace 8'
cmd-shift-9 = 'move-node-to-workspace 9'

cmd-tab = 'workspace-back-and-forth'

[mode.resize.binding]
h = 'resize smart -50'
l = 'resize smart +50'
esc = 'mode main'

[[on-window-detected]]
    if.app-name-regex-substring = '(Emacs|Alacritty)'
    if.during-aerospace-startup = true
    check-further-callbacks = true
    run = ['move-node-to-workspace 1']

[[on-window-detected]]
    if.app-name-regex-substring = '(Firefox|Chrome)'
    if.during-aerospace-startup = true
    check-further-callbacks = true
    run = ['move-node-to-workspace 2']

[[on-window-detected]]
    if.app-name-regex-substring = 'zoom'
    if.during-aerospace-startup = true
    check-further-callbacks = true
    run = ['move-node-to-workspace 3']

[[on-window-detected]]
    if.app-name-regex-substring = 'krita'
    if.during-aerospace-startup = true
    check-further-callbacks = true
    run = ['move-node-to-workspace 5']

[[on-window-detected]]
    if.app-id = 'org.gnu.Emacs'
    if.window-title-regex-substring = 'emacs-popup'
    check-further-callbacks = true
    run = ['layout floating']

skhd

This is the global keybinding manager for OSX. Here is an example configuration for yabai and here is a more generic example configuration demonstrating it's capabilities.

It can be installed through homebrew:

brew install koekeishiya/formulae/skhd
skhd --install-service
skhd --start-service

Application shortcuts

hyper - i : emacsclient --eval "(im-globally (im-select-any-snippet))"
hyper - o : emacsclient --eval "(im-globally (im-people))"
hyper - g : emacsclient --eval "(im-globally (im-gitlab-select-project))"
hyper - v : emacsclient --eval "(empv-toggle-video)"
hyper - return : emacsclient -c -F '((tab-bar-lines . 0) (name . "emacs-temp"))' --eval '(ghostel "new")'

Firefox specific

  • cmd-l (focus urlbar) clashes with my global shortcut, so I simply want to remap it to ctrl-l in Firefox. Unfortunately, Firefox does not expose cmd-l in it's menu, so its not possible to remap it natively using macOS' "Keyboard Shortcuts" settings page. Here I remap ctrl-l to F6 (which also provides the "focus urlbar" functionality).
ctrl - l [
  "Firefox" : skhd -k "f6"
  "LibreWolf" : skhd -k "f6"
  * ~
]
ctrl - f [
  "Firefox" : skhd -k "f3"
  "LibreWolf" : skhd -k "f3"
  * ~
]

Rootless sshd

Port 2222
HostKey /home/isamert/.config/sshd/hostkey
PidFile /home/isamert/.config/sshd/pid

Create the host key and enable starting it at boot:

ssh-keygen -t rsa -f ~/.config/sshd/hostkey -N ''
launchctl load -w ~/Library/LaunchAgents/net.isamert.sshd.plist
  <?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  <plist version="1.0">
  <dict>
      <key>Label</key>
      <string>net.isamert.sshd</string>

      <key>ProgramArguments</key>
      <array>
        <string>/usr/sbin/sshd</string>
<string>-f</string>
<string>/home/isamert/.config/sshd/cfg</string>
      </array>

      <key>KeepAlive</key>
      <true/>

      <key>RunAtLoad</key>
      <true/>

      <key>EnvironmentVariables</key>
      <dict>
          <key>PATH</key>
          <string>/home/isamert/.nix-profile/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
      </dict>
  </dict>
  </plist>

To enable it, run this:

launchctl load -w ~/Library/LaunchAgents/net.isamert.sshd.plist

Start some applications at login

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>net.isamert.kdeconnect</string>

    <key>ProgramArguments</key>
    <array>
      <string>/Applications/kdeconnect-indicator.app/Contents/MacOS/kdeconnect-indicator</string>
    </array>

    <key>KeepAlive</key>
    <true/>

    <key>RunAtLoad</key>
    <true/>

    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/home/isamert/.nix-profile/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
    </dict>
</dict>
</plist>

launchctl load -w ~/Library/LaunchAgents/net.isamert.kdeconnect.plist

Clear all notifications with a keypress

Save the script by running the next code block:

# This fetches the latest version of the script, be sure to verify before
curl 'https://gist.githubusercontent.com/lancethomps/a5ac103f334b171f70ce2ff983220b4f/raw' > ~/.local/bin/macos-clear-all-notifications.js

And here is the skhd binding:

hyper - y : osascript -l JavaScript "$HOME/.local/bin/macos-clear-all-notifications.js"

hammerspoon

I use it minimally for some quality-of-life improvements.

brew install hammerspoon

Enable IPC.

-- * Enable IPC

require("hs.ipc")

Emacs Integration

I have a little menubar that shows the current clocked in task and the current tab-bar of Emacs in MacOS menu. I was going to use a socket instead of an HTTP server but didn't manage to get hs.socket to work for some reason.

-- * Menubar & Emacs

menubar = hs.menubar.new()
menubar:setTitle("-")

workspace = ""
task = ""

function updateTitle()
   -- TODO: https://www.hammerspoon.org/docs/hs.styledtext.html
   local taskIcon = (task == "") and "✖" or "✓"
   menubar:setTitle(string.format("%s %s | ⭾ [%s]", taskIcon, task, workspace))
end

function cb(verb, path, headers, body)
   if path == "/task" then
      task = body
      updateTitle()
   elseif path == "/workspace" then
      workspace = body
      updateTitle()
   else
      print(">> Unknown request to: ", path, headers, body)
   end
   return "ok", 200, {}
end

server = hs.httpserver.new()
server:setPort(4562)
server:setCallback(cb)
server:start()

Global bindings and unicode characters

-- * LeftRightHotkey

LeftRightHotkey = hs.loadSpoon("LeftRightHotkey")
LeftRightHotkey:start()

-- * Shortcuts

function elisp(code)
   os.execute(string.format("/opt/homebrew/bin/emacsclient --eval \"%s\" &", code))
end

function elispf(code)
   return function() os.execute(string.format("/opt/homebrew/bin/emacsclient --eval \"%s\" &", code)) end
end

function increaseVolume()
   local device = hs.audiodevice.defaultOutputDevice()
   local current = device:volume()
   local new = math.min(current + 5, 100)
   device:setVolume(new)
   hs.alert.show("Volume: "  .. math.floor(new) .. "%")
end

function decreaseVolume()
   local device = hs.audiodevice.defaultOutputDevice()
   local current = device:volume()
   local new = math.max(current - 5, 0)
   device:setVolume(new)
   hs.alert.show("Volume: "  .. math.floor(new) .. "%")
end

function clearNotifications()
   elisp("(im-notif-clear-all)")
   os.execute('osascript -l JavaScript "$HOME/.local/bin/macos-clear-all-notifications.js" &')
end



function xtypef(thing)
   return function() hs.execute("/Users/isamert.gurbuz/.local/bin/xtype '" .. thing .. "'") end
end

HYPER = {"lAlt", "lCtrl", "lCmd", "lShift"};

LeftRightHotkey:bind(HYPER, "l", hs.caffeinate.lockScreen)
LeftRightHotkey:bind(HYPER, "i", elispf('(im-globally (im-select-any-snippet))'))
LeftRightHotkey:bind(HYPER, "o", elispf('(im-globally (im-people))'))
LeftRightHotkey:bind(HYPER, "g", elispf('(im-globally (im-gitlab-select-project))'))
LeftRightHotkey:bind(HYPER, "y", clearNotifications)
LeftRightHotkey:bind(HYPER, "return", function() os.execute('/opt/homebrew/bin/alacritty &') end)
LeftRightHotkey:bind(HYPER, "9", decreaseVolume)
LeftRightHotkey:bind(HYPER, "0", increaseVolume)

-- * Unicode stuff

LeftRightHotkey:bind({"rAlt"}, "f", xtypef('=>'))
LeftRightHotkey:bind({"rAlt"}, "g", xtypef('⟹'))
LeftRightHotkey:bind({"rAlt"}, "w", xtypef('↑'))
LeftRightHotkey:bind({"rAlt"}, "s", xtypef('↓'))
-- LeftRightHotkey:bind({"rAlt"}, "a", xtypef('←')) -- This breaks stuff all other bindings. Probably because a is 0, haven't figured it out yet.
LeftRightHotkey:bind({"rAlt"}, "d", xtypef('→'))
LeftRightHotkey:bind({"rAlt"}, "r", xtypef('->'))
LeftRightHotkey:bind({"rAlt"}, "t", xtypef('✔'))
LeftRightHotkey:bind({"rAlt"}, "b", xtypef('λ'))

-- Box drawing
LeftRightHotkey:bind({"rAlt"}, "y", xtypef('┌'))
LeftRightHotkey:bind({"rAlt"}, "h", xtypef('│'))
LeftRightHotkey:bind({"rAlt"}, "n", xtypef('└'))
LeftRightHotkey:bind({"rAlt"}, "u", xtypef('┐'))
LeftRightHotkey:bind({"rAlt"}, "j", xtypef('─'))
LeftRightHotkey:bind({"rAlt"}, "m", xtypef('┘'))

Other files

-- * Work stuff

dofile(hs.configdir .. "/modules/work.lua")

The notch and items in the MacOS menu bar

If the icons in the menubar takes a lot of place, some of them goes under the notch and becomes totally invisible, non-interactable. The following two things help:

  • Setting a lower spacing and padding for the icons (need to restart your computer afterwards):
defaults -currentHost write -globalDomain NSStatusItemSpacing -int 5
defaults -currentHost write -globalDomain NSStatusItemSelectionPadding -int 3
  • Hidden Bar → This lets you selectively hide some items so that only the things you want stay visible.

Ghostty

# window-decoration = false
command = /opt/homebrew/bin/tmux
macos-titlebar-style = hidden
macos-option-as-alt = left
background-opacity = 1.00
confirm-close-surface = false

Toggle GlobalProtect VPN AppleScript

To set up the script as an application, follow these steps:

  1. Open Script Editor.
  2. Paste the provided script into the editor.
  3. Save the script with the following options:
    • Set the file format to "Application".
    • Save under the /Applications directory.
    • Name it something like "ToggleGlobalProtectVPN".

After completing these steps:

  • The application will now appear in your app launcher.
  • You can toggle the VPN directly from there.

Note:

  • The first time you run the application, you will be prompted to grant permission. Ensure you allow it.

Modified from: https://gist.github.com/tmanternach/cbd4c213eab8569e38d6cd021b6255e5

tell application "System Events" to tell process "GlobalProtect"
        click menu bar item 1 of menu bar 2
        set statusText to name of static text 1 of window 1
        if statusText is "Disconnected" then
                # GlobalProtect is disconnected, so let's connect
                click button "Connect" of window 1
                set entireContents to entire contents of window 1
                set resultStatus to "Connected"
        else if statusText is "Connected" then
                # GlobalProtect is connected, so let's disconnect
                set windowText to entire contents of window 1
                repeat with theItem in windowText
                        if (name of theItem contains "Disconnect") then
                                click theItem
                        end if
                end repeat
                set resultStatus to "Disconnected"
        else
                set resultStatus to statusText
        end if
        click menu bar item 1 of menu bar 2
end tell
return resultStatus

Check if VPN is Open script

#!/bin/sh
# If all utun interfaces are UP, this means VPN is open.
! ifconfig | grep -E 'utun[0-9]:' | grep -vq UP

This is an alternative script but takes more time and creates more visual clutter:

#!/usr/bin/osascript
tell application "System Events" to tell process "GlobalProtect"
        click menu bar item 1 of menu bar 2
        set statusText to name of static text 1 of window 1
        set resultStatus to statusText
        click menu bar item 1 of menu bar 2
end tell
return resultStatus

Linux

systemd

Journal files starts to take a lot of disk space. I put a simple limit to that here.

[Journal]
SystemMaxUse=500M

Unit files started with --user will have the following variables.

$HOME/.bin:$HOME/.local/bin:$NPM_PACKAGES/bin:$GOPATH/bin:$HOME/.cargo/bin:$PATH
GOPATH="$HOME/.go"
R_LIBS_USER="$HOME/.rlibs"
NPM_PACKAGES="$HOME/.npm-packages"
NODE_PATH="$HOME/.npm-packages/lib/node_modules"
BROWSER=jaro
VISUAL="jaro --method=edit"
EDITOR="jaro --method=edit"
PATH="$HOME/.bin:$HOME/.local/bin:$NPM_PACKAGES/bin:$GOPATH/bin:$HOME/.cargo/bin:$PATH"

KDE fucks-up the PATH variable (and only the PATH variable) while booting up. This fixes that. I'm not using KDE right now but keeping it here as a reference.

export PATH=$HOME/.bin:$HOME/.local/bin:$NPM_PACKAGES/bin:$GOPATH/bin:$HOME/.cargo/bin:$PATH

After tangling all unit files, run this:

systemctl --user daemon-reload
systemctl --user enable emacsd
systemctl --user enable syncthing

Pacman configuration

Following snippet enables some configurations for pacman:

Parallel
Enables parallel downloads. Really makes a difference, especially while upgrading your system.
Color
Adds color to pacman output.
VerbosePkgLists
This gives you more information about the packages that are going to be installed.
TotalDownload
Adds ETA information for total progress etc.
#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives

#
# GENERAL OPTIONS
#
[options]
# The following paths are commented out with their default values listed.
# If you wish to use different paths, uncomment and update the paths.
#RootDir     = /
#DBPath      = /var/lib/pacman/
#CacheDir    = /var/cache/pacman/pkg/
#LogFile     = /var/log/pacman.log
#GPGDir      = /etc/pacman.d/gnupg/
#HookDir     = /etc/pacman.d/hooks/
HoldPkg     = pacman glibc
#XferCommand = /usr/bin/curl -L -C - -f -o %o %u
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
#CleanMethod = KeepInstalled
Architecture = auto

# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
#IgnorePkg   =
#IgnoreGroup =

#NoUpgrade   =
#NoExtract   =

# Misc options
#UseSyslog
Color
#NoProgressBar
CheckSpace
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
#DisableSandboxFilesystem
#DisableSandboxSyscalls

# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel    = Required DatabaseOptional
LocalFileSigLevel = Optional
#RemoteFileSigLevel = Required

# NOTE: You must run `pacman-key --init` before first using pacman; the local
# keyring can then be populated with the keys of all official Arch Linux
# packagers with `pacman-key --populate archlinux`.

#
# REPOSITORIES
#   - can be defined here or included from another file
#   - pacman will search repositories in the order defined here
#   - local/custom mirrors can be added here or in separate files
#   - repositories listed first will take precedence when packages
#     have identical names, regardless of version number
#   - URLs will have $repo replaced by the name of the current repo
#   - URLs will have $arch replaced by the name of the architecture
#
# Repository entries are of the format:
#       [repo-name]
#       Server = ServerName
#       Include = IncludePath
#
# The header [repo-name] is crucial - it must be present and
# uncommented to enable the repo.
#

# The testing repositories are disabled by default. To enable, uncomment the
# repo name header and Include lines. You can add preferred servers immediately
# after the header, and they will be used before the default mirrors.

#[core-testing]
#Include = /etc/pacman.d/mirrorlist

[core]
Include = /etc/pacman.d/mirrorlist

#[extra-testing]
#Include = /etc/pacman.d/mirrorlist

[extra]
Include = /etc/pacman.d/mirrorlist

# If you want to run 32 bit applications on your x86_64 system,
# enable the multilib repositories as required here.

#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist

[multilib]
Include = /etc/pacman.d/mirrorlist

# An example of a custom package repository.  See the pacman manpage for
# tips on creating your own repositories.
#[custom]
#SigLevel = Optional TrustAll
#Server = file:///home/custompkgs

Fedora (dnf) configuration

[main]
defaultyes=True

External monitor brightness

  • Install ddcutil
  • Enable automatic loading of i2c-dev module with systemd.

    i2c-dev
    
  • Add your user to the i2c group.

    sudo usermod -aG i2c $USER
    
    • The group should've been created by the ddcutil package. If not, do this first:

      sudo groupadd --system i2c
      
  • Give permission to i2c user for /dev/i2c-* devices:

    sudo cp /usr/share/ddcutil/data/45-ddcutil-i2c.rules /etc/udev/rules.d
    # OR do this, if the file does not exist:
    echo 'KERNEL=="i2c-[0-9]*", GROUP="i2c"' >> /etc/udev/rules.d/10-i2c-user-permissions.rules
    

Now you should be able to do:

ddcutil getvcp 10 # Return current brightness value
ddcutil setvcp 10 50 # Set the current brightness value

Also see this gnome extension and it's README for further information: https://github.com/daitj/gnome-display-brightness-ddcutil

Switch monitor on keyboard change

I have a usb splitter that switches between machines when clicked on a physical button. But this does not change my monitors input automatically. The following script is designed to run-according to the given UDEV rule-when a keyboard (my UHK keyboard) my mouse is attached or removed and switches to the right monitor input.

ACTION=="add", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c548", RUN+="/opt/bin/switch-monitor-on-keyboard-change add"
ACTION=="remove", ENV{PRODUCT}=="46d/c548/503", RUN+="/opt/bin/switch-monitor-on-keyboard-change remove"

Reload the udev rules.

sudo udevadm control --reload-rules
#!/usr/bin/env bash

state_file="/tmp/monitor.state"
lock_file="/tmp/monitor.state.lock"

action="$1"
usbc="27"
hdmi="17"

if [[ "$action" == "add" ]]; then
    desired="$hdmi"
elif [[ "$action" == "remove" ]]; then
    desired="$usbc"
else
    echo "Invalid action"
    exit 1
fi

# Use flock to synchronize
(
    flock -n 9 || { echo "Another instance is running. Exiting."; exit 0; }

    last_state=""
    if [[ -f "$state_file" ]]; then
        last_state=$(cat "$state_file")
    fi

    if [[ "$last_state" == "$desired" ]]; then
        echo "State already set to $desired. Doing nothing..."
        exit 0
    fi

    ddcutil setvcp 0x60 "$desired"
    echo "$desired" > "$state_file"

) 9>"$lock_file"

foot terminal emulator

shell = tmux
font = CaskaydiaCove Mono Nerd Font:pixelsize=18
resize-by-cells = no

[csd]
preferred = none

Prefer ipv4 over ipv6 records while resolving DNS

This helps to mitigate some issues I encountered while browsing the web through Emacs. Not quite sure why I have these issues (probably related to my ISP?) but nonetheless:

# Add this to the end of the file:
precedence ::ffff:0:0/96  100

X/Wayland related

xremap

# Run with `RUST_LOG=debug xremap ~/.config/xremap.yml` to see the
# debug logs.

virtual_modifiers:
  - f24

modmap:
  - name: Global
    remap:
      # See: https://github.com/xremap/xremap/pull/339
      CapsLock:
        held: [ctrl_l,super_l,alt_l] # "hyper" key
        alone: Esc
      alt_r: f24

keymap:
  - name: Alt navigation outside Emacs
    application:
      not: emacs
    remap:
      Alt-l: Right
      Alt-j: Down
      Alt-k: Up
      Alt-h: Left
  - name: Global
    remap:
      f24-q: { launch: ["wtype", "↔"] }
      f24-r: { launch: ["wtype", "--", "->"] }
      f24-t: { launch: ["wtype", "✓"] }
      f24-w: { launch: ["wtype", "↑"] }
      f24-a: { launch: ["wtype", "←"] }
      f24-s: { launch: ["wtype", "↓"] }
      f24-d: { launch: ["wtype", "→"] }
      f24-e: { launch: ["wtype", "⇄"] }
      f24-f: { launch: ["wtype", "=>"] }
      f24-g: { launch: ["wtype", "⇒"] }
      f24-x: { launch: ["wtype", "✗"] }
      f24-b: { launch: ["wtype", "λ"] }
      f24-1: { launch: ["wtype", "("] }
      f24-2: { launch: ["wtype", ")"] }
      f24-3: { launch: ["wtype", "«"] }
      f24-4: { launch: ["wtype", "»"] }
[Unit]
Description=XRemap (Keyboard remapper)
PartOf=graphical-session.target
After=graphical-session.target
Requisite=graphical-session.target

[Service]
Type=simple
Restart=always
RestartSec=3
ExecStart=/usr/bin/xremap --watch %h/.config/xremap.yml
Nice=-20

swaywm

Config

GOPATH="$HOME/.go"
R_LIBS_USER="$HOME/.rlibs"
NPM_PACKAGES="$HOME/.npm-packages"
NODE_PATH="$HOME/.npm-packages/lib/node_modules"
BROWSER=jaro
VISUAL="jaro --method=edit"
EDITOR="jaro --method=edit"
PATH="$HOME/.bin:$HOME/.local/bin:$NPM_PACKAGES/bin:$GOPATH/bin:$HOME/.cargo/bin:$PATH"
# * Variables

set $mod Mod4
set $hypr Ctrl+Mod1+Mod4

set $left h
set $down j
set $up k
set $right l

set $term emacsclient -c -F '((tab-bar-lines . 0) (name . "emacs-temp"))' --eval '(ghostel "new")' || footclient
set $calc qalculate-gtk
set $noctalia noctalia msg

# * Monitors

output * render_bit_depth 10

# nwg-displays saves configurations here
include ~/.config/sway/outputs

# Noctalia automatically handles disabling outputs on lid changes etc.

# * Visuals

smart_borders on
focus_wrapping no
default_border pixel 3

# * Behaviour

floating_modifier $mod normal

# * Keybindings - Apps

bindsym $hypr+w exec $calc
bindsym $mod+Return exec $term
bindsym $hypr+Return exec $term

bindsym $hypr+i exec emacsclient --eval "(im-globally (im-select-any-snippet))"
bindsym $hypr+o exec emacsclient --eval "(im-globally (im-people))"
bindsym $hypr+u exec emacsclient --eval "(im-org-capture)"

# * Keybindings - Noctalia

bindsym $hypr+l exec $noctalia session lock
bindsym $hypr+shift+l exec $noctalia session lock-and-suspend
bindsym $hypr+s exec $noctalia screenshot-region
bindsym $hypr+c exec $noctalia panel-toggle clipboard
bindsym $hypr+a exec $noctalia panel-toggle launcher
bindsym $hypr+p exec $noctalia media toggle
bindsym $hypr+bracketright exec $noctalia media next
bindsym $hypr+bracketleft exec $noctalia media previous
bindsym $mod+s exec $noctalia panel-toggle control-center
bindsym $mod+comma exec $noctalia settings-toggle

# * Keybindings - Notifications

bindsym $hypr+y exec $noctalia notification-clear-active

# * Keybindings - Window management

bindsym $mod+$left focus left
bindsym $mod+$down focus down
bindsym $mod+$up focus up
bindsym $mod+$right focus 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+Shift+c reload

bindsym $mod+w kill

bindsym $mod+Shift+e exec swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit'

# * Keybindings - Workspaces

bindsym $mod+1 workspace number 1
bindsym $mod+2 workspace number 2
bindsym $mod+3 workspace number 3
bindsym $mod+4 workspace number 4
bindsym $mod+5 workspace number 5
bindsym $mod+6 workspace number 6
bindsym $mod+7 workspace number 7
bindsym $mod+8 workspace number 8
bindsym $mod+9 workspace number 9
bindsym $mod+0 workspace number 10

bindsym $mod+Shift+1 move container to workspace number 1
bindsym $mod+Shift+2 move container to workspace number 2
bindsym $mod+Shift+3 move container to workspace number 3
bindsym $mod+Shift+4 move container to workspace number 4
bindsym $mod+Shift+5 move container to workspace number 5
bindsym $mod+Shift+6 move container to workspace number 6
bindsym $mod+Shift+7 move container to workspace number 7
bindsym $mod+Shift+8 move container to workspace number 8
bindsym $mod+Shift+9 move container to workspace number 9
bindsym $mod+Shift+0 move container to workspace number 10

# * Keybindings - Splitting and container management

# You can "split" the current object of your focus with
# $mod+b or $mod+v, for horizontal and vertical splits
# respectively.
bindsym $mod+backslash splith
bindsym $mod+minus splitv

# Switch the current container between different layout styles
bindsym $mod+e layout toggle stacking tabbed split

# Make the current focus fullscreen
bindsym $mod+f fullscreen

# Toggle the current focus between tiling and floating mode
bindsym $mod+t floating toggle

# Swap focus between the tiling area and the floating area
bindsym $mod+space focus mode_toggle

# Move focus to the parent container
bindsym $mod+a focus parent

# * Keybindings - Scratchpad

# Move the currently focused window to the scratchpad
bindsym $hypr+e move scratchpad

# Show the next scratchpad window or hide the focused scratchpad window.
# If there are multiple scratchpad windows, this command cycles through them.
bindsym $hypr+r scratchpad show

# * Keybindings - Resize

mode "resize" {
    bindsym $right resize shrink width 10px
    bindsym $up resize grow height 10px
    bindsym $down resize shrink height 10px
    bindsym $left resize grow width 10px

    # Return to default mode
    bindsym Return mode "default"
    bindsym Escape mode "default"
}

bindsym $mod+r mode "resize"

# * Keybindings - Volume/Brightness etc.

# My media keys
bindsym --locked $hypr+0 exec exec $noctalia volume-up
bindsym --locked $hypr+9 exec $noctalia volume-down

# Media keys
bindsym --locked XF86AudioRaiseVolume exec $noctalia volume-up
bindsym --locked XF86AudioLowerVolume exec $noctalia volume-down
bindsym --locked XF86AudioMute exec $noctalia volume-mute
bindsym --locked XF86MonBrightnessUp exec $noctalia brightness-up
bindsym --locked XF86MonBrightnessDown exec $noctalia brightness-down
bindsym --locked XF86AudioMicMute exec $noctalia mic-mute

# * Window configurations

for_window [app_id="qalculate-gtk"] floating enable
for_window [app_id="swayimg"] floating disable
for_window [app_id="com.nextcloud.desktopclient.nextcloud"] floating enable
for_window [app_id="emacs$" title="^emacs-popup$"] floating enable
for_window [app_id="emacs$" title="^org-capture$"] floating enable

# * Launch

# exec --no-startup-id wacom.sh --monitor-and-remap

# This comes with sway-contrib.  Imports the required variables to
# systemd and then starts graphical-session.  Through this the
# startup programs are handled.
include /etc/sway/config.d/*
exec systemctl --user start sway-session.target

exec noctalia

Startup programs

systemctl --user add-wants sway-session.target xremap.service
systemctl --user add-wants sway-session.target foot-server.service

Niri

// * General config

prefer-no-csd

screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"

config-notification {
    disable-failed
}

gestures {
    hot-corners {
        off
    }
}

overview {
    workspace-shadow {
        off
    }
}

environment {
  XDG_CURRENT_DESKTOP "niri"
}

hotkey-overlay {
    skip-at-startup
}

input {
    tablet {
        map-to-output "HDMI-A-2"
        left-handed
        // calibration-matrix 1.0 0.0 0.0 0.0 1.0 0.0
    }
}

layout {
    preset-column-widths {
        proportion 0.25
        proportion 0.33333
        proportion 0.5
        proportion 0.66667
        proportion 0.8
    }

    default-column-width { proportion 0.5; }

    gaps 7

    border {
        off
        width 3
        active-color   "#707070"      // Neutral gray
        inactive-color "#d0d0d0"      // Light gray
        urgent-color   "#cc4444"      // Softer red
    }

    shadow {
        softness 30
        spread 5
        offset x=0 y=5
        color "#0007"
    }

    tab-indicator {
        width 8
        gap 8
        length total-proportion=1.0
        position "top"
        place-within-column
    }
}

window-rule {
    geometry-corner-radius 12
    clip-to-geometry true
    tiled-state true
    draw-border-with-background false
}

// * Window rules

layer-rule {
    match namespace="^quickshell$"
    place-within-backdrop true
}

window-rule {
    match app-id=r#"^org\.gnome\."#
    draw-border-with-background false
    geometry-corner-radius 12
    clip-to-geometry true
}

window-rule {
    match app-id=r#"^gnome-control-center$"#
    match app-id=r#"^pavucontrol$"#
    match app-id=r#"^nm-connection-editor$"#
    default-column-width { proportion 0.5; }
    open-floating false
}

window-rule {
    match app-id=r#"^gnome-calculator$"#
    match app-id=r#"^galculator$"#
    match app-id=r#"^blueman-manager$"#
    match app-id=r#"^org\.gnome\.Nautilus$"#
    match app-id=r#"^steam$"#
    match app-id=r#"^xdg-desktop-portal$"#
    open-floating true
}

window-rule {
    match app-id=r#"^org\.wezfurlong\.wezterm$"#
    match app-id="Alacritty"
    match app-id="zen"
    match app-id="com.mitchellh.ghostty"
    match app-id="kitty"
    draw-border-with-background false
}

window-rule {
    match app-id=r#"firefox$"# title="^Picture-in-Picture$"
    match app-id="zoom"
    open-floating true
}

window-rule {
    match app-id=r#"org.quickshell$"#
    open-floating true
}

window-rule {
    match app-id=r#"emacs$"# title="^emacs-popup$"
    open-floating true
}

debug {
    honor-xdg-activation-with-invalid-serial
}

// * Animations etc

recent-windows {
    binds {
        Alt+Tab         { next-window scope="output"; }
        Alt+Shift+Tab   { previous-window scope="output"; }
        Alt+grave       { next-window filter="app-id"; }
        Alt+Shift+grave { previous-window filter="app-id"; }
    }
}

animations {
    workspace-switch {
        spring damping-ratio=0.80 stiffness=523 epsilon=0.0001
    }
    window-open {
        duration-ms 150
        curve "ease-out-expo"
    }
    window-close {
        duration-ms 150
        curve "ease-out-quad"
    }
    horizontal-view-movement {
        spring damping-ratio=0.85 stiffness=423 epsilon=0.0001
    }
    window-movement {
        spring damping-ratio=0.75 stiffness=323 epsilon=0.0001
    }
    window-resize {
        spring damping-ratio=0.85 stiffness=423 epsilon=0.0001
    }
    config-notification-open-close {
        spring damping-ratio=0.65 stiffness=923 epsilon=0.001
    }
    screenshot-ui-open {
        duration-ms 200
        curve "ease-out-quad"
    }
    overview-open-close {
        spring damping-ratio=0.85 stiffness=800 epsilon=0.0001
    }
}

// * Bindings

// Use `wev` to identify key names

binds {
    // === System & Overview ===
    Mod+O repeat=false { toggle-overview; }
    Mod+Tab repeat=false { toggle-overview; }
    Mod+Shift+Slash { show-hotkey-overlay; }
    Mod+Ctrl+Alt+Shift+Y { spawn "coo" "clear-notifications"; }

    // === Application Launchers ===
    Mod+A  { spawn "dms" "ipc" "call" "spotlight" "toggle"; }
    Mod+Ctrl+Alt+Shift+A { spawn "vicinae" "open"; }
    Mod+Ctrl+Alt+Shift+Return { spawn "coo" "term"; }
    Mod+Ctrl+Alt+Shift+C { spawn "vicinae" "vicinae://extensions/vicinae/clipboard/history"; }
    Mod+Ctrl+Alt+Shift+T {
        spawn "dms" "ipc" "call" "processlist" "focusOrToggle";
    }
    Mod+Ctrl+Alt+Shift+Q hotkey-overlay-title="Power Menu: Toggle" { spawn "dms" "ipc" "call" "powermenu" "toggle"; }
    Mod+Y {
        spawn "dms" "ipc" "call" "dankdash" "wallpaper";
    }
    Mod+Ctrl+Alt+Shift+N hotkey-overlay-title="Notification Center" { spawn "dms" "ipc" "call" "notifications" "toggle"; }

    // === Stuff ===
    Mod+Ctrl+Alt+Shift+I hotkey-overlay-title="Emacs Snippet" {
      spawn "emacsclient" "--eval" "(im-globally (im-select-any-snippet))";
    }
    Mod+Ctrl+Alt+Shift+O hotkey-overlay-title="Emacs People" {
      spawn "emacsclient" "--eval" "(im-globally (im-people))";
    }
    Mod+Ctrl+Alt+Shift+V hotkey-overlay-title="Emacs Video Toggle" {
      spawn "emacsclient" "--eval" "(empv-toggle-video)";
    }

    // === Security ===
    Mod+Ctrl+Alt+Shift+E { quit; }
    Mod+Ctrl+Alt+Shift+B hotkey-overlay-title="Lock Screen" {
        spawn "dms" "ipc" "call" "lock" "lock";
    }
    Ctrl+Alt+Delete hotkey-overlay-title="Task Manager" {
        spawn "dms" "ipc" "call" "processlist" "focusOrToggle";
    }

    // === Audio Controls ===
    Mod+Ctrl+Alt+Shift+0 allow-when-locked=true {
        spawn "dms" "ipc" "call" "audio" "increment" "3";
    }
    Mod+Ctrl+Alt+Shift+9 allow-when-locked=true {
        spawn "dms" "ipc" "call" "audio" "decrement" "3";
    }
    XF86AudioRaiseVolume allow-when-locked=true {
        spawn "dms" "ipc" "call" "audio" "increment" "3";
    }
    XF86AudioLowerVolume allow-when-locked=true {
        spawn "dms" "ipc" "call" "audio" "decrement" "3";
    }
    XF86AudioMute allow-when-locked=true {
        spawn "dms" "ipc" "call" "audio" "mute";
    }
    XF86AudioMicMute allow-when-locked=true {
        spawn "dms" "ipc" "call" "audio" "micmute";
    }
    XF86AudioPause allow-when-locked=true {
        spawn "dms" "ipc" "call" "mpris" "playPause";
    }
    XF86AudioPlay allow-when-locked=true {
        spawn "dms" "ipc" "call" "mpris" "playPause";
    }
    XF86AudioPrev allow-when-locked=true {
        spawn "dms" "ipc" "call" "mpris" "previous";
    }
    XF86AudioNext allow-when-locked=true {
        spawn "dms" "ipc" "call" "mpris" "next";
    }

    // === Brightness Controls ===
    XF86MonBrightnessUp allow-when-locked=true {
       spawn "dms" "ipc" "call" "brightness" "increment" "5" "";
    }
    XF86MonBrightnessDown allow-when-locked=true {
       spawn "dms" "ipc" "call" "brightness" "decrement" "5" "";
    }

    // === Window Management ===
    Mod+Q repeat=false { close-window; }
    Mod+F { maximize-column; }
    Mod+Shift+F { fullscreen-window; }
    Mod+T { toggle-window-floating; }
    Mod+Shift+V { switch-focus-between-floating-and-tiling; }
    Mod+W { toggle-column-tabbed-display; }

    // === Focus Navigation ===
    Mod+Left  { focus-column-left; }
    Mod+Down  { focus-window-down; }
    Mod+Up    { focus-window-up; }
    Mod+Right { focus-column-right; }
    Mod+H     { focus-column-left; }
    Mod+J     { focus-window-down; }
    Mod+K     { focus-window-up; }
    Mod+L     { focus-column-right; }

    // === Window Movement ===
    Mod+Shift+Left  { move-column-left; }
    Mod+Shift+Down  { move-window-down; }
    Mod+Shift+Up    { move-window-up; }
    Mod+Shift+Right { move-column-right; }
    Mod+Shift+H     { move-column-left; }
    Mod+Shift+J     { move-window-down; }
    Mod+Shift+K     { move-window-up; }
    Mod+Shift+L     { move-column-right; }

    // === Column Navigation ===
    Mod+Home { focus-column-first; }
    Mod+End  { focus-column-last; }
    Mod+Ctrl+Home { move-column-to-first; }
    Mod+Ctrl+End  { move-column-to-last; }

    // === Monitor Navigation ===
    Mod+Comma      { focus-monitor-left; }
    Mod+Period     { focus-monitor-right; }

    // === Move to Monitor ===
    Mod+Shift+Comma  { move-column-to-monitor-left; }
    Mod+Shift+Period { move-column-to-monitor-right; }

    // === Workspace Navigation ===
    Mod+Page_Down      { focus-workspace-down; }
    Mod+Page_Up        { focus-workspace-up; }
    Mod+U              { focus-workspace-down; }
    Mod+I              { focus-workspace-up; }
    Mod+Ctrl+Down { move-column-to-workspace-down; }
    Mod+Ctrl+Up   { move-column-to-workspace-up; }
    Mod+Ctrl+U         { move-column-to-workspace-down; }
    Mod+Ctrl+I         { move-column-to-workspace-up; }

    // === Move Workspaces ===
    Mod+Shift+Page_Down { move-workspace-down; }
    Mod+Shift+Page_Up   { move-workspace-up; }
    Mod+Shift+U         { move-workspace-down; }
    Mod+Shift+I         { move-workspace-up; }

    // === Mouse Wheel Navigation ===
    Mod+WheelScrollDown      cooldown-ms=150 { focus-workspace-down; }
    Mod+WheelScrollUp        cooldown-ms=150 { focus-workspace-up; }
    Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; }
    Mod+Ctrl+WheelScrollUp   cooldown-ms=150 { move-column-to-workspace-up; }

    Mod+WheelScrollRight      { focus-column-right; }
    Mod+WheelScrollLeft       { focus-column-left; }
    Mod+Ctrl+WheelScrollRight { move-column-right; }
    Mod+Ctrl+WheelScrollLeft  { move-column-left; }

    Mod+Shift+WheelScrollDown      { focus-column-right; }
    Mod+Shift+WheelScrollUp        { focus-column-left; }
    Mod+Ctrl+Shift+WheelScrollDown { move-column-right; }
    Mod+Ctrl+Shift+WheelScrollUp   { move-column-left; }

    // === Numbered Workspaces ===
    Mod+1 { focus-workspace 1; }
    Mod+2 { focus-workspace 2; }
    Mod+3 { focus-workspace 3; }
    Mod+4 { focus-workspace 4; }
    Mod+5 { focus-workspace 5; }
    Mod+6 { focus-workspace 6; }
    Mod+7 { focus-workspace 7; }
    Mod+8 { focus-workspace 8; }

    // === Move to Numbered Workspaces ===
    Mod+Shift+1 { move-column-to-workspace 1; }
    Mod+Shift+2 { move-column-to-workspace 2; }
    Mod+Shift+3 { move-column-to-workspace 3; }
    Mod+Shift+4 { move-column-to-workspace 4; }
    Mod+Shift+5 { move-column-to-workspace 5; }
    Mod+Shift+6 { move-column-to-workspace 6; }
    Mod+Shift+7 { move-column-to-workspace 7; }
    Mod+Shift+8 { move-column-to-workspace 8; }

    // === Column Management ===
    Mod+BracketLeft  { consume-or-expel-window-left; }
    Mod+BracketRight { consume-or-expel-window-right; }

    // === Sizing & Layout ===
    Mod+R { switch-preset-column-width; }
    Mod+Shift+R { switch-preset-window-height; }
    Mod+Ctrl+R { reset-window-height; }
    Mod+Ctrl+F { expand-column-to-available-width; }
    Mod+C { center-column; }
    Mod+Ctrl+C { center-visible-columns; }

    // === Manual Sizing ===
    Mod+0     { set-column-width "-5%"; }
    Mod+Minus { set-column-width "+5%"; }

    Mod+Equal     { set-window-height "-5%"; }
    Mod+Backspace { set-window-height "+5%"; }

    // === Screenshots ===
    Mod+Ctrl+Alt+Shift+S { screenshot; }
    XF86Launch1 { screenshot; }
    Ctrl+XF86Launch1 { screenshot-screen; }
    Alt+XF86Launch1 { screenshot-window; }
    Print { screenshot; }
    Ctrl+Print { screenshot-screen; }
    Alt+Print { screenshot-window; }

    // === System Controls ===
    Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
    Mod+Shift+P { power-off-monitors; }
}

// * Include dms files

// These are all auto generated, so not included in my config.

include "dms/colors.kdl"
include "dms/alttab.kdl"
include "dms/outputs.kdl"
include "dms/cursor.kdl"

Startup programs

systemctl --user add-wants niri.service xremap.service
systemctl --user add-wants niri.service vicinae.service

wacom

#!/bin/bash

# remap-tablet-to-monitor MONITOR_NAME
remap-tablet-to-monitor() {
    MONITOR_NAME="$1"
    SWAY_INPUTS="$(swaymsg -t get_inputs -r)"
    SWAY_OUTPUTS="$(swaymsg -t get_outputs -r)"
    LIBINPUT_DEVICES="$(libinput list-devices)"
    read MON_WIDTH MON_HEIGHT <<<$(echo "$SWAY_OUTPUTS" | jq -r ".[] | select(.name==\"$MONITOR_NAME\") | .current_mode | .width, .height" | xargs)
    IDENTIFIER=$(echo "$SWAY_INPUTS" | jq -r '.[] | select(.type=="tablet_tool") | .identifier' | head -1)
    TABLET_NAME=$(echo "$SWAY_INPUTS" | jq -r '.[] | select(.type=="tablet_tool") | .name' | head -1)
    TAB_SIZE_LINE=$(echo "$LIBINPUT_DEVICES" | awk -v name="$TABLET_NAME" '
      BEGIN{found=0}
      $0 ~ "Device: *"name"$" {found=1}
      found && $0 ~ /Size: / {print $0; exit}
    ')
    TAB_WIDTH=$(echo "$TAB_SIZE_LINE" | grep -oP '([0-9]+)x' | tr -d 'x')
    TAB_HEIGHT=$(echo "$TAB_SIZE_LINE" | grep -oP 'x([0-9]+)' | tr -d 'x')
    USABLE_RATIO=$(awk -v w="$MON_HEIGHT" -v tw="$TAB_WIDTH" -v mw="$MON_WIDTH" -v th="$TAB_HEIGHT" \
                       'BEGIN { printf "%.3f", (w * tw / mw) / th }')

    # I could simply set USABLE_RATIO=0.9 since my tablet is 16:10 and my
    # monitor is 16:9, but this method calculates USABLE_RATIO generically.
    # If I ever change tablets (which I doubt), this script will still work
    # without needing modification.

    swaymsg input "$IDENTIFIER" map_to_output $MONITOR_NAME
    swaymsg input type:tablet_tool map_from_region 0x0 1x$USABLE_RATIO
    swaymsg input type:tablet_tool left_handed enabled # Rotates the device by 180 degrees
    echo "Mapped tablet '$TABLET_NAME' ($IDENTIFIER) to monitor '$MONITOR_NAME' 1.0x$USABLE_RATIO"
}

# This script remaps a connected tablet tool device to the currently
# focused Sway monitor, adjusting the input area to match the
# monitor's aspect ratio.  It listens for workspace focus changes in
# Sway and, when the focus changes, it finds the tablet device and
# monitor dimensions, calculates a scaling ratio, and applies the
# mapping and input transformation using swaymsg commands.
--monitor-and-remap() {
    # Run it first time for the focused monitor
    remap-tablet-to-monitor $(swaymsg -t get_outputs | jq -r '.[] | select(.focused) | .name')

    # Main: subscribe to Sway workspace focus, emit output name, run remap per event
    export -f remap-tablet-to-monitor
    exec swaymsg -t subscribe -m '["workspace"]' \
        | jq --unbuffered -r 'select(.change == "focus") | .current.output' \
        | xargs  -I{} bash -c 'remap-tablet-to-monitor "$@"' _ {}
}


# ...other wacom related commands...

SUBCMD=$1
shift
$SUBCMD "$@"

swayimg

-- * General settings
swayimg.imagelist.order = "mtime"
swayimg.imagelist.reverse = true
swayimg.imagelist.recursive = false
swayimg.imagelist.adjacent = true
swayimg.overlay = false
swayimg.text.visible = false

-- Helper functions
local function copy_path()
    local img = swayimg.viewer.get_image() or swayimg.gallery.get_image()
    if img then
        os.execute('wl-copy "' .. img.path .. '"')
        print("Path copied: " .. img.path)
    end
end

local function remove_file()
    local img = swayimg.viewer.get_image() or swayimg.gallery.get_image()
    if img then
        os.remove(img.path)
        print("File removed: " .. img.path)
        swayimg.viewer.open("next")
    end
end

-- * Viewer Keybindings

swayimg.viewer.bind_reset()
swayimg.viewer.on_key("g", function() swayimg.viewer.open("first") end)
swayimg.viewer.on_key("Shift+G", function() swayimg.viewer.open("last") end)
swayimg.viewer.on_key("Shift+H", function() swayimg.viewer.open("prev") end)
swayimg.viewer.on_key("Shift+L", function() swayimg.viewer.open("next") end)
swayimg.viewer.on_key("Space", function() swayimg.viewer.open("next") end)
swayimg.viewer.on_key("s", function() swayimg.mode = "slideshow" end)
swayimg.viewer.on_key("n", function() swayimg.viewer.animation = not swayimg.viewer.animation end)
swayimg.viewer.on_key("f", function() swayimg.fullscreen = not swayimg.fullscreen end)
swayimg.viewer.on_key("Return", function() swayimg.mode = "gallery" end)
-- Adjusted movement logic to be more intuitive
swayimg.viewer.on_key("h", function() swayimg.viewer.set_abs_position(swayimg.viewer.get_position().x + 30, swayimg.viewer.get_position().y) end)
swayimg.viewer.on_key("l", function() swayimg.viewer.set_abs_position(swayimg.viewer.get_position().x - 30, swayimg.viewer.get_position().y) end)
swayimg.viewer.on_key("k", function() swayimg.viewer.set_abs_position(swayimg.viewer.get_position().x, swayimg.viewer.get_position().y + 30) end)
swayimg.viewer.on_key("j", function() swayimg.viewer.set_abs_position(swayimg.viewer.get_position().x, swayimg.viewer.get_position().y - 30) end)
swayimg.viewer.on_key({"Equal", "Plus"}, function() swayimg.viewer.set_abs_scale(swayimg.viewer.scale + 0.1) end)
swayimg.viewer.on_key("Minus", function() swayimg.viewer.set_abs_scale(swayimg.viewer.scale - 0.1) end)
swayimg.viewer.on_key("w", function() swayimg.viewer.set_fix_scale("width") end)
swayimg.viewer.on_key("Shift+W", function() swayimg.viewer.set_fix_scale("height") end)
swayimg.viewer.on_key("z", function() swayimg.viewer.set_fix_scale("fit") end)
swayimg.viewer.on_key("Shift+Z", function() swayimg.viewer.set_fix_scale("fill") end)
swayimg.viewer.on_key("0", function() swayimg.viewer.set_fix_scale("real") end)
swayimg.viewer.on_key("9", function() swayimg.viewer.set_fix_scale("optimal") end)
swayimg.viewer.on_key("bracketleft", function() swayimg.viewer.rotate(270) end)
swayimg.viewer.on_key("bracketright", function() swayimg.viewer.rotate(90) end)
swayimg.viewer.on_key("backslash", function() swayimg.viewer.flip_vertical() end)
swayimg.viewer.on_key("BackSpace", function() swayimg.viewer.flip_horizontal() end)
swayimg.viewer.on_key("a", function() swayimg.antialiasing = not swayimg.antialiasing end)
swayimg.viewer.on_key("r", function() swayimg.viewer.reload() end)
swayimg.viewer.on_key("i", function() swayimg.text.visible = not swayimg.text.visible end)
swayimg.viewer.on_key("Shift+Delete", remove_file)
swayimg.viewer.on_key({"Escape", "q"}, function() swayimg.exit() end)
swayimg.viewer.on_key({"c", "y"}, copy_path)

-- * Gallery Keybindings

swayimg.gallery.bind_reset()
swayimg.gallery.on_key("h", function() swayimg.gallery.select("left") end)
swayimg.gallery.on_key("l", function() swayimg.gallery.select("right") end)
swayimg.gallery.on_key("k", function() swayimg.gallery.select("up") end)
swayimg.gallery.on_key("j", function() swayimg.gallery.select("down") end)
swayimg.gallery.on_key("g", function() swayimg.gallery.select("first") end)
swayimg.gallery.on_key("Shift+G", function() swayimg.gallery.select("last") end)
swayimg.gallery.on_key("s", function() swayimg.mode = "slideshow" end)
swayimg.gallery.on_key("f", function() swayimg.fullscreen = not swayimg.fullscreen end)
swayimg.gallery.on_key("Return", function() swayimg.mode = "viewer" end)
swayimg.gallery.on_key("i", function() swayimg.text.visible = not swayimg.text.visible end)
swayimg.gallery.on_key({"Equal", "Plus"}, function() swayimg.gallery.thumb_size = swayimg.gallery.thumb_size + 20 end)
swayimg.gallery.on_key("Minus", function() swayimg.gallery.thumb_size = swayimg.gallery.thumb_size - 20 end)
swayimg.gallery.on_key("Shift+Delete", remove_file)
swayimg.gallery.on_key({"Escape", "q"}, function() swayimg.exit() end)
swayimg.gallery.on_key({"c", "y"}, copy_path)

Mail

I use Emacs for my mail stuff, see my init.el's notmuch configuration.

Proton/Hydroixde bridge

  • Install hydroxide.
  • hydroxide auth <username>
  • Tangle the following and systemctl --user enable --now hydroxide-imap
[Unit]
Description=Third party ProtonMail IMAP Bridge
After=network.target

[Service]
Type=exec
Restart=on-failure
ExecStart=/usr/bin/hydroxide imap

[Install]
WantedBy=default.target

Shell

Fish

if status is-interactive
    /usr/bin/starship init fish | source

    if test -f ~/.extrarc
        source ~/.extrarc
    end

    # I like fish's own ctrl-r interface but it messes everything up
    # when it encounters a very long command. fzf handles it much
    # better, with preview.
    function fzf_fish_history
        commandline --replace $(history --null | fzf --read0 --reverse --height 30 --preview "echo {} | fish_indent --ansi")
    end

    # C-r → fuzzy history search
    bind \cr fzf_fish_history
    # Emacs-like history bindings (Alt+{n,p})
    bind \ep history-token-search-backward
    bind \en history-token-search-forward
end
export GOPATH="$HOME/.go"
export R_LIBS_USER="$HOME/.rlibs"
export NPM_PACKAGES="$HOME/.npm-packages"
export NODE_PATH="$HOME/.npm-packages/lib/node_modules"
export BROWSER=jaro
export VISUAL="jaro --method=edit"
export EDITOR="jaro --method=edit"
export PATH="$HOME/.bin:$HOME/.local/bin:$NPM_PACKAGES/bin:$GOPATH/bin:$HOME/.cargo/bin:$PATH"
# * Stow aliases

alias ssync='stow -d ~/Sync/ -t ~'

# * Package management

abbr -a -- paci 'sudo pacman -S'
abbr -a -- pacr 'sudo pacman -Rns'
abbr -a -- pacs 'pacman -Ss'
abbr -a -- pacupd 'sudo pacman -Sy'
abbr -a -- pacupg 'sudo pacman -Syu'
abbr -a -- pacbin 'pacman -F'

abbr -a -- dnfi 'sudo dnf install'
abbr -a -- dnfr 'sudo dnf remove'
abbr -a -- dnfs 'dnf search'
abbr -a -- dnfp 'dnf provides'
abbr -a -- dnfupd 'sudo dnf update'
abbr -a -- dnfupg 'sudo dnf upgrade'

abbr -a -- brews 'brew search'
abbr -a -- brewi 'brew install'
abbr -a -- brewr 'brew uninstall'
abbr -a -- brewupd 'brew update'
abbr -a -- brewupg 'brew upgrade'

function nixi
    nix-env -iA "nixpkgs.$argv[1]"
end
alias nixr='nix-env -e'
alias nixs='nix search nixpkgs'

# * Systemctl

abbr -a -- ctl 'sudo systemctl'
abbr -a -- ctls 'sudo systemctl --full status'
abbr -a -- ctlr 'sudo systemctl --full restart'
abbr -a -- ctlu 'systemctl --full --user'
abbr -a -- ctlus 'systemctl --full --user status'
abbr -a -- ctlur 'systemctl --full --user restart'
abbr -a -- log 'journalctl --unit'
abbr -a -- logu 'journalctl --user --unit'

# * Opening stuff

abbr -a -- v 'jaro --method=view'
abbr -a -- e 'jaro --method=edit'
abbr -a -- g 'jaro --method=gallery'
abbr -a -- open jaro

# * File/folder stuff

abbr -a -- mkx 'chmod +x'
function mkcd
    mkdir -p "$argv[1]"
    cd "$argv[1]"
end

abbr -a -- tree 'lsd --tree'

abbr -a -- ls 'lsd --group-dirs=first --classify'
abbr -a -- ll 'lsd --group-dirs=first --classify --long --date=relative --timesort --blocks=date,size,name'
abbr -a -- lls 'lsd --group-dirs=first --classify --long --header --date=relative --timesort --git --hyperlink=auto'
abbr -a -- lla 'lsd --group-dirs=first --classify --long --header --date=relative --timesort --git --hyperlink=auto --almost-all'

abbr -a -- cdtemp 'cd $(mktemp -d)'
alias cdf='cd $(fd -t d -d 8 | fzf)' # cd fuzzy

alias find-dups='find . ! -empty -type f -exec md5sum {} + | sort | uniq -w32 -dD'

# * Streaming stuff

# Higlight json parts of the stream and print other lines plain
# ./program_that_may_output_json | logjson
abbr -a --position anywhere -- @logjson 'jq -R -r ". as \$line | try fromjson catch \$line"'

# Less that supports color
abbr -a @less --position anywhere --set-cursor "% | less -r"
# Complements the one above, adds --color=always to current command.
# somecommand @color @less
abbr -a --position anywhere -- @color --color=always

# * Git

abbr -a -- gcm 'git commit -m'
abbr -a -- gds 'git diff --staged'
abbr -a -- gs 'git status'
abbr -a -- gco 'git chekout'

# * Meta

abbr -a :q exit

# * Kubernetes

abbr -a -- kctx 'kubectl config current-context'
abbr -a -- kctxs-list 'kubectl config get-contexts --output=name'
abbr -a -- kctx-use 'kubectl config use-context'

# * Project management

function cdp --description 'Switch to a project, fast'
    set -l selected (gitfind ~/Workspace/projects/ | fzf --tiebreak=index --height=15 --reverse)
    if test -n "$selected"
        gitfind --record $selected
        eval cd $selected
    end
end

function fgp --description 'Pick a background job and bring to foreground'
    jobs -q; or begin; echo "No jobs"; return 1; end

    set -l job (jobs | fzf --reverse --height=10 --header="Foreground which job?")
    or return

    set -l jid (echo $job | cut -f2)
    fg %$jid
end

Starship

[cmd_duration]
# Show system notifications for commands that takes longer than 5 seconds
min_time_to_notify = 5000
show_notifications = true
notification_timeout = 99999

Utility functions

Utilizing ~/.config/fish/functions/ instead of the config file or the conf.d folder is a better idea because the funcitons inside functions folder are lazy loaded.

Compression/decompression

function extract
    if test -f $argv[1]
        switch $argv[1]
            case '*.tar.bz2'
                tar xjf "$argv[1]"
            case '*.tar.gz'
                tar xzf "$argv[1]"
            case '*.bz2'
                bunzip2 "$argv[1]"
            case '*.rar'
                unrar x "$argv[1]"
            case '*.gz'
                gunzip "$argv[1]"
            case '*.tar' '*.tar.xz'
                tar xf "$argv[1]"
            case '*.tbz2'
                tar xjf "$argv[1]"
            case '*.tgz'
                tar xzf "$argv[1]"
            case '*.zip'
                unzip "$argv[1]"
            case '*.Z'
                uncompress "$argv[1]"
            case '*.7z'
                7z x "$argv[1]"
            case '*'
                echo "'$argv[1]' cannot be extracted via extract()"
        end
    else
        echo "Usage:"
        echo "extract <archive-name>"
    end
end
function compress
    set EXT $argv[1]
    set argv $argv[2..-1]  # Shift the arguments to remove the first one

    switch $EXT
        case '-h' '--help'
            echo "Usage:"
            echo "compress <archive-name>.EXT file1 file2"
            echo
            echo "EXT can be one of the following: .7z .tar.gz .tgz .tar.bz2 .zip."
            echo "Also you can add .nocompress to the end of EXT to archive without compressing."
            return
        case '*.7z'
            7z a "$EXT" $argv
        case '*.tar.gz' '*.tgz'
            tar -czvf "$EXT" $argv
        case '*.tar.gz.nocompress' '*.tgz.nocompress'
            tar -cvf (string replace .nocompress '' $EXT) $argv
        case '*.tar.bz2'
            tar -cjvf "$EXT" $argv
        case '*.zip'
            zip -r "$EXT" $argv
        case '*'
            echo "Unrecognized EXT: $EXT"
            echo
            compress --help
    end
end

Encryption/Decryption

function encrypt
    switch $argv[1]
        case '-h' '--help'
            echo "Usage:"
            echo "encrypt <input-file> [<output-file>]"
            echo
            echo "If <output-file> is skipped, then the output will be <input-file>.encrypted"
            return
        case '*'
            set INPUT $argv[1]
            set OUTPUT $argv[2]

            if not test -f "$INPUT"
                echo "$INPUT not found."
                return 1
            end

            if test -z "$OUTPUT"
                set OUTPUT "$INPUT.encrypted"
            end

            if test -f "$OUTPUT"
                echo "$OUTPUT already exists."
                return 1
            end

            gpg --symmetric --cipher-algo AES256 --output "$OUTPUT" "$INPUT"
    end
end
function decrypt
    switch $argv[1]
        case '-h' '--help'
            echo "Usage:"
            echo "decrypt <input-file> [<output-file>]"
            echo
            echo "If <output-file> is skipped, then the output will be <input-file> but the last suffix is removed"
            return
        case '*'
            set INPUT $argv[1]
            set OUTPUT $argv[2]

            if not test -f "$INPUT"
                echo "$INPUT not found."
                return 1
            end

            if test -z "$OUTPUT"
                set OUTPUT (string trim --right --chars='.' (basename "$INPUT"))
            end

            if test -f "$OUTPUT"
                echo "$OUTPUT already exists."
                return 1
            end

            gpg --decrypt --output "$OUTPUT" "$INPUT"
    end
end

Network stuff

function ipinfo
    switch (uname)
        case Linux
            set localips (ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d/ -f1)
        case Darwin
            set localips (ifconfig | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}')
    end

    echo $localips
    echo
    echo ======================
    echo
    curl --silent https://ipinfo.io | jq .
end

Tmux

I use tmux for it's vi mode. Other than that I generally use my window manager for multiple terminals (which is rarely needed because I'm mostly in Emacs.) I also use it on Android (via Termux) as a WM.

TPM – Termux Plugin Manager

Install this first, configuration is embedded in the next section.

git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm

To install newly added plugins, do prefix I.

Configuration

# * General

set -g default-shell /usr/bin/fish
set -g mouse on
set -g base-index 1 # Window indexes starts from 1
setw -g pane-base-index 1 # Pane indexes starts from 1
set -s escape-time 0 # Remove the delay after hitting <ESC>
set -g set-titles on
# Do `set destroy-unattached off` to disable this for a particular session
set-option -g destroy-unattached on

set -g set-titles on
set -g extended-keys on
set -g extended-keys-format csi-u
set -g focus-events on

# * Bindings

# Open copy mode
bind -n M-y copy-mode

# Set prefix to A-a
# unbind C-b
set -g prefix M-a
bind-key M-a send-prefix

# Increase the time of display-panes (PREFIX q)
set -g display-panes-time 4000

# Split remaps
bind \\ split-window -h -c '#{pane_current_path}'
bind - split-window -v -c '#{pane_current_path}'
unbind '"'
unbind %

# Vim-like pane switches
bind k selectp -U
bind j selectp -D
bind h selectp -L
bind l selectp -R

# Vi keys for copy-mode
setw -g mode-keys vi
bind-key -T copy-mode-vi v send-keys -X begin-selection
bind-key -T copy-mode-vi Enter send-keys -X copy-selection-and-cancel
bind-key -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "wl-copy"

# New session (s shows sessions, C-s creates new one)
bind C-s new-session

# * Menu

bind-key Space display-menu -T "Command Palette" \
    "Persist (on)"        p "set destroy-unattached off; refresh-client" \
    "Persist (off)"       P "set destroy-unattached on; refresh-client" \
    "Rename Window"       w "command-prompt 'rename-window %%'" \
    "Rename Session"      s "command-prompt 'rename-session %%'" \
    "" \
    "Horizontal Split"    h "split-window -h" \
    "Vertical Split"      v "split-window -v" \
    "New Window"          n "new-window" \
    "Kill Pane"           x "kill-pane" \
    "Sync Panes"          s "setw synchronize-panes" \
    "" \
    "Reload Config"       R "source-file ~/.tmux.conf; display 'Reloaded'"

# * Only show status bar if there is more then one window

# Source: https://schauderbasis.de/posts/hide_tmux_status_bar_if_its_not_needed/
set -g status off
set-hook -g after-new-window      'if "[ #{session_windows} -gt 1 ]" "set status on"'
set-hook -g after-kill-pane       'if "[ #{session_windows} -lt 2 ]" "set status off"'
set-hook -g pane-exited           'if "[ #{session_windows} -lt 2 ]" "set status off"'
set-hook -g window-layout-changed 'if "[ #{session_windows} -lt 2 ]" "set status off"'

# * Theming (uses terminal colors)

set -g status-style "bg=terminal,fg=terminal"

# Left side: session name
set -g status-left-length 30
set -g status-left "#{?client_prefix,#[fg=colour2] ◉ ,}#[fg=colour0,bg=colour5,bold] #S #[fg=colour5,bg=terminal,nobold]"

# Right side: date and time
set -g status-right-length 80
set -g status-right "#[fg=colour8]#[fg=colour7,bg=colour8] #{?#{==:#{destroy-unattached},on},#[fg=colour1]⏻,#[fg=colour2]⏻} #[fg=colour8]│#[fg=colour7] %a %b %d #[fg=colour5]#[fg=colour0,bg=colour5,bold] %H:%M "

# Window tabs
set -g window-status-format "#[fg=colour7] #I:#W "
set -g window-status-current-format "#[fg=colour5,bold] #I:#W "

# Remove window status separator
set -g window-status-separator ""

# Center window list
set -g status-justify left

# Pane borders
set -g pane-border-style "fg=colour8"
set -g pane-active-border-style "fg=colour5"

# Message styling
set -g message-style "bg=terminal,fg=terminal"

# * TPM

# List of plugins
set -g @plugin 'tmux-plugins/tpm'
set -g @plugin 'tmux-plugins/tmux-resurrect'

# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
run '~/.tmux/plugins/tpm/tpm'

Utilities

git

[user]
  name = Isa Mert Gurbuz
  email = isamertgurbuz@gmail.com
[github]
  user = isamert
[rebase]
  autoStash = true
[pull]
  rebase = true
[fetch]
  prune = true
[status]
  short = true
[init]
  defaultBranch = main
[push]
  followTags = true

jaro

Configuration

In this file I define some file associations. Please refer to jaro README for more info. It's simply an xdg-open alternative.

  • To experiment associations/jaro, do:

    $ guile
    guile> (load ".local/bin/jaro")
    guile> (load ".config/associactions")
    
;; -*- mode: scheme; -*-

;;; Configuration

(set!
 dynamic-menu-program
 (oscase
  #:darwin "choose"
  #:gnu/linux "rofi -dmenu"))

;;; Bindings

(bind
 #:pattern '("(application|text)/(x-)?(pdf|postscript|ps|epub.*)" "image/(x-)?eps" ".*pdf")
 #:program (oscase
            #:darwin '(open %f)
            #:gnu/linux '(zathura %f)))

(bind
 #:pattern '("^text/html" "^application/x?htm")
 #:program 'browser
 #:edit 'editor)

(bind
 #:name 'editor
 #:pattern '("^text/" "^application/(x-)?(shellscript|json|javascript|xml)")
 #:emacs (elisp
          (find-file "%F"))
 ;; If I simply give the file to emacsclient, it opens it in a split
 ;; for some reason, instead of making it the only window in the frame
 #:program '(emacsclient -c --eval "(find-file \"%F\")")
 #:term '(emacsclient -nw -c --eval "(find-file \"%F\")"))

(bind
 #:name 'empv
 #:pattern '("^video/" "^audio/")
 #:program (elisp (empv--with-video-enabled (empv-play "%f"))))

(bind
 #:pattern "inode/directory"
 #:program '(thunar %f)
 #:term '(yazi %f)
 #:gallery 'nomacs)

(bind
 #:pattern "https://.*zoom\\.us/j/(\\w+)\\?pwd=(\\w+)"
 #:program '(zoom zoommtg://zoom.us/join?confno=%1&pwd=%2))

(bind
 #:pattern '("^https?://(www.)?youtube.com/"
             "^https?://(www.)?youtu.be/"
             "^https?://(www.)?v.redd.it/\\w+/DASH"
             "^https?://([a-zA-Z-]+)?streamable.com"
             "^https?://giant.gfycat.com/.+"
             "https?://v.redd.it/.+"
             "^https?://.+/.+\\.(gifv|mp4|webm)(\\?.+)?$")
 #:program 'empv
 #:on-error 'browser)

(bind
 #:pattern "^https?://.+/.+\\.(jpg|png|gif)(\\?.+)?$"
 #:program `(swayimg %f))

(bind
 #:pattern "^image/.*"
 #:program '(swayimg %f)
 #:gallery 'nomacs)

(bind
 #:pattern "^https?://(www.)?reddit.com/r/(\\w+)/comments/(.*?)/"
 #:program (elisp (reddigg-view-comments "https://www.reddit.com/r/%2/comments/%3"))
 #:on-error 'browser)

(bind
 #:pattern '("^magnet:" "\\.torrent$")
 #:program '(qbittorrent --skip-dialog=false %f))

(bind
 #:name 'browser
 #:pattern '("^https?://.*" "^.*\\.html?(#[\\w_-]+)?")
 #:emacs (elisp (eww "%f"))
 ;; #:program (elisp (eww "%f"))
 ;; #:program '(qutebrowser %f)
 ;; #:test '(pgrep qutebrowser)
 #:program '(firefox %f)
 #:edit 'editor)

(bind
 #:pattern "^application/(x-)?(tar|gzip|bzip2|lzma|xz|compress|7z|rar|gtar|zip)(-compressed)?"
 #:program '(file-roller %f))

(bind
 #:pattern "^application/(x-)?(vnd.)?(ms-|ms)?(excel|powerpoint|word)"
 #:program '(desktopeditors %F))

;;; Catch-all

(bind
 #:pattern ".*"
 #:program (select-one-of
            #:alternatives
            #:bindings
            #:binaries))

;;; References

(bind
 #:name 'bat
 #:pattern ".*"
 #:program '(bat --paging=always %f))


(bind
 #:name 'nomacs
 #:pattern "^image/.*"
 #:program '(nomacs %f))

;; vi:syntax=scheme

.mailcap

Just redirect everything to jaro.

text/html; w3m -v -F -T text/html %s; edit=jaro --method=edit; compose=jaro --method=edit; nametemplate=%s.html; copiousoutput
text/*; jaro '%s'; copiousoutput
application/*; jaro '%s'
image/*; jaro '%s'
audio/*; jaro '%s'
video/*; jaro '%s'
message/*; jaro '%s'
model/*; jaro '%s'
*/*; jaro '%s'

.urlview

Redirect everything to jaro.

COMMAND jaro

scli

Signal messenger for terminal, see scli.

open-command=jaro %u
enable-notifications=true
save-history=true
use-formatting=true
wrap-at=75
contacts-autohide=true
color=true
partition-contacts=true

Media

mpv

Keybindings

Key Action
p pause
f fullscreen
C+l show playlist
<, > playlist prev,next
A+0-5 change window scale
9,0 volume down/up
m mute
a change/switch audio
z, Z subtitle delay -/+
+, - scale subtitle
s change/switch subtitle
r, R change sub-position
T, A-t download subtitle (en/tr)
ctrl++ increase audio delay
ctrl+- decrease audio delay
[, ] playback speed scale
. , one frame forward/backward
1-2 contrast
3-4 brightness
5-6 gamma
7-8 saturation
i show video info
c show youtube comments

Configuration

input-ipc-server=/tmp/mpvsocket

# Display Turkish subtitles if available, fall back to English otherwise.
slang=tr,en

# Play Korean audio if available, fall back to English otherwise.
# (I watch Korean stuff a lot and they always gets overridden by English audio)
alang=ko,en,eng

# If the file seems to be valid UTF-8, prefer UTF-8, otherwise use Turkish
# encoding.
sub-codepage=cp1254

# Search these directories for subtitles
sub-file-paths=sub:Sub:subs:Subs:subtitle:Subtitle:subtitles:Subtitles

# Load all subtitles from directories listed above
sub-auto=all

# 10 from bottom
sub-pos=90

# Filter subtitle additions for the deaf or hard-of-hearing (SDH)
sub-filter-sdh=yes
sub-filter-sdh-harder=yes

# Tile properly
no-keepaspect-window

Bindings configuration

# Copy the filename
y run "/bin/sh" "-c" "printf ${filename} | xcopy"; show-text "Filename copied: ${filename}"
& run "/bin/sh" "-c" "jaro --program=browser '${path}'"; show-text "Opening ${path} in browser..."

! add chapter -1 # skip to previous chapter
@ add chapter 1 # next

l seek 5
h seek -5
j seek -60
k seek 60

L no-osd seek  1 exact
H no-osd seek -1 exact
J no-osd seek  5 exact
K no-osd seek -5 exact

f cycle fullscreen
p cycle pause
m cycle mute
c cycle-values loop-file "inf" "no"

0 add volume 2
9 add volume -2

s cycle sub
a cycle audio  # switch audio streams

# resize subtitle
+ add sub-scale +0.1
- add sub-scale -0.1

Alt+0 set window-scale 0.25
Alt+1 set window-scale 0.5
Alt+2 set window-scale 0.75
Alt+3 set window-scale 1
Alt+4 set window-scale 1.5
Alt+5 set window-scale 2

CTRL+l script-message osc-playlist

sponsorblock-minimal-plugin   plugin

Use b key to disable/enable it. It's on by default.

mkdir -p ~/.config/mpv/scripts/
curl https://codeberg.org/jouni/mpv_sponsorblock_minimal/raw/branch/master/sponsorblock_minimal.lua -o ~/.config/mpv/scripts/sponsorblock_minimal.lua
# By default it only skips "sponsor" category, I want more:
sed -Ei 's/([ \t]+)categories =.*/\1categories = '"'"'"sponsor","selfpromo","interaction","intro","outro"'"'"'/' ~/.config/mpv/scripts/sponsorblock_minimal.lua

uosc   plugin

UI for mpv. Pretty looking and very functional. Has a menu that is searchable. Also allows you to switch stream quality on YouTube videos etc. Spectacular.

Installation:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/tomasklaen/uosc/HEAD/installers/unix.sh)"

thumbfast   plugin

Thumbnail plugin. Works with uosc.

Installation:

curl https://raw.githubusercontent.com/po5/thumbfast/master/thumbfast.lua -o ~/.config/mpv/scripts/thumbfast.lua

ytdl-preload   plugin

curl https://raw.githubusercontent.com/bitingsock/ytdl-preload/refs/heads/main/ytdl_preload.lua -o ~/.config/mpv/scripts/ytdl_preload.lua

For first time:

curl https://github.com/bitingsock/ytdl-preload/blob/main/ytdl_preload.conf -o ~/.config/mpv/script-opts/ytdl_preload.conf
#format=bestaudio+bestvideo
subLangs=en
ytdl_opt1=--extractor-args youtube:player_client=android,web
ytdl_opt2=--throttled-rate 1M
ytdl_opt3=-N 5
#ytdl_opt4=

mpv-mpris   plugin

Just install the mpv-mpris package from your distros package manager. This integrates mpv with system media keys and system media player.

Editors

Neovim

I had a fat neovim configuration at past but I don't use vim anymore. This is just the minimal configuration I've started maintaining

" ##################################################
"                   (_)
"         __   ___ _ __ ___  _ __ ___
"         \ \ / / | '_ ` _ \| '__/ __|
"          \ V /| | | | | | | | | (__
"         (_)_/ |_|_| |_| |_|_|  \___|
" ##################################################


" visuals {{{
set background=dark                " rearranges colors for dark background
set colorcolumn=80                 " 80-col line
set termguicolors                  " true color support
set number relativenumber          " line numbers relative to current line ()
set cursorline                     " highlight current line
"hi Normal guibg=none ctermbg=none| " transparent background
" }}}

" tabs and spaces {{{
set mouse=a               " enable mouse (helps precise resizing etc)
set tabstop=4             " tab-char width
set shiftwidth=4          " indent-level width
set softtabstop=4         " column count inserted by the tab key
set expandtab             " tabs -> spaces
set smartindent           " do it smart
filetype plugin indent on " determine indent by plugins
" }}}

" better defaults {{{
" search/completion
set ignorecase " ignore case while searching
set smartcase  " abc -> Abc and abc, Abc -> only Abc (works in combination with ^^)
set splitbelow
set splitright
set foldmethod=syntax " (indent, marker: fold between {{{ }}})
" }}}

" utility {{{
set showmatch             " visually indicate matching parens
set autoread              " update buffer if file is edited externally
set title                 " terminal inherits title
set clipboard=unnamedplus " use system clipboard
set inccommand=nosplit    " show effects of a command live
set spelllang=en_us       " default spelllang
set signcolumn=yes        " removes flickering caused by lang server
set undofile              " saves undo history to file (nvim's undodir default is OK)
set completeopt=menu,menuone,preview,noselect,noinsert
" }}}

" netrw (file browser) {{{
" :help netrw-quickmap
let g:netrw_banner = 0       " remove banner
let g:netrw_liststyle = 3    " tree style listing
let g:netrw_browse_split = 4 " ...
let g:netrw_altv = 1         " spawn it at left split
let g:netrw_usetab = 1       " use tab for expanding/shrinking folders
let g:netrw_winsize = 10     " occupies 10% of window
" }}}

" trailing spaces {{{
set listchars=tab:▸\ ,trail:·       " Show trailing spaces and tabs
set list                            " ^^ enable it
autocmd BufWritePre * :%s/\s\+$//e  " remove trailing spaces on save
" }}}

" stuff {{{
nmap <space> <leader>
inoremap jk <ESC>|         " jk escapes to normal mode
tnoremap jk <C-\><C-n>|    " jk escapes to normal mode (in terminal mode)
tnoremap <Esc> <C-\><C-n>| " esc escapes to normal mode
" }}}

" split mappings {{{
" next sections looks pretty much like my i3 config except Win key is replaced
" with the Alt key
" move between buffers with alt+hjkl
nnoremap <A-h> <C-w>h
nnoremap <A-j> <C-w>j
nnoremap <A-k> <C-w>k
nnoremap <A-l> <C-w>l

" faster resize for buffers
nnoremap <A-J> <C-w>+
nnoremap <A-K> <C-w>-
nnoremap <A-L> <C-w>>
nnoremap <A-H> <C-w><
tnoremap <A-J> <C-\><C-n><C-w>+
tnoremap <A-K> <C-\><C-n><C-w>-
tnoremap <A-L> <C-\><C-n><C-w>>
tnoremap <A-H> <C-\><C-n><C-w><

" faster split creation/deletion
nnoremap <silent> <A--> :split<CR>
nnoremap <silent> <A-\> :vsplit<CR>
nnoremap <silent> <A-d> :bd<CR>

" change buffers
nnoremap <silent> <C-l> :bn<CR>
nnoremap <silent> <C-h> :bp<CR>
" }}}

" tabs {{{
nnoremap <silent> <A-.> :tabnext<CR>|               " alt-.  -> next tab
tnoremap <silent> <A-.> <C-\><C-n>:tabnext<CR>|     " alt-.  -> next tab (terminal mode)
nnoremap <silent> <A-,> :tabprevious<CR>|           " alt-,  -> prev tab
tnoremap <silent> <A-,> <C-\><C-n>:tabprevious<CR>| " alt-,  -> prev tab (terminal mode)
nnoremap <silent> <A-1> :1 tabn<CR>|                " alt-1  -> goes to tab 1
nnoremap <silent> <A-2> :2 tabn<CR>|                " ^^
nnoremap <silent> <A-3> :3 tabn<CR>|                " ^^
nnoremap <silent> <A-4> :4 tabn<CR>|                " ^^
nnoremap <silent> <A-5> :5 tabn<CR>|                " ^^
nnoremap <silent> <C-t> :tabnew<CR>|                " ctrl-t -> new tab
" }}}

" indention mappings {{{
vnoremap <Tab> >gv|     " tab indents in visual mode
vnoremap <S-Tab> <gv|   " s-tab de-indents in visual mode
inoremap <S-Tab> <C-d>| " s-tab de-indents in insert mode
" }}}

" move visual lines (j,k works in traditional way) {{{
onoremap <silent> j gj
onoremap <silent> k gk
nnoremap <silent> j gj
nnoremap <silent> k gk
vnoremap <silent> j gj
vnoremap <silent> k gk
" }}}

" Master Wq bindings {{{
command! Wq wq
command! W w
command! Q q
nnoremap <silent> <C-s> :w<CR>|             " ctrl-s -> save
nnoremap <silent> <C-q> :q<CR>|             " ctrl-q -> quit
tnoremap <silent> <C-q> <C-\><C-n>:q<CR>|   " ctrl-q -> quit (term)
" }}}

" Turkish keyboard mappings {{{
nnoremap Ş :
nnoremap ı i
nnoremap ğ [
nnoremap ü ]
nnoremap Ğ {
nnoremap Ü }
nnoremap ç .
nnoremap Ö <
nnoremap Ç >
vnoremap Ş :
vnoremap ı i
vnoremap ğ [
vnoremap ü ]
vnoremap Ğ {
vnoremap Ü }
vnoremap ç .
vnoremap Ö <
vnoremap Ç >
" }}}

" vi: foldmethod=marker

Browsers

Firefox

userChrome.css

  • I use Sidebery so I hide the tab bar and siderbar heading.
  • I also decrease the width of "flexible space" so that it becomes something useful.
#TabsToolbar {
    visibility: collapse;
}

#sidebar-header {
  visibility: collapse !important;
}

#nav-bar toolbarspring {
    min-width: 10px !important;
    max-width: 20px !important;
}

Also need to enable following config to make userChrome.css work:

// Enable stylesheet support:
user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
// For CSS to properly work:
user_pref("sidebar.revamp", false);

Other configs

// Disable menu popping up when holding alt.
// This kind of breaks my global xremap remappings on Firefox.
user_pref("ui.key.menuAccessKeyFocuses", false);

user_pref("browser.toolbars.bookmarks.visibility", "never");
user_pref("browser.fullscreen.exit_on_escape", false)

// Don't show password suggestions from other subdomains
user_pref("signon.includeOtherSubdomainsInLookup", false);

// Enable syncing of UI customizations
user_pref("services.sync.prefs.sync.browser.uiCustomization.state", true);

// Rest is generated by https://ffprofile.com
user_pref("app.normandy.api_url", "");
user_pref("app.normandy.enabled", false);
user_pref("app.shield.optoutstudies.enabled", false);
user_pref("app.update.auto", false);
user_pref("beacon.enabled", false);
user_pref("breakpad.reportURL", "");
user_pref("browser.aboutConfig.showWarning", false);
user_pref("browser.crashReports.unsubmittedCheck.autoSubmit", false);
user_pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false);
user_pref("browser.crashReports.unsubmittedCheck.enabled", false);
user_pref("browser.disableResetPrompt", true);
user_pref("browser.newtab.preload", false);
user_pref("browser.newtabpage.activity-stream.section.highlights.includePocket", false);
user_pref("browser.newtabpage.enhanced", false);
user_pref("browser.newtabpage.introShown", true);
user_pref("browser.safebrowsing.appRepURL", "");
user_pref("browser.safebrowsing.blockedURIs.enabled", false);
user_pref("browser.safebrowsing.downloads.enabled", false);
user_pref("browser.safebrowsing.downloads.remote.enabled", false);
user_pref("browser.safebrowsing.downloads.remote.url", "");
user_pref("browser.safebrowsing.enabled", false);
user_pref("browser.safebrowsing.malware.enabled", false);
user_pref("browser.safebrowsing.phishing.enabled", false);
user_pref("browser.search.suggest.enabled", false);
user_pref("browser.selfsupport.url", "");
user_pref("browser.send_pings", false);
user_pref("browser.sessionstore.privacy_level", 0);
user_pref("browser.shell.checkDefaultBrowser", false);
user_pref("browser.startup.homepage_override.mstone", "ignore");
user_pref("browser.tabs.crashReporting.sendReport", false);
user_pref("browser.urlbar.groupLabels.enabled", false);
user_pref("browser.urlbar.quicksuggest.enabled", false);
user_pref("browser.urlbar.speculativeConnect.enabled", false);
user_pref("browser.urlbar.trimURLs", false);
user_pref("datareporting.healthreport.service.enabled", false);
user_pref("datareporting.healthreport.uploadEnabled", false);
user_pref("datareporting.policy.dataSubmissionEnabled", false);
user_pref("device.sensors.ambientLight.enabled", false);
user_pref("device.sensors.enabled", false);
user_pref("device.sensors.motion.enabled", false);
user_pref("device.sensors.orientation.enabled", false);
user_pref("device.sensors.proximity.enabled", false);
user_pref("dom.battery.enabled", false);
// This breaks pasting images
// user_pref("dom.event.clipboardevents.enabled", false);
user_pref("experiments.activeExperiment", false);
user_pref("experiments.enabled", false);
user_pref("experiments.manifest.uri", "");
user_pref("experiments.supported", false);
user_pref("extensions.CanvasBlocker@kkapsner.de.whiteList", "");
user_pref("extensions.ClearURLs@kevinr.whiteList", "");
user_pref("extensions.TemporaryContainers@stoically.whiteList", "");
user_pref("extensions.getAddons.cache.enabled", false);
user_pref("extensions.getAddons.showPane", false);
user_pref("extensions.greasemonkey.stats.optedin", false);
user_pref("extensions.greasemonkey.stats.url", "");
user_pref("extensions.pocket.enabled", false);
user_pref("extensions.shield-recipe-client.api_url", "");
user_pref("extensions.shield-recipe-client.enabled", false);
user_pref("extensions.webservice.discoverURL", "");
user_pref("media.autoplay.default", 0);
user_pref("media.autoplay.enabled", true);
user_pref("media.eme.enabled", false);
user_pref("media.gmp-widevinecdm.enabled", false);
user_pref("media.navigator.enabled", false);
user_pref("media.video_stats.enabled", false);
user_pref("network.allow-experiments", false);
user_pref("network.captive-portal-service.enabled", false);
user_pref("network.cookie.cookieBehavior", 1);
user_pref("network.dns.disablePrefetch", true);
user_pref("network.dns.disablePrefetchFromHTTPS", true);
user_pref("network.http.referer.spoofSource", true);
user_pref("network.http.speculative-parallel-limit", 0);
user_pref("network.predictor.enable-prefetch", false);
user_pref("network.predictor.enabled", false);
user_pref("network.prefetch-next", false);
user_pref("network.trr.mode", 5);
user_pref("privacy.donottrackheader.enabled", true);
user_pref("privacy.donottrackheader.value", 1);
user_pref("privacy.query_stripping", true);
user_pref("privacy.trackingprotection.cryptomining.enabled", true);
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.fingerprinting.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
user_pref("privacy.usercontext.about_newtab_segregation.enabled", true);
user_pref("security.ssl.disable_session_identifiers", true);
user_pref("services.sync.prefs.sync.browser.newtabpage.activity-stream.showSponsoredTopSite", false);
user_pref("signon.autofillForms", false);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("toolkit.telemetry.bhrPing.enabled", false);
user_pref("toolkit.telemetry.cachedClientID", "");
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.firstShutdownPing.enabled", false);
user_pref("toolkit.telemetry.hybridContent.enabled", false);
user_pref("toolkit.telemetry.newProfilePing.enabled", false);
user_pref("toolkit.telemetry.prompted", 2);
user_pref("toolkit.telemetry.rejected", true);
user_pref("toolkit.telemetry.reportingpolicy.firstRun", false);
user_pref("toolkit.telemetry.server", "");
user_pref("toolkit.telemetry.shutdownPingSender.enabled", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.unifiedIsOptIn", false);
user_pref("toolkit.telemetry.updatePing.enabled", false);
user_pref("webgl.renderer-string-override", " ");
user_pref("webgl.vendor-string-override", " ");

Sideberry

{
  "ver": "5.6.1",
  "settings": {
    "nativeScrollbars": true,
    "nativeScrollbarsThin": true,
    "nativeScrollbarsLeft": false,
    "selWinScreenshots": false,
    "updateSidebarTitle": true,
    "markWindow": false,
    "markWindowPreface": "[Sidebery] ",
    "copyTreeIndent": "",
    "copyMultiBullet": "",
    "copyTemplates": "HTML:%B<a href=\"%U\">%CT</a>\nMarkdown:%B[%CT](%U)",
    "updTooltipDelay": 100,
    "selLen": true,
    "ctxMenuNative": false,
    "ctxMenuRenderInact": true,
    "ctxMenuRenderIcons": true,
    "ctxMenuIgnoreContainers": "",
    "omniReopenInCtr": true,
    "omniReopenInCtrPrefix": "",
    "omniSwitchToPanel": true,
    "omniSwitchToPanelPrefix": "",
    "omniMoveToPanel": true,
    "omniMoveToPanelPrefix": "",
    "omniMoveToGroup": true,
    "omniMoveToGroupPrefix": "",
    "navBarLayout": "horizontal",
    "navBarInline": false,
    "navBarSide": "left",
    "navBtnCount": true,
    "hideEmptyPanels": false,
    "hideDiscardedTabPanels": false,
    "navActTabsPanelLeftClickAction": "none",
    "navActTabsPanelLeftClickTabPos": "general",
    "navActBookmarksPanelLeftClickAction": "none",
    "navTabsPanelMidClickAction": "discard",
    "navBookmarksPanelMidClickAction": "none",
    "navSwitchPanelsWheel": true,
    "subPanelRecentlyClosedBar": true,
    "subPanelBookmarks": true,
    "subPanelHistory": true,
    "subPanelSync": true,
    "groupLayout": "grid",
    "containersSortByName": false,
    "skipEmptyPanels": false,
    "dndTabAct": true,
    "dndTabActDelay": 750,
    "dndTabActMod": "none",
    "dndExp": "pointer",
    "dndExpDelay": 750,
    "dndExpMod": "none",
    "dndOutside": "win",
    "dndOutsideThresholdTimeout": 350,
    "dndActTabFromLink": true,
    "dndActSearchTab": true,
    "dndMoveTabs": false,
    "dndMoveBookmarks": false,
    "dndTabToPanelPos": "end",
    "searchBarMode": "static",
    "searchPanelSwitch": "any",
    "searchTabSwitch": false,
    "searchMenuTrig": false,
    "searchBookmarksShortcut": "",
    "searchHistoryShortcut": "",
    "searchInputTimeout": 300,
    "warnOnMultiTabClose": "collapsed",
    "activateLastTabOnPanelSwitching": true,
    "activateLastTabOnPanelSwitchingLoadedOnly": false,
    "switchPanelAfterSwitchingTab": "always",
    "scrollPanelAfterSwitchingTab": "always",
    "tabRmBtn": "hover",
    "activateAfterClosing": "next",
    "activateAfterClosingStayInPanel": false,
    "activateAfterClosingGlobal": false,
    "activateAfterClosingNoFolded": true,
    "activateAfterClosingNoDiscarded": false,
    "askNewBookmarkPlace": true,
    "tabsRmUndoNote": true,
    "tabsUnreadMark": false,
    "tabsBadge": true,
    "tabsBadgeRules": "minIdleTime:5000; urgent",
    "tabsReloadLimit": 5,
    "tabsReloadLimitNotif": true,
    "showNewTabBtns": true,
    "newTabBarPosition": "after_tabs",
    "tabsPanelSwitchActMove": false,
    "tabsPanelSwitchActMoveAuto": true,
    "tabsUrlInTooltip": "full",
    "tabsContainerInTooltip": true,
    "newTabCtxReopen": false,
    "tabWarmupOnHover": true,
    "tabSwitchDelay": 0,
    "forceDiscard": true,
    "tabUpdDelay": 150,
    "forceUpdTooltip": false,
    "moveNewTabPin": "start",
    "moveNewTabParent": "last_child",
    "moveNewTabParentIndent": false,
    "moveNewTabParentActPanel": false,
    "moveNewTabButton": "end",
    "moveNewTabButtonActivePin": "start",
    "moveNewTab": "end",
    "moveNewTabActivePin": "start",
    "autoScrollToNewTab": true,
    "pinnedTabsPosition": "panel",
    "pinnedTabsList": false,
    "pinnedAutoGroup": false,
    "pinnedNoUnload": true,
    "pinnedNoUnloadExplicit": true,
    "pinnedForcedDiscard": false,
    "tabsTree": true,
    "tabsTreeLimit": 3,
    "autoFoldTabs": false,
    "autoFoldTabsExcept": "none",
    "autoExpandTabs": false,
    "autoExpandTabsOnNew": false,
    "rmChildTabs": "folded",
    "tabsLvlDots": true,
    "discardFolded": false,
    "discardFoldedDelay": 0,
    "discardFoldedDelayUnit": "sec",
    "tabsTreeBookmarks": true,
    "treeRmOutdent": "branch",
    "autoGroupOnClose": false,
    "autoGroupOnClose0Lvl": false,
    "autoGroupOnCloseMouseOnly": false,
    "ignoreFoldedParent": false,
    "showNewGroupConf": true,
    "sortGroupsFirst": true,
    "colorizeTabs": true,
    "colorizeTabsSrc": "domain",
    "colorizeTabsBranches": false,
    "colorizeTabsBranchesSrc": "url",
    "inheritCustomColor": true,
    "previewTabs": false,
    "previewTabsMode": "p",
    "previewTabsPageModeFallback": "i",
    "previewTabsInlineHeight": 100,
    "previewTabsPopupWidth": 280,
    "previewTabsTitle": 2,
    "previewTabsUrl": 1,
    "previewTabsSide": "right",
    "previewTabsDelay": 500,
    "previewTabsFollowMouse": true,
    "previewTabsInPageOffsetY": 0,
    "previewTabsInPageOffsetX": 0,
    "previewTabsCropRight": 0,
    "hideInact": false,
    "hideFoldedTabs": false,
    "hideFoldedParent": "none",
    "hideUnloadedTabs": false,
    "nativeHighlight": false,
    "warnOnMultiBookmarkDelete": "collapsed",
    "autoCloseBookmarks": false,
    "autoRemoveOther": false,
    "highlightOpenBookmarks": false,
    "activateOpenBookmarkTab": false,
    "showBookmarkLen": true,
    "bookmarksRmUndoNote": true,
    "loadBookmarksOnDemand": true,
    "pinOpenedBookmarksFolder": true,
    "oldBookmarksAfterSave": "ask",
    "historyTitleUpdInterval": 1100,
    "fontSize": "s",
    "fontFamily": "",
    "animations": true,
    "animationSpeed": "norm",
    "theme": "plain",
    "density": "default",
    "colorScheme": "ff",
    "snapNotify": true,
    "snapExcludePrivate": false,
    "snapInterval": 0,
    "snapIntervalUnit": "min",
    "snapLimit": 0,
    "snapLimitUnit": "snap",
    "snapAutoExport": false,
    "snapAutoExportType": "json",
    "snapAutoExportPath": "Sidebery/snapshot-%Y.%M.%D-%h.%m.%s",
    "snapMdFullTree": false,
    "hScrollAction": "switch_panels",
    "onePanelSwitchPerScroll": false,
    "wheelAccumulationX": true,
    "wheelAccumulationY": true,
    "navSwitchPanelsDelay": 128,
    "scrollThroughTabs": "panel",
    "scrollThroughTabsGlobPinIsolate": true,
    "scrollThroughVisibleTabs": true,
    "scrollThroughTabsSkipDiscarded": false,
    "scrollThroughTabsExceptOverflow": true,
    "scrollThroughTabsCyclic": false,
    "scrollThroughTabsScrollArea": 0,
    "scrollThroughTabsPreselAct": false,
    "scrollThroughTabsPreselDelay": 0,
    "autoMenuMultiSel": true,
    "multipleMiddleClose": false,
    "longClickDelay": 500,
    "wheelThreshold": false,
    "wheelThresholdX": 10,
    "wheelThresholdY": 60,
    "tabDoubleClick": "none",
    "tabsSecondClickActPrev": false,
    "tabsSecondClickActPrevPanelOnly": false,
    "tabsSecondClickActPrevNoUnload": true,
    "shiftSelAct": true,
    "ctrlSelAct": true,
    "activateOnMouseUp": false,
    "tabCloseOnMouseUp": true,
    "tabLongLeftClick": "none",
    "tabLongRightClick": "none",
    "tabMiddleClick": "close",
    "tabPinnedMiddleClick": "discard",
    "tabMiddleClickCtrl": "discard",
    "tabMiddleClickShift": "duplicate",
    "tabCloseMiddleClick": "close",
    "tabsPanelLeftClickAction": "none",
    "tabsPanelLeftClickTabPos": "general",
    "tabsPanelDoubleClickAction": "tab",
    "tabsPanelDoubleClickTabPos": "general",
    "tabsPanelRightClickAction": "menu",
    "tabsPanelMiddleClickAction": "tab",
    "tabsPanelMiddleClickTabPos": "general",
    "newTabMiddleClickAction": "new_child",
    "bookmarksLeftClickAction": "open_in_act",
    "bookmarksLeftClickActivate": false,
    "bookmarksLeftClickPos": "default",
    "bookmarksMidClickAction": "open_in_new",
    "bookmarksMidClickActivate": false,
    "bookmarksMidClickRemove": false,
    "bookmarksMidClickPos": "default",
    "historyLeftClickAction": "open_in_act",
    "historyLeftClickActivate": false,
    "historyLeftClickPos": "default",
    "historyMidClickAction": "open_in_new",
    "historyMidClickActivate": false,
    "historyMidClickPos": "default",
    "syncName": "",
    "syncUseFirefox": true,
    "syncUseGoogleDrive": false,
    "syncUseGoogleDriveApi": false,
    "syncUseGoogleDriveApiClientId": "",
    "syncSaveSettings": false,
    "syncSaveCtxMenu": false,
    "syncSaveStyles": false,
    "syncSaveKeybindings": false,
    "selectActiveTabFirst": true,
    "selectCyclic": false,
    "loopPanelsIgnoreHidden": false,
    "kbNewTabInPanelPos": "general"
  },
  "keybindings": {
    "_execute_sidebar_action": "F1",
    "activate": "Alt+Space",
    "reset_selection": "Alt+R",
    "next_panel": "Alt+Period",
    "prev_panel": "Alt+Comma",
    "new_tab_on_panel": "MacCtrl+Space",
    "new_tab_in_group": "MacCtrl+Shift+Space",
    "rm_tab_on_panel": "Alt+D",
    "up": "MacCtrl+Alt+K",
    "down": "MacCtrl+Alt+J",
    "up_shift": "Alt+Shift+Up",
    "down_shift": "Alt+Shift+Down",
    "fold_inact_branches": "F3",
    "move_tabs_up": "Alt+Shift+K",
    "move_tabs_down": "Alt+Shift+J",
    "tabs_indent": "Alt+Shift+L",
    "tabs_outdent": "Alt+Shift+H",
    "switch_to_panel_0": "Alt+1",
    "switch_to_panel_1": "Alt+2",
    "switch_to_panel_2": "Alt+3",
    "switch_to_panel_3": "Alt+4",
    "switch_to_panel_4": "Alt+5",
    "move_tabs_to_panel_0": "Alt+Shift+1",
    "move_tabs_to_panel_1": "Alt+Shift+2",
    "move_tabs_to_panel_2": "Alt+Shift+3",
    "move_tabs_to_panel_3": "Alt+Shift+4",
    "move_tabs_to_panel_4": "Alt+Shift+5",
    "search": "F2",
    "switch_to_next_tab": "Alt+J",
    "switch_to_prev_tab": "Alt+K"
  },
  "sidebar": {
    "panels": {
      "nqYWdHQS2bL0": {
        "type": 2,
        "id": "nqYWdHQS2bL0",
        "name": "Tabs",
        "color": "toolbar",
        "iconSVG": "icon_tabs",
        "iconIMGSrc": "",
        "iconIMG": "",
        "lockedPanel": false,
        "skipOnSwitching": false,
        "noEmpty": false,
        "newTabCtx": "none",
        "dropTabCtx": "none",
        "moveRules": [],
        "moveExcludedTo": -1,
        "bookmarksFolderId": -1,
        "newTabBtns": [
          "Work",
          "Personal",
          "Other"
        ],
        "srcPanelConfig": null
      },
      "zkxIyW_E6AZ0": {
        "type": 2,
        "id": "zkxIyW_E6AZ0",
        "name": "Work",
        "color": "orange",
        "iconSVG": "briefcase",
        "iconIMGSrc": "",
        "iconIMG": "",
        "lockedPanel": false,
        "skipOnSwitching": false,
        "noEmpty": false,
        "newTabCtx": "none",
        "dropTabCtx": "none",
        "moveRules": [],
        "moveExcludedTo": -1,
        "bookmarksFolderId": -1,
        "newTabBtns": [
          "Work"
        ],
        "srcPanelConfig": null
      },
      "dmRm04pnK_B5": {
        "type": 2,
        "id": "dmRm04pnK_B5",
        "name": "Personal",
        "color": "blue",
        "iconSVG": "fingerprint",
        "iconIMGSrc": "",
        "iconIMG": "",
        "lockedPanel": false,
        "skipOnSwitching": false,
        "noEmpty": false,
        "newTabCtx": "none",
        "dropTabCtx": "none",
        "moveRules": [],
        "moveExcludedTo": -1,
        "bookmarksFolderId": -1,
        "newTabBtns": [],
        "srcPanelConfig": null
      },
      "6pf1fzYMrrkm": {
        "type": 2,
        "id": "6pf1fzYMrrkm",
        "name": "Static",
        "color": "turquoise",
        "iconSVG": "icon_clipboard",
        "iconIMGSrc": "",
        "iconIMG": "",
        "lockedPanel": false,
        "skipOnSwitching": false,
        "noEmpty": false,
        "newTabCtx": "none",
        "dropTabCtx": "none",
        "moveRules": [],
        "moveExcludedTo": -1,
        "bookmarksFolderId": -1,
        "newTabBtns": [],
        "srcPanelConfig": null
      },
      "KaZEk8lZmkkm": {
        "type": 2,
        "id": "KaZEk8lZmkkm",
        "name": "Stuff",
        "color": "red",
        "iconSVG": "icon_code",
        "iconIMGSrc": "",
        "iconIMG": "",
        "lockedPanel": false,
        "skipOnSwitching": false,
        "noEmpty": false,
        "newTabCtx": "none",
        "dropTabCtx": "none",
        "moveRules": [],
        "moveExcludedTo": -1,
        "bookmarksFolderId": -1,
        "newTabBtns": [],
        "srcPanelConfig": null
      },
      "9HCG-gD6CUjm": {
        "type": 2,
        "id": "9HCG-gD6CUjm",
        "name": "TODO",
        "color": "purple",
        "iconSVG": "icon_flask",
        "iconIMGSrc": "",
        "iconIMG": "",
        "lockedPanel": false,
        "skipOnSwitching": false,
        "noEmpty": false,
        "newTabCtx": "none",
        "dropTabCtx": "none",
        "moveRules": [],
        "moveExcludedTo": -1,
        "bookmarksFolderId": -1,
        "newTabBtns": [],
        "srcPanelConfig": null
      },
      "history": {
        "type": 4,
        "id": "history",
        "name": "History",
        "color": "toolbar",
        "iconSVG": "icon_clock",
        "tempMode": false,
        "lockedPanel": false,
        "skipOnSwitching": false,
        "viewMode": "history"
      }
    },
    "nav": [
      "nqYWdHQS2bL0",
      "zkxIyW_E6AZ0",
      "dmRm04pnK_B5",
      "6pf1fzYMrrkm",
      "KaZEk8lZmkkm",
      "9HCG-gD6CUjm",
      "sp-0",
      "history",
      "remute_audio_tabs",
      "settings"
    ]
  }
}

QuteBrowser

# * Notes

# Sb → bookmarks page
# Sh → history page
# f C-r → rapid hinting

# * Configs

# ** Adblock & content

c.content.blocking.adblock.lists = ['https://easylist.to/easylist/easylist.txt', 'https://easylist.to/easylist/easyprivacy.txt']
c.content.blocking.method = 'both'
c.content.blocking.enabled = True
c.content.pdfjs = True

# ** Appearance & fonts

c.fonts.default_family = ['Cascadia Code NF']
c.fonts.default_size = '16pt'
c.fonts.hints = 'normal 16pt Helvetica'
c.window.hide_decoration = True
c.window.title_format = "qutebrowser"

# ** Tabs

c.tabs.padding = {"bottom": 7, "left": 5, "right": 5, "top": 7}
c.tabs.position = 'right'
c.tabs.show = 'switching'
c.tabs.show_switching_delay = 1000

# ** Hints

c.hints.chars = 'asdfghjklqweuio'
c.hints.uppercase = True

# ** Input & mouse

c.input.insert_mode.auto_load = True
c.input.mouse.rocker_gestures = True

# ** Status bar & completion

c.statusbar.position = 'bottom'
c.statusbar.show = 'always'
c.completion.shrink = False
c.completion.height = '30%'
c.completion.use_best_match = True

# ** URLs, search engines, editor

c.url.default_page = 'https://start.duckduckgo.com/'
c.url.yank_ignored_parameters = ['ref', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']
c.url.searchengines = {
    'DEFAULT': 'https://kagi.com/search?q={}',
    'k': 'https://kagi.com/search?q={}',
    'g': 'https://google.com/search?q={}',
    'd': 'https://duckduckgo.com/?q={}',
    'yt': 'https://www.youtube.com/results?search_query={}',
    'r': 'https://reddit.com/r/{}',
    'a': 'https://web.archive.org/web/{}',
    'nix': 'https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query={}',
}
c.editor.command = ['/opt/homebrew/bin/emacsclient', '-c', '{}']

# * Bindings

# ** Navigation & scrolling

config.bind('j', 'scroll-px 0 200')
config.bind('k', 'scroll-px 0 -200')
config.bind('J', 'tab-next')
config.bind('K', 'tab-prev')
config.bind('<Alt-J>', 'tab-move +')
config.bind('<Alt-K>', 'tab-move -')

# ** Tab management

config.bind('b', 'cmd-set-text -sr :tab-focus')
config.bind('B', 'cmd-set-text -s :quickmark-load -t')
config.bind('t', 'cmd-set-text -s :open -t')
config.bind('T', 'cmd-set-text :open -t -r {url:pretty}')
config.bind('O', 'cmd-set-text :open {url:pretty}')
config.bind('x', 'tab-close')
config.bind('d', 'scroll-page 0 0.5')

# ** Hints

config.bind('yf', 'hint links yank')
config.bind(';i', 'hint images tab')
config.bind(';I', 'hint images yank')
config.bind(',M', 'hint links spawn mpv {hint-url}')

# ** Media & external

config.bind(',m', 'spawn mpv {url}')

# ** Command mode & misc

config.bind('<F1>', 'config-cycle tabs.show always switching')
config.bind('<Alt-x>', 'cmd-set-text :')

# ** Insert mode overrides

config.bind('<Ctrl+g>', 'fake-key <Escape>', mode='insert')
config.unbind('<Ctrl-E>', mode='insert')
config.bind('<Alt-A>', 'edit-text', mode='insert')
config.bind('<Alt-E>', 'edit-text', mode='insert')

# ** User scripts

config.bind('P', 'spawn --userscript org-pass')

# ** Aliases

c.aliases = {
    'w': 'session-save',
    'q': 'close',
    'qa': 'quit',
    'wq': 'quit --save',
    'wqa': 'quit --save',
    'tbp-get-cookie': "jseval ...",
    'tbp-set-cookie': "jseval ...",
}

# ** Whitelist

c.content.blocking.whitelist = ['https://*.trendyol.com']

# * Load

config.load_autoconfig(False)

chawan

cha is kind of w3m on steroids.

I tried to use it within Emacs with vterm and eat but neither was able to show a good performance. I use it inside a separate term, which is fine. It integrates well with system, you can copy/paste stuff pretty easily and it has all vim keys.

I still use Emacs eww most of the time for text-heavy browsing but cha is something between eww and a real browser, where you can see the layout of a website more clearly. It's is better suited for surfing.

[start]
visual-home = "https://lite.duckduckgo.com/"

[buffer]
images = true
autofocus = true
cookie = "save"
# scripting = true # Enable per site

[search]
wrap = false

[external]
history-file = "~/.local/share/chawan/history.uri"


[display]
color-mode = "true-color"

[omnirule.kagi]
match = '^kagi:'
substitute-url = '(x) => "https://kagi.com/html/search?q=" + encodeURIComponent(x.split(":").slice(1).join(":"))'

[page]
'H' = "cmd.pager.prevBuffer"
'L' = "cmd.pager.nextBuffer"

# 'o' = "cmd.pager.webSearch"
'o' = '() => pager.load("kagi:")'
'O' = "cmd.pager.load"

'yy' = "cmd.pager.copyURL"

# 'M-y' = cmd.pager.copyURL
# 'yu' = cmd.pager.copyCursorLink

[[siteconf]]
url = 'https://isamert\.net/.*'
scripting = true

[[siteconf]]
url = 'https://kagi\.com/.*'
scripting = true

[[siteconf]]
url = 'https://github\.com/.*'
scripting = true

Some desktop files

.local/share/applications/jaro.desktop

[Desktop Entry]
Name=jaro
GenericName=Resource opener
Terminal=false
Exec=jaro %F
Type=Application
Categories=Utility;

Scripts

This part is almost completely untouched. Needs some revamp.

Bash Library

Convert command line arguments to variables automatically

Run PARAM=VALUE for every parameter passed as --param=value. Dashes are converted into underscores before doing the assignment. As an example, if your script is called like ./script --param1=value --param-2=value2 then you'll have PARAM1 variable set to value and PARAM_2 variable set to VALUE2 inside your script.

while [[ $# -gt 0 ]]; do
    case $1 in
        --*)
            TMP_ARG=${1#--}
            TMP_ARG=${TMP_ARG%=*}
            TMP_ARG=${TMP_ARG//-/_}
            TMP_VAL=${1#*=}
            declare "${TMP_ARG^^}"="$TMP_VAL"
            ;;
    esac
    shift
done

uploadfile

Uploads given file to a predetermined folder in your box.com account and returns you a public download link for the file.

Need to install boxcli for this to work.

  • You need to create an application on box.com first.
    • Go figure out in the developer console.
    • Select User Authentication (OAuth 2.0) for your application. It works well with box cli.
    • Go to your application page, go to "Configuration" and select "Write all files and folders stored in Box" to give write permissions.
  • yarn global add "@box/cli"
  • box login

The variable BOX_UPLOAD_DIR is defined in ~/.extrarc (I don't upload this file as it contains personal information). This variable simply holds the id of a folder you've created in box.com. You can list all folders in your root by issuing this command: box folders:items 0. Then copy the folder id that you want your files to be uploaded and set this variable. I use this script for being able to quickly share files with people, I don't do complex uploads with it, so all files under one folder suffices my needs.

#!/bin/bash

set -eo pipefail
FILE=$1
FILE_ID=$(box files:upload --id-only "$FILE" --parent-id "$BOX_UPLOAD_DIR")
box shared-links:create --json "$FILE_ID" file | jq -j '.url' | tee >(xcopy)

sub-extract

#!/bin/python

import os
import sys

def extract_auto():
    """ Automatically find the movie and extract SUB_ARCHIVE with proper name """
    movies = get_movies()
    movies_normalized = list(enumerate(map(normalize, movies)))
    movie_index, _ = max(movies_normalized, key=lambda tup: matches(tup[1]))
    movie_full_path = movies[movie_index]

    extract(movie_full_path)

def extract_interactive():
    import subprocess
    selected_movie = subprocess \
            .run(['/bin/sh', '-c', 'echo -n "' + '\n'.join(get_movies()) + '" | fzf --header="Subtitle name: '+ SUB_ARCHIVE +'" --preview=""'], stdout=subprocess.PIPE) \
        .stdout.decode('utf-8') \
        .strip()

    if selected_movie != "":
        extract(selected_movie)

def extract(movie_full_path):
    """ Extract sub file from SUB_ARCHIVE """
    srt_full_path = mk_srt_path(movie_full_path)
    srt_archive_ext = os.path.splitext(SUB_ARCHIVE)[1]

    print("Given sub file: " + SUB_ARCHIVE)
    print("Movie: " + movie_full_path)
    print("Sub  : " + srt_full_path)

    yn = 'y' if NOCONFIRM else input("y/n? ")
    if yn != 'y':
        exit(1)

    if srt_archive_ext == ".zip":
        import zipfile
        with zipfile.ZipFile(SUB_ARCHIVE) as z:
            # Just take the first srt file
            srt_file = list(filter(lambda f: ".srt" in f, [file_info.filename for file_info in z.filelist]))[0]
            with open(srt_full_path, 'wb') as f:
                f.write(z.read(srt_file))
    elif srt_archive_ext == ".rar":
        import rarfile
        with rarfile.RarFile(SUB_ARCHIVE) as z:
            # Just take the first srt file
            srt_file = list(filter(lambda f: ".srt" in f, z.namelist()))[0]
            with open(srt_full_path, 'wb') as f:
                f.write(z.read(srt_file))
    else:
        print("wut? (for now)")

    print("Done.")

# #############################################################################
# Utility functions
# #############################################################################
def get_movies():
    movie_exts = [".mkv", ".mp4", ".avi"]
    movies = []
    for movie_dir in MOVIE_DIRS:
        for root, _, fs in os.walk(movie_dir):
            for f in fs:
                name, ext = os.path.splitext(os.path.basename(f))
                # Skip non-movie files and sample files
                # (and hope the movie name does not contain "sample")
                if ext in movie_exts and not "sample" in name.lower():
                    movies.append(os.path.join(root, f))
    return movies

def mk_srt_path(movie_full_path):
    """ Replace movie extension with .srt """
    return os.path.splitext(movie_full_path)[0] + ".srt"

def normalize(s):
    # 1080p, 720p etc makes matching harder because sometimes the downloaded
    # subtitle has different resolution spec
    return s.lower() \
            .replace("-", " ") \
            .replace(".", " ") \
            .replace("_", " ") \
            .replace("1080p", "") \
            .replace("720p", "") \
            .replace("bdrip", "") \
            .replace("blueray", "") \
            .replace("x264", "")

def matches(text):
    return sum(word in text for word in SUB_NAME)

# #############################################################################
# Here we go
# #############################################################################

SUB_ARCHIVE = sys.argv[-1]
SUB_NAME = normalize(SUB_ARCHIVE).split()
MOVIE_DIRS = [os.path.expanduser("~/Videos")]
NOCONFIRM = "--no-confirm" in sys.argv

if "--movie_dirs" in sys.argv:
    arg_index = sys.argv.index("--movie_dirs")
    MOVIE_DIRS = sys.argv[arg_index + 1].split(",")
    MOVIE_DIRS = [os.path.expanduser(x.strip()) for x in MOVIE_DIRS]
    for mdir in MOVIE_DIRS:
        if not os.path.exists(mdir):
            print("Movie directory does not exist: " + mdir)
            exit(1)

if "--help" in sys.argv:
    print("sub-extract [--(interactive|auto)] [--noconfirm] [--help] archive-file")
    print("This program extracts a subtitle file from an archive file into the selected movie folder.")
    print("")
    print("\t--auto")
    print("\t\tAutomatically matches the sub file with the movie using some heuristics. (Default)")
    print("\t--interactive")
    print("\t\tOpen fzf to find matching movie file.")
    print("\t--no-confirm")
    print("\t\tDo not ask for user consent and automatically copy the sub file.")
    print("\t--movie-dirs")
    print("\t\tA comma separated list of movie directories that you want to be searched. (Default: ~/Videos)")
    print("\t\tExample: sub-extract --movie-dirs ~/Movies,~/Shows")
elif os.path.exists(SUB_ARCHIVE):
    if "--auto" in sys.argv and "--interactive" not in sys.argv:
        extract_auto()
    elif "--interactive" in sys.argv and "--auto" not in sys.argv:
        extract_interactive()
else:
    print("File not found: " + SUB_ARCHIVE)
    print("Archive path should be the last argument.")

togif

#!/bin/bash

in_file="$1"
out_file="$2"
height_px=512
start_sec=00
end_sec=59
color_count=256
framerate=15

for i in "$@"; do
case $i in
    -i=*|--input=*)  in_file="${i#*=}"; shift ;;
    -o=*|--output=*) out_file="${i#*=}"; shift ;;
    -h=*|--height=*) height_px="${i#*=}"; shift ;;
    -s=*|--start=*)  start_sec="${i#*=}"; shift ;;
    -e=*|--end=*)    end_sec="${i#*=}"; shift ;;
    -c=*|--color=*)  color_count="${i#*=}"; shift ;;
    -r=*|--framerate=*)  framerate="${i#*=}"; shift ;;
esac
done

if [ $1 = "help" ] || [ $1 = "--help" ] || [ $1 = "-h" ] || [ $1 = "" ]; then
    echo -e "togif in_file out_file [OPTION...]\n"
    echo -e "OPTIONS"
    echo -e "\t-i FILE, --input=FILE\n"
    echo -e "\t-o FILE, --output=FILE\n"
    echo -e "\t-h HEIGHT, --height=HEIGHT"
    echo -e "\t\tWidth will be scaled according to given HEIGHT. Default: 512\n"
    echo -e "\t-s SEC, --start=SEC"
    echo -e "\t\tStarts the video from given SEC. Default: 00\n"
    echo -e "\t-e SEC, --end=SEC"
    echo -e "\t\tEnds the video at the given SEC. Default: 59\n"
    echo -e "\t-c COUNT, --color=COUNT"
    echo -e "\t\tReduce the color palette to COUNT colors. (If it's lower already, does nothing.) (Only works for gif outputs) Default: 256\n"
    echo -e "\t-r COUNT, --framerate=COUNT"
    echo -e "\t\tReduce videos framerate to COUNT. Default: 15"
else

    echo "=== CONVERTING ==="
    ffmpeg \
      -i "$in_file" \
      -r $framerate \
      -vf scale=$height_px:-1 \
      -ss 00:00:$start_sec -to 00:00:$end_sec \
      "$out_file"
    convert_result=$?
    echo "=== DONE ==="

    # Optimize if it's a gif
    if [[ $convert_result == 0 ]] && [[ "$out_file" == *.gif ]]; then
        echo ""
        echo "=== OPTIMIZING ==="
        gifsicle -i "$out_file" --optimize=3 --colors $color_count -o "${out_file}_optimized"
        rm "$out_file"
        mv "${out_file}_optimized" "$out_file"
        echo "=== DONE ==="
    fi

fi

xcopy

#!/bin/bash

file="$1"
input="$*"

if command -v wl-copy &>/dev/null; then
    CLIP_CMD="wl-copy"
elif command -v xclip &>/dev/null; then
    CLIP_CMD="xclip -selection clipboard"
elif command -v pbcopy &>/dev/null; then
    CLIP_CMD="pbcopy"
else
    echo "Install wl-copy or xclip." >&2
    exit 1
fi

if [[ -f "$file" ]]; then
    if [[ "$CLIP_CMD" == "pbcopy" ]]; then
        echo "Not supported by pbcopy." >&2
        exit 1
    fi

    mime="$(file -b --mime-type "$file")"
    if [[ "$CLIP_CMD" == "wl-copy" ]]; then
        wl-copy --type "$mime" < "$file"
    else
        xclip -selection clipboard -t "$mime" -i "$file"
    fi
elif [[ -z "$input" ]]; then
    $CLIP_CMD <&0
else
    printf '%s' "$input" | $CLIP_CMD
fi

find-duplicate-images

#!/bin/bash

# Source: https://askubuntu.com/questions/1308613/how-to-remove-slightly-modified-duplicate-images

if [[ $1 = "--help" ]] || [[ $1 = "-h" ]]; then
    echo "Find duplicate images in current directory. Images are compared WITHOUT the metadata."
    echo "It lists duplicate files. You need to delete them manually."
    echo
    echo "USAGE:"
    echo "  find-duplicate-images"
    exit
fi

echo "Scanning images... This may take some time..."

find -type f -a '(' \
        -iname '*.jpg' -o \
        -iname '*.png' -o \
        -iname '*.jpeg' -o \
        -iname '*.mov' -o \
        -iname '*.mpg' -o \
        -iname '*.mpeg' -o \
        -iname '*.avi' \
    ')' -print0 |perl -n0e '
    my $f = $_;
    chomp($f);
    (my $fe = $f) =~ s|\x27|\x27\\\x27\x27|g;
    my $md5;
    if($f =~ m|\.[aA][vV][iI]$| or $f =~ m|\.[mM][pP][gG]$|) {
      $md5 = `cat \x27$fe\x27 |md5sum`;
    } else {
      $md5 = `exiftool \x27$fe\x27 -all= -o - |md5sum`;
    }
    chomp($md5); $md5 =~ s| +-\n||;
    print("$md5 $f\n");
' | sort | uniq --check-chars=32 --all-repeated

git-file-history-grep

#!/bin/bash

# TODO: Create an emacs wrapper which fuzzy searches through these
# results and opens the file on that revision using
# (vc-revision-other-window REV)

case "$1" in
    -h|--help)
        echo "Search STRING in all revisions of given FILE."
        echo
        echo "Usage:"
        echo "git-file-hist-grep STRING FILE"
        ;;
    *)
        SEARCH_STRING=$1
        FILE_NAME=$2
        git rev-list --all "$FILE_NAME" | while read REVISION; do
            git --no-pager grep -F "$SEARCH_STRING" "$REVISION" "$FILE_NAME"
        done
        ;;
esac

set-timezone

#!/usr/bin/env bash

set -euo pipefail

TIMEZONE=$(curl -s --fail ipinfo.io | jq -r '.timezone // empty')

if [[ -z "$TIMEZONE" ]]; then
    echo "Error: Could not determine timezone" >&2
    exit 1
fi

sudo timedatectl set-timezone "$TIMEZONE"
echo "Timezone set to: $TIMEZONE"

Tools

dconf-editor
List and explore gnome/gshell/app settings.
d-feet
List and explore running dbus instances.
peek
Screen recorder (records gifs etc.)
tokei
CLOC (count lines of code)
subdl
dowload subtitles from opensubtitles.org
socat
needed for communicating with mpv trough unix sockets
entr
Listen/subscribe to file changes. (linux)
fswatch
Listen/subscribe to file changes. (cross-platform)
  • fswatch -o src/main | xargs -n1 ./mvnw compile
    • -o one per batch

Postamble

  • The following thing automatically loads the code necessary when this file is opened.
  • This basically makes use of file local variables.
  • It also changes org-babel-noweb-wrap-{start,end} variables so that when noweb references are used inside sh/bash blocks, it does not mess up the code highlighting. You need to use «ref» instead of <<ref>> to include noweb references inside code blocks.