Skip to content

Latest commit

 

History

History
4053 lines (3503 loc) · 134 KB

index.org

File metadata and controls

4053 lines (3503 loc) · 134 KB

Dotfiles

Introduction

These are my dotfiles. Please see ./.emacs.d/index.org 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.
    «executable_hook»
    «conditions»
        

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

Nix

Well, nix is mainly a package manager but it also is a programming language.

The following enables nix search command.

experimental-features = nix-command flakes

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
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-f1 = 'layout v_accordion'
cmd-f2 = 'layout h_accordion'
cmd-f3 = '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-shift-minus = 'resize smart -50'
cmd-shift-equal = 'resize smart +50'

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'
cmd-alt-ctrl-f13 = 'layout floating tiling'
cmd-alt-ctrl-f14 = 'fullscreen'

cmd-alt-ctrl-f11 = 'resize smart -50'
cmd-alt-ctrl-f12 = 'resize smart +50'

cmd-alt-ctrl-f19 = 'focus-monitor left'
cmd-alt-ctrl-f20 = 'focus-monitor right'
cmd-alt-ctrl-shift-f19 = 'move-node-to-monitor left'
cmd-alt-ctrl-shift-f20 = 'move-node-to-monitor right'

cmd-alt-ctrl-f15 = 'focus --boundaries all-monitors-outer-frame --boundaries-action stop left'
cmd-alt-ctrl-f16 = 'focus --boundaries all-monitors-outer-frame --boundaries-action stop down'
cmd-alt-ctrl-f17 = 'focus --boundaries all-monitors-outer-frame --boundaries-action stop up'
cmd-alt-ctrl-f18 = 'focus --boundaries all-monitors-outer-frame --boundaries-action stop right'

cmd-alt-ctrl-shift-f15 = 'move left'
cmd-alt-ctrl-shift-f16 = 'move down'
cmd-alt-ctrl-shift-f17 = 'move up'
cmd-alt-ctrl-shift-f18 = 'move right'

cmd-alt-ctrl-f1 = 'workspace 1'
cmd-alt-ctrl-f2 = 'workspace 2'
cmd-alt-ctrl-f3 = 'workspace 3'
cmd-alt-ctrl-f4 = 'workspace 4'
cmd-alt-ctrl-f5 = 'workspace 5'
cmd-alt-ctrl-f6 = 'workspace 6'
cmd-alt-ctrl-f7 = 'workspace 7'
cmd-alt-ctrl-f8 = 'workspace 8'
cmd-alt-ctrl-f9 = 'workspace 9'
cmd-alt-ctrl-f10 = 'workspace 0'

cmd-alt-ctrl-shift-f1 = 'move-node-to-workspace 1'
cmd-alt-ctrl-shift-f2 = 'move-node-to-workspace 2'
cmd-alt-ctrl-shift-f3 = 'move-node-to-workspace 3'
cmd-alt-ctrl-shift-f4 = 'move-node-to-workspace 4'
cmd-alt-ctrl-shift-f5 = 'move-node-to-workspace 5'
cmd-alt-ctrl-shift-f6 = 'move-node-to-workspace 6'
cmd-alt-ctrl-shift-f7 = 'move-node-to-workspace 7'
cmd-alt-ctrl-shift-f8 = 'move-node-to-workspace 8'
cmd-alt-ctrl-shift-f9 = 'move-node-to-workspace 9'
cmd-alt-ctrl-shift-f10 = 'move-node-to-workspace 0'
# 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]
1 = 'main'
2 = 'main'
3 = 'main'
4 = 'main'
5 = 'main'
6 = 'main'
7 = 'secondary'
8 = 'secondary'
9 = 'secondary'
0 = '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]
# - Letters.        a, b, c, ..., z
# - Numbers.        0, 1, 2, ..., 9
# - Keypad numbers. keypad0, keypad1, keypad2, ..., keypad9
# - F-keys.         f1, f2, ..., f20
# - Special keys.   minus, equal, period, comma, slash, backslash, quote, semicolon, backtick,
#                   leftSquareBracket, rightSquareBracket, space, enter, esc, backspace, tab
# - Keypad special. keypadClear, keypadDecimalMark, keypadDivide, keypadEnter, keypadEqual,
#                   keypadMinus, keypadMultiply, keypadPlus
# - Arrows.         left, down, up, right
# All possible modifiers: cmd, alt, ctrl, shift

#cmd-f1 = 'layout v_accordion'
#cmd-f2 = 'layout h_accordion'
#cmd-f3 = 'layout tiles horizontal vertical'

# «aerospace-mac-keyboard-bindings»
«aerospace-uhk-bindings»

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 - r : emacsclient -c
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 : open -n -a 'Alacritty.app'

# Define a mode name shortcuts
:: shortcuts @
# From default mode, F13 opens shortcuts mode
f13 ; shortcuts
# In shortcuts mode, F13 returns back to default mode
shortcuts < f13 ; default
# In shortcuts mode, m returns back to normal mode by simulating f13
# key and then does the command
shortcuts < m : skhd -k "f13";  emacsclient --eval "(im-toggle-mic)"

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 [
  # F6 key
  "Firefox" : skhd -k "0x61"
  * ~
]

Rootless sshd

Port 2222
HostKey «sh("echo $HOME")»/.config/sshd/hostkey
PidFile «sh("echo $HOME")»/.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
«mk-launchagent(name="sshd", args='("/usr/sbin/sshd" "-f" "${HOME}/.config/sshd/cfg"))»

To enable it, run this:

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

Start some applications at login

«mk-launchagent(name="kdeconnect", args='("/Applications/kdeconnect-indicator.app/Contents/MacOS/kdeconnect-indicator"))»
launchctl load -w ~/Library/LaunchAgents/net.isamert.kdeconnect.plist

Clear all notifications with a keypress

Save the script by running the next code block:

curl 'https://gist.githubusercontent.com/lancethomps/a5ac103f334b171f70ce2ff983220b4f/raw/50a04a65f16349a70884f490f856f27021ac396e/close_notifications_applescript.js' > ~/.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

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()

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.

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"
PATH=«path-variable»

KDE fucks-up the PATH variable (and only the PATH variable) while booting up. This fixes that:

export PATH=«path-variable»

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.
«sh("cat /etc/pacman.conf | sed -E 's/^#(Parallel|Color|VerbosePkgLists|TotalDownload)/\\1/'")»

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

X related

KMonad

In the past I used Xmodmap & xcpae based solution for remapping keys. Now I am experimenting with KMonad which give more or less the same flexibility with a better configuration format. It’s killer feature is that it also works on Mac[fn:: Well, almost. I had to change a few stuff in source to make it work.]. This means that I can truly keep my work computer and personal computers keyboard layouts & keyboard shortcuts in sync.

Some references for editing this configuration:

Installation, Linux

I use the AUR package kmonad-git for my Linux machine and compile it manually on Mac. Following configuration is needed for Linux and taken from here.

# Add uinput group
sudo groupadd uinput
# Add current user to input and uinput groups
sudo usermod -aG input $USER
sudo usermod -aG uinput $USER
KERNEL=="uinput", MODE="0660", GROUP="uinput", OPTIONS+="static_node=uinput"

Restart your machine.

Installation, MacOS

git clone --recursive https://github.com/kmonad/kmonad.git
cd kmonad
nix build "./nix?submodules=1"

Generic configuration

(let ((usb-kbd (car (file-expand-wildcards "/dev/input/by-*/usb-*Microsoft*kbd")))
      (onboard-kbd (car (file-expand-wildcards "/dev/input/by-*/platform*kbd"))))
  (cond
   ((eq system-type 'darwin) "(iokit-name)")
   ((and usb-kbd (file-exists-p usb-kbd)) (format "(device-file \"%s\")" usb-kbd))
   ((and onboard-kbd (file-exists-p onboard-kbd)) (format "(device-file \"%s\")" onboard-kbd))))
(defcfg
  input  «kmonad-device()»
  output «when-on(linux="(uinput-sink \"My KMonad output\")", darwin="(kext)")»
  fallthrough true
  allow-cmd true)

Unicode characters

Here I use an external program (xtype, which is explained below) to type unicode characters instead of utilizing XCompose as suggested by KMonad. Managing, making it running is hard with XCompose. Also it does not work on Mac.

Also see these:

(defalias
  ;; Cool unicode chars
  ¿ (cmd-button "xtype ¿")
  λ (cmd-button "xtype λ")
  ≤ (cmd-button "xtype ≤")
  ≥ (cmd-button "xtype ≥")
  ¬ (cmd-button "xtype ¬")
  ✓ (cmd-button "xtype ✓")
  ↑ (cmd-button "xtype ↑")
  ↓ (cmd-button "xtype ↓")
  ← (cmd-button "xtype ←")
  → (cmd-button "xtype →")
  ≠ (cmd-button "xtype ≠")
  « (cmd-button "xtype «")
  » (cmd-button "xtype »")
  ⇒ (cmd-button "xtype ⇒")
  ↣ (cmd-button "xtype ↣")
  ↢ (cmd-button "xtype ↢")
  ⇄ (cmd-button "xtype ⇄")

  ;; Turkish chars
  ş (cmd-button "xtype ş")
  ğ (cmd-button "xtype ğ")
  ü (cmd-button "xtype ü")
  ı (cmd-button "xtype ı")
  ö (cmd-button "xtype ö")
  ç (cmd-button "xtype ç"))

xtype for linux

This uses pynput python package (which can be installed with pip install pynput or with aur trizen -S python-pynput). The alternative is using xdotool type (or ydotool type) but they both fail on unicode inputs, they simply skip them. pynput works quite well. I found it here.

THIS ALSO WORKS FOR MAC. But I decided to not to use it for two reasons:

  • It’s a bit slow on Mac.
  • It’s a bit hard to manage python dependencies on Mac.
#!/usr/bin/env python

import sys
from pynput.keyboard import Controller

Controller().type(' '.join(sys.argv[1:]))

xtype for macos

This is a simple program for inserting given characters (including unicode) to currently open application. Taken from here.

#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>

int main(int argc, const char * argv[]) {
  @autoreleasepool {
    if (argc > 1) {
      NSString *theString = [NSString stringWithUTF8String:argv[1]];
      NSUInteger len = [theString length];
      NSUInteger n, i = 0;
      CGEventRef keyEvent = CGEventCreateKeyboardEvent(nil, 0, true);
      unichar uChars[20];
      while (i < len) {
        n = i + 20;
        if (n>len){n=len;}
        [theString getCharacters:uChars range:NSMakeRange(i, n-i)];
        CGEventKeyboardSetUnicodeString(keyEvent, n-i, uChars);
        CGEventPost(kCGHIDEventTap, keyEvent); // key down
        CGEventSetType(keyEvent, kCGEventKeyUp);
        CGEventPost(kCGHIDEventTap, keyEvent); // key up (type 20 characters maximum)
        CGEventSetType(keyEvent, kCGEventKeyDown);
        i = n;
        [NSThread sleepForTimeInterval:0.004]; // wait 4/1000 of second, 0.002 it's OK on my computer, I use 0.004 to be safe, increase it If you still have issues
      }
      CFRelease(keyEvent);
    }
  }
  return 0;
}

Run this to install it:

cd ~/.cache
clang -framework Foundation -framework ApplicationServices TypeChars.m -l objc -o xtype
cp ./xtype ~/.local/bin/xtype

Coding macros

(defalias
  ;; Fat arrow
  fa  #(= >)
  ;; light arrow
  la  #(- >)
  ;; home path
  hm #(~ /))

Generic layers

(string-join
 (mapcar
  (lambda (it)
    (let ((chr (downcase (char-to-string it))))
      (format "p%s C-M-A-S-%s" chr chr)))
  (seq-remove (lambda (it) (seq-contains '(?\( ?_ ?\)) it))
              (number-sequence ?! ?`)))
 "\n")
(defalias
  sym (layer-toggle symbols)
  hyp (tap-next esc (layer-toggle hyper)))

(deflayer symbols
  _   _    _    _    _    _    _    _    _    _    _    _    _
  _   @«   @»   "    @⇒    @↢    @↣    @⇄    _    _    _    _    @≠   _
  _   \(   @↑   \)  @la  @✓   _    @ü   @ı   @ö   _    \(   \)
  _   @←   @ş   @→   @fa  @ğ   left down up   rght _    _    _    _
  _   _    _    @↓   @ç   _    @λ   @¬   _    @≤   @≥   _    _
  _   _    _    _              _              _    _    _    _)

(defalias
  pret C-M-A-S-ret
  pspc C-M-A-S-spc
  ptb  C-M-A-S-tab
  «kmonad-generate-aliases-for-hyper-layer()»)

;; Instead of utilizing Hyper key for creating shortcuts like I did
;; with my Xmodmap configuration, I use C-M-A-S as the so called hyper
;; key. This makes the key behave exactly same on Linux and Mac.
(deflayer hyper
  _    _    _    _    _    _    _    _    _    _    _    _    _
  @p`  @p1  @p2  @p3  @p4  @p5  @p6  @p7  @p8  vold volu @p-  @p=  _
  @ptb @pq  @pw  @pe  @pr  @pt  @py  @pu  @pi  @po  @pp  @p[  @p]
  _    @pa  @ps  @pd  @pf  @pg  @ph  @pj  @pk  @pl  @p;  @p'  @p\  @pret
  _    @p\  @pz  @px  @pc  @pv  @pb  @pn  @pm  @p,  @p.  @p/  _
  _    _    _    _              @pspc          _    _    _    _)

Linux config

I use this config on both linux and mac. This is the configuration for my external keyboard. Some of the uses of \ character is just to make the total key count same as the generic layers I defined above. They are mapped to itself again in the main layer.

«kmonad-cfg»
«kmonad-unicode»
«kmonad-macros»

(defsrc
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ]
  caps a    s    d    f    g    h    j    k    l    ;    '    \    ret
  lsft \    z    x    c    v    b    n    m    ,    .    /    rsft
  \    lctl lmet lalt           spc            ralt rmet cmp  rctl)

(deflayer main
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ]
  @hyp a    s    d    f    g    h    j    k    l    ;    '    \    ret
  lsft #    z    x    c    v    b    n    m    ,    .    /    rsft
  \    lctl lmet lalt           spc            @sym rmet cmp rctl)

«kmonad-layers»

SystemD service entry

[Unit]
Description=KMonad keyboard config

[Service]
Type=simple
Restart=always
RestartSec=3
ExecStart=which("kmonad") %h/.config/kmonad-linux.kbd -l warn
Nice=-20

[Install]
WantedBy=xdg-desktop-autostart.target

Mac config

This is the configuration for the embedded keyboard on mac.

«kmonad-cfg»
«kmonad-unicode»
«kmonad-macros»

(defsrc
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ]
  caps a    s    d    f    g    h    j    k    l    ;    '    \    ret
  lsft \    z    x    c    v    b    n    m    ,    .    /    rsft
  fn   lctl lmet lalt           spc            rmet ralt  cmp  rctl)

(deflayer main
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ]
  @hyp a    s    d    f    g    h    j    k    l    ;    '    \    ret
  lsft #    z    x    c    v    b    n    m    ,    .    /    rsft
  lctl fn   lalt lmet           spc            @sym rmet rctrl rctl)

«kmonad-layers»
«kmonad-cfg»
«kmonad-unicode»
«kmonad-macros»

(defsrc
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ]
  caps a    s    d    f    g    h    j    k    l    ;    '    \    ret
  lsft \    z    x    c    v    b    n    m    ,    .    /    rsft
  lctl lmet lalt fn             spc            rmet ralt  cmp  rctl)

(deflayer main
  esc  f1   f2   f3   f4   f5   f6   f7   f8   f9   f10  f11  f12
  grv  1    2    3    4    5    6    7    8    9    0    -    =    bspc
  tab  q    w    e    r    t    y    u    i    o    p    [    ]
  @hyp a    s    d    f    g    h    j    k    l    ;    '    \    ret
  lsft #    z    x    c    v    b    n    m    ,    .    /    rsft
  lctl lalt lmet fn             spc            @sym rmet rctrl rctl)

«kmonad-layers»

sxhkd

This is the global keybinding manager. I don’t use GNOME’s own keybinding manager as it’s clumsy to use.

TODO: unify this and skhd configuration?

super + alt + ctrl + shift
# Clear notifications
«hyper» + v
  emacsclient --eval '(empv-toggle-video)'

«hyper» + Return
  term

«hyper» + d
  dolphin

«hyper» + s
  flameshot gui

«hyper» + c
  copyq toggle

# Show all snippets and select one interactively
«hyper» + i
  emacsclient --eval "(im-globally (im-select-any-snippet))"

«hyper» + p
  emacsclient --eval "(im-globally (im-password-act))"

«hyper» + t
  term -e trans -shell

ctrl + alt + l
  loginctl lock-session

Here is the systemd service entry:

[Unit]
Description=SXHKD

[Service]
Type=simple
Restart=always
RestartSec=3
ExecStart=«which("sxhkd"Nice=-20

[Install]
WantedBy=xdg-desktop-autostart.target

i3 & KDE

[Unit]
Description=Launch Plasma with i3
Before=plasma-workspace.target

[Service]
ExecStart=/usr/bin/i3
Restart=on-failure

[Install]
WantedBy=plasma-workspace.target

To activate i3:

systemctl mask plasma-kwin_x11.service # Disable kwin
systemctl enable plasma-i3 # Enable i3

To return back to kwin:

systemctl --user enable plasma-kwin_x11.service # Enable kwin
systemctl --user mask plasma-i3 # Disable i3

i3 config

set $mod Mod4
set $hyper Mod4+Mod1+Shift+Control
# No borders if it's the only window in the desktop
smart_borders on
default_border pixel 3
focus_wrapping no
font pango:monospace 8

exec --no-startup-id dex --autostart --environment i3
exec --no-startup-id feh --bg-scale ~/Documents/wallpapers/paul-gilmore-KT3WlrL_bsg-unsplash.jpg
exec_always --no-startup-id picom -bc

# Use Mouse+$mod to drag floating windows to their wanted position
floating_modifier $mod
# move tiling windows via drag & drop by left-clicking into the title bar,
# or left-clicking anywhere into the window while holding the floating modifier.
tiling_drag modifier titlebar

# start a terminal
bindsym $mod+Return exec konsole

# WM bindings
bindsym $mod+w kill

bindsym $mod+h focus left
bindsym $mod+j focus down
bindsym $mod+k focus up
bindsym $mod+l focus right

bindsym $mod+Shift+h move left
bindsym $mod+Shift+j move down
bindsym $mod+Shift+k move up
bindsym $mod+Shift+l move right

bindsym $mod+backslash split h
bindsym $mod+minus split v

bindsym $mod+f fullscreen toggle
bindsym $mod+Shift+f floating toggle

bindsym $mod+s layout stacking
bindsym $mod+t layout tabbed
bindsym $mod+i layout toggle split

# Define names for default workspaces for which we configure key bindings later on.
# We use variables to avoid repeating the names in multiple places.
set $ws1 "1"
set $ws2 "2"
set $ws3 "3"
set $ws4 "4"
set $ws5 "5"
set $ws6 "6"
set $ws7 "7"
set $ws8 "8"
set $ws9 "9"

# switch to workspace
bindsym $mod+1 workspace number $ws1
bindsym $mod+2 workspace number $ws2
bindsym $mod+3 workspace number $ws3
bindsym $mod+4 workspace number $ws4
bindsym $mod+5 workspace number $ws5
bindsym $mod+6 workspace number $ws6
bindsym $mod+7 workspace number $ws7
bindsym $mod+8 workspace number $ws8
bindsym $mod+9 workspace number $ws9

# move focused container to workspace
bindsym $mod+Shift+1 move container to workspace number $ws1
bindsym $mod+Shift+2 move container to workspace number $ws2
bindsym $mod+Shift+3 move container to workspace number $ws3
bindsym $mod+Shift+4 move container to workspace number $ws4
bindsym $mod+Shift+5 move container to workspace number $ws5
bindsym $mod+Shift+6 move container to workspace number $ws6
bindsym $mod+Shift+7 move container to workspace number $ws7
bindsym $mod+Shift+8 move container to workspace number $ws8
bindsym $mod+Shift+9 move container to workspace number $ws9
bindsym $mod+Shift+0 move container to workspace number $ws10

bindsym $mod+Shift+c reload
bindsym $mod+Shift+r restart

# resize window (you can also use the mouse for that)
mode "resize" {
  bindsym h resize shrink width 10 px or 10 ppt
  bindsym j resize grow height 10 px or 10 ppt
  bindsym k resize shrink height 10 px or 10 ppt
  bindsym l resize grow width 10 px or 10 ppt

  # back to normal: Enter or Escape or $mod+r
  bindsym Return mode "default"
  bindsym Escape mode "default"
  bindsym $mod+r mode "default"
}

bindsym $mod+r mode "resize"

# Start i3bar to display a workspace bar (plus the system information i3status
# finds out, if available)
bar {
  status_command i3status
}

for_window [class="copyq"] floating enable; resize set 1280 720; move position center;

# Plasma compatibility improvements
# From https://github.com/heckelson/i3-and-kde-plasma
for_window [title="Desktop @ QRect.*"] kill; floating enable; border none
for_window [window_role="pop-up"] floating enable
for_window [window_role="task_dialog"] floating enable
for_window [class="yakuake"] floating enable
for_window [class="systemsettings"] floating enable
for_window [class="plasmashell"] floating enable;
for_window [class="Plasma"] floating enable; border none
for_window [title="plasma-desktop"] floating enable; border none
for_window [title="win7"] floating enable; border none
for_window [class="krunner"] floating enable; border none
for_window [class="Kmix"] floating enable; border none
for_window [class="Klipper"] floating enable; border none
for_window [class="Plasmoidviewer"] floating enable; border none
for_window [class="(?i)*nextcloud*"] floating disable
for_window [class="plasmashell" window_type="notification"] border none, move position 70 ppt 81 ppt
no_focus [class="plasmashell" window_type="notification"]

Alacritty terminal

See config documentation.

window:
  dynamic_title: true
  opacity: 0.8
  decorations: none

font:
  normal:
    family: Iosevka
  size: «when-on(linux="12.5", darwin="14")»

draw_bold_text_with_bright_colors: true
live_config_reload: false

shell:
  program: zsh

hints:
  enabled:
   - regex: "(ipfs:|ipns:|magnet:|mailto:|gemini:|gopher:|https:|http:|news:|file:|git:|ssh:|ftp:)\
             [^\u0000-\u001F\u007F-\u009F<>\"\\s{-}\\^⟨⟩`]+"
     command: jaro
     post_processing: true
     mouse:
       enabled: true
       mods: None
     binding:
       key: F
       mods: Control|Shift

# Colors (Gruvbox dark)
colors:
  # Default colors
  primary:
    # hard contrast: background = '#1d2021'
    background: '#282828'
    # soft contrast: background = '#32302f'
    foreground: '#ebdbb2'

  # Normal colors
  normal:
    black:   '#282828'
    red:     '#cc241d'
    green:   '#98971a'
    yellow:  '#d79921'
    blue:    '#458588'
    magenta: '#b16286'
    cyan:    '#689d6a'
    white:   '#a89984'

  # Bright colors
  bright:
    black:   '#928374'
    red:     '#fb4934'
    green:   '#b8bb26'
    yellow:  '#fabd2f'
    blue:    '#83a598'
    magenta: '#d3869b'
    cyan:    '#8ec07c'
    white:   '#ebdbb2'


key_bindings:
  - { key: U,  mods: Shift|Control,   mode: ~Alt, action: ScrollPageUp,  }
  - { key: D,  mods: Shift|Control,   mode: ~Alt, action: ScrollPageDown }

  # Vi mode
  - { key: A,                           mode: Vi, action: ToggleViMode   }
  - { key: Return,                      mode: Vi, action: ToggleViMode   }
  - { key: 5,  mods: Shift,             mode: Vi, action: Last }
  # ^ See https://github.com/alacritty/alacritty/issues/4111

Zsh

Check out these links to get a grasp of how all of these stuff work. I also tried to add notes to each file.

~/.zshenv

  • This file is sourced first.
  • This file is sourced every time, no matter which type of shell you are firing up (interactive/non-interactive/login/non-login).
  • System-wide equivalent of this file is /etc/zshenv or /etc/zsh/zshenv.
  • Having your essential environment variables (like PATH) set here is also important.
    • For example when you run unison to sync content between your computers, unison connects to the other computer through ssh. This connection is done on a non-interactive/non-login shell (or just take this as an example: ssh some-computer 'echo $PATH' this is also done on a non-interactive/non-login shell). So if your unison binary is not in one of the paths that appear in default PATH variable, it’ll fail to find it. So you need to add the path that

Unfortunately, this file is not sourced by GDM at login. So I’m sourcing it in ~/.profile manually:

source ~/.zshenv
# Run ts_onfinish when a tsp job is finished
export TS_ONFINISH=ts_onfinish
export SHELL=/bin/zsh
export «env-variables»

~/.zprofile

  • This file is sourced after .zshenv.
  • This file is read only while logging in and it’s only sourced once.
  • System-wide equivalent of this file is /etc/zprofile or /etc/zsh/zprofile.
  • This is where I run startx which essentially calls .xinitrc
  • I put stuff that is not going to change during the session, this may include
    • Stuff that is static.
    • Stuff that takes time to load. Because this file is loaded at the start and only sourced once, it makes sense to load heavy stuff here.
  • A lot of programs (like Java, Flatpak or anything that wants to edit your PATH or similar environment variables) put their configuration under /etc/profile.d/. Normally, /etc/profile (which is automatically sourced by bash at startup), also sources these files. In my system (or Arch Linux in general) also have /etc/zsh/zprofile which contains the following: emulate sh -c 'source /etc/profile'. So essentially sourcing the stuff under /etc/profile.d/ is automatically handled.

Unfortunately, this file is (also) not sourced by GDM at login. So I’m sourcing it in ~/.profile manually:

source ~/.zprofile
# Use `qt5ct` program to configure qt themes
# and use `lxappearance` for gtk
export QT_QPA_PLATFORMTHEME=qt5ct

export BROWSER=jaro
export VISUAL="jaro --method=edit"
export EDITOR="jaro --method=edit"

export XDG_CONFIG_HOME="$HOME/.config"
export BSPWM_SOCKET="/tmp/bspwm-socket"
export XDG_CONFIG_DIRS=/usr/etc/xdg:/etc/xdg

Dumb mode

# Don't do any configuration if dumb terminal is requested
[[ $TERM == "dumb" ]] && unsetopt zle && PS1='$ ' && return

Custom completions

You can add completion files under $HOME/.config/zsh/completions and zsh will pick them up automatically. This should be one of the first things that appear in .zshrc.

export fpath=($HOME/.config/zsh/completions $fpath)

To rebuild completions, if something is not right, use the following:

rm -f ~/.zcompdump
rm -f $ANTIGEN_COMPDUMP

Installing plugins

# Disable auto-escape-on-insert functionality
DISABLE_MAGIC_FUNCTIONS=true

### Added by Zinit's installer
if [[ ! -f $HOME/.local/share/zinit/zinit.git/zinit.zsh ]]; then
    print -P "%F{33} %F{220}Installing %F{33}ZDHARMA-CONTINUUM%F{220} Initiative Plugin Manager (%F{33}zdharma-continuum/zinit%F{220})…%f"
    command mkdir -p "$HOME/.local/share/zinit" && command chmod g-rwX "$HOME/.local/share/zinit"
    command git clone https://github.com/zdharma-continuum/zinit "$HOME/.local/share/zinit/zinit.git" && \
        print -P "%F{33} %F{34}Installation successful.%f%b" || \
            print -P "%F{160} The clone has failed.%f%b"
fi

source "$HOME/.local/share/zinit/zinit.git/zinit.zsh"
autoload -Uz _zinit
(( ${+_comps} )) && _comps[zinit]=_zinit

# To make themes work
setopt promptsubst

zinit light-mode for \
      zdharma-continuum/zinit-annex-as-monitor \
      zdharma-continuum/zinit-annex-bin-gem-node \
      zdharma-continuum/zinit-annex-patch-dl \
      zdharma-continuum/zinit-annex-rust \
      zsh-users/zsh-autosuggestions \
      zsh-users/zsh-syntax-highlighting \
      zsh-users/zsh-history-substring-search \
      kutsan/zsh-system-clipboard \
      Aloxaf/fzf-tab

# Required some plugins (like fzf-tab) to work
autoload -Uz compinit
compinit

Key bindings

  • Enable emacs keybindings
bindkey -e

Edit command in full screen editor

Following snippet let’s you edit current command with C-x C-e in your default editor.

autoload -z edit-command-line
zle -N edit-command-line
bindkey "^X^E" edit-command-line

Theme & module settings

See the documentation for starship theme. You need to install it first from your system package manager. And then:

eval "$(starship init zsh)"

The configuration is done externally:

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

Plugin configuration

history-substring-search

# bind UP and DOWN arrow keys to history substring search
zmodload zsh/terminfo
bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
bindkey -M vicmd 'k' history-substring-search-up
bindkey -M vicmd 'j' history-substring-search-down

fzf-tab

  • You also may need to run build-fzf-tab-module for the first time.
# disable sort when completing `git checkout`
zstyle ':completion:*:git-checkout:*' sort false
# set descriptions format to enable group support
zstyle ':completion:*:descriptions' format '[%d]'
# preview directory's content with lsd when completing cd
zstyle ':fzf-tab:complete:cd:*' fzf-preview 'lsd -1 --icon=always --color=always $realpath'
# replace current query with current candidate's text (so that you
# trigger continuous completion with "/")
#zstyle ':fzf-tab:*' fzf-bindings 'tab:replace-query'
# zstyle ':fzf-tab:*' fzf-command ftb-tmux-popup

enable-fzf-tab

Utility functions

Generic interactive commands

function mkcd { mkdir -p "$1"; cd "$1"; } # Make and cd to the dir
function cpcd { cp "$1" "$2" && cd "$2"; } # Copy and go to the directory
function mvcd { mv "$1" "$2" && cd "$2"; } # Move and cd to the dir
function cheat { curl http://cheat.sh/"$1"; }

Compression/decompression

function extract {
    if [[ -f $1 ]] ; then
        case $1 in
            *.tar.bz2) tar xjf "$1"   ;;
            *.tar.gz)  tar xzf "$1"   ;;
            *.bz2)     bunzip2 "$1"   ;;
            *.rar)     unrar x "$1"   ;;
            *.gz)      gunzip "$1"    ;;
            *.tar)     tar xf "$1"    ;;
            *.tbz2)    tar xjf "$1"   ;;
            *.tgz)     tar xzf "$1"   ;;
            *.zip)     unzip "$1"     ;;
            *.Z)       uncompress "$1";;
            *.7z)      7z x "$1"      ;;
            *)        echo "'$1' cannot be extracted via ex()" ;;
        esac
    else
        echo "Usage:"
        echo "ex <archive-name>"
    fi
}

function compress {
    local EXT="$1"; shift
    case "$EXT" in
        -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."
            ;;
        *.7z)
            7z a "$EXT" "$@"
            ;;
        *.tar.gz|*.tgz)
            tar -czvf "$EXT" "$@"
            ;;
        *.tar.gz.nocompress|*.tgz.nocompress)
            tar -cvf "${EXT%.*}" "$@"
            ;;
        *.tar.bz2)
            tar -cjvf "$EXT" "$@"
            ;;
        *.zip)
            zip -r "$EXT" "$@"
            ;;
        *)
            echo "Unrecognized EXT: $1"
            echo
            compress --help
            ;;
    esac
}

Encryption/Decryption

function encrypt {
    case "$1" in
        -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"
            ;;
        *)
            local INPUT="$1"
            local OUTPUT="$2"

            if [[ ! -f "$INPUT" ]]; then
                echo "$INPUT not found."
                exit 1
            fi

            if [[ -z "$OUTPUT" ]]; then
                OUTPUT="${INPUT}.encrypted"
            fi

            if [[ -f "$OUTPUT" ]]; then
                echo "$OUTPUT already exists."
                exit 1
            fi

            gpg --symmetric --cipher-algo AES256 --output "$OUTPUT" "$INPUT"
            ;;
    esac
}

function decrypt {
    case "$1" in
        -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"
            ;;
        *)
            local INPUT="$1"
            local OUTPUT="$2"

            if [[ ! -f "$INPUT" ]]; then
                echo "$INPUT not found."
                exit 1
            fi

            if [[ -z "$OUTPUT" ]]; then
                OUTPUT="${INPUT%.*}"
            fi

            if [[ -f "$OUTPUT" ]]; then
                echo "$OUTPUT already exists."
                exit 1
            fi

            gpg --decrypt --output "$OUTPUT" "$INPUT"
            ;;
    esac
}

Some kubernetes commands

kctx

Easily switch between contexts with completion.

function kctx {
    if [[ -z "$1" ]]; then
        kubectl config current-context
        echo "--"
        kubectl config get-contexts --output=name
    else
        echo "Switching to $1"
        kubectl config use-context $1
    fi
}
#compdef kctx

_kctx() {
    _arguments "1:contexts:($(kubectl config get-contexts --output=name | tr "\\n" " "))"
}

_kctx "$@"

Git utilities

# TODO: Create an emacs wrapper which fuzzy searches through these
# results and opens the file on that revision using
# (vc-revision-other-window REV)
function git-file-hist-grep {
    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
}

Print current path in a shorter form

  • ~/Workspace/projects/jaro~/W/p/jaro
# https://github.com/sorin-ionescu/prezto/blob/master/modules/prompt/functions/prompt-pwd
function short_dir {
    setopt localoptions extendedglob
    local current_pwd="${PWD/#$HOME/~}"
    local ret_directory

    if [[ "$current_pwd" == (#m)[/~] ]]; then
        ret_directory="$MATCH"
        unset MATCH
    else
        ret_directory="${${${${(@j:/:M)${(@s:/:)current_pwd}##.#?}:h}%/}//\%/%%}/${${current_pwd:t}//\%/%%}"
    fi

    unset current_pwd
    echo "$ret_directory"
}

General settings

FILES_TO_SOURCE=(
    $HOME/.config/aliases/*
    # ^ All aliases, also sourcing it from other shells
    /usr/share/fzf/key-bindings.zsh
    # ^ fzf history search keybindings
    $HOME/.nix-profile/share/fzf/key-bindings.zsh
    # ^ fzf history search keybindings
    $HOME/.config/zsh/*.sh
    # ^ Stuff that I tangle from other files
    $HOME/.extrarc
    # ^ Contains stuff that I don't want to commit to git
)

for file in $FILES_TO_SOURCE; do
    [[ -f "$file" ]] && source $file
done

# Colors for less
export LESS_TERMCAP_mb=$'\E[1;31m'     # begin bold
export LESS_TERMCAP_md=$'\E[1;36m'     # begin blink
export LESS_TERMCAP_me=$'\E[0m'        # reset bold/blink
export LESS_TERMCAP_so=$'\E[01;44;33m' # begin reverse video
export LESS_TERMCAP_se=$'\E[0m'        # reset reverse video
export LESS_TERMCAP_us=$'\E[1;32m'     # begin underline
export LESS_TERMCAP_ue=$'\E[0m'        # reset underline
export GROFF_NO_SGR=1                  # for konsole and gnome-terminal

# Some variables
export FZF_DEFAULT_OPTS='--reverse --bind="tab:replace-query"'

# incappendhistory -> incrementally append history so that if shell
#   closes unexpectedly, do not loose the history
# nosharehistory -> sharehistory causes open zsh sessions to pick up
#   newly added history items immediately. This reserves that
# histreduceblanks -> remove superfluous blanks from commands while
#   appending them to the history
# interactivecomments -> enable comments on interactive shells. I
#   sometimes add little notes to my commands so that I can easily find
#   them whenever I do a fuzzy history search
setopt autocd histignoredups incappendhistory nosharehistory histreduceblanks interactivecomments
# unsetopt BEEP
unsetopt LIST_BEEP

# Case insensitive tab completion
zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}'
# automatically find new executables in path
zstyle ':completion:*' rehash true
zstyle ':completion:*' accept-exact '*(N)'
zstyle ':completion:*' use-cache on
zstyle ':completion:*' cache-path ~/.zsh/cache

# History settings

setopt inc_append_history        # Write to the history file immediately, not when the shell exits.
setopt hist_ignore_all_dups      # Delete old recorded entry if new entry is a duplicate.
setopt hist_find_no_dups         # Do not display a line previously found.
setopt hist_reduce_blanks        # Remove superfluous blanks before recording entry.

HISTSIZE=100000
SAVEHIST=100000
HISTORY_SUBSTRING_SEARCH_FUZZY=1
HISTFILE=~/.zsh_history

Emacs configuration

# * EAT integration
[[ -n "$EAT_SHELL_INTEGRATION_DIR" ]] && \
    source "$EAT_SHELL_INTEGRATION_DIR/zsh"

# * VTERM integration

# With this function we can send elisp commands while we are on emacs vterm
# for example, "elisp message hey" would send (message "hey") to emacs.
vterm_cmd() {
    local vterm_elisp
    vterm_elisp=""
    while [[ $# -gt 0 ]]; do
        vterm_elisp="$vterm_elisp""$(printf '"%s" ' "$(printf "%s" "$1" | sed -e 's|\\|\\\\|g' -e 's|"|\\"|g')")"
        shift
    done
    vterm_printf "51;E$vterm_elisp"
}

# Changed the following a bit so that it can detect tmux and screen properly
vterm_printf() {
    if [[ -n "$TMUX" ]] && [[ "${TERM}" = "tmux"* ]]; then
        # Tell tmux to pass the escape sequences through
        printf "\ePtmux;\e\e]%s\007\e\\" "$1"
    elif [[ "${TERM}" = "screen"* ]]; then
        # GNU screen (screen, screen-256color, screen-256color-bce)
        printf "\eP\e]%s\007\e\\" "$1"
    else
        printf "\e]%s\e\\" "$1"
    fi
}

if [[ "$INSIDE_EMACS" = 'vterm' ]]; then
    # Rebind clear so that scrollback is also cleared on emacs vterm
    alias clear='vterm_printf "51;Evterm-clear-scrollback";tput clear'

    function emacs-vterm-precmd() {
        vterm_printf "51;A$USER@$HOST:$PWD" >$TTY
    }

    # See this: https://github.com/romkatv/powerlevel10k/issues/2294#issuecomment-1535767681
    autoload -Uz add-zsh-hook
    add-zsh-hook precmd emacs-vterm-precmd
fi

Mac OSX configuration

# - To get the latest pip bin path: echo $(python3 -c 'import site; print(site.USER_BASE)')/bin
export PATH="$HOME/Library/Python/3.9/bin:/usr/local/bin:$PATH"

# Assuming you've installed GNU tools with
# $ brew install coreutils findutils gnu-tar gnu-sed gawk gnutls gnu-indent gnu-getopt grep
# - Add updated openssl (required for barriers to work) to PATH
# - Add pip/bin to PATH
# - Add barrier{c,s,} to path
# - Add GNU utils to path and replace with mac ones
export PATH="/Applications/Barrier.app/Contents/MacOS:$PATH"
export PATH="/usr/local/opt/openssl@1.1/bin:$PATH"
export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/findutils/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/gnu-tar/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/gnu-indent/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/grep/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/findutils/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/gnu-tar/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/gnu-indent/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/grep/libexec/gnubin:$PATH"
export PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

# Hunspell dict path from nix-profile, hunspell picks up this variable
export DICPATH=$HOME/.nix-profile/share/hunspell
# Without the following, hunspell in emacs for some reason
export DICTIONARY=en_US

# Source fzf keybindings
source /usr/local/opt/fzf/shell/key-bindings.zsh

source $HOME/.nix-profile/etc/profile.d/nix.sh
source $HOME/.nix-profile/etc/profile.d/nix-daemon.sh

Other

kubectl completions

For some reason it does not work. However running the same command after shell is started just works fine.

if (( $+commands[kubectl] )); then
  source <(kubectl completion zsh)
fi

Aliases

This file is sourced by both zsh and bash.
alias aur='trizen'
alias aurin='trizen -S'
alias aurs='trizen -Ss'
alias aurupg='trizen -Syu'
alias pac='fuzzy packages'      # A fuzzy, interactive package finder
alias pacs='pacman -Ss'
alias pacin='sudo pacman -S'
alias pacins='sudo pacman -U'   # Install from file
alias pacupd='sudo pacman -Sy'
alias pacupg='sudo pacman -Syu'
alias pacbin='pacman -F'        # Same as above
alias pacre='sudo pacman -R'    # Leave dependencies and configurations
alias pacrem='sudo pacman -Rns'

alias ctl='systemctl'
alias ctls='systemctl status'
alias ctlr='systemctl restart'
alias ctlu='systemctl --full --user'
alias ctlus='systemctl --full --user status'
alias ctlur='systemctl --full --user restart'
alias logu='journalctl --user --unit'

alias xpaste='xclip -selection clipboard -o' # paste cb content
alias pacs='brew search'
alias pacin='brew install'
alias pacupd='brew update'
alias pacupg='brew upgrade'
alias pacrem='brew uninstall'
# package management
#eshell nixin='nix-env -iA "nixpkgs.$1"'
function nixin { nix-env -iA "nixpkgs.$1" }
alias nixrem='nix-env -e'
alias nixs='nix search nixpkgs'
alias nixls='nix-env --query'

# process management
alias nameof='ps -o comm= -p' # Get the name of given PID (reverse pidof)
alias fuckall='killall -s 9'
alias fkill='fuzzy kill'

function fuckemacs {
    if pgrep Emacs; then
        kill -USR2 $(pgrep Emacs)
    else
        kill -USR2 $(pgrep emacs)
    fi
}

# utility
alias cdtemp='cd $(mktemp -d)'
alias ...='cd ../..'
alias ....='cd ../../..'
alias .....='cd ../../../..'
alias df='df -H'
alias du='ncdu'
alias fastssh='ssh -Y -C -c chacha20-poly1305@openssh.com'
alias cdd='cd $(fd -t d -d 8 | fzf)'

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

alias gcm='git commit -m'
alias gds='git diff --staged'
alias gs='git status'

# abbrv
alias n='nvim'
alias v='jaro --method=view'
alias e='jaro --method=edit'
alias o='jaro'
alias mt='jaro --mime-type'
alias mkx='chmod +x'
alias ytdl='yt-dlp'
alias ytmp3='yt-dlp --extract-audio --audio-format mp3 --output "%(title)s.%(ext)s"'

# master Wq
alias :q='exit'
alias :wq='exit'

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

# stuff
alias ipaddr='curl https://api.ipify.org'
alias ipinfo='curl https://ipinfo.io'

# highlight streams with bat
alias hjson='bat --language=json --paging=never --style=plain'
alias hlog='bat --language=log --paging=never --style=plain'

# higlight json parts of the stream and print other lines plain
# ./program_that_may_output_json | logjson
alias logjson='jq -R -r ". as \$line | try fromjson catch \$line"'

Tmux

Install TPM (Tmux Plugin Manager)

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

After issuing the command above, you need to do PREFIX I to install all plugins.

The configuration

# ####################################################
#      __                                         ____
#     / /_____ ___  __  ___  __ _________  ____  / __/
#    / __/ __ `__ \/ / / / |/_// ___/ __ \/ __ \/ /_
#  _/ /_/ / / / / / /_/ />  <_/ /__/ /_/ / / / / __/
# (_)__/_/ /_/ /_/\__,_/_/|_(_)___/\____/_/ /_/_/
# ####################################################

# Add the plugin manager (PREFIX I -> install them)
set -g @plugin 'tmux-plugins/tpm'

# PREFIX C-s -> save, PREFIX C-r -> restore
set -g @plugin 'tmux-plugins/tmux-resurrect'

# Highlight when prefix is pressed, in copy mode etc.
set -g @plugin 'tmux-plugins/tmux-prefix-highlight'
set -g @prefix_highlight_show_copy_mode 'on'
set -g @prefix_highlight_copy_mode_attr 'fg=white,bg=yellow,bold' # default is 'fg=default,bg=yellow'
set -g @prefix_highlight_show_sync_mode 'on'
set -g @prefix_highlight_sync_mode_attr 'fg=black,bg=green' # default is 'fg=default,bg=yellow'

# PREFIX o -> captures the output of last command
set -g @plugin 'artemave/tmux_capture_last_command_output'
set -g @command-capture-key o
set -g @command-capture-prompt-pattern '➜ '

set -g default-shell $PREFIX/bin/zsh
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-option -g set-titles off
set-option -g allow-rename off

# Reload config
bind r source-file ~/.tmux.conf

# 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

# Pane switches (without prefix key)
bind -n M-h select-pane -L
bind -n M-j select-pane -D
bind -n M-k select-pane -U
bind -n M-l select-pane -R
bind -n M-\\ split-window -h -c '#{pane_current_path}'
bind -n M--  split-window -v -c '#{pane_current_path}'

# Swapping shortcuts
bind-key W choose-tree -Zw "swap-window -t '%%'"
bind-key P choose-tree -Zw "swap-pane -t '%%'"

# 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 "xclip -selection clipboard"

bind -n M-s run-shell -b tmux-switch

# Status bar theme
set -g status-position bottom
set -g status-left-length 32

set -g status-fg white
set -g status-bg black

set -g status-left '#[fg=colour235,bg=colour252,bold] #S #[fg=colour252,bg=colour238,nobold]#[fg=colour245,bg=colour238,bold] #(whoami) #[fg=colour238,bg=black,nobold]'
set -g window-status-format "#[fg=white,bg=black] #I #W "
set -g window-status-current-format "#[fg=black,bg=colour39]#[fg=colour25,bg=colour39,noreverse,bold] #I  #W #[fg=colour39,bg=black,nobold]"
set -g status-right "#{prefix_highlight}"

# Load tmux plugin manager
run '~/.tmux/plugins/tpm/tpm'

Utilities

git

[user]
  name = «sh("git config --global --get user.name", "Isa Mert Gurbuz")»
  email = «sh("git config --global --get user.email", "isamertgurbuz@gmail.com")»
[github]
  user = isamert
[rebase]
  autoStash = true
[pull]
  rebase = true
[fetch]
  prune = true
[status]
  short = 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")
        
(assoc
 #:pattern '("(application|text)/(x-)?(pdf|postscript|ps|epub.*)" "image/(x-)?eps")
 #:program '(zathura %f))

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

(assoc
 #:name 'editor
 #:pattern '("^text/" "^application/(x-)?(shellscript|json|javascript|xml)")
 #:emacs (elisp (find-file %F))
 #:program '(emacsclient -c %f)
 #:term '(emacsclient -c %f)
 #:view 'bat)

(assoc
 #:name 'empv
 #:pattern '("^video/" "^audio/")
 #:program (elisp (empv-enqueue "%F"))
 #:on-error '(mpv %f))

(assoc
 #:pattern "inode/directory"
 #:program '(term -e ranger %f)
 #:term '(ranger %f)
 #:gallery 'nomacs)

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

(assoc
 #: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 (open-with 'browser))

(assoc
 #:name 'feh
 #:pattern "^https?://.+/.+\\.(jpg|png|gif)(\\?.+)?$"
 #:program '(feh --start-at %f))

(assoc
 #:name 'nomacs
 #:pattern "^image/.*"
 #:program '(nomacs %f)
 #:on-error 'feh)

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

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

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

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

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

(assoc
 #:pattern ".*"
 #:program (select-alternative-with "dmenu"))

;;
;; Rest is used only with references
;;

(assoc
 #:name 'bat
 #:pattern ".*"
 #:program '(bat --paging=always %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

ranger

set confirm_on_delete never
set preview_images true
set preview_images_method «when-on(linux="ueberzug", darwin="iterm2")»
set draw_borders both
set dirname_in_tabs true
set update_tmux_title false

map gh cd ~
map gn cd ~/Documents/notes/
map gd cd ~/Downloads/
map gD cd ~/Documents/
map gs cd ~/Music/Sound Recordings/
map gc cd ~/Pictures/f3/Camera/

map gi eval fm.cd('/run/media/' + os.getenv('USER'))
has jaro, flag f = jaro "$@"

Media

mpv

Keybindings

KeyAction
ppause
ffullscreen
C+lshow playlist
<, >playlist prev,next
A+0-5change window scale
9,0volume down/up
mmute
achange/switch audio
z, Zsubtitle delay -/+
+, -scale subtitle
schange/switch subtitle
r, Rchange sub-position
T, A-tdownload subtitle (en/tr)
ctrl++increase audio delay
ctrl+-decrease audio delay
[, ]playback speed scale
. ,one frame forward/backward
1-2contrast
3-4brightness
5-6gamma
7-8saturation
ishow video info
cshow 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

# Show youtube comments
# This gets the video ID from filename, as mpv sets it this way.
c run "term" "--float" "-e" "/bin/bash" "-c" "pipe-viewer --comments-order=top --comments='${path}' --page=1 --no-interactive"

# 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

# Download subtitle
T run     "mediastuff" "mpv-subdl" "${path}" "eng" # english subtitle
Alt+t run "mediastuff" "mpv-subdl" "${path}" "tur" # turkish subtitle

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
b 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

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

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

Thumbnail plugin. Works with uosc.

Installation:

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

nsxiv

#!/bin/bash
while read -r file; do
    case "$1" in
        "d")
            trash "$file" ;;
        "D")
            rm "$file" ;;
        "greater")
            convert -rotate 90 "$file" "$file" ;;
        "less")
            convert -rotate '-90' "$file" "$file" ;;
        "y")
            echo -n "$file" | xclip -selection clipboard ;;
        "w")
            feh --bg-scale "$file" ;;
        "W")
            rm ~/.config/wall.png
            cp "$file" ~/.config/wall.png
            feh --bg-scale "$file" ;;
    esac
done

Editors

Emacs

This file is just used for loading the configuration. The configuration is actually an org file and it should’ve been already tangled to make this work.

(let ((enable-local-variables :all)
      (vc-follow-symlinks t))
  (load-file "~/.emacs.d/index.el"))

emacsd

systemd unit for emacs daemon. It starts after graphical-session.target so that it correctly inherits environment variables.

[Unit]
Description=Emacs text editor
Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/

[Service]
Type=forking
ExecStart=/usr/bin/emacs --daemon
ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"
Restart=on-failure

[Install]
WantedBy=graphical-session.target

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

vimium-c configuration

{
  "name": "Vimium C",
  "@time": "8/28/2024, 12:28:14 AM",
  "time": 1724797694599,
  "environment": {
    "extension": "1.99.997",
    "platform": "mac",
    "firefox": 129
  },
  "exclusionRules": [],
  "keyLayout": 2,
  "keyMappings": [
    "#!no-check",
    "map b Vomnibar.activateTabs",
    "map t Vomnibar.activateInNewTab",
    "map T Vomnibar.activateEditUrlInNewTab",
    "map O Vomnibar.activateEditUrl",
    "map sf LinkHints.activateSearchLinkText",
    "unmap <f1>",
    "unmap <f2>",
    ""
  ],
  "localeEncoding": "",
  "searchEngines": [
    "s|sp|startpage:",
    "https://www.startpage.com/sp/search?abp=-1&t=device&lui=english&sc=xJgFkqH2tfCu20&cat=web&prfe=bcfd50b9911b2c8c90fe567dcc034a47c25b6bbad9d49325c02d5e7063258f5310102504f00de9c5b9f11331d7811b22555d35fa08425db6ca42cb38773906a0c08e86291a93527d8d2183e9&query=%s \\",
    "  blank=https://startpage.com/ Startpage",
    "b|bing: https://www.bing.com/search?q=%s \\",
    "  blank=https://www.bing.com/ Bing",
    "g|go|gg|google|Google: https://www.google.com/search?q=%s \\",
    "  www.google.com re=/^(?:\\.[a-z]{2,4})?\\/search\\b.*?[#&?]q=([^#&]*)/i\\",
    "  blank=https://www.google.com/ Google",
    "b|br|brave: https://search.brave.com/search?q=%s Brave",
    "d|dd|ddg|duckduckgo: https://duckduckgo.com/?q=%s DuckDuckGo",
    "qw|qwant: https://www.qwant.com/?q=%s Qwant",
    "y|ya|yd|yandex: https://yandex.com/search/?text=%s Yandex",
    "maps|gm|gmap|gmaps: https://www.google.com/maps?q=%s \\",
    "  blank=https://www.google.com/maps Google Maps",
    "y|yt: https://isamertiv.duckdns.org/search?q=%s \\",
    "  blank=https://www.youtube.com/ YouTube",
    "w|wiki: https://www.wikipedia.org/w/index.php?search=%s Wikipedia",
    "gh|github: https://github.com/search?q=$s \\",
    "  blank=https://github.com/ GitHub",
    ""
  ],
  "searchUrl":
"https://www.startpage.com/sp/search?abp=-1&t=device&lui=english&sc=xJgFkqH2tfCu20&cat=web&prfe=bcfd50b9911b2c8c90fe567dcc034a47c25b6bbad9d49325c02d5e7063258f5310102504f00de9c5b9f11331d7811b22555d35fa08425db6ca42cb38773906a0c08e86291a93527d8d2183e9&query=$s Startpage",
  "vimSync": true,
  "vomnibarOptions": {
    "actions": "",
    "maxMatches": 15,
    "queryInterval": 200,
    "sizes": "77,3,44,0.8",
    "styles": "mono-url"
  }
}

sidebery configuration

Here is my sidebery config export:

{
  "settings": {
    "nativeScrollbars": true,
    "nativeScrollbarsThin": true,
    "nativeScrollbarsLeft": false,
    "selWinScreenshots": false,
    "updateSidebarTitle": true,
    "markWindow": false,
    "markWindowPreface": "[Sidebery] ",
    "ctxMenuNative": false,
    "ctxMenuRenderInact": true,
    "ctxMenuRenderIcons": true,
    "ctxMenuIgnoreContainers": "",
    "navBarLayout": "vertical",
    "navBarInline": true,
    "navBarSide": "left",
    "hideAddBtn": false,
    "hideSettingsBtn": false,
    "navBtnCount": true,
    "hideEmptyPanels": true,
    "hideDiscardedTabPanels": false,
    "navActTabsPanelLeftClickAction": "none",
    "navActBookmarksPanelLeftClickAction": "none",
    "navTabsPanelMidClickAction": "discard",
    "navBookmarksPanelMidClickAction": "none",
    "navSwitchPanelsWheel": true,
    "subPanelRecentlyClosedBar": true,
    "subPanelBookmarks": true,
    "subPanelHistory": false,
    "groupLayout": "grid",
    "containersSortByName": false,
    "skipEmptyPanels": false,
    "dndTabAct": true,
    "dndTabActDelay": 750,
    "dndTabActMod": "none",
    "dndExp": "pointer",
    "dndExpDelay": 750,
    "dndExpMod": "none",
    "dndOutside": "win",
    "dndActTabFromLink": true,
    "dndActSearchTab": true,
    "dndMoveTabs": false,
    "dndMoveBookmarks": false,
    "searchBarMode": "dynamic",
    "searchPanelSwitch": "any",
    "searchBookmarksShortcut": "",
    "searchHistoryShortcut": "",
    "warnOnMultiTabClose": "collapsed",
    "activateLastTabOnPanelSwitching": true,
    "activateLastTabOnPanelSwitchingLoadedOnly": false,
    "switchPanelAfterSwitchingTab": "always",
    "tabRmBtn": "hover",
    "activateAfterClosing": "next",
    "activateAfterClosingStayInPanel": false,
    "activateAfterClosingGlobal": false,
    "activateAfterClosingNoFolded": true,
    "activateAfterClosingNoDiscarded": false,
    "askNewBookmarkPlace": true,
    "tabsRmUndoNote": true,
    "tabsUnreadMark": false,
    "tabsUpdateMark": "all",
    "tabsUpdateMarkFirst": true,
    "tabsReloadLimit": 5,
    "tabsReloadLimitNotif": true,
    "showNewTabBtns": true,
    "newTabBarPosition": "after_tabs",
    "tabsPanelSwitchActMove": false,
    "tabsPanelSwitchActMoveAuto": true,
    "tabsUrlInTooltip": "full",
    "newTabCtxReopen": false,
    "tabWarmupOnHover": true,
    "tabSwitchDelay": 0,
    "moveNewTabPin": "start",
    "moveNewTabParent": "last_child",
    "moveNewTabParentActPanel": false,
    "moveNewTab": "end",
    "moveNewTabActivePin": "start",
    "pinnedTabsPosition": "panel",
    "pinnedTabsList": false,
    "pinnedAutoGroup": false,
    "pinnedNoUnload": true,
    "pinnedForcedDiscard": false,
    "tabsTree": true,
    "groupOnOpen": 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": true,
    "previewTabsMode": "p",
    "previewTabsPageModeFallback": "i",
    "previewTabsInlineHeight": 100,
    "previewTabsPopupWidth": 280,
    "previewTabsSide": "right",
    "previewTabsDelay": 500,
    "previewTabsFollowMouse": true,
    "previewTabsWinOffsetY": 36,
    "previewTabsWinOffsetX": 6,
    "previewTabsInPageOffsetY": 0,
    "previewTabsInPageOffsetX": 0,
    "previewTabsCropRight": 0,
    "hideInact": false,
    "hideFoldedTabs": false,
    "hideFoldedParent": "none",
    "nativeHighlight": false,
    "warnOnMultiBookmarkDelete": "collapsed",
    "autoCloseBookmarks": false,
    "autoRemoveOther": false,
    "highlightOpenBookmarks": false,
    "activateOpenBookmarkTab": false,
    "showBookmarkLen": true,
    "bookmarksRmUndoNote": true,
    "loadBookmarksOnDemand": true,
    "pinOpenedBookmarksFolder": true,
    "oldBookmarksAfterSave": "ask",
    "loadHistoryOnDemand": true,
    "fontSize": "s",
    "animations": true,
    "animationSpeed": "norm",
    "theme": "plain",
    "density": "default",
    "colorScheme": "ff",
    "sidebarCSS": false,
    "groupCSS": false,
    "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",
    "scrollThroughVisibleTabs": true,
    "scrollThroughTabsSkipDiscarded": false,
    "scrollThroughTabsExceptOverflow": true,
    "scrollThroughTabsCyclic": false,
    "scrollThroughTabsScrollArea": 0,
    "autoMenuMultiSel": true,
    "multipleMiddleClose": false,
    "longClickDelay": 500,
    "wheelThreshold": false,
    "wheelThresholdX": 10,
    "wheelThresholdY": 60,
    "tabDoubleClick": "none",
    "tabsSecondClickActPrev": false,
    "tabsSecondClickActPrevPanelOnly": false,
    "shiftSelAct": true,
    "activateOnMouseUp": false,
    "tabLongLeftClick": "none",
    "tabLongRightClick": "none",
    "tabMiddleClick": "close",
    "tabMiddleClickCtrl": "discard",
    "tabMiddleClickShift": "duplicate",
    "tabCloseMiddleClick": "close",
    "tabsPanelLeftClickAction": "none",
    "tabsPanelDoubleClickAction": "tab",
    "tabsPanelRightClickAction": "menu",
    "tabsPanelMiddleClickAction": "tab",
    "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": "",
    "syncSaveSettings": false,
    "syncSaveCtxMenu": false,
    "syncSaveStyles": false,
    "syncSaveKeybindings": false,
    "selectActiveTabFirst": true
  },
  "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"
        ],
        "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",
      "add_tp",
      "settings"
    ]
  },
  "ver": "5.2.0",
  "keybindings": {
    "_execute_sidebar_action": "F1",
    "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",
    "activate": "Alt+Space",
    "reset_selection": "Alt+R",
    "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"
  }
}

userChrome.css

I use Sidebery so I hide the tab bar and siderbar heading.

#TabsToolbar {
    visibility: collapse;
}

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

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

user_pref("toolkit.legacyUserProfileCustomizations.stylesheets", true);

Other configs

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", " ");

uBlock custom filters

I filter distracting content on websites I frequently visit. You need to manually import these to uBlock.

Enabling uBlock cloud storage feature may help.

! youtube.com
www.youtube.com##ytd-watch-next-secondary-results-renderer.ytd-watch-flexy.style-scope > .ytd-watch-next-secondary-results-renderer.style-scope
www.youtube.com##ytd-rich-section-renderer.ytd-rich-grid-renderer.style-scope
www.youtube.com###shorts-container
www.youtube.com##ytd-reel-shelf-renderer.ytd-item-section-renderer.style-scope

! stack-exchange
##.tex2jax_ignore.module
##.m0.p0.d-block
##.overflow-hidden.blr-sm.fc-black-600.pb6.p12.bc-black-075.bb.bl.bt
##.js-sticky-leftnav.left-sidebar--sticky-container
##.js-dismissable-hero.ps-relative.fc-black-200.bg-black-750.py24.sm\:d-none
##.py2.fs-body2.ff-sans.fc-white.bg-black-700.js-announcement-banner
##.js-footer.site-footer
stackoverflow.com##.js-footer.site-footer

! https://eksisozluk.com
eksisozluk.com###partial-index
eksisozluk.com###aside
eksisozluk.com###bgright
eksisozluk.com###rightwrap
eksisozluk.com##.main-left-frame.robots-nocontent
||seyler.eksisozluk.com/sozluk/baslik/294386?style=dark$subdocument

QuteBrowser

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
c.fonts.default_family = ['Iosevka Nerd Font']
c.fonts.default_size = '14pt'
c.hints.chars = 'asdfghjklqweuio'
c.fonts.hints = 'normal 16pt Helvetica'
c.hints.uppercase = True
c.input.insert_mode.auto_load = True
c.input.mouse.rocker_gestures = True
c.statusbar.position = 'top'
c.statusbar.show = 'never'
c.completion.shrink = True
c.url.default_page = 'https://start.duckduckgo.com/'
c.url.searchengines = {
    'DEFAULT': 'https://google.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.url.yank_ignored_parameters = ['ref', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']
c.editor.command = ['emacsclient', '-c', '{}']
c.fonts.hints = 'normal 16pt Helvetica'
c.window.hide_decoration = True

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('b', 'cmd-set-text -sr :tab-focus')
config.bind('B', 'cmd-set-text -s :quickmark-load -t')

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 +')

config.bind('<Alt-x>', 'cmd-set-text :')

config.bind(',m', 'spawn mpv {url}')
config.bind(',M', 'hint links spawn mpv {hint-url}')

config.bind(';i', 'hint images tab')
config.bind(';I', 'hint images yank')

# Ctrl-E is the go-to EOL binding and I don't want qutebrowser to
# override it
config.unbind('<Ctrl-E>', mode='insert')
config.bind('<Alt-A>', 'edit-text', mode='insert')
config.bind('<Alt-E>', 'edit-text', mode='insert')

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

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

c.aliases = {
    'w': 'session-save',
    'q': 'close',
    'qa': 'quit',
    'wq': 'quit --save',
    'wqa': 'quit --save',
}

config.load_autoconfig(False)

Some desktop files

.local/share/applications/jaro.desktop

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

.local/share/applications/keepassxc-nopass.desktop

[Desktop Entry]
Name=KeePassXC (No password)
Comment=KeePassXC but no password required
Exec=/bin/sh -c "secret-tool lookup key keepassxc | keepassxc --pw-stdin $HOME/Documents/private/passwords.kdbx"
Icon=keepassxc
StartupWMClass=keepassxc
Terminal=false
Type=Application
Version=1.0
Categories=Utility;Security;Qt;
MimeType=application/x-keepass2;

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

shortenurl

Shorten given url. To make this work:

  • I simply created a firebase application.
  • Added my domain to it (from “Build → Hosting” menu). I used a subdomain, like “urlshortener.mydomain.com”. You’ll see the why in a minute.
  • Installed firebase cli application
    • yarn global add "firebase-tools"
  • Configured my project with firebase-tools.
    • firebase login
    • firebase projects:list → Just to check if it works or not
    • firebase init → Select “Hosting: Configure files for Firebase Hosting and (optionally) set up GitHub Action deploy”.
    • Here is the firebase.json that I use which rewrites all requests to your domain with the ones provided by “Dynamic Links” application. So it’s wise use a subdomain for this application as I outlined above.
      {
        "hosting": {
          // Following two lines are the important ones
          "appAssociation": "AUTO",
          "rewrites": [ { "source": "/**", "dynamicLinks": true } ],
      
          "ignore": [
            "firebase.json",
            "**/.*",
            "**/node_modules/**"
          ]
        }
      }
      
              
  • Then you may need to open “Engage → Dynamic Links” page and click to “Get Started”. Select your domain from the list and finish it.
#!/bin/bash

set -eo pipefail

case "$1" in
    -h|--help)
        echo "Usage:"
        echo "  $(basename "$0") URL"
        echo "  some-command-that-outputs-a-long-url | $(basename "$0")"
        exit
        ;;
