{ config, lib, pkgs, ... }: let inherit ((import ../common.nix).colorschemes) solarized; setOptionForeachPattern = option: value: patterns: let formatValue = value: if lib.isBool value then (if value then "True" else "False") else (if lib.isString value then "r\"${value}\"" else (toString value)); in lib.concatMapStringsSep "\n" (pattern: "config.set(\"${option}\", ${formatValue value}, \"${pattern}\")") patterns; permissionVideo = [ "https://chat.sbruder.de" "https://meet.jalr.de" ]; permissionAudio = [ "https://app.sipgate.com" ] ++ permissionVideo; # capturing video almost always also requires capturing audio permissionNotifications = [ "https://app.sipgate.com" "https://chat.sbruder.de" ]; cookieExceptions = [ ]; in { programs.qutebrowser = { enable = true; aliases = { q = "tab-close"; # one tab qa = "close"; # one window "qa!" = "quit"; # everything }; keyBindings = { normal = { ",rm" = "spawn -u readability"; # reasonable tab cycling J = "tab-prev"; K = "tab-next"; gJ = "tab-move -"; gK = "tab-move +"; # mpv ",mv" = "spawn mpv --profile=clear-speed {url}"; ",ma" = "spawn mpv --player-operation-mode=pseudo-gui --ytdl-format=251/bestaudio/best {url}"; ",mq" = "spawn umpv {url}"; ",Mv" = "hint links spawn mpv --profile=clear-speed {hint-url}"; ",Ma" = "hint links spawn mpv --player-operation-mode=pseudo-gui --ytdl-format=251/bestaudio/best {hint-url}"; ",Mq" = "hint links spawn umpv {hint-url}"; }; }; searchEngines = { DEFAULT = "https://bangs.sbruder.de/eval?engine=https://duckduckgo.com/?q=%25s&query={}"; }; settings = { colors = let fgbg = fg: bg: { inherit fg bg; }; topbottom = colour: { top = colour; bottom = colour; }; in with solarized; { completion = rec { fg = base1; odd.bg = base02; even.bg = base03; match.fg = green; scrollbar = fgbg base1 base03; category = { inherit (fgbg yellow base03) fg bg; border = topbottom base03; }; item.selected = { inherit (fgbg base1 base01) fg bg; border = topbottom base01; inherit match; }; }; contextmenu = { disabled = fgbg base0 base02; menu = fgbg base1 base03; selected = fgbg base1 base01; }; downloads = { bar.bg = base03; start = fgbg base03 blue; stop = fgbg base03 cyan; error.fg = red; }; hints = { inherit (fgbg base03 yellow) fg bg; match.fg = base1; }; keyhint = { inherit (fgbg base1 base03) fg bg; suffix.fg = base1; }; messages = { error = { inherit (fgbg base03 red) fg bg; border = red; }; warning = { inherit (fgbg base03 violet) fg bg; border = violet; }; info = { inherit (fgbg base1 base03) fg bg; border = base03; }; }; prompts = { inherit (fgbg base1 base03) fg bg; border = base03; selected = fgbg base1 base01; }; statusbar = { normal = fgbg green base03; insert = fgbg base03 blue; passthrough = fgbg base03 cyan; private = fgbg base03 base02; command = { inherit (fgbg base1 base03) fg bg; private = fgbg base1 base03; }; caret = { inherit (fgbg base03 violet) fg bg; selection = fgbg base03 blue; }; progress.bg = blue; url.fg = base1; url.error.fg = red; url.hover.fg = base1; url.success.http.fg = cyan; url.success.https.fg = green; url.warn.fg = violet; }; tabs = rec { bar.bg = base03; even = fgbg base1 base03; odd = even; indicator = { start = blue; stop = cyan; error = red; }; selected = rec { even = fgbg base02 green; odd = even; }; pinned = { inherit even odd selected; }; }; }; # UI scrolling.smooth = true; completion.web_history.max_items = 0; # no history # Fonts fonts = { web = { family = rec { serif = "Georgia"; sans_serif = "PT Sans"; standard = sans_serif; }; }; }; # Behaviour auto_save.session = true; session.lazy_restore = true; content.autoplay = false; downloads = { location.directory = "/tmp"; open_dispatcher = "${pkgs.xdg-utils}/bin/xdg-open"; # QDesktopServices.openUrl always opens Firefox }; editor.command = [ "foot" "-e" "nvim" "-f" "{file}" "-c" "normal {line}G{column0}l" ]; spellcheck.languages = [ "de-DE" "en-GB" ]; url.default_page = "about:blank"; url.start_pages = [ "about:blank" ]; # Privacy content.cookies.accept = "no-3rdparty"; content.headers.user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0"; content.headers.accept_language = "en-US,en;q=0.5"; # Filtering (many don’t get used yet due to lack of cosmetic filtering) # https://github.com/gorhill/uBlock/blob/master/assets/assets.json # EasyList is using a mirror because upstream’s servers are slow content.blocking.adblock.lists = [ "https://secure.fanboy.co.nz/easylist.txt" "https://secure.fanboy.co.nz/easyprivacy.txt" "https://secure.fanboy.co.nz/fanboy-social.txt" "https://easylist-downloads.adblockplus.org/easylistgermany.txt" "https://filters.adtidy.org/extension/ublock/filters/17.txt" "https://filters.adtidy.org/extension/ublock/filters/3.txt" "https://filters.adtidy.org/extension/ublock/filters/4.txt" "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/annoyances.txt" "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/badlists.txt" "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/badware.txt" "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/filters.txt" "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/privacy.txt" "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/resource-abuse.txt" "https://raw.githubusercontent.com/uBlockOrigin/uAssets/master/filters/unbreak.txt" "https://secure.fanboy.co.nz/fanboy-annoyance.txt" "https://secure.fanboy.co.nz/fanboy-antifacebook.txt" "https://secure.fanboy.co.nz/fanboy-cookiemonster.txt" ]; }; extraConfig = /* python */ '' import glob c.content.user_stylesheets = glob.glob("${config.xdg.configHome}/qutebrowser/userstyles/*.css") c.qt.environ = { # otherwise results in severe banding (https://github.com/qutebrowser/qutebrowser/issues/5528) "QT_WAYLAND_DISABLE_WINDOWDECORATION": "0", } c.content.headers.custom = { "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" } c.tabs.padding["top"] = 3 c.tabs.padding["bottom"] = 3 # Permissions ${setOptionForeachPattern "content.media.audio_capture" true permissionAudio} ${setOptionForeachPattern "content.media.audio_video_capture" true permissionVideo} ${setOptionForeachPattern "content.media.video_capture" true permissionVideo} ${setOptionForeachPattern "content.notifications.enabled" true permissionNotifications} # Cookie exceptions ${setOptionForeachPattern "content.cookies.accept" "all" cookieExceptions} ''; }; xdg.configFile = let replaceExtension = newExtension: filename: "${lib.concatStringsSep "." (lib.init (lib.splitString "." filename))}.${newExtension}"; regularFilesIn = dir: lib.filterAttrs (_: v: v == "regular") (builtins.readDir (./. + "/${dir}")); compileScss = name: file: pkgs.runCommand (replaceExtension "css" name) { } '' ${pkgs.sassc}/bin/sassc ${file} $out ''; in { "qutebrowser/bookmarks/urls".source = config.lib.file.mkOutOfStoreSymlink "${config.xdg.dataHome}/qutebrowser/synced-bookmarks/bookmarks"; "qutebrowser/quickmarks".source = config.lib.file.mkOutOfStoreSymlink "${config.xdg.dataHome}/qutebrowser/synced-bookmarks/quickmarks"; } // (lib.mapAttrs' (k: _: lib.nameValuePair "qutebrowser/greasemonkey/${k}" { source = ./userscripts + "/${k}"; }) (regularFilesIn "userscripts")) // (lib.mapAttrs' (k: _: lib.nameValuePair "qutebrowser/userstyles/${replaceExtension "css" k}" { source = compileScss k (./userstyles + "/${k}"); }) (regularFilesIn "userstyles")); xdg.dataFile = lib.mapAttrs' (dict: sha256: lib.nameValuePair "qutebrowser/qtwebengine_dictionaries/${dict}.bdic" { source = (pkgs.fetchurl { url = "https://chromium.googlesource.com/chromium/deps/hunspell_dictionaries/+/18e09b9197a3b1d771c077c530d1a4ebad04c167/${dict}.bdic?format=TEXT"; inherit sha256; postFetch = '' base64 -d "$out" > "$TMPDIR/decoded" mv "$TMPDIR/decoded" "$out" ''; }); }) { "de-DE-3-0" = "sha256-B2pHBwDb0Kpiu4s9JMNOE0C9/oPLvPwDXOly8jwUBAA="; "en-GB-9-0" = "sha256-c8eaQQ+AkgwpsFX3upB9k0A7BajBfQDo5wVO22L3Maw="; }; home.packages = [ (pkgs.writeShellScriptBin "qbmarks" /* bash */ '' set -euo pipefail git() { echo "[$] git $@" command git -C "${config.xdg.dataHome}/qutebrowser/synced-bookmarks" "$@" } git commit --no-gpg-sign -a -m "Sync on $(hostname)" || true git pull --rebase --no-gpg-sign git push '') ]; }