From e5fb48289f569a8d57e0f21854f9abe63cc8f252 Mon Sep 17 00:00:00 2001 From: js0ny Date: Mon, 13 Jan 2025 10:21:34 +0000 Subject: [PATCH] feat(surfingkeys): Implement visual mode and ACE editor keymaps --- .gitignore | 2 + Justfile | 7 +- .../linux/{hyprland => }/hypr/hyprland.conf | 0 .../linux/{hyprland => }/hypr/hyprpaper.conf | 0 .../{hyprland => wayland}/code-flags.conf | 4 +- .../{hyprland => wayland}/electron-flags.conf | 4 +- tools/browser/surfingkeys.js | 463 ++++++++++++------ tools/nvim/lua/keymaps/nvim-tree.lua | 2 + tools/zsh/mod/prompt.zsh | 3 +- 9 files changed, 327 insertions(+), 158 deletions(-) rename platforms/linux/{hyprland => }/hypr/hyprland.conf (100%) rename platforms/linux/{hyprland => }/hypr/hyprpaper.conf (100%) rename platforms/linux/{hyprland => wayland}/code-flags.conf (86%) rename platforms/linux/{hyprland => wayland}/electron-flags.conf (87%) diff --git a/.gitignore b/.gitignore index 725742f..09a3366 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ platforms/win/komorebi/applications.json gitconfig *.exe + +spacemacs/ diff --git a/Justfile b/Justfile index 271933b..76ac4b8 100644 --- a/Justfile +++ b/Justfile @@ -1,4 +1,5 @@ -set shell := ["pwsh", "-c"] +set shell := ["fish", "-c"] +set windows-shell := ["pwsh", "-c"] pull: git pull github master @@ -7,3 +8,7 @@ pull: push: git push github 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 diff --git a/platforms/linux/hyprland/hypr/hyprland.conf b/platforms/linux/hypr/hyprland.conf similarity index 100% rename from platforms/linux/hyprland/hypr/hyprland.conf rename to platforms/linux/hypr/hyprland.conf diff --git a/platforms/linux/hyprland/hypr/hyprpaper.conf b/platforms/linux/hypr/hyprpaper.conf similarity index 100% rename from platforms/linux/hyprland/hypr/hyprpaper.conf rename to platforms/linux/hypr/hyprpaper.conf diff --git a/platforms/linux/hyprland/code-flags.conf b/platforms/linux/wayland/code-flags.conf similarity index 86% rename from platforms/linux/hyprland/code-flags.conf rename to platforms/linux/wayland/code-flags.conf index 0333988..5a37672 100644 --- a/platforms/linux/hyprland/code-flags.conf +++ b/platforms/linux/wayland/code-flags.conf @@ -10,5 +10,5 @@ # --enable-features=UseOzonePlatform # --ozone-platform=wayland ---ozone-platform-hint=x11 -# --enable-wayland-ime +--ozone-platform-hint=auto +--enable-wayland-ime diff --git a/platforms/linux/hyprland/electron-flags.conf b/platforms/linux/wayland/electron-flags.conf similarity index 87% rename from platforms/linux/hyprland/electron-flags.conf rename to platforms/linux/wayland/electron-flags.conf index 020a9de..7ff2980 100644 --- a/platforms/linux/hyprland/electron-flags.conf +++ b/platforms/linux/wayland/electron-flags.conf @@ -11,5 +11,5 @@ # --enable-features=UseOzonePlatform # --ozone-platform=wayland # --enable-icd -# --enable-wayland-ime ---ozone-platform-hint=x11 +--ozone-platform-hint=auto +--enable-wayland-ime diff --git a/tools/browser/surfingkeys.js b/tools/browser/surfingkeys.js index 604a63e..e8fe28f 100644 --- a/tools/browser/surfingkeys.js +++ b/tools/browser/surfingkeys.js @@ -1,7 +1,6 @@ // Paste this into surfingkeys advanced settings // or use: // Load settings from: https://raw.githubusercontent.com/js0ny/dotfiles/refs/heads/master/tools/browser/surfingkeys.js -// TODO: Visual Mode // #region Example /** Examples @@ -25,245 +24,405 @@ settings.language = "zh-CN"; settings.showModeStatus = false; // Keymap, reference https://github.com/texiwustion/colemak_config_for_surfingkeys/tree/main // #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 = { - add: function (key) { // 转发即将被 unmap 的键 - return api.map(`for${key}`, key) + add: function (key) { + // 转发即将被 unmap 的键 + return api.map(`for${key}`, key); }, - cancel: function (key) { // 删除转发生成的键 - api.unmap(`for${key}`) - api.unmap(key) + cancel: function (key) { + // 删除转发生成的键 + api.unmap(`for${key}`); + api.unmap(key); }, use: function (key) { - return `for${key}` - } -} + return `for${key}`; + }, +}; const colemak = { - forward: function (key) { // 转发即将被 unmap 的键 - api.map(key, `col${key}`) - api.unmap(`col${key}`) - + forward: function (key) { + // 转发即将被 unmap 的键 + api.map(key, `col${key}`); + api.unmap(`col${key}`); }, use: function (key) { - return `col${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) + api.map(colemak.use(a), forward.use(b)); }, - 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) { - return `vfor${key}` - } -} - -const vcolemak = { - forward: function (key) { // 转发即将被 unmap 的键 - api.vmap(key, `vcol${key}`) - api.vunmap(`vcol${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}` + return `vcol${key}`; }, map: function (a, b) { - api.vmap(vcolemak.use(a), vforward.use(b)) - } -} + api.vmap(vColemak.use(a), vForward.use(b)); + }, +}; const forwardFactory = { - push: function (mapLists) { // forward original keys + push: function (mapLists) { + // forward original keys for (let key in mapLists) { - forward.add(mapLists[key]) + forward.add(mapLists[key]); } }, map: function (mapLists) { for (let key in mapLists) { - colemak.map(key, mapLists[key]) + colemak.map(key, mapLists[key]); } }, pull: function (mapLists) { for (let key in mapLists) { - forward.cancel(mapLists[key]) + forward.cancel(mapLists[key]); } for (let key in mapLists) { - colemak.forward(key) + colemak.forward(key); } - } -} -const vforwardFactory = { - push: function (mapLists) { // forward original keys + }, +}; +const vForwardFactory = { + push: function (mapLists) { + // forward original keys for (let key in mapLists) { - vforward.add(mapLists[key]) + vForward.add(mapLists[key]); } }, map: function (mapLists) { for (let key in mapLists) { - vcolemak.map(key, mapLists[key]) + vColemak.map(key, mapLists[key]); } }, pull: function (mapLists) { for (let key in mapLists) { - vforward.cancel(mapLists[key]) + vForward.cancel(mapLists[key]); } for (let key in mapLists) { - vcolemak.forward(key) + vColemak.forward(key); } - } -} + }, +}; const parseSearchResponse = function (response) { 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) { - api.addSearchAlias(alias, name, searchUrl, searchPrefix, acUrl, parseResponse); -} +const _addSearchAlias = function ( + alias, + name, + searchUrl, + searchPrefix = "s", + acUrl = "https://duckduckgo.com/ac/?q=", + parseResponse = parseSearchResponse, +) { + api.addSearchAlias( + alias, + name, + searchUrl, + searchPrefix, + acUrl, + parseResponse, + ); +}; // #endregion // #region Keymap const mapLists = { /// scroll page // Arrow - 'n': 'j', - 'e': 'k', - 'i': 'l', + n: "j", + e: "k", + i: "l", // l <-> i - 'l': 'i', - 'L': 'I', + l: "i", + L: "I", // k <-> n - 'k': 'n', - 'K': 'N', + k: "n", + K: "N", // j <-> e - 'j': 'e', + j: "e", // PrevTab < H - I > NextTab - 'H': 'E', - 'I': 'R', + H: "E", + I: "R", // E,N -> Up/Down HalfPage - 'N': 'd', - 'E': 'e', + N: "d", + E: "e", // F -> Open Link in New Tab - 'F': 'af', + F: "af", // oH -> Tab History - 'oH': 'H', + oH: "H", // gh/gi -> Prev/Next History - 'gh': 'S', - 'gi': 'D', + gh: "S", + gi: "D", // t -> Open Link in New Tab - 't': 'gf', + t: "gf", // 缩放 - 'zu': 'zi', - 'zo': 'ze', - 'zz': 'zr', -} + zu: "zi", + zo: "ze", + zz: "zr", +}; -const vmapLists = { - 'n': 'j', - 'N': 'J', - 'e': 'k', - 'E': 'K', - 'i': 'l', - 'I': 'L', - 'j': 'e', - 'J': 'E', - 'k': 'n', - 'K': 'N', -} +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) +forwardFactory.push(mapLists); +forwardFactory.map(mapLists); -vforwardFactory.push(vmapLists) -vforwardFactory.map(vmapLists) +vForwardFactory.push(vMapLists); +vForwardFactory.map(vMapLists); // 鼠标点击 -api.unmap('gi') -api.unmap('[[') -api.unmap(']]') -api.unmap(';m') -api.unmap(';fs') -api.unmap('O') -api.unmap('C') -api.map('g/', 'gU') // Goto Root Domain -forwardFactory.pull(mapLists) +api.unmap("gi"); +api.unmap("[["); +api.unmap("]]"); +api.unmap(";m"); +api.unmap(";fs"); +api.unmap("O"); +api.unmap("C"); +api.map("g/", "gU"); // Goto Root Domain +// p to site-specific +api.unmap("p"); +api.unmap(""); // Leader Key +forwardFactory.pull(mapLists); +vForwardFactory.pull(vMapLists); // #endregion - // #region Search Alias -api.unmap('os') // StackOverflow -api.vunmap('ss') -api.unmap('ob') // Baidu -api.vunmap('sb') -api.unmap('og') // Google -api.vunmap('sg') -api.unmap('od') // DuckDuckGo -api.vunmap('sd') +api.unmap("os"); // StackOverflow +api.vunmap("ss"); +api.unmap("ob"); // Baidu +api.vunmap("sb"); +api.unmap("og"); // Google +api.vunmap("sg"); +api.unmap("od"); // DuckDuckGo +api.vunmap("sd"); +api.unmap("ow"); // Bing +api.vunmap("sw"); /// Common -_addSearchAlias('dd', 'DuckDuckGo', 'https://duckduckgo.com/?q=') -_addSearchAlias('gg', 'Google', 'https://www.google.com/search?q=') -_addSearchAlias('bd', 'Baidu', 'https://www.baidu.com/s?wd=') -_addSearchAlias('bi', 'Bing', 'https://www.bing.com/search?q=') +_addSearchAlias("dd", "DuckDuckGo", "https://duckduckgo.com/?q="); +_addSearchAlias("gg", "Google", "https://www.google.com/search?q="); +_addSearchAlias("bd", "Baidu", "https://www.baidu.com/s?wd="); +_addSearchAlias("bi", "Bing", "https://www.bing.com/search?q="); /// AI Search -_addSearchAlias('fe', 'Felo', 'https://felo.ai/search?q=') -_addSearchAlias('pp', 'Perplexity', 'https://www.perplexity.ai/?q=') -_addSearchAlias('cg', 'ChatGPT', 'https://chat.openai.com/?q=') +_addSearchAlias("fe", "Felo", "https://felo.ai/search?q="); +_addSearchAlias("pp", "Perplexity", "https://www.perplexity.ai/?q="); +_addSearchAlias("cg", "ChatGPT", "https://chat.openai.com/?q="); /// EECS Related -_addSearchAlias('gh', '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=') +_addSearchAlias( + "gh", + "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 -_addSearchAlias('sc', 'Scoop', 'https://scoop.sh/#/apps?q=') -_addSearchAlias('br', 'Brew', 'https://duckduckgo.com/?q=!brew ') +_addSearchAlias("sc", "Scoop", "https://scoop.sh/#/apps?q="); +_addSearchAlias("br", "Brew", "https://duckduckgo.com/?q=!brew "); // #endregion // #region Site-specific // chatgpt.com -api.unmap('t', /chatgpt.com/); -api.mapkey('tn', 'New Chat', function () { - var btn = document.querySelector('div.no-draggable:nth-child(3) > span:nth-child(1) > button:nth-child(1)') +const chatgptNewChat = function () { + var btn = document.querySelector( + "div.no-draggable:nth-child(3) > span:nth-child(1) > button:nth-child(1)", + ); btn.click(); -}, { domain: /chatgpt.com/ }); -api.mapkey('ts', 'Start/Stop Generating', function () { - var btn = document.querySelector('button.h-8:nth-child(2)'); +}; +const chatgptStartStop = function () { + var btn = document.querySelector("button.h-8:nth-child(2)"); btn.click(); -}, { domain: /chatgpt.com/ }); -api.mapkey('ts', 'Start/Stop Generating', function () { - var btn = document.querySelector('button.h-8:nth-child(2)'); - btn.click(); -}, { domain: /chatgpt.com/ }); -api.mapkey('S', 'Start/Stop Generating', function () { - var btn = document.querySelector('button.h-8:nth-child(2)'); - btn.click(); -}, { domain: /chatgpt.com/ }); -api.mapkey('an', 'New Chat', function () { - var btn = document.querySelector('div.no-draggable:nth-child(3) > span:nth-child(1) > button:nth-child(1)') - 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('as', 'Start/Stop Generating', function () { - var btn = document.querySelector('button.h-8:nth-child(2)'); - btn.click(); -}, { domain: /chatgpt.com/ }); +}; +api.unmap("t", /chatgpt.com/); +api.mapkey("tn", "New Chat", chatgptNewChat, { domain: /chatgpt.com/ }); +api.mapkey("ts", "Start/Stop Generating", chatgptStartStop, { + domain: /chatgpt.com/, +}); +api.mapkey("S", "Start/Stop Generating", chatgptStartStop, { + domain: /chatgpt.com/, +}); +api.mapkey("an", "New Chat", chatgptNewChat, { domain: /chatgpt.com/ }); +api.mapkey("as", "Start/Stop Generating", chatgptStartStop, { + domain: /chatgpt.com/, +}); + //api.mapkey('tm', 'Toggle Model', function () { // var btn = document.querySelector('#radix -\: r2i\:'); // btn.click(); //}, { domain: /chatgpt.com/ }); // perplexity.ai -api.unmap('', /perplexity.ai/); - +api.unmap("", /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 diff --git a/tools/nvim/lua/keymaps/nvim-tree.lua b/tools/nvim/lua/keymaps/nvim-tree.lua index b0e2736..0fa4446 100644 --- a/tools/nvim/lua/keymaps/nvim-tree.lua +++ b/tools/nvim/lua/keymaps/nvim-tree.lua @@ -2,6 +2,7 @@ local M = {} M.global = { { mode = "n", keys = "e", cmd = ":NvimTreeToggle" }, + { mode = "n", keys = "", cmd = ":NvimTreeFocus" }, } function M.plugin(api, opts) @@ -74,6 +75,7 @@ function M.plugin(api, opts) { keys = "", cmd = api.node.open.tab, opts = opts("Open: New Tab") }, { keys = "", cmd = api.node.open.vertical, opts = opts("Open: Vertical Split") }, { keys = "", cmd = api.node.open.horizontal, opts = opts("Open: Horizontal Split") }, + { keys = "", cmd = ":b#", opts = opts("Focus to previous buffer") }, -- Mouse 鼠标键 { keys = "<2-LeftMouse>", cmd = api.node.open.edit, opts = opts("Open") }, { keys = "<2-RightMouse>", cmd = api.tree.change_root_to_node, opts = opts("CD") }, diff --git a/tools/zsh/mod/prompt.zsh b/tools/zsh/mod/prompt.zsh index 67d111b..1f4ec4e 100644 --- a/tools/zsh/mod/prompt.zsh +++ b/tools/zsh/mod/prompt.zsh @@ -3,6 +3,7 @@ # Author: js0ny # 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)" +