Skip to content

Instantly share code, notes, and snippets.

@SARDONYX-sard
Last active February 21, 2024 00:00
Show Gist options
  • Save SARDONYX-sard/05e72c3cb4b55bc5b54c4f0d46046df3 to your computer and use it in GitHub Desktop.
Save SARDONYX-sard/05e72c3cb4b55bc5b54c4f0d46046df3 to your computer and use it in GitHub Desktop.
Surfingkeys Settings
/*
MIT License
Copyright (c) 2015 brookhong
- https://github.com/brookhong/Surfingkeys/blob/6d0ee1dfda4e5231920bda0a20996134d1e0ba29/docs/API.md
*/
declare namespace api {
/**
* Create a shortcut in normal mode to execute your own action.
* @param keys - The key sequence for the shortcut.
* @param annotation - A help message to describe the action, which will displayed in help opened by ?.
* @param jscode - A Javascript function to be bound. If the function needs an argument, next pressed key will be fed to the function.
* @param options - (optional, default null)
* - `domain: regex`, a Javascript regex pattern to identify the domains that this mapping works, for example, `/github\.com/i` says that this mapping works only for github.com,
* - `repeatIgnore`: boolean, whether this action can be repeated by dot command.
*/
export function mapkey(
keys: string,
annotation: string,
jscode: (nextPressKey: string) => void,
options?: {
domain?: RegExp;
repeatIgnore?: boolean;
}
): void;
/**
* Map a key sequence to another in normal mode.
* @param new_keystroke - A key sequence to replace
* @param old_keystroke - A key sequence to be replaced
* @param domain - A Javascript regex pattern to identify the domains that this mapping works. (optional, default null)
* @param new_annotation - Use it instead of the annotation from old_keystroke if provided. (optional, default null)
*/
export function map(
new_keystroke: string,
old_keystroke: string,
domain?: RegExp,
new_annotation?: string
): void;
/**
* Unmap a key sequence to another in normal mode.
* @param keystroke - A key sequence to be removed
* @param domain - A Javascript regex pattern to identify the domains that this mapping will be removed. (optional, default null)
*/
export function unmap(keystroke: string, domain?: RegExp): void;
/**
* Map a key sequence to another in insert mode.
* @param new_keystroke - A key sequence to replace
* @param old_keystroke - A key sequence to be replaced
* @param domain - A Javascript regex pattern to identify the domains that this mapping works. (optional, default null)
* @param new_annotation - Use it instead of the annotation from old_keystroke if provided. (optional, default null)
*/
export function imap(
new_keystroke: string,
old_keystroke: string,
domain?: RegExp,
new_annotation?: string
): void;
/**
* Unmap a key sequence to another in insert mode.
* @param keystroke - A key sequence to be removed
* @param domain - A Javascript regex pattern to identify the domains that this mapping will be removed. (optional, default null)
*/
export function iunmap(keystroke: string, domain?: RegExp): void;
/**
* Add a search engine alias into Omnibar.
* @param alias - The key to trigger this search engine, one or several chars, used as search alias, when you input the string and press space in omnibar, the search engine will be triggered.
* @param prompt - A caption to be placed in front of the omnibar.
* @param search_url - The URL of the search engine, for example, https://www.s.com/search.html?query=, if there are extra parameters for the search engine, you can use it as https://www.s.com/search.html?query={0}&type=cs or https://www.s.com/search.html?type=cs&query=(since order of URL parameters usually does not matter).
* @param search_leader_key - <search_leader_key><alias> in normal mode will search selected text with this search engine directly without opening the omnibar, for example sd. (optional, default s)
* @param suggestion_url - The URL to fetch suggestions in omnibar when this search engine is triggered. (optional, default null)
* @param callback_to_parser_suggestion - A function to parse the response from suggestion_url and return a list of strings as suggestions. Receives two arguments: response, the first argument, is an object containing a property text which holds the text of the response; and request, the second argument, is an object containing the properties query which is the text of the query and url which is the formatted URL for the request. (optional, default null)
* @param only_this_site_key - <search_leader_key><only_this_site_key><alias> in normal mode will search selected text within current site with this search engine directly without opening the omnibar, for example sod. (optional, default o)
* @param options - (optional, default null)
* - `favicon_url` URL for favicon for this search engine,
* - `skipMaps` if true disable creating key mappings for this search engine
*/
export function addSearchAlias(
alias: string,
prompt: string,
search_url: string,
search_leader_key?: string,
suggestion_url?: string,
callback_to_parser_suggestion?: Function,
only_this_site_key?: string,
options?: {
favicon_url?: string;
skipMaps?: boolean;
}
): void;
export class Clipboard {
/**
* Read from clipboard.
* @param onReady - A callback function to handle text read from clipboard.
*/
static read(onReady: (response: { data: string }) => void): void;
/**
* Write text to clipboard.
* @param text - the text to be written to clipboard.
*/
static write(text: string): void;
}
/**
* Call background action with args, the callback will be executed with response from background.
* @param action - A background action to be called.
* @param args - The parameters to be passed to the background action.
* @param callback - A function to be executed with the result from the background action.
*/
export function RUNTIME(action: string, args: Object, callback?: Function): void;
}
//@ts-check
// -- General
api.Hints.characters = "asdfghjklnmvbrtyu";
settings.focusAfterClosed = "last";
settings.focusFirstCandidate = false;
settings.hintAlign = "left";
settings.hintShiftNonActive = true;
settings.modeAfterYank = "Normal";
settings.omnibarPosition = "bottom";
settings.scrollStepSize = 120;
settings.nextLinkRegex = /((forward|>>|next|次[のへ]|→)+)/i;
settings.prevLinkRegex = /((back|<<|prev(ious)?|前[のへ]|←)+)/i;
settings.blacklistPattern = new RegExp(
`
localhost.*|
github.dev.*|
vscode.dev.*|
mail.google.com.*/
`.replace(/\s/gm, ""),
"i" // use case-insenitive mode
);
// -- Keymapping
// syntax: map('<To>', '<from>')
// --- normal ---
api.map("h", "S"); // prev page
api.map("l", "D"); // next page
api.map("H", "E"); // previous tab
api.map("L", "R"); // next tab
api.map("zz", "zr"); // zoom reset
api.map("d", "x"); // close current tab
api.map("u", "X"); // restore tab
api.map("o", "go"); // open a url in current tab
api.unmap("."); // For GitHub.dev
api.unmap("gn");
api.unmap("go");
// --- insert ---
api.imap("<Ctrl-[>", "<Esc>");
api.iunmap(":"); // disable emoji completion
api.iunmap("<Ctrl-f>");
api.iunmap("<Ctrl-u>");
api.iunmap("<Ctrl-i>");
api.addSearchAlias("a", "Amazon.co.jp", "https://www.amazon.co.jp/s?k={0}&emi=AN1VRQENFRJN5");
// --- qmark ---
// cf. https://gist.github.com/chroju/2118c2193fb9892d95b9686eb95189d2
const overlayedGlobalMarks = /** @type {const} */ ({
// web service
l: ["netlify", "https://netlify.com/"],
m: ["gmail", "https://mail.google.com/mail/u/0/"],
t: ["twitter", "https://twitter.com/"],
g: ["GitHub gist", "https://gist.github.com"],
s: ["scrapbox", "https://scrapbox.io/"],
// personal
c: ["new My gist", "https://gist.github.com/SARDONYX-sard/05e72c3cb4b55bc5b54c4f0d46046df3/edit"],
G: ["My repositories", "https://github.com/SARDONYX-sard"],
o: [
"Surfingkeys Settings",
"chrome-extension://gfbliohnnapiefjpjlpjnehglfpaknnc/pages/options.html",
],
});
/**
* @typedef {{
* url?: string;
* scrollLeft?: number;
* scrollTop?: number;
* tab :{tabbed: boolean?, active: boolean?}
* }} KeymapOption
*/
/**
* @param {string} mapPrefix
* @param {[keyof typeof overlayedGlobalMarks, overlayedGlobalMarks[keyof typeof overlayedGlobalMarks]]} keymap
* @param {string} explain
* @param {KeymapOption?} options
*/
const createJumpLinkKeymap = (mapPrefix, keymap, explain, options) => {
const [keyName, [urlName, url]] = keymap;
const mergedOpts = /** @type {const} */ ({
...{
url,
scrollLeft: 0,
scrollTop: 0,
},
...options,
});
api.mapkey(`${mapPrefix}${keyName}`, `#10Open ${urlName} ${explain}`, () =>
api.RUNTIME(`openLink`, mergedOpts)
);
};
const newTabOpts = {
tab: {
tabbed: true,
active: true,
},
};
const curentTabOpts = {
tab: {
tabbed: false,
active: false,
},
};
Object.entries(overlayedGlobalMarks).forEach((keymap) => {
const asKeymap =
/** @type {[keyof typeof overlayedGlobalMarks, overlayedGlobalMarks[keyof typeof overlayedGlobalMarks]]} */ (
keymap
);
createJumpLinkKeymap("gn", asKeymap, "in new tab", newTabOpts);
createJumpLinkKeymap("go", asKeymap, "in this tab", curentTabOpts);
});
// --- copy ---
// ref. https://github.com/hushin/dotfiles/blob/master/docs/SurfingkeysSetting.js
/**
* @param {string} format
*/
const copyTitleAndUrl = (format) => {
const text = format.replace("%URL%", location.href).replace("%TITLE%", document.title);
api.Clipboard.write(text);
};
api.mapkey(",u", "#7Copy link", () => {
copyTitleAndUrl("%URL%");
});
api.mapkey(",m", "#7Copy title and link to markdown", () => {
copyTitleAndUrl("[%TITLE%](%URL%)");
});
api.mapkey(",c", "#7Copy title and link to textile", () => {
copyTitleAndUrl('"%TITLE%" - %URL%');
});
api.mapkey(",s", "#7Copy title and link to scrapbox", () => {
copyTitleAndUrl("[%TITLE% %URL%]");
});
api.mapkey(",C", "#7Copy title and link to Chatwork", () => {
copyTitleAndUrl("[info][title]%TITLE%[/title]%URL%[/info]");
});
// copy readable url
api.mapkey(",r", "#7Copy readable url", () => {
api.Clipboard.write(decodeURI(location.href));
});
// short Amazon URL
api.mapkey(",a", "Update Amazon.co.jp URL to short", function () {
/** @type {HTMLInputElement|null} */
const asin = document.body.querySelector("input[name^='ASIN']");
location.href = `https://amazon.co.jp/dp/${asin?.value}`;
});
// --- theme ---
settings.theme = /* css */ `
.sk_theme {
font-family: Input Sans Condensed, Charcoal, sans-serif;
background: #002B36;
color: #93A1A1;
}
.sk_theme input {
color: #93A1A1;
}
.sk_theme .url {
color: #268BD2;
}
.sk_theme .annotation {
color: #93A1A1;
}
.sk_theme kbd {
background: #EEE8D5;
color: #111;
}
.sk_theme .omnibar_highlight {
color: #CB4B16;
}
.sk_theme .omnibar_folder {
color: #2AA198;
}
.sk_theme .omnibar_timestamp {
color: #657B83;
}
.sk_theme .omnibar_visitcount {
color: #B58900;
}
.sk_theme .prompt, .sk_theme .resultPage {
color: #93A1A1;
}
.sk_theme .feature_name {
color: #859900;
}
.sk_theme .separator {
color: #859900;
}
.sk_theme #sk_omnibarSearchResult ul li:nth-child(odd) {
background: #002F3B;
}
.sk_theme #sk_omnibarSearchResult ul li.focused {
background: #083D4A;
}
#sk_status, #sk_find {
font-size: 12pt;
}
#sk_keystroke {
background: #002B36;
}
/* -- omnibar -- */
#sk_omnibar {
opacity: 0.9;
width: 100%;
left: 0;
}
.sk_theme #sk_omnibarSearchArea input,
.sk_theme #sk_omnibarSearchArea span {
font-size: 12px;
}
.expandRichHints span.annotation {
color: #93A1A1;
}`;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment