summaryrefslogtreecommitdiff
path: root/common/.vimfx
diff options
context:
space:
mode:
authorSimon Parri <simonparri@ganzeria.com>2025-07-22 00:56:22 +0200
committerSimon Parri <simonparri@ganzeria.com>2025-07-22 11:49:01 +0200
commitd2c331b9f036951eef062dd5141c75182375ba12 (patch)
tree325bf3c56b70ec845e8de9349f45408ed844e747 /common/.vimfx
downloaddotfiles-d2c331b9f036951eef062dd5141c75182375ba12.tar.gz
dotfiles-d2c331b9f036951eef062dd5141c75182375ba12.zip
Add current configuration
Diffstat (limited to 'common/.vimfx')
-rw-r--r--common/.vimfx/config.js608
-rw-r--r--common/.vimfx/frame.js24
l---------common/.vimfx/tabfs1
-rw-r--r--common/.vimfx/userChrome.css64
-rw-r--r--common/.vimfx/userContent.css47
5 files changed, 744 insertions, 0 deletions
diff --git a/common/.vimfx/config.js b/common/.vimfx/config.js
new file mode 100644
index 0000000..b384a21
--- /dev/null
+++ b/common/.vimfx/config.js
@@ -0,0 +1,608 @@
+// -*- eval: (outline-minor-mode 1) -*-
+// * Imports
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components
+const {Preferences} = ChromeUtils.importESModule('resource://gre/modules/Preferences.sys.mjs', {})
+const {AddonManager} = ChromeUtils.importESModule("resource://gre/modules/AddonManager.sys.mjs")
+const {PlacesUtils} = ChromeUtils.importESModule("resource://gre/modules/PlacesUtils.sys.mjs")
+const {FileUtils} = ChromeUtils.importESModule("resource://gre/modules/FileUtils.sys.mjs")
+const nsIEnvironment = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment)
+Cu.importGlobalProperties(['PathUtils']);
+
+// * Helpers
+let vimFXDirectory = __dirname.replace(new RegExp("^file://"), "")
+
+vimfx.on("locationChange", ({vim}) => vimfx.send(vim, "locationChange"))
+
+// `pathSearch' and `exec' adapted from
+// https://github.com/azuwis/.vimfx/blob/master/config.js
+
+function pathSearch(bin) {
+ if (PathUtils.isAbsolute(bin))
+ return bin
+ let pathListSep = (Services.appinfo.OS == 'WINNT') ? ';' : ':'
+ let dirs = nsIEnvironment.get("PATH").split(pathListSep)
+ let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile)
+ for (let dir of dirs) {
+ let path = PathUtils.join(dir, bin)
+ file.initWithPath(path)
+ if (file.exists() && file.isFile() && file.isExecutable())
+ return path
+ }
+ return null
+}
+
+function makeProcess(cmd) {
+ let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile)
+ file.initWithPath(pathSearch(cmd))
+ let process = Cc['@mozilla.org/process/util;1'].createInstance(Ci.nsIProcess)
+ process.init(file)
+ return process
+}
+
+function exec(cmd, ...args) {
+ return makeProcess(cmd).runAsync(args, args.length)
+}
+
+function execSync(cmd, ...args) {
+ return makeProcess(cmd).run(true, args, args.length)
+}
+
+function curry(fn, ...args) {
+ return function() { fn(...args, ...arguments) }
+}
+
+function rcurry(fn, ...args) {
+ return function() { fn(...arguments, ...args) }
+}
+
+function hint(args, fn, command="follow") {
+ vimfx.modes.normal.commands[command].run(Object.assign({}, args, {
+ callbackOverride(args) { // I know it's shadowed
+ fn(args)
+ return args.timesLeft > 1
+ }
+ }))
+}
+
+function execOnLink(args, ...execArgs) {
+ hint(args, ({href}) => exec(...execArgs.map(arg => arg.replace("%u", href))))
+}
+
+function execOnCurrent({vim}, ...execArgs) {
+ exec(...execArgs.map(arg => arg.replace("%u", vim.browser.currentURI.spec)))
+}
+
+function setBarText(args, text) {
+ vimfx.modes.normal.commands.focus_location_bar.run(args)
+ args.vim.window.gURLBar.value = text
+}
+// * Commands
+function toggleImages(args) {
+ let val = Preferences.get("permissions.default.image") == 1 ? 2 : 1
+ Preferences.set({"permissions.default.image": val})
+ args.vim.browser.reload()
+}
+
+// inspired by https://old.reddit.com/r/FirefoxCSS/comments/cia5n2/is_there_a_way_to_refresh_userchromecss_without/
+function reloadUserChrome(callback) {
+ callback ||= () => 0
+ try {
+ const {io, stylesheet} = Services
+ let userChrome = Services.dirsvc.get("UChrm", Ci.nsIFile)
+ userChrome.append("userChrome.css")
+ userChrome = io.newFileURI(FileUtils.File(userChrome.path))
+
+ if (stylesheet.sheetRegistered(userChrome, stylesheet.USER_SHEET))
+ stylesheet.unregisterSheet(userChrome, stylesheet.USER_SHEET)
+ stylesheet.loadAndRegisterSheet(userChrome, stylesheet.USER_SHEET)
+ callback("userChrome.css successfully reloaded")
+ } catch (e) {
+ callback("Error reloading userChrome.css; check the browser console")
+ throw e
+ }
+}
+
+function tabGroupsToggleTabView({vim}) {
+ vim.window.TabView.toggle()
+}
+
+function tabGroupsSwitch({vim}) {
+ vim.window.TabView.button.click()
+}
+
+function tabGroupsMoveTab({vim}) {
+ let m = vim.window.document.querySelector("#tabGroups-context_tabViewMenu")
+ m.parentElement.openPopup()
+ m.click()
+}
+
+function listTabs({vim}) {
+ vim.window.gTabsPanel.showAllTabsPanel()
+}
+
+const sendFn = msg =>
+ ({vim}) => vimfx.send(vim, msg, null, m => vim.notify(m))
+
+let commands = {
+ emacsclient: rcurry(execOnLink, "emacsclient", "%u"),
+ emacsclient_media: rcurry(execOnLink, "emacsclient", "--eval", "(bongo-play-file \"%u\")"),
+ emacsclient_eww: rcurry(execOnLink, "emacsclient", "--eval", "(eww \"%u\")"),
+ toggle_images: toggleImages,
+ reload_userchrome: ({vim}) => reloadUserChrome(a => vim.notify(a)),
+ tab_groups_view: tabGroupsToggleTabView,
+ tab_groups_switch: tabGroupsSwitch,
+ tab_groups_move: tabGroupsMoveTab,
+ list_tabs: listTabs,
+ browse_git: rcurry(execOnCurrent, "browse-git", "%u"),
+ browse_git_follow: rcurry(execOnLink, "browse-git", "%u"),
+ go_way_back: sendFn("goWayBack"),
+ go_way_forward: sendFn("goWayForward"),
+}
+// ** Apply
+Object.entries(commands)
+ .forEach(([name, fn]) => vimfx.addCommand({name,
+ description: name},
+ fn))
+// * Keys
+// g: navigation
+// s: elements
+// x: less-common commands
+// xt: tabs
+mappings = {
+ normal: {
+ location: [
+ ["o", "focus_location_bar"],
+ ["", "focus_search_bar"],
+ ["y <c-y>", "paste_and_go"],
+ ["uy <c-u><c-y>", "paste_and_go_in_tab"],
+ ["w <a-w>", "copy_current_url"],
+ ["gu <a-g>u", "go_up_path"],
+ ["gU <a-g>U", "go_to_root"],
+ ["gm <a-g>m", "go_home"],
+ ["B", "history_back"],
+ ["F", "history_forward"],
+ ["gh <a-g>h", "history_list"],
+ ["r", "reload"],
+ ["R", "reload_force"],
+ ["xtr <c-x>tr", "reload_all"],
+ ["xtR <c-x>tR", "reload_all_force"],
+ ["xg <c-x>g", "stop"],
+ ["xtg <c-x>tg", "stop_all"],
+ ["gg", "my/browse_git"],
+ ["gwb", "my/go_way_back"],
+ ["gwf", "my/go_way_forward"],
+ ],
+ scrolling: [
+ ["b", "scroll_left"],
+ ["f", "scroll_right"],
+ ["n", "scroll_down"],
+ ["p", "scroll_up"],
+ ["<space>", "scroll_page_down"],
+ ["<s-space> <backspace>", "scroll_page_up"],
+ ["} )", "scroll_half_page_down"],
+ ["{ (", "scroll_half_page_up"],
+ ["<lt> <a-lt>", "scroll_to_top"],
+ ["<gt> <a-gt>", "scroll_to_bottom"],
+ ["a <c-a>", "scroll_to_left"],
+ ["e <c-e>", "scroll_to_right"],
+ ["<c-space>", "mark_scroll_position"],
+ ["u<space> <c-u><space>", "scroll_to_mark"],
+ ["g<lt>", "scroll_to_previous_position"],
+ ["g<gt>", "scroll_to_next_position"],
+ ],
+ tabs: [
+ ["O xtn <c-x>tN", "tab_new"],
+ ["uO <c-u>O uxtN <c-u><c-x>tN", "tab_new_after_current"],
+ ["xtn <c-x>tn", "tab_duplicate"],
+ ["P", "tab_select_previous"],
+ ["N", "tab_select_next"],
+ ["xto <c-x>to", "tab_select_most_recent"],
+ ["xtl <c-x>tl", "tab_select_oldest_unvisited"],
+ ["xtP <c-x>tP", "tab_move_backward"],
+ ["xtN <c-x>tN", "tab_move_forward"],
+ ["xt4 <c-x>t5", "tab_move_to_window"],
+ ["xta <c-x>ta", "tab_select_first"],
+ ["", "tab_select_first_non_pinned"],
+ ["xte <c-x>te", "tab_select_last"],
+ ["", "tab_toggle_pinned"],
+ ["k xt0 <c-x>t0 d", "tab_close"],
+ ["/ xtu <c-x>tu", "tab_restore"],
+ ["u/ uxtu <c-u><c-x>tu", "tab_restore_list"],
+ ["", "tab_close_to_end"],
+ ["", "tab_close_other"],
+ ["T", "my/list_tabs"],
+ ["t", "my/tab_groups_switch"],
+ ["xtm <c-x>tm", "my/tab_groups_move"],
+ ["xtv <c-x>tv", "my/tab_groups_view"],
+ ],
+ browsing: [
+ ["j ss <a-s>s", "follow"],
+ ["uj <c-u>j uss", "follow_multiple"],
+ ["J x4j <c-x>4j SS", "follow_in_tab"],
+ ["uJ <c-u>J ux4j <c-u><c-x>4j uSS", "follow_in_focused_tab"],
+ ["x5j <c-x>5j", "follow_in_window"],
+ ["ux5j <c-u><c-x>5j us5f <c-u><a-s>5f", "follow_in_private_window"],
+ ["uw <c-u><a-w>", "follow_copy"],
+ ["sj <a-s>j", "follow_focus"],
+ ["sc <a-s>c", "open_context_menu"],
+ ["sb <a-s>b", "click_browser_element"],
+ ["[", "follow_previous"],
+ ["]", "follow_next"],
+ ["si <a-s>i", "focus_text_input"],
+ ["v sv <a-s>v", "element_text_caret"],
+ ["uv usv <c-u><a-s>v", "element_text_select"],
+ ["sw <a-s>w", "element_text_copy"],
+ ["se <a-s>e", "my/emacsclient"],
+ ["use <c-u><a-s>e", "my/emacsclient_eww"],
+ ["sE <a-s>E", "my/emacsclient_media"],
+ ["sg <a-s>g", "my/browse_git_follow"],
+ ],
+ find: [
+ ["", "find"],
+ ["<c-s>", "find_highlight_all"],
+ ["", "find_links_only"],
+ ["", "find_next"],
+ ["", "find_previous"],
+ ],
+ misc: [
+ ["x55 <c-x>55", "window_new"],
+ ["ux55 <c-u><c-x>55", "window_new_private"],
+ ["i", "enter_mode_ignore"],
+ ["I", "quote"],
+ ["gr <a-g>r", "enter_reader_view"],
+ ["", "edit_blacklist"],
+ ["xc <c-x>c", "reload_config_file"],
+ ["?", "help"],
+ ["<force><escape>", "esc"],
+ ["xi <c-x>i", "my/toggle_images"],
+ ["uxc <c-u><c-x>c", "my/reload_user_chrome"],
+ ],
+ },
+ caret: {
+ "": [
+ ["b", "move_left"],
+ ["f", "move_right"],
+ ["n", "move_down"],
+ ["p", "move_up"],
+ ["<a-b>", "move_word_left"],
+ ["<a-f>", "move_word_right"],
+ ["a <c-a>", "move_to_line_start"],
+ ["e <c-e>", "move_to_line_end"],
+ ["<space> <c-space>", "toggle_selection"],
+ ["o", "toggle_selection_direction"],
+ ["w <a-w>", "copy_selection_and_exit"],
+ ["<escape>", "exit"],
+ ],
+ },
+ hints: {
+ "": [
+ ["<escape>", "exit"],
+ ["<enter> <c-enter> <a-enter>", "activate_highlighted"],
+ ["<c-space>", "rotate_markers_forward"],
+ ["<s-space>", "rotate_markers_backward"],
+ ["<backspace>", "delete_char"],
+ ["<c-backspace>", "toggle_complementary"],
+ ["<up>", "increase_count"],
+ ],
+ },
+ ignore: {
+ "": [
+ ["<s-escape>", "exit"],
+ ["<s-f1>", "unquote"],
+ ],
+ },
+ find: {
+ "": [
+ ["<escape> <enter>", "exit"],
+ ],
+ },
+ marks: {
+ "": [
+ ["<escape>", "exit"],
+ ],
+ },
+}
+// ** Apply
+Object.entries(mappings).forEach(([mode, data]) => {
+ Object.entries(data).forEach(([cat, data]) => {
+ data.forEach(([key, cmd]) => {
+ let pre = ""
+ if (cmd.startsWith("my/"))
+ cmd = cmd.slice(3), pre = "custom."
+ vimfx.set(`${pre}mode.${mode}.${cmd}`, key)
+ })
+ })
+})
+// * VimFX settings
+vimfx.set("scroll.last_find_mark", "<space>")
+vimfx.set("scroll.last_position_mark", "<space>")
+// * about:config preferences
+const prefs = {
+// ** I know what I'm doing, thanks
+ "browser.aboutConfig.showWarning": false,
+// ** Basic anti-tracking
+ "privacy.donottrackheader.enabled": true,
+ "privacy.trackingprotection.enabled": true,
+ "privacy.trackingprotection.socialtracking.enabled": true,
+// ** Basic stuff
+ "browser.uidensity": 1,
+ "browser.search.suggest.enabled": false,
+ "general.smoothScroll": false,
+ "font.size.variable.x-western": 14,
+ "toolkit.legacyUserProfileCustomizations.stylesheets": true,
+ "browser.backspace_action": 1,
+ "browser.download.useDownloadDir": false,
+ "network.dns.disablePrefetch": true,
+ "network.predictor.enabled": false,
+ "network.security.ports.banned.override": "10080",
+ "browser.ctrlTab.sortByRecentlyUsed": true,
+ "browser.download.dir": "/home/simon/tmp/dl",
+ "browser.startup.page": 3, // enables session restore
+ "ui.key.menuAccessKeyFocuses": false,
+ "browser.tabs.insertAfterCurrent": true,
+ // ** Whee! Autoscrolling!
+ "general.autoScroll": true,
+ "middlemouse.paste": false,
+ // ** I like my scrolling the old-fashioned way
+ "general.smoothScroll": false,
+ "general.smoothScroll.lines": false,
+ "general.smoothScroll.other": false,
+ "general.smoothScroll.pages": false,
+ "general.smoothScroll.pixels": false,
+ // ** Devtools
+ "devtools.three-pane-enabled": false,
+ // ** Printing setttings
+ "print.print_headerleft": "",
+ "print.print_headerright": "",
+ "print.print_footerleft": "",
+ "print.print_footerright": "",
+ "print.print_margin_top": 0,
+ "print.print_margin_bottom": 0,
+ "print.print_margin_left": 0,
+ "print.print_margin_right": 0,
+ // ** Keep things in the current tab, please
+ "browser.link.open_newwindow": 1,
+ "browser.link.open_newwindow.restriction": 0, // use above setting
+ "browser.link.open_newwindow.override.external": 3, // external links in new tab
+ // ** Prefer Japanese to Chinese and Korean
+ "font.cjk_pref_fallback_order": "ja,zh-cn,zh-hk,zh-tw,ko",
+ // ** Let me debug my own browser
+ "devtools.chrome.enabled": true,
+ "devtools.debugger.remote-enabled": true,
+ // ** Make VimFx able to interact with iframes
+ "fission.webContentIsolationStrategy": 0,
+ // ** We use PassFF now
+ "signon.rememberSignons": false,
+ "signon.autofillForms": false,
+ "extensions.formautofill.addresses.enabled": false,
+ "extensions.formautofill.creditCards.enabled": false,
+ "signon.formlessCapture.enabled": false,
+ // ** No thanks, I don't care
+ "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons": false,
+ "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features": false,
+ "browser.newtabpage.activity-stream.default.sites": "",
+ "browser.newtabpage.activity-stream.feeds.section.highlights": false,
+ "browser.newtabpage.activity-stream.feeds.section.snippets": false,
+ "browser.newtabpage.activity-stream.feeds.section.topsites": false,
+ "browser.newtabpage.activity-stream.feeds.section.topstories": false,
+ "browser.newtabpage.activity-stream.feeds.snippets": false,
+ "browser.newtabpage.activity-stream.feeds.topsites": false,
+ "browser.newtabpage.activity-stream.feeds.weatherfeed": false,
+ "browser.newtabpage.activity-stream.showSponsored": false,
+ "browser.newtabpage.activity-stream.showSponsoredTopSites": false,
+ "browser.newtabpage.activity-stream.showWeather": false,
+ "browser.preferences.moreFromMozilla": false,
+ "browser.shopping.experience2023.active": false,
+ "browser.shopping.experience2023.enabled": false,
+ "browser.shopping.experience2023.optedIn": 0,
+ "browser.topsites.useRemoteSetting": false,
+ "browser.urlbar.suggest.quicksuggest.sponsored": false,
+ "browser.urlbar.suggest.quicksuggest.nonsponsored": false,
+ "extensions.getAddons.showPane": false,
+ "extensions.htmlaboutaddons.recommendations.enabled": false,
+ "identity.fxaccounts.enabled": false,
+ "identity.fxaccounts.toolbar.pxiToolbarEnabled": false,
+ "lightweightThemes.getMoreURL": "",
+ "sidebar.main.tools": "history",
+ "webchannel.allowObject.urlWhitelist": "",
+ // ** Disable telemetry
+ "toolkit.telemetry.unified": false,
+ "toolkit.telemetry.enabled": false,
+ "toolkit.telemetry.server": "data:,",
+ "toolkit.telemetry.archive.enabled": false,
+ "toolkit.telemetry.newProfilePing.enabled": false,
+ "toolkit.telemetry.updatePing.enabled": false,
+ "toolkit.telemetry.firstShutdownPing.enabled": false,
+ "toolkit.telemetry.shutdownPingSender.enabled": false,
+ "toolkit.telemetry.bhrPing.enabled": false,
+ "toolkit.telemetry.cachedClientID": "",
+ "toolkit.telemetry.previousBuildID": "",
+ "toolkit.telemetry.server_owner": "",
+ "toolkit.coverage.opt-out": true,
+ "toolkit.telemetry.coverage.opt-out": true,
+ "toolkit.coverage.enabled": false,
+ "toolkit.coverage.endpoint.base": "",
+ "toolkit.crashreporter.infoURL": "",
+ "datareporting.healthreport.uploadEnabled": false,
+ "datareporting.policy.dataSubmissionEnabled": false,
+ "security.protectionspopup.recordEventTelemetry": false,
+ "browser.ping-centre.telemetry": false,
+ "app.normandy.enabled": false,
+ "app.normandy.api_url": "",
+ "app.shield.optoutstudies.enabled": false,
+ "browser.discovery.enabled": false,
+ "browser.tabs.crashReporting.sendReport": false,
+ "breakpad.reportURL": "",
+ "network.connectivity-service.enabled": false,
+ "network.captive-portal-service.enabled": false,
+ "captivedetect.canonicalURL": "",
+ "dom.private-attribution.submission.enabled": false,
+ "app.update.service.enabled": false,
+ "app.update.background.scheduling.enabled": false,
+ "default-browser-agent.enabled": false,
+ "network.protocol-handler.external.ms-windows-store": false,
+ "toolkit.winRegisterApplicationRestart": false,
+ "app.update.auto": false,
+ // ** Enable "experimental" features
+ "dom.dialog_element.enabled": true,
+ "layout.css.has-selector.enabled": true,
+ // ** Configure Tab Groups
+ "extensions.tabgroups.displayMode": "grid",
+ "extensions.tabgroups.stackTabs": false,
+ "extensions.tabgroups.showThumbs": false,
+ "extensions.tabgroups.sortGroupsByName": true,
+ "extensions.tabgroups.gridDynamicSize": false,
+ "extensions.tabgroups.nextGroupAccel": false,
+ "extensions.tabgroups.prevGroupAccel": false,
+ "extensions.tabgroups.tabViewAccel": false,
+}
+// ** Apply
+Preferences.set(prefs)
+// * Addons
+let addons = {
+ "uBlock Origin": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi",
+ "Music Score Downloader": "https://addons.mozilla.org/firefox/downloads/latest/music-score-downloader/latest.xpi",
+ "PassFF": "https://addons.mozilla.org/firefox/downloads/latest/passff/latest.xpi",
+ "Tab Groups": "https://github.com/117649/Tab-Groups/releases/download/dev-build/Tab-Groups-2.1.4b1_r20250208.1821.xpi",
+ "Chrome Mask": "https://addons.mozilla.org/firefox/downloads/latest/chrome-mask/latest.xpi",
+}
+// ** Apply
+function installAddonURL(url) {
+ AddonManager.getInstallForURL(url)
+ .then(aI => aI.install())
+}
+
+function installAddonFile(file) {
+ let nsIFile = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile)
+ nsIFile.initWithPath(file)
+ AddonManager.getInstallForFile(nsIFile)
+ .then(aI => aI.install())
+}
+
+function addonInstalledP(name) {
+ return AddonManager.getAllAddons()
+ .then(aa => (aa.map(a => a.name).includes(name)))
+}
+
+for (let [name, url] of Object.entries(addons)) {
+ addonInstalledP(name)
+ .then(installedp => {
+ if (!installedp)
+ installAddonURL(url)
+ })
+}
+// ** TabFS
+(async function() {
+ if (Services.appinfo.OS != "Linux") return
+ if (await addonInstalledP("TabFS")) return
+
+ let tabFSDirectory = PathUtils.join(vimFXDirectory, "tabfs")
+ const sh = c => execSync("sh", "-c", c)
+
+ // compile native messenger
+ sh(`cd ${tabFSDirectory} && make -C fs`)
+ if (!FileUtils.File(PathUtils.join(tabFSDirectory, "fs", "tabfs")).exists())
+ throw new Error("TabFS compilation failed, investigate manually")
+
+ // install native messenger
+ sh(`cd ${tabFSDirectory} && ./install.sh firefox`)
+
+ // install addon
+ sh(`cd ${tabFSDirectory}/extension && zip TabFS.xpi manifest.json background.js -r vendor`)
+ installAddonFile(PathUtils.join(tabFSDirectory, "extension", "TabFS.xpi"))
+})();
+// * UserChrome & UserContent
+async function ensureUserFile(name) {
+ let chromeDir = PathUtils.join(PathUtils.profileDir, "chrome")
+ let userFileProfile = PathUtils.join(chromeDir, name)
+ let userFileVimFX = PathUtils.join(vimFXDirectory, name)
+ execSync("mkdir", chromeDir) // TODO: Maybe use XPCOM APIs?
+ if (!FileUtils.File(userFileProfile).exists())
+ exec("ln", "-s", userFileVimFX, userFileProfile)
+}
+ensureUserFile("userChrome.css")
+ensureUserFile("userContent.css")
+// * Redirects
+let redirects = {
+ // can't have regex literals in object, so we use new RegExp later
+ "^(.+?)://(|www\\.)reddit.com(/(?!media).*)?$": "$1://old.reddit.com$3",
+ "^(.+?)://(.+\\.)?youtube\\.com(/.*)?$/": "$1://inv.nadeko.net$3",
+ "^(.+?)://(.+\\.)?youtu\\.be(/.*)?$": "$1://inv.nadeko.net$3",
+ "^(.+?)://(twitter|x)\\.com(/.*)?$": "$1://nitter.poast.org$3",
+ "^(.+?)://genius\\.com(/.*)?$": "$1://dm.vern.cc$2",
+ "^(.+?)://(.+\\.)?medium\\.com(/.*)?$": "$1://$2scribe.rip$3",
+ "^(.+?)://imdb.com(/.*)?$": "$1://librembd.lunar.icu$2",
+ "^(.+?)://(bsky.app/.*)$": "$1://skyview.social?url=$2",
+ "^(.+?)://(i\\.)?imgur\\.com(/.*)?$": "$1://rimgo.catsarch.com$3",
+ "^(.+?)://(.+)\\.fandom.com(/.*)?$": "$1://antifandom.com/$2$3",
+}
+// ** Implementation
+function redirect(url) {
+ for (let [pat, rep] of Object.entries(redirects)) {
+ pat = new RegExp(pat)
+ if (pat.test(url))
+ return url.replace(pat, rep)
+ }
+}
+
+vimfx.on("locationChange", ({vim, location: oldURL}) => {
+ let newURL = redirect(oldURL.toString())
+ if (newURL)
+ vimfx.send(vim, "location.replace", newURL)
+})
+// * Search engines
+let searchEngines = {
+ "Anna's Archive": {key: "aa", url: "https://annas-archive.org/search?q=%s"},
+ "Arch Wiki": {key: "aw", url: "https://wiki.archlinux.org/index.php?search=%s"},
+ "Brave Search": {key: "b", url: "https://search.brave.com/search?q=%s"},
+ "DDG": {key: "d", url: "https://duckduckgo.com/?q=%s"},
+ "Debian Bugs": {key: "dbg", url: "https://bugs.debian.org/%s"},
+ "Debian Packages": {key: "p", url: "https://packages.debian.org/%s"},
+ "Debian Wiki": {key: "dw", url: "https://wiki.debian.org/FrontPage?action=fullsearch&titlesearch=0&value=%s"},
+ "Gentoo Packages": {key: "gp", url: "https://packages.gentoo.org/packages/search?q=%s"},
+ "Gentoo Portage Overlays": {key: "gpo", url: "https://gpo.zugaina.org/Search?search=%s"},
+ "Gentoo Wiki": {key: "gw", url: "https://wiki.gentoo.org/index.php?title=Special:Search&search=%s"},
+ "IMSLP": {key: "imslp", url: "https://search.brave.com/search?q=%s+site%3Aimslp.org"},
+ "Internet Archive": {key: "arch", url: "https://archive.org/search.php?query=%s"},
+ "Jisho": {key: "ji", url: "https://jisho.org/search/%s"},
+ "Kbin": {key: "m", url: "https://fedia.io/m/%s"},
+ "MDN": {key: "mdn", url: "https://developer.mozilla.org/en-US/search?q=%s"},
+ "MusicBrainz": {key: "mbz", url: "https://musicbrainz.org/search?type=release&method=indexed&query=%s"},
+ "Marginalia": {key: "mg", url: "https://search.marginalia.nu/search?query=%s"},
+ "Nix Packages": {key: "np", url: "https://search.nixos.org/packages?query=%s"},
+ "Wiby": {key: "wb", url: "https://wiby.me/?q=%s"},
+ "Wikipedia": {key: "w", url: "https://wikipedia.org/w/index.php?search=%s"},
+ "Wiktionary (Japanese)": {key: "wkj", url: "https://wiktionary.org/w/index.php?search=%s#Japanese"},
+ "Wiktionary (Latin)": {key: "wkl", url: "https://wiktionary.org/w/index.php?search=%s#Latin"},
+ "Wiktionary": {key: "wk", url: "https://wiktionary.org/w/index.php?search=%s"},
+ "Wolfram|Alpha": {key: "wa", url: "https://www.wolframalpha.com/input?i=%s"},
+ "X Files": {key: "x", url: "http://zoar.cx/~andrea/x/%s"},
+}
+let defaultEngine = "Brave Search"
+// ** Set
+const getEngine = name => Services.search.getEngineByName(name)
+const removeEngine = name =>
+ Services.search.removeEngine(getEngine(name))
+const addEngine = async (name, {key, url}) =>
+ Services.search.addUserEngine(name, url.replace("%s", "{searchTerms}"), key)
+
+async function resetEngine(name, opts) {
+ if (getEngine(name)) await removeEngine(name)
+ return addEngine(name, opts)
+}
+
+(async function() {
+ for (let [name, opts] of Object.entries(searchEngines))
+ getEngine(name) || await addEngine(name, opts)
+
+ let oldDef = Services.search.defaultEngine
+ Services.search.defaultEngine = getEngine(defaultEngine)
+ if (!searchEngines[oldDef.name])
+ Services.search.removeEngine(oldDef)
+
+ for (let s of await Services.search.getEngines())
+ if (searchEngines[s._name])
+ await resetEngine(s._name, searchEngines[s._name])
+ else
+ await removeEngine(s._name)
+})()
diff --git a/common/.vimfx/frame.js b/common/.vimfx/frame.js
new file mode 100644
index 0000000..d06bd2b
--- /dev/null
+++ b/common/.vimfx/frame.js
@@ -0,0 +1,24 @@
+vimfx.listen("goWayBack", async (_, msg) => {
+ try {
+ let eUrl = encodeURIComponent(content.location)
+ let aUrl = `https://archive.org/wayback/available?url=${eUrl}&closest=either&status_code=200`
+ let res = await content.fetch(aUrl)
+ if (!res.ok) return msg("Network error; failed to time travel")
+ let json = await res.json()
+ let nUrl = json?.archived_snapshots?.closest?.url
+ if (nUrl)
+ content.location = nUrl
+ else
+ msg("No URLs to travel to")
+ } catch { msg("Failed to time travel") }
+})
+
+vimfx.listen("goWayForward", (_, msg) => {
+ content.location.href =
+ content.location.href
+ .replace(new RegExp("^.+://web.archive.org/web/[0-9*]+/"), "")
+})
+
+vimfx.listen("location.replace", (url, msg) => {
+ content.location.replace(url)
+})
diff --git a/common/.vimfx/tabfs b/common/.vimfx/tabfs
new file mode 120000
index 0000000..9580445
--- /dev/null
+++ b/common/.vimfx/tabfs
@@ -0,0 +1 @@
+../../modules/tabfs \ No newline at end of file
diff --git a/common/.vimfx/userChrome.css b/common/.vimfx/userChrome.css
new file mode 100644
index 0000000..3a1f71f
--- /dev/null
+++ b/common/.vimfx/userChrome.css
@@ -0,0 +1,64 @@
+/* -*- eval: (outline-minor-mode 1) -*- */
+/* * Debloat navbar */
+#back-button,
+#forward-button,
+#reload-button,
+#stop-button,
+#home-button,
+#library-button,
+#fxa-toolbar-menu-button,
+#PanelUI-button,
+#save-to-pocket-button,
+#sidebar-button
+{ display: none }
+
+/* Empty space before and after the url bar */
+#customizableui-special-spring1,
+#customizableui-special-spring2
+{ display: none }
+/* * De-bloat urlbar */
+#identity-box,
+#reader-mode-button,
+#star-button-box,
+#urlbar-go-button,
+.urlBarView-button,
+.search-one-offs
+{ display: none !important; }
+
+#tracking-protection-icon-container {
+ margin: 0 0.3em;
+}
+/* * Slim down tabs bar */
+#TabsToolbar,
+#TabsToolbar * {
+ max-height: 20px !important;
+ min-height: 20px !important;
+ border-radius: 0 !important;
+}
+
+#TabsToolbar img, #TabsToolbar image {
+ max-height: initial !important;
+ min-height: initial !important;
+}
+
+#new-tab-button, #firefox-view-button
+{ display: none }
+
+#alltabs-button {
+ min-width: 21px !important;
+ max-width: 21px !important;
+ min-height: 12px !important;
+ max-height: 12px !important;
+}
+
+#TabsToolbar .tab-background { margin-block: 0 !important }
+/* * Remove sidebar header */
+#sidebar-header { display: none }
+/* * Always hide bookmark toolbar */
+#PersonalToolbar { display: none }
+/* * Findbar */
+findbar { padding-block: 0 !important }
+/* * Monospace fonts */
+findbar, #statuspanel, #TabsToolbar, #nav-bar {
+ font-family: monospace;
+}
diff --git a/common/.vimfx/userContent.css b/common/.vimfx/userContent.css
new file mode 100644
index 0000000..a230e0b
--- /dev/null
+++ b/common/.vimfx/userContent.css
@@ -0,0 +1,47 @@
+@-moz-document domain("wikipedia.org"),
+ domain("wiktionary.org"),
+ domain("wikiquote.org") {
+ p, li, dl {
+ text-align: justify;
+ line-height: initial;
+ }
+ h1, h2 {
+ font-family: serif !important;
+ }
+}
+
+@-moz-document domain("gentoo.org") {
+ body > * {
+ font-family: sans-serif;
+ }
+ h1, h2, h3, h4, h5, h6 {
+ font-family: serif !important;
+ }
+ h1 small, h2 small, h3 small, h4 small, h5 small, h6 small {
+ font-family: sans-serif !important;
+ }
+}
+
+@-moz-document domain("khanacademy.org") {
+ ._2qe0as { padding: 0.5em !important }
+ ._lqicet { margin-bottom: 0.5em !important }
+ ._oziornq { padding: 0 !important }
+ ._13ise4e h3 { display: none }
+}
+
+@-moz-document domain("snikket.org") {
+ p { font-size: 1rem !important }
+ h1, h2 { font-family: initial !important }
+ body {
+ font-family: initial !important;
+ line-height: initial !important;
+ }
+}
+
+@-moz-document domain("laparola.net") {
+ [lang=el] { font-family: inherit !important }
+
+ div.p { line-height: initial !important }
+
+ div.p span.v { padding: 0 !important }
+}