esac

URL=$(echo "${1:-$(</dev/stdin)}" | xargs)

curl \
    --silent \
    -H 'Content-Type: application/json' \
    -d '{"dynamicLinkInfo":{"domainUriPrefix":"'$FIREBASE_URL_SHORTENER_PREFIX'","link":"'$URL'",},"suffix":{"option":"SHORT"}}' \
    "https://firebasedynamiclinks.googleapis.com/v1/shortLinks?key=$FIREBASE_WEB_API_KEY" \
    | jq -r '.shortLink' | tee >(xcopy)

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)

mediastuff

#!/bin/bash

# This whole script is based on the fact that I'm not that retard to
# listen/watch more than one audio/video streams at the same time.
# If I do, I'll get punished for that sin.

MPV_SOCKET=/tmp/mpvsocket

mpv_pause() {
    echo '{ "command": ["set_property", "pause", true] }' | socat - "$MPV_SOCKET"
}

mpv_toggle() {
    echo '{"command": ["cycle", "pause"]}' | socat - "$MPV_SOCKET"
}

mpv_seek() {
    if [[ $1 == *% ]]; then # seek $1 percent
        echo 'percent'
        echo '{"command": ["seek", "'"${1%\%}"'",  "relative-percent"]}' | socat - "$MPV_SOCKET"
    else # seek $1 seconds
        echo '{"command": ["seek", "'"$1"'"]}' | socat - "$MPV_SOCKET"
    fi
}

