From cf221c7c4b4c3e5db3cce7526f81e533a4e80d00 Mon Sep 17 00:00:00 2001 From: js0ny Date: Fri, 31 Oct 2025 21:47:00 +0000 Subject: [PATCH] surfingkeys --- home/dot_config/nvim/lsp/ts_ls.lua | 7 + misc/browser/surfingkeys+colemak.js | 1040 +++++++++++++++++++++++++++ misc/browser/surfingkeys.js | 179 +---- nixcfgs/hosts/zephyrus/default.nix | 2 +- 4 files changed, 1082 insertions(+), 146 deletions(-) create mode 100644 home/dot_config/nvim/lsp/ts_ls.lua create mode 100644 misc/browser/surfingkeys+colemak.js diff --git a/home/dot_config/nvim/lsp/ts_ls.lua b/home/dot_config/nvim/lsp/ts_ls.lua new file mode 100644 index 0000000..a685811 --- /dev/null +++ b/home/dot_config/nvim/lsp/ts_ls.lua @@ -0,0 +1,7 @@ +---@type vim.lsp.Config +return { + cmd = { "typescript-language-server", "--stdio" }, + root_markers = { "tsconfig.json", "jsconfig.json", "package.json" }, + filetypes = { "javascript", "javascriptreact", "javascript.jsx", "typescript", "typescriptreact", "typescript.tsx" }, + settings = {}, +} diff --git a/misc/browser/surfingkeys+colemak.js b/misc/browser/surfingkeys+colemak.js new file mode 100644 index 0000000..411db84 --- /dev/null +++ b/misc/browser/surfingkeys+colemak.js @@ -0,0 +1,1040 @@ +// vim:foldmethod=marker:foldmarker=#region,#endregion:foldlevel=0 +// Paste this into surfingkeys advanced settings +// or use: +// Load settings from: https://raw.githubusercontent.com/js0ny/dotfiles/refs/heads/master/misc/browser/surfingkeys+colemak.js +// Browse to Extension > Surfingkeys > Allow access to file URLs to enable local file access +// Windows: file:///C:/Users/username/.dotfiles/tools/browser/surfingkeys.js +// Linux: file:///home/username/.dotfiles/tools/browser/surfingkeys.js +// macOS: file:///Users/username/.dotfiles/tools/browser/surfingkeys.js + +// #region Example +/** Examples + +// an example to create a new mapping `ctrl-y` +api.mapkey('', 'Show me the money', function() { + Front.showPopup('a well-known phrase uttered by characters in the 1996 film Jerry Maguire (Escape to close).'); +}); + +// an example to replace `T` with `gt`, click `Default mappings` to see how `T` works. +api.map('gt', 'T'); + + +// an example to remove mapkey `Ctrl-i` +api.unmap(''); + +*/ +// #endregion + +// #region Settings +settings.language = "zh-CN"; +settings.showModeStatus = false; +// #endregion + +// #region Helper +// import the API so that no need to use `api` prefix +const { + aceVimMap, + addVimMapKey, + mapkey, + imap, + imapkey, + getClickableElements, + vmapkey, + map, + unmap, + cmap, + addSearchAlias, + removeSearchAlias, + tabOpenLink, + readText, + Clipboard, + Front, + Hints, + Visual, + RUNTIME, +} = api; +// Keymap, reference https://github.com/texiwustion/colemak_config_for_surfingkeys/tree/main +const forward = { + add: function (key) { + // 转发即将被 unmap 的键 + return api.map(`for${key}`, key); + }, + cancel: function (key) { + // 删除转发生成的键 + api.unmap(`for${key}`); + api.unmap(key); + }, + use: function (key) { + return `for${key}`; + }, +}; + +const colemak = { + forward: function (key) { + // 转发即将被 unmap 的键 + api.map(key, `col${key}`); + api.unmap(`col${key}`); + }, + use: function (key) { + return `col${key}`; + }, + map: function (a, b) { + api.map(colemak.use(a), forward.use(b)); + }, +}; + +const vForward = { + add: function (key) { + // 转发即将被 unmap 的键 + return api.vmap(`vfor${key}`, key); + }, + cancel: function (key) { + // 删除转发生成的键 + api.vunmap(`vfor${key}`); + api.vunmap(key); + }, + use: function (key) { + return `vfor${key}`; + }, +}; + +const vColemak = { + forward: function (key) { + // 转发即将被 unmap 的键 + api.vmap(key, `vcol${key}`); + api.vunmap(`vcol${key}`); + }, + use: function (key) { + return `vcol${key}`; + }, + map: function (a, b) { + api.vmap(vColemak.use(a), vForward.use(b)); + }, +}; + +const forwardFactory = { + push: function (mapLists) { + // forward original keys + for (const key in mapLists) { + // `const` better than `let` + forward.add(mapLists[key]); + } + }, + map: function (mapLists) { + for (const key in mapLists) { + colemak.map(key, mapLists[key]); + } + }, + pull: function (mapLists) { + for (const key in mapLists) { + forward.cancel(mapLists[key]); + } + for (const key in mapLists) { + colemak.forward(key); + } + }, +}; +const vForwardFactory = { + push: function (mapLists) { + // forward original keys + for (const key in mapLists) { + vForward.add(mapLists[key]); + } + }, + map: function (mapLists) { + for (const key in mapLists) { + vColemak.map(key, mapLists[key]); + } + }, + pull: function (mapLists) { + for (const key in mapLists) { + vForward.cancel(mapLists[key]); + } + for (const key in mapLists) { + vColemak.forward(key); + } + }, +}; +// TODO: Add more search completion source (with json) +const parseSearchResponse = function (response) { + const res = JSON.parse(response.text); + return res.map((r) => r.phrase); +}; + +const _addSearchAlias = function ( + alias, + name, + searchUrl, + acUrl = "https://duckduckgo.com/ac/?q=", + searchPrefix = "s", + parseResponse = parseSearchResponse, +) { + api.addSearchAlias( + alias, + name, + searchUrl, + searchPrefix, + acUrl, + parseResponse, + ); +}; +// Shortcut for querySelector +const q = (selector) => document.querySelector(selector); +const qs = (selector) => document.querySelectorAll(selector); +// #endregion + +// #region Keymap +// Normal Mode Keymap +const mapLists = { + /// scroll page + // Arrow + n: "j", + e: "k", + i: "l", + // l <-> i + l: "gi", // Focus on first input box by default + L: "I", + // k <-> n + k: "n", + K: "N", + // j <-> e + j: "e", + // PrevTab < H - I > NextTab + H: "E", + I: "R", + // E,N -> Up/Down HalfPage + N: "d", + E: "e", + // F -> Open Link in New Tab + F: "af", + // oH -> Tab History + oH: "H", + // gh/gi -> Prev/Next History + gh: "S", + gi: "D", + gl: "i", // Use `gl` to search and focus on input box + // t -> Open Link in New Tab + t: "gf", + // 缩放 + zu: "zi", + zo: "ze", + zz: "zr", +}; +// Visual Mode Keymap +const vMapLists = { + n: "j", + N: "J", + e: "k", + E: "K", + i: "l", + I: "L", + j: "e", + J: "E", + k: "n", + K: "N", +}; + +forwardFactory.push(mapLists); +forwardFactory.map(mapLists); + +vForwardFactory.push(vMapLists); +vForwardFactory.map(vMapLists); + +// All other unmapped keys should be defined here +// TODO: Add more mouse click keymap +api.unmap("gi"); // conflict with `gi` in `mapLists` +api.unmap("C"); // Use `F` instead (Open Link in New Tab) +api.map("g/", "gU"); // Goto Root Domain +// TODO: Add SPC keymap as leader (maybe change `,` to `SPC`) +api.unmap(""); // Leader Key + +forwardFactory.pull(mapLists); +vForwardFactory.pull(vMapLists); + +api.map("gH", "g/"); +// #endregion + +// #region Omnibar NOTE: Dosn't work +// api.cmap("", ""); +// api.cmap("", ""); +// api.cmap("", ""); +// api.cmap("", ""); +// api.cmap("", ""); +// api.cmap("", ""); +// api.cmap("", ""); +// api.cmap("", ""); +// #endregion + +// #region Search Alias + +const removedSearchAlias = [ + "b", // Baidu + "d", // DuckDuckGo + "e", // Wikipedia + "g", // Google + "s", // StackOverflow + "w", // Bing + "y", // YouTube +]; + +removedSearchAlias.forEach((alias) => removeSearchAlias(alias)); + +const searchAliases = [ + ["a2", "AlternativeTo", "https://alternativeto.net/browse/search/?q="], + ["ap", "APT", "https://packages.ubuntu.com/search?keywords="], + ["au", "AUR", "https://aur.archlinux.org/packages?K="], + ["aw", "ArchWiki", "https://wiki.archlinux.org/index.php?search="], + ["bd", "Baidu", "https://www.baidu.com/s?wd="], + ["bi", "Bing", "https://www.bing.com/search?q="], + ["bl", "Bilibili", "https://search.bilibili.com/all?keyword="], + ["br", "HomeBrew", "https://duckduckgo.com/?q=!brew "], + ["cg", "ChatGPT", "https://chat.openai.com/?q="], + ["cr", "Chrome Web Store", "https://chrome.google.com/webstore/search/"], + ["dd", "DuckDuckGo", "https://duckduckgo.com/?q="], + ["de", "Thesaurus", "https://www.onelook.com/?w="], + ["eb", "ebay", "https://www.ebay.co.uk/sch/i.html?kw="], + ["fe", "Felo", "https://felo.ai/search?q="], + ["gh", "GitHub", "https://github.com/search?type=repositories&q="], + ["gg", "Google", "https://www.google.com/search?q="], + ["mc", "Metacritic", "https://www.metacritic.com/search/"], + ["nx", "NixPackages", "https://search.nixos.org/packages?query="], + ["ng", "NuGet", "https://www.nuget.org/packages?q="], + ["np", "npm", "https://www.npmjs.com/search?q="], + ["pa", "Pacman", "https://archlinux.org/packages/?q="], + ["pp", "Perplexity", "https://www.perplexity.ai/?q="], + ["py", "pypi", "https://pypi.org/search/?q="], + ["re", "Reddit", "https://www.reddit.com/search?q="], + ["sc", "Scoop", "https://scoop.sh/#/apps?q="], + ["se", "StackExchange", "https://stackexchange.com/search?q="], + ["so", "StackOverflow", "https://stackoverflow.com/search?q="], + ["st", "Steam", "https://store.steampowered.com/search/?term="], + ["tw", "X", "https://twitter.com/search?q="], + ["ud", "UrbanDictionary", "https://www.urbandictionary.com/define.php?term="], + ["wa", "WolframAlpha", "https://www.wolframalpha.com/input/?i="], + ["wg", "winget", "https://winget.ragerworks.com/search/all/"], + [ + "wk", + "Wikipedia", + "https://en.wikipedia.org/w/index.php?title=Special:Search&search=", + ], + [ + "ww", + "WantWords", + "https://www.shenyandayi.com/wantWordsResult?lang=zh&query=", + ], + ["yt", "YouTube", "https://www.youtube.com/results?search_query="], +]; + +api.unmap("on"); + +// Add all search aliases +searchAliases.forEach(([alias, name, url]) => { + _addSearchAlias(alias, name, url); +}); +// #endregion + +// #region Site-specific + +// This is a global keymap +mapkey("yY", "yank link without parameter", function () { + const url = new URL(window.location.href); + Clipboard.write(url.origin + url.pathname); +}); + +unmap("yma"); +unmap("ymc"); +unmap("ymv"); + +mapkey("ym", "yank link as markdown", function () { + const url = new URL(window.location.href); + const title = document.title; + Clipboard.write(`[${title}](${url.origin + url.pathname})`); +}); + +// #region bilibili.com +mapkey( + ",n", + "[n]ext Video", + function () { + window.location.href = q("div.next-play").querySelector("a").href; + }, + { domain: /bilibili.com/ }, +); +// #endregion + +// #region chatgpt.com +const chatgptNewChat = function () { + var btn = q( + "div.no-draggable:nth-child(3) > span:nth-child(1) > button:nth-child(1)", + ); + btn.click(); +}; +const chatgptStartStop = function () { + var btn = q("button.h-8:nth-child(2)"); + btn.click(); +}; +mapkey(",n", "New Chat", chatgptNewChat, { domain: /chatgpt.com/ }); +mapkey(",s", "Start/Stop Generating", chatgptStartStop, { + domain: /chatgpt.com/, +}); +// #endregion + +// #region chat.deepseek.com +mapkey( + ",s", + "Toggle Sidebar", + function () { + var btn = qs("div.ds-icon-button"); + btn[0].click(); + }, + { domain: /chat.deepseek.com/ }, +); +mapkey( + ",e", + "[e]dit last input", + function () { + var btn = qs("div.ds-icon-button"); + btn[btn.length - 5].click(); + }, + { domain: /chat.deepseek.com/ }, +); +mapkey( + ",y", + "[y]ank last oupput", + function () { + var btn = qs("div.ds-icon-button"); + btn[btn.length - 4].click(); + }, + { domain: /chat.deepseek.com/ }, +); +mapkey( + ",r", + "[r]egenerate last output", + function () { + var btn = qs("div.ds-icon-button"); + btn[btn.length - 3].click(); + }, + { domain: /chat.deepseek.com/ }, +); +mapkey( + ",n", + "[n]ew Chat", + function () { + window.location.href = "https://chat.deepseek.com/"; + }, + { domain: /chat.deepseek.com/ }, +); +mapkey( + ",t", + "Toggle co[t](R1)", + function () { + var btns = qs("div.ds-button"); + btns[0].click(); + }, + { domain: /chat.deepseek.com/ }, +); +mapkey( + ",w", + "Toggle [w]eb Search", + function () { + var btns = qs("div.ds-button"); + btns[1].click(); + }, + { domain: /chat.deepseek.com/ }, +); +// #endregion + +//#region dropbox.com +//https://www.dropbox.com/scl/fi/u58c2qmqbwq672y3hwmfn/setup.sh?rlkey=d3figouv5eqk1xfwdtyzfr7ua&e=1&st=ehttmy2r&dl=0 +//https://dl.dropboxusercontent.com/scl/fi/u58c2qmqbwq672y3hwmfn/setup.sh?rlkey=d3figouv5eqk1xfwdtyzfr7ua&e=1&st=ehttmy2r +mapkey( + ",r", + "Extract [r]aw link", + function () { + const url = new URL(window.location.href); + if (url.href.endsWith("&dl=0")) { + url.searchParams.delete("dl"); + url.hostname = "dl.dropboxusercontent.com"; + Clipboard.write(url.href); + } + }, + { domain: /dropbox.com/ }, +); +mapkey( + ",d", + "Extract [d]ownload link", + function () { + const url = new URL(window.location.href); + if (url.href.endsWith("&dl=0")) { + url.searchParams.set("dl", "1"); + Clipboard.write(url.href); + } + }, + { domain: /dropbox.com/ }, +); +//#endregion + +// #region app.follow.is +mapkey( + ",t", + "Toggle ", + function () { + var btn = qs("button.no-drag-region"); + btn[btn.length - 4].click(); + }, + { domain: /app.follow.is/ }, +); + +mapkey( + ",a", + "Toggle AI Summary", + function () { + var btn = qs("button.no-drag-region"); + btn[btn.length - 3].click(); + }, + { domain: /app.follow.is/ }, +); + +mapkey( + ",o", + "Toggle Original Website", + function () { + var btn = qs("button.no-drag-region"); + btn[btn.length - 4].click(); + }, + { domain: /app.follow.is/ }, +); +// #endregion + +// #region GitHub +// utils +const gh = {}; +gh.repoLink = (owner, repo) => `https://github.com/${owner}/${repo}`; +gh.pageLink = (owner, repo) => `https://${owner}.github.io/${repo}/`; +gh.sourceLink = (owner, repo, path) => + `${gh.repoLink(owner, repo)}/tree/${path}`; +gh.rawToSource = (url) => { + const ps = url.split("/").slice(3); + return gh.sourceLink(ps[0], ps[1], ps.slice(4).join("/")); +}; +// github.com +mapkey( + ",e", + "Use Web Editor", + function () { + const url = new URL(window.location.href); + url.hostname = "github.dev"; + window.location.href = url.href; + }, + { domain: /github.com/ }, +); +mapkey( + ",E", + "Use Web Editor (New Page)", + function () { + const url = new URL(window.location.href); + url.hostname = "github.dev"; + tabOpenLink(url.href); + }, + { domain: /github.com/ }, +); +mapkey( + ",p", + "Switch to GitHub Page", + function () { + href = window.location.href; + owner = href.split("/")[3]; + repo = href.split("/")[4]; + window.location.href = gh.pageLink(owner, repo); + }, + { domain: /github.com/ }, +); +/// This might be useful for Vim plugins +mapkey( + ",y", + "[y]ank short refeference owner/repo", + function () { + const href = window.location.href; + owner = href.split("/")[3]; + repo = href.split("/")[4]; + Clipboard.write(`${owner}/${repo}`); + }, + { domain: /github.com/ }, +); +// github.dev +mapkey( + ",r", + "Switch to GitHub Repo", + function () { + const url = new URL(window.location.href); + url.hostname = "github.com"; + window.location.href = url.href; + }, + { domain: /github.dev/ }, +); +// github.io +mapkey( + ",r", + "Switch to GitHub Repo", + function () { + const href = window.location.href; + owner = href.split("/")[2].split(".")[0]; + repo = href.split("/")[3]; + tabOpenLink(gh.repoLink(owner, repo)); + }, + { domain: /github.io/ }, +); +mapkey( + ",R", + "Go to GitHub Repo (New tab)", + function () { + const href = window.location.href; + owner = href.split("/")[2].split(".")[0]; + repo = href.split("/")[3]; + tabOpenLink(gh.repoLink(owner, repo)); + }, + { domain: /github.io/ }, +); +// raw.githubusercontent.com +mapkey( + ",r", + "Switch to GitHub Repo", + function () { + const url = new URL(window.location.href); + var owner, repo; + (owner, (repo = url.pathname.split("/").slice(1, 3))); + window.location.href = gh.repoLink(owner, repo); + }, + { domain: /raw.githubusercontent.com/ }, +); +mapkey( + ",R", + "Switch to GitHub Repo", + function () { + const url = new URL(window.location.href); + var owner, repo; + (owner, (repo = url.pathname.split("/").slice(1, 3))); + tabOpenLink(gh.repoLink(owner, repo)); + }, + { domain: /raw.githubusercontent.com/ }, +); +mapkey( + ",s", + "Open Source in GitHub", + function () { + window.location.href = gh.rawToSource(window.location.href); + }, + { domain: /raw.githubusercontent.com/ }, +); +mapkey( + ",S", + "Open Source in GitHub (New Page)", + function () { + tabOpenLink(gh.rawToSource(window.location.href)); + }, + { domain: /raw.githubusercontent.com/ }, +); +// #endregion GitHub + +//#region app.microsoft.com +// https://apps.microsoft.com/detail/9nl6kd1h33v3?hl=en-GB&gl=GB +// This is useful in `winget` (Windows Package Manager) +mapkey( + ",y", + "[y]ank app id", + function () { + const url = new URL(window.location.href); + const id = url.pathname.split("/")[2]; + Clipboard.write(id); + }, + { domain: /apps.microsoft.com/ }, +); +//#endregion + +// #region perplexity.ai +/** + * 0 - 网络 + * 1 - 学术 + * 2 - 社交 + */ +unmap("", /perplexity.ai/); // allows to use perplexity web keybindings +mapkey( + ",b", + "Add Perplexity [b]ookmark", + function () { + // button.border:nth-child(2) + q("div.sticky.left-0").querySelectorAll("button")[2].click(); + }, + { domain: /perplexity.ai/ }, +); +mapkey( + ",M", + "Toggle [M]odel switching", + function () { + q("div.rounded-md").querySelectorAll("span")[2].click(); + //setTimeout(() => { + // // Wait for the DOM to update + // qs("div.shadow-subtle div.group\\/item")[0].click(); + //}, 100); + }, + { domain: /perplexity.ai/ }, +); +mapkey( + ",m", + "Toggle default [m]odel (Claude 3.7 Sonnet)", + function () { + q("div.rounded-md").querySelectorAll("span")[1].click(); + setTimeout(() => { + // Wait for the DOM to update + qs("div.shadow-subtle div.group\\/item")[3].click(); + }, 100); + }, + { domain: /perplexity.ai/ }, +); +mapkey( + ",w", + "Toggle [w]riting/[w]eb Search", + function () { + q("div.rounded-md").querySelectorAll("span")[2].click(); + setTimeout(() => { + // Wait for the DOM to update + qs("div.shadow-subtle div.group\\/item")[0].click(); + }, 100); + }, + { domain: /perplexity.ai/ }, +); +mapkey( + ",s", + "[s]tart Generating", + function () { + var btns = qs("span.grow button"); + btns[btns.length - 1].click(); + }, + { domain: /perplexity.ai/ }, +); +mapkey( + ",y", + "[y]ank Last Output", + function () { + var toolbars = qs("div.mt-sm"); + var last = toolbars[toolbars.length - 1]; + var btns = last.querySelectorAll("button"); + btns[5].click(); + }, + { domain: /perplexity.ai/ }, +); +mapkey( + ",R", + "Change model to [R]egenerate last output", + function () { + var toolbars = qs("div.mt-sm"); + var last = toolbars[toolbars.length - 1]; + var btns = last.querySelectorAll("button"); + btns[1].click(); + }, + { domain: /perplexity.ai/ }, +); +mapkey( + ",r", + "Toggle [r]easoning", + function () { + q("div.rounded-md").querySelectorAll("span")[0].click(); + setTimeout(() => { + // Wait for the DOM to update + qs("div.shadow-subtle div.group\\/item")[2].click(); + }, 100); + }, + { domain: /perplexity.ai/ }, +); +// #endregion + +// #region sspai.com +unmap("[[", /sspai.com/); +unmap("]]", /sspai.com/); +unmap(",", /sspai.com/); +mapkey( + "[[", + "Previous Page", + function () { + q("button.btn-prev").click(); + }, + { domain: /sspai.com/ }, +); +mapkey( + "]]", + "Next Page", + function () { + q("button.btn-next").click(); + }, + { domain: /sspai.com/ }, +); + +// #endregion + +// #region pixiv.net +// Use site-specific paging method +unmap("[[", /pixiv.net/); +unmap("]]", /pixiv.net/); +unmap(",", /pixiv.net/); +const isArtwork = (url) => /pixiv.net\/artworks/.test(url.href); + +mapkey( + "[[", + "Previous Page", + function () { + const url = new URL(window.location.href); + if (url.href === url.origin) { + return; + } + const page = url.searchParams.get("p"); + const newPage = page ? parseInt(page) - 1 : 1; + url.searchParams.set("p", newPage); + window.location.href = url.href; + }, + { domain: /pixiv.net/ }, +); + +mapkey( + "]]", + "Next Page", + function () { + const url = new URL(window.location.href); + if (url.href === url.origin) { + return; + } + const page = url.searchParams.get("p"); + const newPage = page ? parseInt(page) + 1 : 2; + url.searchParams.set("p", newPage); + window.location.href = url.href; + }, + { domain: /pixiv.net/ }, +); +mapkey( + ",b", + "Add to [b]ookmark", + function () { + const url = new URL(window.location.href); + if (!isArtwork(url)) { + return; + } + const toolbar = q('section [class$="Toolbar"]'); + toolbar.querySelectorAll("div")[2].querySelector("button").click(); + }, + { domain: /pixiv.net/ }, +); +mapkey( + ",B", + "Add to private [B]ookmark", + function () { + const url = new URL(window.location.href); + if (!isArtwork(url)) { + return; + } + const toolbar = q('section [class$="Toolbar"]'); + toolbar.querySelectorAll("div")[0].querySelector("button").click(); + setTimeout(() => { + // Wait for the DOM to update + q("div[role=menu]").querySelector("li").click(); + }, 100); + }, + { domain: /pixiv.net/ }, +); +mapkey( + ",v", + "Up[v]ote Artwork", + function () { + const url = new URL(window.location.href); + if (!isArtwork(url)) { + return; + } + const toolbar = q('section [class$="Toolbar"]'); + toolbar.querySelectorAll("div")[3].querySelector("button").click(); + }, + { domain: /pixiv.net/ }, +); +mapkey( + ",f", + "Toggle [f]ollow the author", + function () { + const url = new URL(window.location.href); + if (!isArtwork(url)) { + return; + } + q("aside").querySelector("section").querySelector("button").click(); + }, + { domain: /pixiv.net/ }, +); +// #endregion + +// #region youtube.com +mapkey( + ",n", + "[n]ext Video", + function () { + window.location.href = q("ytd-compact-video-renderer").querySelector( + "a", + ).href; + }, + { domain: /youtube.com/ }, +); + +mapkey( + ",v", + "Up[v]ote Video", + function () { + qs("like-button-view-model")[0].querySelector("button").click(); + }, + { domain: /youtube.com/ }, +); + +mapkey( + ",V", + "Down[v]ote Video", + function () { + qs("dislike-button-view-model")[0].querySelector("button").click(); + }, + { domain: /youtube.com/ }, +); +// class="ytp-subtitles-button ytp-button" +mapkey( + ",c", + "toggle [c]aptions", + function () { + q("button.ytp-subtitles-button").click(); + }, + { domain: /youtube.com/ }, +); +// #endregion + +//#region zhihu.com +mapkey( + ",d", + "Toggle [d]ark mode", + function () { + const url = new URL(window.location.href); + if (url.searchParams.get("theme") === "dark") { + url.searchParams.set("theme", "light"); + } else { + url.searchParams.set("theme", "dark"); + } + window.location.href = url.href; + }, + { domain: /zhihu.com/ }, +); +//#endregion +// #endregion + +// #region ACE Editor +addVimMapKey( + // Navigation + { + keys: "k", + type: "motion", + motion: "findNext", + motionArgs: { forward: true, toJumplist: true }, + }, + { + keys: "K", + type: "motion", + motion: "findNext", + motionArgs: { forward: false, toJumplist: true }, + }, + // Word movement + { + keys: "j", + type: "motion", + motion: "moveByWords", + motionArgs: { forward: true, wordEnd: true, inclusive: true }, + }, + { + keys: "J", + type: "motion", + motion: "moveByWords", + motionArgs: { + forward: true, + wordEnd: true, + bigWord: true, + inclusive: true, + }, + }, + // Insert mode entries + { + keys: "l", + type: "action", + action: "enterInsertMode", + isEdit: true, + actionArgs: { insertAt: "inplace" }, + context: "normal", + }, + { + keys: "gl", + type: "action", + action: "enterInsertMode", + isEdit: true, + actionArgs: { insertAt: "lastEdit" }, + context: "normal", + }, + { + keys: "L", + type: "action", + action: "enterInsertMode", + isEdit: true, + actionArgs: { insertAt: "firstNonBlank" }, + context: "normal", + }, + { + keys: "gL", + type: "action", + action: "enterInsertMode", + isEdit: true, + actionArgs: { insertAt: "bol" }, + context: "normal", + }, + { + keys: "L", + type: "action", + action: "enterInsertMode", + isEdit: true, + actionArgs: { insertAt: "startOfSelectedArea" }, + context: "visual", + }, + { + keys: "n", + type: "motion", + motion: "moveByLines", + motionArgs: { forward: true, linewise: true }, + }, + { + keys: "e", + type: "motion", + motion: "moveByLines", + motionArgs: { forward: false, linewise: true }, + }, + { + keys: "i", + type: "motion", + motion: "moveByCharacters", + motionArgs: { forward: true }, + }, + { + keys: "H", + type: "keyToKey", + toKeys: "^", + }, + { + keys: "I", + type: "keyToKey", + toKeys: "$", + }, + { + keys: "Y", + type: "keyToKey", + toKeys: "y$", + }, +); + +// #endregion + +// #region Hints +api.Hints.setCharacters("qwfpgarstdcv"); // Left-hand keys +// #endregion diff --git a/misc/browser/surfingkeys.js b/misc/browser/surfingkeys.js index 4f72541..f1093fb 100644 --- a/misc/browser/surfingkeys.js +++ b/misc/browser/surfingkeys.js @@ -1,7 +1,7 @@ // vim:foldmethod=marker:foldmarker=#region,#endregion:foldlevel=0 // Paste this into surfingkeys advanced settings // or use: -// Load settings from: https://raw.githubusercontent.com/js0ny/dotfiles/refs/heads/master/tools/browser/surfingkeys.js +// Load settings from: https://raw.githubusercontent.com/js0ny/dotfiles/refs/heads/master/misc/browser/surfingkeys.js // Browse to Extension > Surfingkeys > Allow access to file URLs to enable local file access // Windows: file:///C:/Users/username/.dotfiles/tools/browser/surfingkeys.js // Linux: file:///home/username/.dotfiles/tools/browser/surfingkeys.js @@ -186,33 +186,22 @@ const qs = (selector) => document.querySelectorAll(selector); // #region Keymap // Normal Mode Keymap const mapLists = { - /// scroll page - // Arrow - n: "j", - e: "k", - i: "l", - // l <-> i - l: "gi", // Focus on first input box by default - L: "I", - // k <-> n - k: "n", - K: "N", - // j <-> e - j: "e", - // PrevTab < H - I > NextTab - H: "E", - I: "R", - // E,N -> Up/Down HalfPage - N: "d", - E: "e", + // Prev History < H - L > Next History + H: "S", + L: "D", + // J,K -> Up/Down HalfPage + // J: "d", + // K: "e", // F -> Open Link in New Tab F: "af", // oH -> Tab History oH: "H", - // gh/gi -> Prev/Next History - gh: "S", - gi: "D", - gl: "i", // Use `gl` to search and focus on input box + // gh/gl -> Prev/Next Tab + gh: "E", + gl: "R", + "": "E", + "": "R", + gi: "i", // Use `gl` to search and focus on input box // t -> Open Link in New Tab t: "gf", // 缩放 @@ -220,25 +209,12 @@ const mapLists = { zo: "ze", zz: "zr", }; -// Visual Mode Keymap -const vMapLists = { - n: "j", - N: "J", - e: "k", - E: "K", - i: "l", - I: "L", - j: "e", - J: "E", - k: "n", - K: "N", -}; forwardFactory.push(mapLists); forwardFactory.map(mapLists); -vForwardFactory.push(vMapLists); -vForwardFactory.map(vMapLists); +// vForwardFactory.push(vMapLists); +// vForwardFactory.map(vMapLists); // All other unmapped keys should be defined here // TODO: Add more mouse click keymap @@ -249,20 +225,22 @@ api.map("g/", "gU"); // Goto Root Domain api.unmap(""); // Leader Key forwardFactory.pull(mapLists); -vForwardFactory.pull(vMapLists); +// vForwardFactory.pull(vMapLists); api.map("gH", "g/"); +api.map("J", "d"); +api.map("K", "e"); // #endregion // #region Omnibar NOTE: Dosn't work -// api.cmap("", ""); -// api.cmap("", ""); -// api.cmap("", ""); -// api.cmap("", ""); -// api.cmap("", ""); -// api.cmap("", ""); -// api.cmap("", ""); -// api.cmap("", ""); +api.cmap("", ""); +api.cmap("", ""); +api.cmap("", ""); +api.cmap("", ""); +api.cmap("", ""); +api.cmap("", ""); +api.cmap("", ""); +api.cmap("", ""); // #endregion // #region Search Alias @@ -298,6 +276,7 @@ const searchAliases = [ ["gg", "Google", "https://www.google.com/search?q="], ["mc", "Metacritic", "https://www.metacritic.com/search/"], ["nx", "NixPackages", "https://search.nixos.org/packages?query="], + ["no", "NixOptions", "https://search.nixos.org/options?query="], ["ng", "NuGet", "https://www.nuget.org/packages?q="], ["np", "npm", "https://www.npmjs.com/search?q="], ["pa", "Pacman", "https://archlinux.org/packages/?q="], @@ -341,9 +320,9 @@ mapkey("yY", "yank link without parameter", function () { Clipboard.write(url.origin + url.pathname); }); -unmap("yma") -unmap("ymc") -unmap("ymv") +unmap("yma"); +unmap("ymc"); +unmap("ymv"); mapkey("ym", "yank link as markdown", function () { const url = new URL(window.location.href); @@ -602,7 +581,7 @@ mapkey( function () { const url = new URL(window.location.href); var owner, repo; - owner, (repo = url.pathname.split("/").slice(1, 3)); + (owner, (repo = url.pathname.split("/").slice(1, 3))); window.location.href = gh.repoLink(owner, repo); }, { domain: /raw.githubusercontent.com/ }, @@ -613,7 +592,7 @@ mapkey( function () { const url = new URL(window.location.href); var owner, repo; - owner, (repo = url.pathname.split("/").slice(1, 3)); + (owner, (repo = url.pathname.split("/").slice(1, 3))); tabOpenLink(gh.repoLink(owner, repo)); }, { domain: /raw.githubusercontent.com/ }, @@ -926,103 +905,13 @@ mapkey( // #region ACE Editor addVimMapKey( - // Navigation - { - keys: "k", - type: "motion", - motion: "findNext", - motionArgs: { forward: true, toJumplist: true }, - }, - { - keys: "K", - type: "motion", - motion: "findNext", - motionArgs: { forward: false, toJumplist: true }, - }, - // Word movement - { - keys: "j", - type: "motion", - motion: "moveByWords", - motionArgs: { forward: true, wordEnd: true, inclusive: true }, - }, - { - keys: "J", - type: "motion", - motion: "moveByWords", - motionArgs: { - forward: true, - wordEnd: true, - bigWord: true, - inclusive: true, - }, - }, - // Insert mode entries - { - keys: "l", - type: "action", - action: "enterInsertMode", - isEdit: true, - actionArgs: { insertAt: "inplace" }, - context: "normal", - }, - { - keys: "gl", - type: "action", - action: "enterInsertMode", - isEdit: true, - actionArgs: { insertAt: "lastEdit" }, - context: "normal", - }, - { - keys: "L", - type: "action", - action: "enterInsertMode", - isEdit: true, - actionArgs: { insertAt: "firstNonBlank" }, - context: "normal", - }, - { - keys: "gL", - type: "action", - action: "enterInsertMode", - isEdit: true, - actionArgs: { insertAt: "bol" }, - context: "normal", - }, - { - keys: "L", - type: "action", - action: "enterInsertMode", - isEdit: true, - actionArgs: { insertAt: "startOfSelectedArea" }, - context: "visual", - }, - { - keys: "n", - type: "motion", - motion: "moveByLines", - motionArgs: { forward: true, linewise: true }, - }, - { - keys: "e", - type: "motion", - motion: "moveByLines", - motionArgs: { forward: false, linewise: true }, - }, - { - keys: "i", - type: "motion", - motion: "moveByCharacters", - motionArgs: { forward: true }, - }, { keys: "H", type: "keyToKey", toKeys: "^", }, { - keys: "I", + keys: "L", type: "keyToKey", toKeys: "$", }, @@ -1036,5 +925,5 @@ addVimMapKey( // #endregion // #region Hints -api.Hints.setCharacters("qwfpgarstdcv"); // Left-hand keys +api.Hints.setCharacters("qwertasdfgzx"); // Left-hand keys // #endregion diff --git a/nixcfgs/hosts/zephyrus/default.nix b/nixcfgs/hosts/zephyrus/default.nix index 6b9fdb0..e0f6bbe 100644 --- a/nixcfgs/hosts/zephyrus/default.nix +++ b/nixcfgs/hosts/zephyrus/default.nix @@ -14,7 +14,7 @@ ../../modules/nixos/desktop/laptop.nix # hardware drivers - ../../modules/nixos/hardware/nvidia.nix + ../../modules/nixos/hardware/nvidia-disable.nix # udev rules ../../modules/nixos/hardware/udev/basys3.nix