feat(surfingkeys): Implement visual mode and ACE editor keymaps

This commit is contained in:
js0ny 2025-01-13 10:21:34 +00:00
parent 073c60f07d
commit e5fb48289f
9 changed files with 327 additions and 158 deletions

2
.gitignore vendored
View file

@ -36,3 +36,5 @@ platforms/win/komorebi/applications.json
gitconfig gitconfig
*.exe *.exe
spacemacs/

View file

@ -1,4 +1,5 @@
set shell := ["pwsh", "-c"] set shell := ["fish", "-c"]
set windows-shell := ["pwsh", "-c"]
pull: pull:
git pull github master git pull github master
@ -7,3 +8,7 @@ pull:
push: push:
git push github master git push github master
git push codeberg master git push codeberg master
init:
git remote add github git@github.com:js0ny/dotfiles.git
git remote add codeberg git@codeberg.org:js0ny/dotfiles.git

View file

@ -10,5 +10,5 @@
# --enable-features=UseOzonePlatform # --enable-features=UseOzonePlatform
# --ozone-platform=wayland # --ozone-platform=wayland
--ozone-platform-hint=x11 --ozone-platform-hint=auto
# --enable-wayland-ime --enable-wayland-ime

View file

@ -11,5 +11,5 @@
# --enable-features=UseOzonePlatform # --enable-features=UseOzonePlatform
# --ozone-platform=wayland # --ozone-platform=wayland
# --enable-icd # --enable-icd
# --enable-wayland-ime --ozone-platform-hint=auto
--ozone-platform-hint=x11 --enable-wayland-ime

View file

@ -1,7 +1,6 @@
// Paste this into surfingkeys advanced settings // Paste this into surfingkeys advanced settings
// or use: // 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/tools/browser/surfingkeys.js
// TODO: Visual Mode
// #region Example // #region Example
/** Examples /** Examples
@ -25,245 +24,405 @@ settings.language = "zh-CN";
settings.showModeStatus = false; settings.showModeStatus = false;
// Keymap, reference https://github.com/texiwustion/colemak_config_for_surfingkeys/tree/main // Keymap, reference https://github.com/texiwustion/colemak_config_for_surfingkeys/tree/main
// #region Helper // #region Helper
const {
aceVimMap,
addVimMapKey,
mapkey,
imap,
imapkey,
getClickableElements,
vmapkey,
map,
unmap,
cmap,
addSearchAlias,
removeSearchAlias,
tabOpenLink,
readText,
Clipboard,
Front,
Hints,
Visual,
RUNTIME,
} = api;
const forward = { const forward = {
add: function (key) { // 转发即将被 unmap 的键 add: function (key) {
return api.map(`for${key}`, key) // 转发即将被 unmap 的键
return api.map(`for${key}`, key);
}, },
cancel: function (key) { // 删除转发生成的键 cancel: function (key) {
api.unmap(`for${key}`) // 删除转发生成的键
api.unmap(key) api.unmap(`for${key}`);
api.unmap(key);
}, },
use: function (key) { use: function (key) {
return `for${key}` return `for${key}`;
} },
} };
const colemak = { const colemak = {
forward: function (key) { // 转发即将被 unmap 的键 forward: function (key) {
api.map(key, `col${key}`) // 转发即将被 unmap 的键
api.unmap(`col${key}`) api.map(key, `col${key}`);
api.unmap(`col${key}`);
}, },
use: function (key) { use: function (key) {
return `col${key}` return `col${key}`;
}, },
map: function (a, b) { map: function (a, b) {
api.map(colemak.use(a), forward.use(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) 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) { use: function (key) {
return `vfor${key}` return `vfor${key}`;
} },
} };
const vcolemak = {
forward: function (key) { // 转发即将被 unmap 的键
api.vmap(key, `vcol${key}`)
api.vunmap(`vcol${key}`)
const vColemak = {
forward: function (key) {
// 转发即将被 unmap 的键
api.vmap(key, `vcol${key}`);
api.vunmap(`vcol${key}`);
}, },
use: function (key) { use: function (key) {
return `vcol${key}` return `vcol${key}`;
}, },
map: function (a, b) { map: function (a, b) {
api.vmap(vcolemak.use(a), vforward.use(b)) api.vmap(vColemak.use(a), vForward.use(b));
} },
} };
const forwardFactory = { const forwardFactory = {
push: function (mapLists) { // forward original keys push: function (mapLists) {
// forward original keys
for (let key in mapLists) { for (let key in mapLists) {
forward.add(mapLists[key]) forward.add(mapLists[key]);
} }
}, },
map: function (mapLists) { map: function (mapLists) {
for (let key in mapLists) { for (let key in mapLists) {
colemak.map(key, mapLists[key]) colemak.map(key, mapLists[key]);
} }
}, },
pull: function (mapLists) { pull: function (mapLists) {
for (let key in mapLists) { for (let key in mapLists) {
forward.cancel(mapLists[key]) forward.cancel(mapLists[key]);
} }
for (let key in mapLists) { for (let key in mapLists) {
colemak.forward(key) colemak.forward(key);
} }
} },
} };
const vforwardFactory = { const vForwardFactory = {
push: function (mapLists) { // forward original keys push: function (mapLists) {
// forward original keys
for (let key in mapLists) { for (let key in mapLists) {
vforward.add(mapLists[key]) vForward.add(mapLists[key]);
} }
}, },
map: function (mapLists) { map: function (mapLists) {
for (let key in mapLists) { for (let key in mapLists) {
vcolemak.map(key, mapLists[key]) vColemak.map(key, mapLists[key]);
} }
}, },
pull: function (mapLists) { pull: function (mapLists) {
for (let key in mapLists) { for (let key in mapLists) {
vforward.cancel(mapLists[key]) vForward.cancel(mapLists[key]);
} }
for (let key in mapLists) { for (let key in mapLists) {
vcolemak.forward(key) vColemak.forward(key);
} }
} },
} };
const parseSearchResponse = function (response) { const parseSearchResponse = function (response) {
const res = JSON.parse(response.text); const res = JSON.parse(response.text);
return res.map(r => r.phrase); return res.map((r) => r.phrase);
}; };
const _addSearchAlias = function (alias, name, searchUrl, searchPrefix = 's', acUrl = "https://duckduckgo.com/ac/?q=", parseResponse = parseSearchResponse) { const _addSearchAlias = function (
api.addSearchAlias(alias, name, searchUrl, searchPrefix, acUrl, parseResponse); alias,
} name,
searchUrl,
searchPrefix = "s",
acUrl = "https://duckduckgo.com/ac/?q=",
parseResponse = parseSearchResponse,
) {
api.addSearchAlias(
alias,
name,
searchUrl,
searchPrefix,
acUrl,
parseResponse,
);
};
// #endregion // #endregion
// #region Keymap // #region Keymap
const mapLists = { const mapLists = {
/// scroll page /// scroll page
// Arrow // Arrow
'n': 'j', n: "j",
'e': 'k', e: "k",
'i': 'l', i: "l",
// l <-> i // l <-> i
'l': 'i', l: "i",
'L': 'I', L: "I",
// k <-> n // k <-> n
'k': 'n', k: "n",
'K': 'N', K: "N",
// j <-> e // j <-> e
'j': 'e', j: "e",
// PrevTab < H - I > NextTab // PrevTab < H - I > NextTab
'H': 'E', H: "E",
'I': 'R', I: "R",
// E,N -> Up/Down HalfPage // E,N -> Up/Down HalfPage
'N': 'd', N: "d",
'E': 'e', E: "e",
// F -> Open Link in New Tab // F -> Open Link in New Tab
'F': 'af', F: "af",
// oH -> Tab History // oH -> Tab History
'oH': 'H', oH: "H",
// gh/gi -> Prev/Next History // gh/gi -> Prev/Next History
'gh': 'S', gh: "S",
'gi': 'D', gi: "D",
// t -> Open Link in New Tab // t -> Open Link in New Tab
't': 'gf', t: "gf",
// 缩放 // 缩放
'zu': 'zi', zu: "zi",
'zo': 'ze', zo: "ze",
'zz': 'zr', zz: "zr",
} };
const vmapLists = { const vMapLists = {
'n': 'j', n: "j",
'N': 'J', N: "J",
'e': 'k', e: "k",
'E': 'K', E: "K",
'i': 'l', i: "l",
'I': 'L', I: "L",
'j': 'e', j: "e",
'J': 'E', J: "E",
'k': 'n', k: "n",
'K': 'N', K: "N",
} };
forwardFactory.push(mapLists) forwardFactory.push(mapLists);
forwardFactory.map(mapLists) forwardFactory.map(mapLists);
vforwardFactory.push(vmapLists) vForwardFactory.push(vMapLists);
vforwardFactory.map(vmapLists) vForwardFactory.map(vMapLists);
// 鼠标点击 // 鼠标点击
api.unmap('gi') api.unmap("gi");
api.unmap('[[') api.unmap("[[");
api.unmap(']]') api.unmap("]]");
api.unmap(';m') api.unmap(";m");
api.unmap(';fs') api.unmap(";fs");
api.unmap('O') api.unmap("O");
api.unmap('C') api.unmap("C");
api.map('g/', 'gU') // Goto Root Domain api.map("g/", "gU"); // Goto Root Domain
forwardFactory.pull(mapLists) // p to site-specific
api.unmap("p");
api.unmap("<space>"); // Leader Key
forwardFactory.pull(mapLists);
vForwardFactory.pull(vMapLists);
// #endregion // #endregion
// #region Search Alias // #region Search Alias
api.unmap('os') // StackOverflow api.unmap("os"); // StackOverflow
api.vunmap('ss') api.vunmap("ss");
api.unmap('ob') // Baidu api.unmap("ob"); // Baidu
api.vunmap('sb') api.vunmap("sb");
api.unmap('og') // Google api.unmap("og"); // Google
api.vunmap('sg') api.vunmap("sg");
api.unmap('od') // DuckDuckGo api.unmap("od"); // DuckDuckGo
api.vunmap('sd') api.vunmap("sd");
api.unmap("ow"); // Bing
api.vunmap("sw");
/// Common /// Common
_addSearchAlias('dd', 'DuckDuckGo', 'https://duckduckgo.com/?q=') _addSearchAlias("dd", "DuckDuckGo", "https://duckduckgo.com/?q=");
_addSearchAlias('gg', 'Google', 'https://www.google.com/search?q=') _addSearchAlias("gg", "Google", "https://www.google.com/search?q=");
_addSearchAlias('bd', 'Baidu', 'https://www.baidu.com/s?wd=') _addSearchAlias("bd", "Baidu", "https://www.baidu.com/s?wd=");
_addSearchAlias('bi', 'Bing', 'https://www.bing.com/search?q=') _addSearchAlias("bi", "Bing", "https://www.bing.com/search?q=");
/// AI Search /// AI Search
_addSearchAlias('fe', 'Felo', 'https://felo.ai/search?q=') _addSearchAlias("fe", "Felo", "https://felo.ai/search?q=");
_addSearchAlias('pp', 'Perplexity', 'https://www.perplexity.ai/?q=') _addSearchAlias("pp", "Perplexity", "https://www.perplexity.ai/?q=");
_addSearchAlias('cg', 'ChatGPT', 'https://chat.openai.com/?q=') _addSearchAlias("cg", "ChatGPT", "https://chat.openai.com/?q=");
/// EECS Related /// EECS Related
_addSearchAlias('gh', 'GitHub', 'https://github.com/search?type=repositories&q=') _addSearchAlias(
_addSearchAlias('so', 'StackOverflow', 'https://stackoverflow.com/search?q=') "gh",
_addSearchAlias('aw', 'ArchWiki', 'https://wiki.archlinux.org/index.php?search=') "GitHub",
"https://github.com/search?type=repositories&q=",
);
_addSearchAlias("so", "StackOverflow", "https://stackoverflow.com/search?q=");
_addSearchAlias(
"aw",
"ArchWiki",
"https://wiki.archlinux.org/index.php?search=",
);
/// Software /// Software
_addSearchAlias('sc', 'Scoop', 'https://scoop.sh/#/apps?q=') _addSearchAlias("sc", "Scoop", "https://scoop.sh/#/apps?q=");
_addSearchAlias('br', 'Brew', 'https://duckduckgo.com/?q=!brew ') _addSearchAlias("br", "Brew", "https://duckduckgo.com/?q=!brew ");
// #endregion // #endregion
// #region Site-specific // #region Site-specific
// chatgpt.com // chatgpt.com
api.unmap('t', /chatgpt.com/); const chatgptNewChat = function () {
api.mapkey('tn', 'New Chat', function () { var btn = document.querySelector(
var btn = document.querySelector('div.no-draggable:nth-child(3) > span:nth-child(1) > button:nth-child(1)') "div.no-draggable:nth-child(3) > span:nth-child(1) > button:nth-child(1)",
);
btn.click(); btn.click();
}, { domain: /chatgpt.com/ }); };
api.mapkey('ts', 'Start/Stop Generating', function () { const chatgptStartStop = function () {
var btn = document.querySelector('button.h-8:nth-child(2)'); var btn = document.querySelector("button.h-8:nth-child(2)");
btn.click(); btn.click();
}, { domain: /chatgpt.com/ }); };
api.mapkey('ts', 'Start/Stop Generating', function () { api.unmap("t", /chatgpt.com/);
var btn = document.querySelector('button.h-8:nth-child(2)'); api.mapkey("tn", "New Chat", chatgptNewChat, { domain: /chatgpt.com/ });
btn.click(); api.mapkey("ts", "Start/Stop Generating", chatgptStartStop, {
}, { domain: /chatgpt.com/ }); domain: /chatgpt.com/,
api.mapkey('S', 'Start/Stop Generating', function () { });
var btn = document.querySelector('button.h-8:nth-child(2)'); api.mapkey("S", "Start/Stop Generating", chatgptStartStop, {
btn.click(); domain: /chatgpt.com/,
}, { domain: /chatgpt.com/ }); });
api.mapkey('an', 'New Chat', function () { api.mapkey("an", "New Chat", chatgptNewChat, { domain: /chatgpt.com/ });
var btn = document.querySelector('div.no-draggable:nth-child(3) > span:nth-child(1) > button:nth-child(1)') api.mapkey("as", "Start/Stop Generating", chatgptStartStop, {
btn.click(); domain: /chatgpt.com/,
}, { domain: /chatgpt.com/ }); });
api.mapkey('as', 'Start/Stop Generating', function () {
var btn = document.querySelector('button.h-8:nth-child(2)');
btn.click();
}, { domain: /chatgpt.com/ });
api.mapkey('as', 'Start/Stop Generating', function () {
var btn = document.querySelector('button.h-8:nth-child(2)');
btn.click();
}, { domain: /chatgpt.com/ });
//api.mapkey('tm', 'Toggle Model', function () { //api.mapkey('tm', 'Toggle Model', function () {
// var btn = document.querySelector('#radix -\: r2i\:'); // var btn = document.querySelector('#radix -\: r2i\:');
// btn.click(); // btn.click();
//}, { domain: /chatgpt.com/ }); //}, { domain: /chatgpt.com/ });
// perplexity.ai // perplexity.ai
api.unmap('<Ctrl-i>', /perplexity.ai/); api.unmap("<Ctrl-i>", /perplexity.ai/); // allows to use perplexity web keybindings
api.map();
// #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 // #endregion

View file

@ -2,6 +2,7 @@ local M = {}
M.global = { M.global = {
{ mode = "n", keys = "<leader>e", cmd = ":NvimTreeToggle<CR>" }, { mode = "n", keys = "<leader>e", cmd = ":NvimTreeToggle<CR>" },
{ mode = "n", keys = "<A-0>", cmd = ":NvimTreeFocus<CR>" },
} }
function M.plugin(api, opts) function M.plugin(api, opts)
@ -74,6 +75,7 @@ function M.plugin(api, opts)
{ keys = "<C-t>", cmd = api.node.open.tab, opts = opts("Open: New Tab") }, { keys = "<C-t>", cmd = api.node.open.tab, opts = opts("Open: New Tab") },
{ keys = "<C-v>", cmd = api.node.open.vertical, opts = opts("Open: Vertical Split") }, { keys = "<C-v>", cmd = api.node.open.vertical, opts = opts("Open: Vertical Split") },
{ keys = "<C-h>", cmd = api.node.open.horizontal, opts = opts("Open: Horizontal Split") }, { keys = "<C-h>", cmd = api.node.open.horizontal, opts = opts("Open: Horizontal Split") },
{ keys = "<A-0>", cmd = ":b#<CR>", opts = opts("Focus to previous buffer") },
-- Mouse 鼠标键 -- Mouse 鼠标键
{ keys = "<2-LeftMouse>", cmd = api.node.open.edit, opts = opts("Open") }, { keys = "<2-LeftMouse>", cmd = api.node.open.edit, opts = opts("Open") },
{ keys = "<2-RightMouse>", cmd = api.tree.change_root_to_node, opts = opts("CD") }, { keys = "<2-RightMouse>", cmd = api.tree.change_root_to_node, opts = opts("CD") },

View file

@ -3,6 +3,7 @@
# Author: js0ny # Author: js0ny
# Sourced by user's zshrc 在用户的 zshrc 中被引用 # Sourced by user's zshrc 在用户的 zshrc 中被引用
# export STARSHIP_CONFIG=$DOTFILES/tools/starship/starship_zsh.toml export STARSHIP_CONFIG=$DOTFILES/tools/starship/starship_zsh.toml
eval "$(starship init zsh)" eval "$(starship init zsh)"