# TODO: somehow pause videos/audios playing in firefox/qutebrowser
all_pause() {
    mpv_pause
    mpc pause
}

all_toggle() {
    # Give priority to mpv
    if pgrep mpv; then
        mpv_toggle
    else
        mpc toggle
    fi
}

all_seek() {
    if pgrep mpv; then
        mpv_seek "$@"
    else
        mpc seek "$@"
    fi
}

get_sink_name_from_sink_id() {
    local ids="${1:-$(</dev/stdin)}"
    echo "$ids" | while read -r id; do
        echo "($id) $(pactl list sinks | grep -E "(Sink #$id)|(device.description)" | grep -A1 "Sink #$id" | sed -n "2p" | cut -d'"' -f2)"
    done
}

switch_audio_channel() {
    if [[ $1 = "--help" ]]; then
        echo "Changes default sink to next one and moves all inputs to new default sink."
        echo "Try to use it when something is already playing."
    fi

    readarray -t sinks <<< "$(pactl list sinks short | cut -f1)"
    readarray -t inputs <<< "$(pactl list sink-inputs short | cut -f1)"
    current_sink=$(pactl list sinks short | grep "RUNNING" | head -c 1)

    if [[ -z $current_sink ]]; then
        notify-send "Error while switching audio channels" "Could not detect default sink. Playing something may help."
        exit 1
    fi

    if [[ $1 = --interactive ]]; then
        new_sink=$(printf "%s\n" "${sinks[@]}" | get_sink_name_from_sink_id | rofi -dmenu | grep -Po "\(\K[0-9]*")
    else
        new_sink=${sinks[0]}
        for sink in "${sinks[@]}"; do
            if (( sink > current_sink )); then
                new_sink="$sink"
                break
            fi
        done
    fi

    [[ -z $new_sink ]] && exit;

    notify-send "Switching audio channel" "New default channel is $(get_sink_name_from_sink_id $new_sink), moving all inputs to that."

    # Move every input to new sink
    for input in "${inputs[@]}"; do
        pacmd move-sink-input "$input" "$new_sink"
    done

    # Make new sink the default
    pactl set-default-sink "$new_sink"
}

# Get the movie name from file/folder name and find the imdb-id
find_imdb_id_from_filename() {
    MOVIE_NAME=$(echo "$@" | sed -r 's/((\w{1,}[-. ]?)*?)(\(?[0-9]{4}\)?[. -]).*/\1\3/; s/[.()-]/ /g; s/  / /g')
    curl 'https://searx.prvcy.eu/search' \
         --data-urlencode "q=$MOVIE_NAME" \
         --data-urlencode 'language=en-US' \
         --data-urlencode 'format=csv' \
         --silent \
        | grep -oP 'imdb.com/title/\K\w+' -m 1
}

mpv_subdl() {
    MPV_SOCKET=/tmp/mpvsocket
    file_path=$1
    language=$2
    sub_file_path="${file_path%.*}.srt"

    # First try if there is a zip/rar file that has been downloaded in last 5 mins
    # if so try to extract it
    SUB_FILE=$(find ~/Downloads -cmin -5 | grep -E '(rar|zip)')
    if [[ -n $SUB_FILE ]]; then
        if sub-extract --no-confirm --auto "$SUB_FILE"; then
            echo 'show-text "Subtitle EXTRACTED from ~/Downloads."' | socat - $MPV_SOCKET
            echo "sub-add \"$sub_file_path\"" | socat - $MPV_SOCKET
            exit
        else
            echo 'show-text "Failed to extract, trying to download."' | socat - $MPV_SOCKET
            sleep 2
        fi
    fi

    # Now try `subdl`
    echo 'show-text "Downloading subtitle with subdl..."' | socat - $MPV_SOCKET
    if subdl --lang="$language" "$file_path"; then
        echo 'show-text "Subtitle downloaded."' | socat - $MPV_SOCKET
        echo "sub-add \"$sub_file_path\"" | socat - $MPV_SOCKET
    else
        IMDB_ID=$(find_imdb_id_from_filename "$file_path")
        echo "show-text \"Failed! Trying for $IMDB_ID.\"" | socat - $MPV_SOCKET
        if subdl --lang="$language" --imdb-id="$IMDB_ID" --force-imdb --download=best-rating "$file_path"; then
            echo 'show-text "Alternative method worked!"' | socat - $MPV_SOCKET
            echo "sub-add \"$sub_file_path\"" | socat - $MPV_SOCKET
            exit
        fi
    fi

    # Try `subliminal`
    echo 'show-text "Downloading subtitle with subliminal..."' | socat - $MPV_SOCKET
    SUBLIMINAL_OUTPUT=$(subliminal download -l "$language")
    if [[ -n $(echo $SUBLIMINAL_OUTPUT | sed -nr '/Downloaded [1-9] subtitle/p') ]]; then
        # Load all srt files into mpv, subliminal does not output the srt name
        for srt in ./*.srt; do
            echo "sub-add \"$srt\"" | socat - $MPV_SOCKET
        done

        echo 'show-text "Subtitle downloaded with subliminal."' | socat - $MPV_SOCKET
        exit
    fi

}


opt=$1; shift
case "$opt" in
    *help) echo "mediastuff [mpv-(subdl|toggle|pause|seek)|all-(toggle|pause|seek)|switch-audio-channel|connect-bt-headphones|find-imdb]" ;;
    mpv*toggle)              mpv_toggle                 "$@" ;;
    mpv*pause)               mpv_pause                  "$@" ;;
    mpv*seek)                mpv_seek                   "$@" ;;
    all*toggle)              all_toggle                 "$@" ;;
    all*pause)               all_pause                  "$@" ;;
    all*seek)                all_seek                   "$@" ;;
    switch*audio*channel)    switch_audio_channel       "$@" ;;
    connect*bt*headphones)   connect_bt_headphones      "$@" ;;
    mpv*subdl)               mpv_subdl                  "$@" ;;
    find*imdb)               find_imdb_id_from_filename "$@" ;;
esac

menu

#!/bin/bash

function trim {
    local var="${*:-$(</dev/stdin)}"
    var="${var#"${var%%[![:space:]]*}"}"
    var="${var%"${var##*[![:space:]]}"}"
    echo -n "$var"
}

function dmenu {
    rofi -dmenu -fuzzy -i "$@"
}

function files {
    f=$( ( git --git-dir="$HOME"/.dotfiles/ --work-tree="$HOME" ls-files; fd . --no-ignore-vcs --color=never --max-depth=5 ) | dmenu)

    if [[ "$1" == "--open" ]] && [[ -n "$f" ]]; then
        jaro "$f"
    else
        echo "$f"
    fi
}

function folders {
    f=$(fd . "$HOME" --no-ignore-vcs --color=never --type=d --max-depth=5 | dmenu)

    if [[ "$1" == "--open" ]] && [[ -n "$f" ]]; then
        jaro "$f"
    else
        echo "$f"
    fi
}

function file_contents {
    term --float -e /bin/sh -c "fuzzy file-contents Documents \$(git --git-dir="$HOME"/.dotfiles/ --work-tree="$HOME" ls-files --full-name)"
}

function bookmarks {
    grep -E '^*' ~/Documents/notes/bookmarks.org | grep '\[\[' | sed -E 's/\[\[(.*)\]\[(.*)\]\]/\2  <span foreground="grey" size="small">\1<\/span>/; s/\**//' | dmenu -i -markup-rows | grep -Eo 'https?://[^ ]+' | sed 's/<\/span>//' | jaro
}


cmd="$1"
shift
case $cmd in
    *help) echo "menu [files|folders|file-contents|passwords]";;
    files) files "$@";;
    folders) folders "$@";;
    file*contents) file_contents "$@";;
    bookmarks) bookmarks "$@";;
    calc*) rofi -show calc -modi calc -no-show-match -no-sort ;;
    *) rofi -show combi "$@" ;;
esac

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.")

term

#!/bin/bash

# When using st, it expects window class properties while urxvt expects
# window name properties for enabling floating windows. So
# Rule for urxvt:
# bspc rule --add '*:float'   state=floating
# Rule for st:
# bspc rule --add 'float'     state=floating

# st, urxvt, urxvtc, alacritty
RUNNER='alacritty'
FLOAT=''
OPAQUE=''
GEOMETRY=''
TITLE=''
OPTS=()

for arg; do
    case "$arg" in
        "--term="*) RUNNER=${arg#*=}; shift ;;
        "--title="*) TITLE=${arg#*=}; shift ;;
        "--geometry="*) GEOMETRY=${arg#*=}; shift ;;
        "--float") FLOAT='1'; shift ;;
        "--opaque") OPAQUE='1'; shift ;;
        "--tophalf") TOPHALF='1'; shift ;;
    esac
done

if [[ $RUNNER == 'urxvtc' ]]; then
    if ! pgrep urxvtd; then
        urxvtd & disown
        sleep 0.5
    fi
fi

if [[ -n "$FLOAT" ]]; then
    case "$RUNNER" in
        "st")        OPTS+=(-c float) ;;
        "urxvt"*)    OPTS+=(-name float) ;;
        "alacritty") OPTS+=(--class float) ;;
    esac
fi

if [[ -n "$TOPHALF" ]]; then
    case "$RUNNER" in
        "st")        OPTS+=(-c tophalf) ;;
        "urxvt"*)    OPTS+=(-name tophalf) ;;
        "alacritty") OPTS+=(--class tophalf) ;;
    esac
fi

if [[ -n "$TITLE" ]]; then
    case "$RUNNER" in
        "st")        OPTS+=(-t "$TITLE") ;;
        "urxvt"*)    OPTS+=(-name "$TITLE") ;;
        "alacritty") OPTS+=(--title "$TITLE") ;;
    esac
fi

if [[ -n "$OPAQUE" ]]; then
    case "$RUNNER" in
        "st")        OPTS+=(-A 1) ;;
        "urxvt"*)    OPTS+=(-bg "$(xrdb-get-value '*background')") ;;
        "alacritty") OPTS+=(--option background_opacity=1) ;;
    esac
fi

if [[ -n "$GEOMETRY" ]]; then
    case "$RUNNER" in
        "st"|"urxvt"*) OPTS+=(-g "$GEOMETRY") ;;
        "alacritty")
            echo "Not supported"
            # TODO: Use bspwm rules for geometry
            ;;
    esac
fi
echo "${OPTS[@]}"
$RUNNER "${OPTS[@]}" "$@"

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

ts_onfinish

#!/bin/bash

# When a job that is called with tsp finishes, this script is called.
# Need to set $TS_ONFINISH variable to path of this script. (See ~/.profile)

job_id="$1"
err="$2"
out_file="$3"
cmd="$4"

remaining_job_count=$(($(tsp | tail -n +2 | grep -cvE '^[0-9]+ +finished') - 1))

if [[ "$err" = 0 ]]; then
    icon=terminal
    title="finished"
    duration=5
else
    icon=error
    title="failed"
    duration=10

    # Put cmd into clipboard
    echo "$cmd" | xclip -selection clipboard
fi

notify-send \
    -i "$icon" \
    -t $((duration*1000))\
    "[TSP] job $title (remaining: $remaining_job_count)" \
    "$cmd"

xcopy

#!/bin/sh

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

if which xclip &>/dev/null; then
    CLIP_CMD="xclip -selection clipboard"
elif which pbcopy &>/dev/null; then
    CLIP_CMD="pbcopy"
else
    echo "Install xclip."
    exit 1
fi

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

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

media-downloader

Downloads given file in given folder with given name. Useful for adding keybindings in browsers etc.

#!/bin/bash

«bash-initialize-variables»

URL=${URL-$(zenity --entry --text="Enter url to download:")}
FILE_NAME=${FILE_NAME-$(zenity --entry --text="Enter file name (without extension).")}
SAVE_PATH=${SAVE_PATH-$(zenity --entry --text="Where to save?" --entry-text="${HOME}/")}

cd "${SAVE_PATH}" || exit

if [[ -n "$FILE_NAME" ]]; then
    youtube-dl --no-mtime --output "$FILE_NAME.%(ext)s" "$URL"
else
    youtube-dl --no-mtime "$URL"
fi

echo -n "$(pwd)/$(/bin/ls -tr | tail -n 1)" | xcopy
notify-send "Download finished!" "File path copied to your clipboard."

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

mgm

#!/usr/bin/env ruby

require 'net/http'
require 'uri'
require 'json'

CONDITIONS = {
  "A"     => "Açık",
  "AB"    => "Az Bulutlu",
  "PB"    => "Parçalı Bulutlu",
  "CB"    => "Çok Bulutlu",
  "HY"    => "Hafif Yağmurlu",
  "Y"     => "Yağmurlu",
  "KY"    => "Kuvvetli Yağmurlu",
  "KKY"   => "Karla Karışık Yağmurlu",
  "HKY"   => "Hafif Kar Yağışlı",
  "K"     => "Kar Yağışlı",
  "YKY"   => "Yoğun Kar Yağışlı",
  "HSY"   => "Hafif Sağanak Yağışlı",
  "SY"    => "Sağanak Yağışlı",
  "KSY"   => "Kuvvetli Sağanak Yağışlı",
  "MSY"   => "Mevzi Sağanak Yağışlı",
  "DY"    => "Dolu",
  "GSY"   => "Gökgürültülü Sağanak Yağışlı",
  "KGY"   => "Kuvvetli Gökgürültülü Sağanak Yağışlı",
  "SIS"   => "Sisli",
  "PUS"   => "Puslu",
  "DMN"   => "Dumanlı",
  "KF"    => "Kum veya Toz Taşınımı",
  "R"     => "Rüzgarlı",
  "GKR"   => "Güneyli Kuvvetli Rüzgar",
  "KKR"   => "Kuzeyli Kuvvetli Rüzgar",
  "SCK"   => "Sıcak",
  "SGK"   => "Soğuk",
  "HHY"   => "Yağışlı"
}

def fetch_data(url, params)
  uri = URI(url)
  uri.query = URI.encode_www_form(params)

  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  request = Net::HTTP::Get.new(uri.request_uri)
  request['Origin'] = 'https://mgm.gov.tr'
  request['Host'] = 'mgm.gov.tr'
  request['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'

  http.request(request)
  response = http.request(request)
  return JSON.parse(response.body)
end

if ARGV.empty?
  puts "Usage: mgm SEHIR"
  exit 1
end

merkez = fetch_data('https://servis.mgm.gov.tr/web/merkezler', { il: ARGV.first })[0]
durum = fetch_data("https://servis.mgm.gov.tr/web/sondurumlar", { merkezid: merkez["merkezId"] })[0]
puts <<-HERE
Hadise           :: #{CONDITIONS[durum["hadiseKodu"]]}
Sicaklik         :: #{durum["sicaklik"]} °C
Nem              :: #{durum["nem"]}%
Yagmur olasiligi :: #{durum["yagis00Now"]}%
HERE

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

Work stuff

Barrier

To be able to access my personal computer while I’m on my work computer (or vice-versa) without needing to physically switch keyboards, I use barrier. See here for detailed configuration documentation.

section: screens
  trendyol:
  x220:
end

section: aliases
end

section: links
  trendyol:
    right = x220
  x220:
    left = trendyol
end

section: options
  screenSaverSync = true
  clipboardSharing = true
  keystroke(alt+BracketL) = switchToScreen(trendyol)
  keystroke(alt+BracketR) = switchToScreen(x220)
end

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.