From 821367af3f5f29409e083edbada02afeeb133cb7 Mon Sep 17 00:00:00 2001 From: Simon Bruder Date: Tue, 9 Nov 2021 21:45:50 +0100 Subject: [PATCH] qutebrowser: Init This also makes it the default browser. --- users/simon/modules/default.nix | 1 + users/simon/modules/programs.nix | 2 +- users/simon/modules/qutebrowser/default.nix | 308 ++++++++++++++++++ .../userscripts/bandcamp-volume.user.js | 30 ++ .../userscripts/better-nginx-index.user.js | 56 ++++ .../qutebrowser/userscripts/invidious.user.js | 9 + .../qutebrowser/userscripts/libreddit.user.js | 9 + .../qutebrowser/userscripts/nitter.user.js | 8 + .../userscripts/per-domain-userstyles.user.js | 16 + .../userscripts/teams-meme.user.js | 7 + .../qutebrowser/userstyles/AriaNg.scss | 6 + .../qutebrowser/userstyles/monospace.scss | 22 ++ users/simon/modules/sway/default.nix | 1 + users/simon/modules/xdg.nix | 2 +- 14 files changed, 475 insertions(+), 2 deletions(-) create mode 100644 users/simon/modules/qutebrowser/default.nix create mode 100644 users/simon/modules/qutebrowser/userscripts/bandcamp-volume.user.js create mode 100644 users/simon/modules/qutebrowser/userscripts/better-nginx-index.user.js create mode 100644 users/simon/modules/qutebrowser/userscripts/invidious.user.js create mode 100644 users/simon/modules/qutebrowser/userscripts/libreddit.user.js create mode 100644 users/simon/modules/qutebrowser/userscripts/nitter.user.js create mode 100644 users/simon/modules/qutebrowser/userscripts/per-domain-userstyles.user.js create mode 100644 users/simon/modules/qutebrowser/userscripts/teams-meme.user.js create mode 100644 users/simon/modules/qutebrowser/userstyles/AriaNg.scss create mode 100644 users/simon/modules/qutebrowser/userstyles/monospace.scss diff --git a/users/simon/modules/default.nix b/users/simon/modules/default.nix index ab9ea6e..e7c44d3 100644 --- a/users/simon/modules/default.nix +++ b/users/simon/modules/default.nix @@ -14,6 +14,7 @@ ./neovim ./pass.nix ./programs.nix + ./qutebrowser ./scripts ./sway ./tmate.nix diff --git a/users/simon/modules/programs.nix b/users/simon/modules/programs.nix index 66ba69d..d50ae22 100644 --- a/users/simon/modules/programs.nix +++ b/users/simon/modules/programs.nix @@ -143,7 +143,7 @@ in ] ++ lib.optionals nixosConfig.sbruder.gui.enable [ # communication claws-mail # email client that looks ugly but just works - firefox-esr # the least bad browser + firefox-esr # a bad browser ((mumble.override { pulseSupport = true; }).overrideAttrs (o: o // { patches = o.patches ++ [ # TODO: remove patch once stable release supports this diff --git a/users/simon/modules/qutebrowser/default.nix b/users/simon/modules/qutebrowser/default.nix new file mode 100644 index 0000000..6f5d74d --- /dev/null +++ b/users/simon/modules/qutebrowser/default.nix @@ -0,0 +1,308 @@ +{ 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 = [ + ] ++ permissionVideo; # capturing video almost always also requires capturing audio + permissionNotifications = [ + "https://chat.sbruder.de" + "https://teams.microsoft.com" + ]; + + cookieExceptions = [ + # Microsoft Teams + # https://docs.microsoft.com/en-us/microsoftteams/troubleshoot/teams-sign-in/sign-in-loop#resolution + # TODO: remove when I don’t have to use this garbage any more + "https://microsoft.com" + "https://microsoftonline.com" + "https://teams.skype.com" + "https://teams.microsoft.com" + "https://sfbassets.com" + "https://skypeforbusiness.com" + ]; +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"; + editor.command = [ "alacritty" "-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 + content.blocking.adblock.lists = [ + "https://easylist.to/easylist/easylist.txt" + "https://easylist.to/easylist/easyprivacy.txt" + "https://easylist.to/easylist/fanboy-social.txt" + "https://easylist.to/easylistgermany/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 + git push + '') + ]; +} diff --git a/users/simon/modules/qutebrowser/userscripts/bandcamp-volume.user.js b/users/simon/modules/qutebrowser/userscripts/bandcamp-volume.user.js new file mode 100644 index 0000000..4e3070d --- /dev/null +++ b/users/simon/modules/qutebrowser/userscripts/bandcamp-volume.user.js @@ -0,0 +1,30 @@ +// ==UserScript== +// @name Bandcamp: Add volume slider +// @include https://*.bandcamp.com/* +// @include https://sewerslvt.com/* +// @include https://store.sigurros.com/* +// ==/UserScript== +function setVolume(volume) { + document.querySelectorAll("audio").forEach(el => el.volume = volume / 100) + localStorage.setItem("volume", volume) +} + +function loadStoredVolume() { + return localStorage.getItem("volume") ? Number(localStorage.getItem("volume")) : 25 +} + +const volumeControlContainer = document.createElement("div") +volumeControlContainer.style.position = "fixed" +volumeControlContainer.style.top = 0 +volumeControlContainer.style.left = 0 +volumeControlContainer.style["z-index"] = 10000 +const volumeControlSlider = document.createElement("input") +volumeControlSlider.type = "range" +volumeControlSlider.min = 0 +volumeControlSlider.max = 100 +volumeControlSlider.value = loadStoredVolume() +volumeControlSlider.addEventListener("input", e => {setVolume(e.target.value)}) +volumeControlContainer.appendChild(volumeControlSlider) +document.body.appendChild(volumeControlContainer) + +setInterval(() => {setVolume(loadStoredVolume())}, 1000) diff --git a/users/simon/modules/qutebrowser/userscripts/better-nginx-index.user.js b/users/simon/modules/qutebrowser/userscripts/better-nginx-index.user.js new file mode 100644 index 0000000..5b0866c --- /dev/null +++ b/users/simon/modules/qutebrowser/userscripts/better-nginx-index.user.js @@ -0,0 +1,56 @@ +// ==UserScript== +// @name nginx: Better directory index +// @include http://localhost:8888/torrent/download/* +// @include https://ci.sbruder.de/nix-store/* +// ==/UserScript== + +// https://stackoverflow.com/a/14919494 +function humanFileSize(bytes) { + const thresh = 1024 + if(Math.abs(bytes) < thresh) { + return bytes + ' B' + } + const units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'] + var u = -1; + do { + bytes /= thresh; + ++u; + } while(Math.abs(bytes) >= thresh && u < units.length - 1); + return bytes.toFixed(1)+' '+units[u] +} + + +function textToA(line) { + let outerElement = document.createElement('div') + outerElement.innerHTML = line + return outerElement.getElementsByTagName('a')[0] +} + +function parseLine(line) { + const href = textToA(line).href + const filename = href.substr(-1) === '/' ? decodeURIComponent(href.split('/').slice(-2, -1)[0]) : decodeURIComponent(href.split('/').pop()) + const size = line.split(' ').pop() + return { + href: href, + filename: filename, + size: size + } +} + +function processLine(line) { + meta = parseLine(line) + if (meta.filename.endsWith('.aria2') || meta.filename.endsWith('.torrent') || meta.filename === 'dht.dat' || meta.filename === 'torrents' || meta.filename === 'torrents.old' || meta.filename === 'history.csv') { + return '' + } + return `${meta.filename}${meta.size === '-' ? '-' : humanFileSize(meta.size)}` +} + +const collator = new Intl.Collator('kn', {numeric: true}) + +document.querySelector('pre').outerHTML = '' + document.querySelector('pre').innerHTML + .split('\n') + .filter(line => line !== '') + .filter(line => line !== '../') + .map(processLine) + .sort(collator.compare) + .join('\n') + '
../-
' diff --git a/users/simon/modules/qutebrowser/userscripts/invidious.user.js b/users/simon/modules/qutebrowser/userscripts/invidious.user.js new file mode 100644 index 0000000..738ce5d --- /dev/null +++ b/users/simon/modules/qutebrowser/userscripts/invidious.user.js @@ -0,0 +1,9 @@ +// ==UserScript== +// @name Invidious Redirect +// @include http://www.youtube.com/* +// @include https://www.youtube.com/* +// @run-at document-start +// ==/UserScript== + +document.close(); +window.location.replace(window.location.href.replace(/www\.youtube\.com/, "iv.sbruder.xyz").replace("/shorts/", "/watch?v=")) diff --git a/users/simon/modules/qutebrowser/userscripts/libreddit.user.js b/users/simon/modules/qutebrowser/userscripts/libreddit.user.js new file mode 100644 index 0000000..9c4262a --- /dev/null +++ b/users/simon/modules/qutebrowser/userscripts/libreddit.user.js @@ -0,0 +1,9 @@ +// ==UserScript== +// @name Libreddit Redirect +// @include https://www.reddit.com/* +// @include https://old.reddit.com/* +// @run-at document-start +// ==/UserScript== + +document.close(); +window.location.replace(window.location.href.replace(/(old|www)?\.reddit\.com/, "libreddit.sbruder.xyz")) diff --git a/users/simon/modules/qutebrowser/userscripts/nitter.user.js b/users/simon/modules/qutebrowser/userscripts/nitter.user.js new file mode 100644 index 0000000..53063ea --- /dev/null +++ b/users/simon/modules/qutebrowser/userscripts/nitter.user.js @@ -0,0 +1,8 @@ +// ==UserScript== +// @name Nitter Redirect +// @include https://twitter.com/* +// @run-at document-start +// ==/UserScript== + +document.close(); +window.location.replace(window.location.href.replace(/twitter\.com/, "nitter.sbruder.xyz")) diff --git a/users/simon/modules/qutebrowser/userscripts/per-domain-userstyles.user.js b/users/simon/modules/qutebrowser/userscripts/per-domain-userstyles.user.js new file mode 100644 index 0000000..7615071 --- /dev/null +++ b/users/simon/modules/qutebrowser/userscripts/per-domain-userstyles.user.js @@ -0,0 +1,16 @@ +// ==UserScript== +// @name Per-Domain Userstyles +// @include * +// @run-at document-start +// @author Original by Olmo Kramer +// ==/UserScript== + +(() => { + document.addEventListener("readystatechange", () => { + if (document.readyState == "interactive") { + const doc = document.documentElement + doc.setAttribute("data-qb-url", window.location.href) + doc.setAttribute("data-qb-domain", window.location.host) + } + }) +})() diff --git a/users/simon/modules/qutebrowser/userscripts/teams-meme.user.js b/users/simon/modules/qutebrowser/userscripts/teams-meme.user.js new file mode 100644 index 0000000..fc64421 --- /dev/null +++ b/users/simon/modules/qutebrowser/userscripts/teams-meme.user.js @@ -0,0 +1,7 @@ +// ==UserScript== +// @name Es heißt TEAMS +// @include https://teams.microsoft.com/* +// ==/UserScript== + +// pfusch +setInterval(() => {Array.from(document.querySelectorAll(".teams-title")).map(el => {el.innerHTML = "TEAMS"; el.style='font-family: "Comic Sans MS";'})}, 10000) diff --git a/users/simon/modules/qutebrowser/userstyles/AriaNg.scss b/users/simon/modules/qutebrowser/userstyles/AriaNg.scss new file mode 100644 index 0000000..6af258d --- /dev/null +++ b/users/simon/modules/qutebrowser/userstyles/AriaNg.scss @@ -0,0 +1,6 @@ +[data-qb-domain="torrent.sbruder.de"], +[data-qb-url^="http://localhost:8888/torrent/"] { + body { + user-select: unset !important; + } +} diff --git a/users/simon/modules/qutebrowser/userstyles/monospace.scss b/users/simon/modules/qutebrowser/userstyles/monospace.scss new file mode 100644 index 0000000..268d8a9 --- /dev/null +++ b/users/simon/modules/qutebrowser/userstyles/monospace.scss @@ -0,0 +1,22 @@ +[data-qb-domain="pad.sbruder.de"] .CodeMirror { + font-family: monospace; +} + +[data-qb-domain="github.com"] { + code, + pre, + tt, + .blob-code-inner, + .text-mono { + font-family: monospace !important; + } +} + +[data-qb-domain="git.sbruder.de"] { + pre, + code, + kbd, + samp { + font-family: monospace !important; + } +} diff --git a/users/simon/modules/sway/default.nix b/users/simon/modules/sway/default.nix index 16d344e..1df2baa 100644 --- a/users/simon/modules/sway/default.nix +++ b/users/simon/modules/sway/default.nix @@ -167,6 +167,7 @@ in assigns = { "2" = [ { app_id = "firefox"; } + { app_id = "org.qutebrowser.qutebrowser"; } #{ class="Chromium"; } ]; "3" = [ diff --git a/users/simon/modules/xdg.nix b/users/simon/modules/xdg.nix index 8e5a050..6a91f08 100644 --- a/users/simon/modules/xdg.nix +++ b/users/simon/modules/xdg.nix @@ -5,7 +5,7 @@ enable = nixosConfig.sbruder.gui.enable; defaultApplications = let - browser = "firefox.desktop"; + browser = "org.qutebrowser.qutebrowser.desktop"; in { "application/pdf" = "org.pwmt.zathura.desktop";