Last active
November 3, 2025 12:11
-
-
Save cemiu/3a0847a2783b1898fc5815db772bf84f to your computer and use it in GitHub Desktop.
surfingkeys config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // permalink: | |
| // https://gist.github.com/cemiu/3a0847a2783b1898fc5815db772bf84f/raw/surfingkeys_config.js | |
| // /* | |
| api.unmapAllExcept([ | |
| 'i', 'gi', // text box | |
| 'f', 'gf', // click link (active tab; non-active tab) | |
| 'e', 'd', 'j', 'k', // navigate page | |
| '/', 'n', 'N', 'T', | |
| ';e', | |
| // '?', | |
| ], null); | |
| // */ | |
| api.RUNTIME('updateSettings', {settings: {"noPdfViewer": 1}}); | |
| settings.digitForRepeat = false; | |
| settings.hintShiftNonActive = true; | |
| // Disable Surfingkeys (will keep =a / =c enabled) // | |
| api.unmapAllExcept([], /excalidraw.com/); | |
| api.unmapAllExcept([], /outlook.office.com/); | |
| api.unmapAllExcept([], /docs.google.com/); | |
| api.unmapAllExcept([], /mail.notion.so/); | |
| api.unmapAllExcept([], /maps.google.com/); | |
| api.unmapAllExcept([], /sharepoint.com/); | |
| api.unmapAllExcept([], /jup:/); | |
| api.unmapAllExcept([], /cemiu.net/); | |
| api.unmapAllExcept([], /vim-hero.com/); | |
| api.unmap("f", /youtube.com/); | |
| ///////////////////////////// | |
| // MORE OPTIONS (specific) // | |
| ///////////////////////////// | |
| // util functions (bastardised version of https://github.com/b0o/surfingkeys-conf ) | |
| const [util, actions] = [{}, new Proxy({}, { get: (o, k) => o[k] || (o[k] = {}) })]; | |
| function registerMaps(maps, domain = null) { maps.forEach(({key, desc, action}) => { const options = domain ? {domain} : {}; api.mapkey(key, desc, action, options); }); } | |
| util.defaultSelector = "a[href]:not([href^=javascript])"; | |
| util.createHints = (selector = util.defaultSelector, action = api.Hints.dispatchMouseClick, attrs = {}) => new Promise(resolve => { api.Hints.create(selector, (...args) => { resolve(...args); if (typeof action === "function") action(...args); }, attrs); }); | |
| util.isRectVisibleInViewport = rect => rect.height > 0 && rect.width > 0 && rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth); | |
| util.isElementInViewport = e => e.offsetHeight > 0 && e.offsetWidth > 0 && !e.getAttribute("disabled") && util.isRectVisibleInViewport(e.getBoundingClientRect()); | |
| util.doiSelector = "a[href*='doi.org/10.']"; | |
| actions.openAnchor = ({ newTab = false, active = true, prop = "href" } = {}) => (a) => actions.openLink(a[prop], { newTab, active }); | |
| actions.openLink = (url, { newTab = false, active = true } = {}) => { if (newTab) { api.RUNTIME("openLink", { tab: { tabbed: true, active }, url: url instanceof URL ? url.href : url, }); return; } window.location.assign(url) }; | |
| actions.getGoogleCacheUrl = ({ href = window.location.href } = {}) => `https://webcache.googleusercontent.com/search?q=cache:${href}`; | |
| // actions.getWaybackUrl = ({ href = window.location.href } = {}) => `https://web.archive.org/web/*/${href}`; | |
| actions.getArchiveTodayUrl = ({ href = window.location.href } = {}) => `https://archive.today/?run=1&url=${href.split("#")[0]}`; | |
| actions.openInScidb = (href) => { const id = href.replace(/^https?:\/\/(dx\.)?doi\.org\//i, ""); actions.openLink(`https://annas-archive.org/scidb/${id}/`, { newTab: true }); }; | |
| actions.pagination.clickElementWithText = (targetTexts) => { | |
| const candidates = Array.from(document.querySelectorAll('a, span')).filter(el => el.innerText && targetTexts.includes(el.innerText.trim()) && !el.hasAttribute('disabled')); | |
| const elToClick = candidates.find(el => util.isElementInViewport(el)) || candidates[0]; | |
| elToClick ? elToClick.click() : api.Front.showBanner(`No suitable element found for: ${targetTexts.join(' / ')}`); | |
| }; | |
| actions.pagination.clickPrevious = () => actions.pagination.clickElementWithText(["Previous"]); | |
| actions.pagination.clickNext = () => actions.pagination.clickElementWithText(["Next", "More"]); | |
| // adapted from https://github.com/t-mart/kill-sticky | |
| actions.toggleSticky=(()=>{const ID="__killStickyCSS__",FIX="data-nosticky",OFL="data-nooverflow";return()=>{const s=document.getElementById(ID);if(s){document.querySelectorAll(`[${FIX}],[${OFL}]`).forEach(e=>{e.removeAttribute(FIX);e.removeAttribute(OFL)});s.remove();return}const css=`[${FIX}]{position:static!important;top:auto!important;bottom:auto!important}[${OFL}],html[${OFL}]{overflow:visible!important;overflow-x:visible!important;overflow-y:visible!important}`,st=document.createElement("style");st.id=ID;st.textContent=css;document.documentElement.appendChild(st);document.querySelectorAll("body *").forEach(n=>{const c=getComputedStyle(n);(c.position==="fixed"||c.position==="sticky")&&n.setAttribute(FIX,"");(c.overflow==="hidden"||c.overflowX==="hidden"||c.overflowY==="hidden")&&n.setAttribute(OFL,"")});const ch=getComputedStyle(document.documentElement);(ch.overflow==="hidden"||ch.overflowX==="hidden"||ch.overflowY==="hidden")&&document.documentElement.setAttribute(OFL,"")}})(); | |
| /////// KEYMAPS START HERE //////// | |
| // All Pages // | |
| const generalMaps = [ | |
| {key: "=c", desc: "Show Google's cached version of page", action: () => actions.openLink(actions.getGoogleCacheUrl(), { newTab: true })}, | |
| {key: "=a", desc: "Archive.today", action: () => actions.openLink(actions.getArchiveTodayUrl(), { newTab: true })}, | |
| {key: "=s", desc: "Open first DOI in Anna's Archive", action: () => { const a = document.querySelector(util.doiSelector); a ? actions.openInScidb(a.href) : api.Front.showBanner("No DOI link found"); }}, | |
| // {key: "=s", desc: "Open selected DOI in Anna's Archive", action: () => util.createHints(util.doiSelector, a => actions.openInScidb(a.href))}, | |
| {key: "}", desc: "Click 'Next' or 'More' link/button", action: actions.pagination.clickNext}, | |
| {key: "{", desc: "Click 'Previous' link/button", action: actions.pagination.clickPrevious}, | |
| {key: "`", desc: "Toggle kill sticky", action: () => actions.toggleSticky()}, | |
| ]; | |
| registerMaps(generalMaps); | |
| // Google // | |
| const googleSearchResultSelector = "a:has(>h3),h3 a,a[href^='/search']:not(.fl):not(#pnnext,#pnprev):not([role]):not(.hide-focus-ring),g-scrolling-carousel a,.rc > div:nth-child(2) a,.kno-rdesc a,.kno-fv a,.isv-r > a:first-child,.dbsr > a:first-child,.X5OiLe,.WlydOe,.fl"; | |
| const googleMaps = [ | |
| { key: "a", desc: "Open search result", action: () => util.createHints(googleSearchResultSelector) }, | |
| { key: "A", desc: "Open search result (new tab)", action: () => util.createHints(googleSearchResultSelector, actions.openAnchor({ newTab: true, active: false })) }, | |
| // { key: "d", desc: "Open search in DuckDuckGo", action: actions.go.ddg } | |
| ]; | |
| registerMaps(googleMaps, /www.google.com/i); | |
| // Hacker News // | |
| actions.hn.goParent = () => { const par = document.querySelector(".navs>a[href^='item']"); if (!par) return; actions.openLink(par.href); }; | |
| actions.hn.collapseNextComment = () => { const vis = Array.from(document.querySelectorAll("a.togg")).filter(e => e.innerText === "[–]" && util.isElementInViewport(e)); if (vis.length > 0) vis[0].click(); }; | |
| actions.hn.goPage = (dist = 1) => { let u; try { u = new URL(window.location.href); } catch (e) { return; } let page = u.searchParams.get("p") || "1"; const cur = parseInt(page, 10); if (Number.isNaN(cur) || cur + dist < 1) return; u.searchParams.set("p", cur + dist); actions.openLink(u.href); }; | |
| actions.hn.openLinkAndComments = (e) => { const linkUrl = e.querySelector(".titleline>a").href; const commentsUrl = e.nextElementSibling.querySelector("a[href^='item']:not(.titlelink)").href; actions.openLink(commentsUrl, { newTab: true }); actions.openLink(linkUrl, { newTab: true }); }; | |
| const hnMaps = [ | |
| {key: "x", desc: "Collapse comment", action: () => util.createHints(".togg")}, | |
| {key: "X", desc: "Collapse next comment", action: actions.hn.collapseNextComment}, | |
| {key: "s", desc: "Upvote", action: () => util.createHints(".votearrow[title='upvote']")}, | |
| {key: "S", desc: "Downvote", action: () => util.createHints(".votearrow[title='downvote']")}, | |
| {key: "a", desc: "View post (link)", action: () => util.createHints(".titleline>a")}, | |
| {key: "A", desc: "View post (link and comments)", action: () => util.createHints(".athing", actions.hn.openLinkAndComments)}, | |
| {key: "c", desc: "View post (comments)", action: () => util.createHints(".subline>a[href^='item']")}, | |
| {key: "C", desc: "View post (comments) (non-active new tab)", action: () => util.createHints(".subline>a[href^='item']", actions.openAnchor({ newTab: true, active: false }))}, | |
| {key: "e", desc: "View external link", action: () => util.createHints("a[rel=nofollow]")}, | |
| {key: "gp", desc: "Go to parent", action: actions.hn.goParent}, | |
| {key: "]]", desc: "Next page", action: () => actions.hn.goPage(1)}, | |
| {key: "[[", desc: "Prev page", action: () => actions.hn.goPage(-1)}, | |
| ]; | |
| registerMaps(hnMaps, /news.ycombinator.com/i); | |
| // Wikipedia // | |
| actions.wp._switchLang = (langCode) => { | |
| if (document.documentElement.lang === langCode) return; | |
| const selector = `.vector-menu-content-list a.interlanguage-link-target[hreflang='${langCode}'], #p-lang ul li a[hreflang='${langCode}']`; | |
| const link = document.querySelector(selector); | |
| link ? actions.openLink(link.href) : api.Front.showBanner(`Wikipedia page in '${langCode}' not found.`); | |
| }; | |
| actions.wp.switchToDe = () => actions.wp._switchLang('de'); | |
| actions.wp.switchToEn = () => actions.wp._switchLang('en'); | |
| const wikipediaMaps = [ | |
| { key: "ld", desc: "Switch to German (de) Wikipedia page", action: actions.wp.switchToDe }, | |
| { key: "le", desc: "Switch to English (en) Wikipedia page", action: actions.wp.switchToEn } | |
| ]; | |
| registerMaps(wikipediaMaps, /.*\.wikipedia\.org/i); | |
| // Reddit // | |
| actions.re.collapseNextComment = () => { | |
| const vis = Array.from( | |
| document.querySelectorAll(".noncollapsed.comment") | |
| ).filter((e) => util.isElementInViewport(e)) | |
| if (vis.length > 0) { | |
| vis[0].querySelector(".expand").click() | |
| } | |
| } | |
| const redditMaps = [ | |
| {key: "x", desc: "Collapse comment", action: () => util.createHints(".expand")}, | |
| {key: "X", desc: "Collapse next comment", action: actions.re.collapseNextComment}, | |
| ]; | |
| registerMaps(redditMaps, /reddit\.com/i); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment