SurfingKeys API 全解析:用 JavaScript 与键盘映射扩展浏览器的官方接口指南
开发工具前端【免费下载链接】SurfingkeysMap your keys for web surfing, expand your browser with javascript and keyboard.项目地址https://gitcode.com/gh_mirrors/su/Surfingkeys点击查看免费下载本篇技术指南以 SurfingKeys 官方 API 参考文档docs/API.md为骨架系统梳理其暴露给用户脚本的完整 API 面从mapkey键位映射、搜索引擎别名、Hints 提示符到RUNTIME后台通信与 ACE 编辑器映射。结合仓库源码src/content_scripts/common/api.js、src/user_scripts/index.js 等印证各 API 的真实调用链与底层实现。读完本文你将能够独立编写一套可运行、可维护的 SurfingKeys 用户脚本为任意站点定制键盘操作、搜索流程与页面交互。一、API 从何而来用户脚本与内容脚本的双世界桥接SurfingKeys 的用户脚本User Scripts运行在用户脚本世界user-script world中而真正执行键位映射、Hints 渲染的代码位于内容脚本世界content-script world。两者通过dispatchSKEvent进行消息桥接。从源码看src/user_scripts/index.js 定义了一个api对象其中每个方法mapkey、Hints.create、Clipboard.write等都会将调用参数打包并通过dispatchSKEvent(api, [...])转发而 src/content_scripts/common/api.js 中的createAPI()函数则负责在内容脚本侧真正实现这些 API并在 api.js 第 479-533 行 的返回对象中将其暴露出去。因此你在浏览器扩展的Settings设置页面中编写并保存的用户脚本本质上就是在调用上面这套 API 来注册按键、定制搜索、生成 Hints 与弹出 Omnibar。本文后续所有代码示例均默认书写于用户脚本环境。二、键位映射 API为各模式创建/修改/删除快捷键SurfingKeys 将页面交互划分为多种模式normal普通模式、insert插入模式、visual可视模式、omnibar命令栏模式、lurk潜伏模式。键位映射 API 围绕这些模式展开。2.1 mapkey / vmapkey / imapkey绑定自定义动作这是最核心的 API用于在指定模式下创建一个快捷键并绑定自己的 JavaScript 动作。三者参数完全一致区别仅在于生效的模式参数类型说明keysstring快捷键序列如Space、;dannotationstring帮助说明文字会显示在按?打开的帮助面板中jscodefunction绑定的 JavaScript 函数若该函数需要参数下一次按下的键会被作为参数传入optionsobject可选默认nulldomain正则表达式限定映射生效的域名如/github\.com/i表示仅在 github.com 生效repeatIgnore布尔值控制该动作是否可被.点命令重复执行原文档给出的经典示例——在 YouTube 上暂停/恢复播放mapkey(Space, pause/resume on youtube, function() { var btn document.querySelector(button.ytp-ad-overlay-close-button) || document.querySelector(button.ytp-ad-skip-button) || document.querySelector(ytd-watch-flexy button.ytp-play-button); btn.click(); }, {domain: /youtube.com/i});对应的底层实现位于 src/content_scripts/common/api.js 的_mapkey()它会先通过_isDomainApplicable()判断当前域名是否匹配options.domainapi.js 第 42-44 行匹配对象是document.location.href或window.origin随后用KeyboardUtils.encodeKeystroke()对按键序列编码写入对应模式normal/visual/insert的mappings一个基于 Trie 前缀树的映射表。如果新按键覆盖了已有映射还会通过LOG(warn, ...)输出警告。三个 API 的调用关系为mapkey→_mapkey(normal, ...)api.js 第 91-93 行vmapkey→_mapkey(visual, ...)api.js 第 105-107 行imapkey→_mapkey(insert, ...)api.js 第 119-121 行2.2 map / imap / cmap / vmap / lmap按键重映射map系列用于将某个按键序列替换为另一个按键序列而非绑定函数在 normal、insert、omnibar、visual、lurk 五种模式下分别对应map、imap、cmap、vmap、lmap参数类型说明new_keystrokestring替换后的按键序列old_keystrokestring被替换的按键序列domainregex可选默认null限定映射生效的域名new_annotationstring可选默认null若提供用它替代原按键的 annotation 说明示例map(;d, Ctrl-Alt-d);从源码实现看map还支持一种特殊语法若old_keystroke以:开头如:open则会将该键序列绑定到执行对应命令的动作api.js 第 136-141 行若old_keystroke是Mode.specialKeys中的特殊键则会被追加到特殊键列表api.js 第 143-145 行。而cmap的实现则是通过dispatchSKEvent(front, [addMapkey, Omnibar, ...])将映射注册到 Omnibar 前端api.js 第 248-252 行lmap则调用normal.addLurkMap()api.js 第 294-298 行。2.3 unmap / iunmap / vunmap / unmapAllExcept移除映射API参数说明unmap(keystroke, domain)keystroke要移除的按键序列domain正则可选移除 normal 模式映射iunmap(keystroke, domain)同上移除 insert 模式映射api.js 第 232-236 行vunmap(keystroke, domain)同上移除 visual 模式映射api.js 第 278-282 行unmapAllExcept(keystrokes, domain)keystrokes要保留的按键数组移除除指定键以外的全部键位映射示例unmap(, /youtube.com/); unmapAllExcept([E,R,T], /google.com|twitter.com/);unmap的实现细节先在 normal 模式映射表中查找若不存在则遍历Mode.specialKeys并将该按键从特殊键列表中移除api.js 第 162-176 行。unmapAllExcept则同时重建 normal 与 insert 两个模式的 Trie 映射表只保留指定按键api.js 第 188-206 行。三、搜索引擎与选中文本搜索 API3.1 addSearchAlias注册搜索引擎别名该 API 将搜索引擎注册进 Omnibar。输入别名后按空格即可触发对应搜索。参数多达 8 个参数类型说明aliasstring触发该搜索引擎的别名一个或多个字符在 omnibar 输入该字符串并按下空格即触发promptstring显示在 omnibar 前面的提示文字search_urlstring搜索引擎 URL如https://www.s.com/search.html?query若带额外参数可写作https://www.s.com/search.html?query{0}typecs或https://www.s.com/search.html?typecsqueryURL 参数顺序通常无关紧要search_leader_keystring可选默认snormal 模式下search_leader_keyalias可直接搜索选中文本而不打开 omnibar如sdsuggestion_urlstring可选默认null触发该搜索引擎时获取建议词的 URLcallback_to_parse_suggestionfunction可选默认null解析suggestion_url响应并返回建议词字符串数组的回调接收两个参数response含text属性即响应文本与request含query查询文本与url格式化后的请求 URLonly_this_site_keystring可选默认onormal 模式下search_leader_keyonly_this_site_keyalias可只在当前站点内搜索选中文本如sodoptionsobject可选默认nullfavicon_url该搜索引擎的图标 URLskipMaps若为true则不为该搜索引擎创建按键映射原文档示例DuckDuckGo含建议词解析addSearchAlias(d, duckduckgo, https://duckduckgo.com/?q, s, https://duckduckgo.com/ac/?q, function(response) { var res JSON.parse(response.text); return res.map(function(r){ return r.phrase; }); });从源码看addSearchAlias内部会做两件重要的事api.js 第 320-358 行校验别名要求别名必须是 ASCII 字符/^[\u0000-\u007f]*$/否则抛出异常自动生成多组映射包括(search_leader_key || s) alias的 normal/visual 模式「搜索选中文本」映射、o alias的「打开 Omnibar 搜索」映射、s o alias的「站内搜索」映射若别名含小写字母还会自动为大写别名生成交互式搜索映射interactivetrue便于在搜索前微调选中内容。若options.skipMaps为true则跳过这些映射。建议词解析回调在 src/user_scripts/index.js 第 92-101 行 的getSearchSuggestions处理器中被调用异常时会回退为空数组。3.2 removeSearchAlias移除搜索引擎别名参数类型说明aliasstring要移除的搜索引擎别名search_leader_keystring可选默认s与注册时对应only_this_site_keystring可选默认o与注册时对应示例removeSearchAlias(d);对应实现会同步移除 3.1 中自动生成的全部映射含大写别名的映射见 api.js 第 370-386 行。3.3 searchSelectedWith直接搜索选中文本参数类型说明sestring搜索引擎的搜索 URLonlyThisSiteboolean可选默认false是否只在当前站点内搜索需要搜索引擎支持interactiveboolean可选默认false是否以交互模式搜索适用于需要对选中内容做小幅修改的场景aliasstring可选默认仅用于交互模式此时se的 URL 被忽略SurfingKeys 会从addSearchAlias注册的别名构造搜索 URL示例searchSelectedWith(https://translate.google.com/?hlen#auto/en/);底层逻辑api.js 第 400-413 行先取window.getSelection()的选中文本若为空则尝试从剪贴板读取onlyThisSite时会在查询词前拼接site: window.location.hostnameinteractive时调用front.openOmnibar({type: SearchEngine, extra: alias, pref: query})否则通过tabOpenLink(constructSearchURL(se, encodeURIComponent(query)))打开搜索结果。URL 的构造规则见 src/content_scripts/common/utils.js 第 890-898 行优先替换{0}占位符其次替换%s否则直接拼接。四、Clipboard剪贴板读写API参数说明Clipboard.read(onReady)onReady回调函数读取剪贴板文本通过回调拿到response.dataClipboard.write(text)text要写入的文本写入剪贴板示例Clipboard.read(function(response) { console.log(response.data); }); Clipboard.write(window.location.href);剪贴板实现位于 src/content_scripts/common/clipboard.js在用户脚本世界Clipboard.read会先将回调暂存为onClipboardReadFn再通过dispatchSKEvent(api, [clipboard:read])发起异步读取最终由 src/user_scripts/index.js 第 119-121 行 的onClipboardRead处理器触发回调。五、Hints链接提示符 APIHints提示符是 SurfingKeys 的核心交互——按下触发键后页面上的可点击元素会标注字母标签键入标签即触发点击。Hints对象提供以下 API。5.1 Hints.setNumeric / Hints.setCharactersHints.setNumeric()使用数字作为提示符标签设置后可输入文本过滤链接。此 API 取代了旧的Hints.numericHints true;写法。Hints.setCharacters(characters)设置生成提示符所用的字符集取代旧的Hints.characters asdgqwertzxcvb;写法Hints.setNumeric(); Hints.setCharacters(asdgqwertzxcvb);源码实现见 src/content_scripts/common/hints.js 第 244 行与第 258 行。在内容脚本侧Hints.setCharacters还会同步通知前端 UIapi.js 第 516-521 行。5.2 Hints.dispatchMouseClick默认提示符点击处理这是Hints.create的默认onHintKey实现。参数element为提示符指向的 HTMLElement。mapkey(q, click on images, function() { Hints.create(div.media_box img, Hints.dispatchMouseClick); }, {domain: /weibo.com/i});实现位于 hints.js 第 368 行并在 hints.js 第 447 行 被用作默认回调。5.3 Hints.click点击元素或生成提示符参数类型说明linksstring 或 HTMLElement 数组当数组中只有一个元素或force为true时直接点击否则为这些元素生成提示符。若传入字符串则作为 CSS 选择器传给getClickableElementsforceboolean可选默认false无论元素数量多少强制点击第一个输入元素示例隐藏微博回复mapkey(zz, Hide replies, function() { Hints.click(document.querySelectorAll(#less-replies:not([hidden])), true); });5.4 Hints.create为元素生成提示符参数类型说明cssSelectorstring 或 HTMLElement 数组传入字符串时作为 CSS 选择器onHintKeyfunction按下提示符键时的回调函数attrsobject可选默认nullactive打开链接时是否激活新标签页tabbed是否在新标签页打开链接multipleHits触发一个提示符后是否停留在提示符模式返回一个Promise其解析值为创建的提示符数量。示例复制链接 URL 为 Markdown 格式mapkey(yA, #7Copy a link URL to the clipboard, function() { Hints.create(*[href], function(element) { Clipboard.write( element.innerText ); }); });在用户脚本世界Hints.create会在传入元素数组时将元素临时打上surfingkeys--hints--creating类再转成选择器src/user_scripts/index.js 第 225-243 行提示符点击结果通过onHintCreated回调 Promise。底层实现createHints位于 src/content_scripts/common/hints.js 第 182 行。5.5 Hints.style设置提示符样式参数类型说明cssstring提示符样式modestring可选默认null提示符子模式使用text可进入可视化文本提示符模式示例Hints.style(border: solid 3px #552a48; color:#efe1eb; background: none; background-color: #552a48;); Hints.style(div{border: solid 3px #707070; color:#efe1eb; background: none; background-color: #707070;} div.begin{color:red;}, text);六、Normal普通模式控制 APIAPI参数说明Normal.passThrough(timeout)timeoutnumber可选进入 PassThrough穿透模式指定毫秒数后自动退出不指定则一直停留直到按下 EscapeNormal.scroll(type)typestring在当前目标内滚动可取down、up、pageDown、fullPageDown、pageUp、fullPageUp、top、bottom、left、right、leftmost、rightmost、byRatioNormal.feedkeys(keys)keysstring将按键序列注入 Normal 模式Normal.jumpVIMark(mark)markstring跳转到 vim 风格标记七、Visual可视模式样式参数类型说明elementstring可视模式中的元素可为marks标记与cursor光标stylestringCSS 样式示例Visual.style(marks, background-color: #89a1e2;); Visual.style(cursor, background-color: #9065b7;);八、Front前端交互 API8.1 Front.showEditor启动 vim 编辑器参数类型说明elementHTMLElement启动 vim 编辑器的目标元素也可传入字符串作为编辑器默认内容onWritefunction编辑器写回内容时执行的回调typestring可选默认null编辑器类型可为url不提供时使用目标元素的标签名useNeovimboolean可选默认false默认使用内置 JS 实现的 vim 编辑器若为true则通过原生消息native messaging使用 Neovim示例编辑当前 URL 并重新加载mapkey(;U, #4Edit current URL with vim editor, and reload, function() { Front.showEditor(window.location.href, function(data) { window.location.href data; }, url); });Neovim 相关的原生消息服务端实现见 src/nvim/server/server.lua 与 src/nvim/server/Readme.md。8.2 Front.openOmnibar打开 Omnibar参数args为 object核心字段type指定 Omnibar 的子类型可取Bookmarks、AddBookmark、History、URLs、RecentlyClosed、TabURLs、Tabs、Windows、VIMarks、SearchEngine、Commands、OmniQuery、UserURLs。原文档示例打开 AWS 服务列表mapkey(ou, #8Open AWS services, function() { var services Array.from(top.document.querySelectorAll(#awsc-services-container li[data-service-href])).map(function(li) { return { title: li.querySelector(span.service-label).textContent, url: li.getAttribute(data-service-href) }; }); if (services.length 0) { services Array.from(top.document.querySelectorAll(div[data-testidawsc-nav-service-list] li[data-testid]a)).map(function(a) { return { title: a.innerText, url: a.href }; }); } Front.openOmnibar({type: UserURLs, extra: services}); }, {domain: /console.amazonaws|console.aws.amazon.com/i});8.3 Front.registerInlineQuery注册行内查询参数args为 objecturlstring 或 function字典服务的 URL 或返回 URL 的函数parseResultfunction解析字典服务结果并返回渲染说明的 HTML 字符串headersobject可选用于字典服务需要鉴权的场景。注册后选中文本即可触发行内词典查询其异步请求通过 src/user_scripts/index.js 第 103-115 行 的performInlineQuery处理器执行支持httpRequest与自定义 headers。8.4 Front.showBanner / Front.showPopup消息提示API参数说明Front.showBanner(msg, timeout)msgstringtimeoutnumber可选默认1600在横幅中显示消息timeout毫秒后消失Front.showPopup(msg)msgstring在弹层中显示消息示例Front.showBanner(window.location.href); Front.showPopup(window.location.href);两者分别通过dispatchSKEvent(front, [showBanner, msg, timeout])与[showPopup, msg]交由前端 UI 渲染src/content_scripts/common/utils.js 第 265-280 行。九、RUNTIME调用后台动作参数类型说明actionstring要调用的后台动作名称argsobject传给后台动作的参数callbackfunction收到后台响应后执行的回调示例获取当前窗口的标签页列表RUNTIME(getTabs, {queryInfo: {currentWindow: true}}, response { console.log(response); });RUNTIME是内容脚本与扩展后台src/background/start.js通信的通道其实现见 src/content_scripts/common/runtime.js。它可以让用户脚本调用扩展在后台拥有的浏览器权限能力如标签页管理是编写高级用户脚本的钥匙。十、ACE 编辑器映射SurfingKeys 内置了基于 ACE 编辑器的 vim 实现用于网页中的可编辑区域。相关 API 有两个10.1 aceVimMap单键映射参数类型说明lhsstring要替换的按键序列rhsstring替换后的按键序列ctxstring模式如insert、normal示例在 normal 模式下用J切换下一个 bufferaceVimMap(J, :bn, normal);10.2 addVimMapKey批量添加键映射参数objects为 object可传多个每个对象定义一条 ACE vim 键映射结构参照 ace/keyboard/vim.js 中的 motion 定义原文档引用版本为 vim.js 第 L927-L1099 区间当前仓库中无该第三方文件具体字段以 ACE 文档为准addVimMapKey( { keys: n, type: motion, motion: moveByCharacters, motionArgs: { forward: false } }, { keys: e, type: motion, motion: moveByLines, motionArgs: { forward: true, linewise: true } } );十一、浏览器与页面工具函数11.1 getBrowserName获取当前浏览器名称返回Chrome、Firefox或Safari。11.2 isElementPartiallyInViewport参数类型说明elElement待检查的元素ignoreSizeboolean可选默认false是否忽略元素尺寸否则元素必须满足 4×4 的最小尺寸返回 boolean。实现见 src/content_scripts/common/utils.js 第 435-443 行通过getBoundingClientRect()判断元素矩形与视口矩形是否存在交集。11.3 getLargeElements获取视口中的大元素获取当前视口内可见的大尺寸元素。所谓「大元素」指占据视口相当比例的元素参数类型说明minWidthnumber可选默认0.3最小宽度为视口宽度的比例0.0 ~ 1.0minHeightnumber可选默认0.3最小高度为视口高度的比例0.0 ~ 1.0返回ArrayElement大尺寸可见元素数组。// 获取至少占视口尺寸 30% 的元素 var largeElements getLargeElements(); // 获取至少占视口尺寸 50% 的元素 var veryLargeElements getLargeElements(0.5, 0.5);该函数实现在 src/content_scripts/common/utils.js 第 484-514 行实现中会排除body、尺寸与视口几乎完全相同的元素、以及连续重复的矩形并过滤掉透明度过低opacity 0.1、隐藏或display: none的元素。11.4 getClickableElements获取可点击元素SurfingKeys 有自己的可点击元素识别逻辑如HTMLAnchorElement、cursor: pointer的元素。该函数提供两个额外参数用于识别 SurfingKeys 未能识别的可点击元素参数类型说明selectorStringstring可点击元素的额外 CSS 选择器patternregex匹配可点击元素文本的正则表达式返回可点击元素数组。var elms getClickableElements([rellink], /click this/);实现见 src/content_scripts/common/utils.js 第 624-635 行遍历元素树筛选出具有实际尺寸offsetHeight offsetWidth、cursor为pointer且匹配选择器或文本正则的元素最后通过filterOverlapElements过滤重叠元素。11.5 tabOpenLink批量打开链接参数类型说明strstring要打开的链接多个链接用\n换行分隔simultaneousnessnumber可选默认5同时打开的标签页数量其余链接排队在某个标签页关闭后继续打开示例tabOpenLink(https://github.com/brookhong/Surfingkeys)实现位于 src/content_scripts/common/utils.js 第 908 行支持字符串按换行拆分、数组与NodeList三种输入当链接数超过simultaneousness时会先弹出确认对话框再通过RUNTIME(openLink, ...)批量打开并排队剩余链接。十二、从源码看 API 的整体架构将以上 API 串联起来的核心是 src/content_scripts/common/api.js 中的createAPI()工厂函数。它接收clipboard、insert、normal、hints、visual、front、browser等模块实例组装出完整的 API 对象后导出并在 api.js 第 415-478 行 通过initSKFunctionListener(api, {...})注册为内容脚本侧的事件处理器——这正是用户脚本世界dispatchSKEvent(api, [...])消息的落地点。同时src/user_scripts/index.js 中的api对象是用户脚本直接面对的「门面」它把mapkey、Hints、Clipboard、Front、Normal、Visual等全部转发为跨世界消息。整个链路可概括为用户脚本src/user_scripts/index.js 的 api 门面 → dispatchSKEvent(api, [...]) 跨世界消息 → initSKFunctionListener 处理器src/content_scripts/common/api.js → createAPI() 内的真实实现mappings / hints / front / RUNTIME ...结语本文覆盖了 docs/API.md 中登记的全部 34 个 API包括 5 种模式的键位映射、搜索引擎别名体系、剪贴板、Hints 提示符、Normal/Visual 控制、Front 前端交互、RUNTIME 后台通信、ACE vim 映射以及页面工具函数并给出了各 API 在源码中的真实落点。你可以打开扩展的 Settings 页面将本文示例粘贴为用户脚本直接体验更进阶的用法如行内查询、Neovim 集成、Front.openOmnibar的UserURLs自定义列表则可进一步研读 src/content_scripts/common/api.js、src/content_scripts/front.js 与 src/content_scripts/common/utils.js 的完整实现。赞分享开发工具前端【免费下载链接】SurfingkeysMap your keys for web surfing, expand your browser with javascript and keyboard.项目地址https://gitcode.com/gh_mirrors/su/Surfingkeys点击查看免费下载相关推荐Surfingkeys用JavaScript和键盘扩展你的Chrome浏览器Surfingkeys用JavaScript和键盘扩展你的Chrome浏览器 在数字时代浏览器已成为我们日常工作和生活中不可或缺的工具。然而你是否曾因频繁开发工具前端Vimium 键盘流浏览器扩展实战指南快捷键、自定义映射与源码原理Vimium 键盘流浏览器扩展实战指南快捷键、自定义映射与源码原理 Vimium 是一款以 Vim 编辑器理念驱动的浏览器扩展让用户完全通过键盘完成页面滚动开发工具NVIDIA Qwen3.5-122B-A10B-NVFP4 评测在MMMU Pro与GPQA Diamond上的惊人表现对比NVIDIA Qwen3.5 122B A10B NVFP4 评测在MMMU Pro与GPQA Diamond上的惊人表现对比 NVIDIA Qwen3.5上一篇vscode-drawio 插件加载完全指南配置、授权机制与源码实现解析下一篇无需联网的电路仿真利器CircuitJS1 Desktop Mod 离线版上手完全指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考