# Surfingkeys - Expand your browser with JavaScript and keyboard. [![Node CI](https://github.com/brookhong/Surfingkeys/workflows/Node%20CI/badge.svg?branch=master)](https://github.com/brookhong/Surfingkeys/actions?query=workflow%3A%22Node+CI%22+branch%3Amaster) Surfingkeys is a browser extension (for Google Chrome, Chromium-based browsers, Firefox, and Safari) that provides keyboard-based navigation and control of the web in the spirit of the VIM editor. But it's not just for VIM users; it's for anyone who needs more shortcuts for their own functions. Surfingkeys is created with all settings described in JavaScript, so it's easy for anyone to map any keystrokes to their own defined JavaScript functions. For example, api.mapkey('', 'Show me the money', function() { api.Front.showPopup('a well-known phrase uttered by characters in the 1996 film Jerry Maguire (Escape to close).'); }); Surfingkeys does its best to make full use of the keyboard for web browsing, but there are some limitations from Google Chrome itself; please see the [Brook Build of Chromium](https://brookhong.github.io/2021/04/18/brook-build-of-chromium.html) for a more thorough experience. ## Installation * [Surfingkeys - Chrome Web Store](https://chrome.google.com/webstore/detail/surfingkeys/gfbliohnnapiefjpjlpjnehglfpaknnc) for Google Chrome, Chromium based browsers * [Surfingkeys โ€“ Get this Extension for ๐ŸฆŠ Firefox](https://addons.mozilla.org/en-US/firefox/addon/surfingkeys_ff/) for Firefox * [Surfingkeys - Microsoft Edge Addons](https://microsoftedge.microsoft.com/addons/detail/kgnghhfkloifoabeaobjkgagcecbnppg) for Microsoft Edge * [Surfingkeys on the Mac App Store](https://apps.apple.com/us/app/surfingkeys/id1609752330) for Safari, works on both macOS and iOS, except that on iOS an external keyboard must be connected to your device. There is one exception of the [special feature designed for iOS devices](https://youtu.be/xaTf2booQkQ) -- `Search selected with`. ### Feature availability | Features \ Browsers | Chromium family (above 45) | Firefox (above 57) | Safari (above 15) | |:---------------|:-----|:-----|:-----| | Follow links | Y | Y | Y | | Surfingkeys modes | Y | Y | Y | | Omnibar | Y | Y | partly | | Search selected with | Y | Y | Y | | Vim-like marks | Y | Y | Y | | Switch tabs | Y | Y | Y | | Windows management | Y | Y | N | | Commands | Y | Y | Y | | Smooth scroll | Y | Y | Y | | Session management | Y | Y | Y | | Repeats action by pressing number before mapkey | Y | Y | Y | | Hotkey to toggle Surfingkeys | Y | Y | Y | | VIM editor and Emacs editor | Y | Y | Y | | Dot to repeat previous action | Y | Y | Y | | Capture page | Y | Y | Y | | PDF viewer | Y | N | N | | Sync across devices | Y | N | Y | | Tab Groups | Y | Y | N | | Proxy | Y | N | N | | Markdown preview |Y | Y | N | | Load settings from a local file | needs native host | needs native host | macOS only | ### TABLE OF CONTENTS * [Feature list](#feature-list) * [Quick start](#quick-start) * [Follow links](#follow-links) * [Surfingkeys modes](#surfingkeys-modes) * [Omnibar](#omnibar) * [Search selected with](#search-selected-with) * [Vim-like marks](#vim-like-marks) * [Switch tabs](#switch-tabs) * [Windows management](#windows-management) * [Commands](#commands) * [Smooth scroll](#smooth-scroll) * [Session management](#session-management) * [Repeats action by pressing number before mapkey](#repeats-action-by-pressing-number-before-mapkey) * [Hotkey to toggle Surfingkeys](#hotkey-to-toggle-surfingkeys) * [Proxy settings](#proxy-settings) * [VIM editor and Emacs editor](#vim-editor-and-emacs-editor) * [Dot to repeat previous action](#dot-to-repeat-previous-action) * [Markdown preview](#markdown-preview) * [Capture page](#capture-page) * [PDF viewer](#pdf-viewer) * [Edit your own settings](#edit-your-own-settings) * [Load settings from a local file](#load-settings-from-a-local-file) * [Chat with LLM](#chat-with-llm) * [License](#license) ## Feature list * All settings are set up within a javascript file, which makes it easy to create mappings to user-customized functions. * A large cursor in visual mode, which makes visual mode better. * Search selected with, which works in both normal mode and visual mode. * Help messages are automatically generated for mappings. * `*` to search word under cursor in visual mode. * Scroll actions like page up/down (`e` `d`) work not only for the top window but also for scrollable DIVs. * `w` to switch frames if there are any. * Session management * A versatile bookmark/url finder * Count prefixes to repeat actions * Use vim editor to edit input on page * Dot to repeat previous action * `;pm` to preview markdown * Emoji completion in Insert mode * Rich hints for keystroke * Everything in Surfingkeys works for PDFs * Regional Hints mode * Chat with LLM ## Quick start After you install the extension from [Chrome Web Store](https://chrome.google.com/webstore/detail/surfingkeys/gfbliohnnapiefjpjlpjnehglfpaknnc) or [Firefox Add-ons](https://addons.mozilla.org/en-US/firefox/addon/surfingkeys_ff/), open a site you'd like to browse. Then press `?` or `u` to take a quick look at the default mappings first. Press `Esc` to hide the usage popover. Try some mappings described in the usage popover. For example, press `e` to scroll a page up, `d` to scroll a page down, `;e` to open settings page. * `?` to show help ![help](https://user-images.githubusercontent.com/288207/72702854-0a74a480-3b8f-11ea-9be3-69745c280c3f.png) * `t` to search bookmarks/history ![urls](https://cloud.githubusercontent.com/assets/288207/16182031/58e15ec4-36d4-11e6-9cc5-ff35970df25f.png) * `/` to find in current page ![find](https://cloud.githubusercontent.com/assets/288207/16182044/65f4713c-36d4-11e6-9e21-6b61a858f080.png) * `f` to follow links ![follow](https://cloud.githubusercontent.com/assets/288207/16182118/18d27678-36d5-11e6-9759-d8b5ff49930c.png) * `v` to toggle visual mode ![visual](https://cloud.githubusercontent.com/assets/288207/16182120/1cc536da-36d5-11e6-9e08-293cdb8fbcd2.png) * `T` to switch tabs ![tabs](https://cloud.githubusercontent.com/assets/288207/10328839/f0143ffe-6ceb-11e5-8eee-962db94b2c22.png) ## Surfingkeys modes There are three modes in Surfingkeys: normal, visual and insert. ### Normal mode, the default mode. When you open a page, it enters normal mode automatically. All mappings added with `mapkey` work in this mode. ### Visual mode, the mode for text selection and actions on the selected text. Press `v` to toggle visual mode. You'll see an indicator at the bottom of the current page - `Caret` or `Range`, and a large cursor on the page. The cursor is made large for visibility, as sometimes it's not easy for humans to locate a normal cursor on a web page. `Caret` indicates that the cursor is moved when you press jkhl, `Range` indicates that you'll select text when moving the cursor. Now here is a small practice, 1. press `v`, you'll see `Caret` 1. use the VIM key bindings to move the cursor somewhere. 1. press `v` again, you'll see `Range`. 1. use the VIM key bindings to select some text. 1. press `sg` to see what will happen. 1. press `v` again to go back to normal mode. All mappings added with `vmapkey` work in this mode, with some built-in mappings like those in VIM - `j` `k` `h` `l` `b` `w` `0` `$` etc. ![search_selected](https://cloud.githubusercontent.com/assets/288207/17644215/759f1e70-61b3-11e6-8bf8-0bdff7d0c933.gif) * `zz` make cursor at center of window. * `f` forward to next char. * `F` backward to next char. * `;` repeat latest f, F. * `,` repeat latest f, F in opposite direction. ### Hints mode Press `f` to enter Hints mode to follow links. There are several other keystrokes to enter Hints mode with different behaviors, such as `cf` for continuous following, `af` for active following. The default hint characters for links are `asdfgqwertzxcvb`. Hints mode quits when a non-hint key is pressed. Add the line below to your settings to make it right-handed: api.Hints.setCharacters('yuiophjklnm'); // for right hand When hints are overlapped, press `Shift` to flip them. Hold `space` to hold hints temporarily, release `space` to restore hints. Hints are placed at the center of target links; you could add the line below to your settings to align them left. settings.hintAlign = "left"; #### Regional Hints mode Press `L` to enter regional Hints mode by picking a visually large element. There are some built-in actions in regional Hints mode, * `Esc` to exit regional hints mode * `ct` to copy text from target element * `ch` to copy HTML from target element * `d` to delete target element * `l` to chat with AI about the text of the element * `p` to select the parent element of the target element [Demo on YouTube](https://www.youtube.com/watch?v=pFPOzAZDO38) ### Insert mode When focus is switched into any editable element by whatever means (`i` hints, `f` hints, or mouse click), Insert mode is on. All mappings added with `imapkey` work in this mode. * `Ctrl-i` to open vim editor to edit. * `Ctrl-'` to toggle quotes in an input element, which is useful for search engines like Google. * `Ctrl-e` to move the cursor to the end of the line. * `Ctrl-a` to move the cursor to the beginning of the line; use `Ctrl-f` in Windows to avoid conflict with select all. * `Ctrl-u` to delete all entered characters before the cursor. * `Alt-b` to move the cursor backward 1 word. * `Alt-f` to move the cursor forward 1 word. * `Alt-w` to delete a word backwards. * `Alt-d` to delete a word forwards. * `Ctrl-g` to correct the grammar of the current input with LLM. `imap` and `iunmap` work for insert mode. api.imap(',,', ""); // press comma twice to leave current input box. api.imap(';;', ""); // press semicolon twice to toggle quote. #### Emoji completion When a user inputs a colon and 2 (set by `settings.startToShowEmoji`) characters such as `:gr` in insert mode, Surfingkeys will try to find matching emoji and list them if any are found. ![emoji](https://cloud.githubusercontent.com/assets/288207/23602453/924ed762-028b-11e7-86f3-bf315c0a2499.gif) If you want this feature disabled completely, use below settings: api.iunmap(":"); If you'd like emoji suggestions popup as soon as you input colon, use below: settings.startToShowEmoji = 0; [Complete list of Emoji](https://github.com/brookhong/Surfingkeys/blob/master/src/pages/emoji.tsv) ### Find `Find` is not actually a mode; it's just another way to enter visual mode. Press `/` to open the find bar, which sits at almost the same position as the Mode indicator, and type something there. All occurrences of your input will be highlighted. Press `Enter` to finish the finding, and you're in `Caret` visual mode now; press `n` to find next, `N` to find previous. Press `Ctrl-Enter` to find the exact whole word entered, like with the input `\bkeyword\b`. ### PassThrough mode Pressing `Alt-i` to enter PassThrough mode gives you a chance to temporarily suppress SurfingKeys, which means Surfingkeys will not care about any key press until you leave this mode by pressing `Esc`. In this mode, you could use built-in shortcuts from any site itself. Please see [Feature Request: implement Vimium-style insert mode ยท Issue #656](https://github.com/brookhong/Surfingkeys/issues/656) for why we brought this in and the difference between `Alt-i` and `Alt-s`. Pressing `p` enters an ephemeral PassThrough mode, which automatically quits after 1 second. ### Lurk mode Users can specify the pages where Surfingkeys will lurk until it is called out by `Alt-i` or `p` (for the ephemeral case), such as settings.lurkingPattern = /https:\/\/github\.com|.*confluence.*/i; If the loading page matches with the `lurkingPattern`, Surfingkeys will enter `lurk` mode by default, in which mode only `Alt-i` and `p` are registered by Surfingkeys to activate `normal` mode. When user presses `Esc` or timeout, Surfingkeys reverts back to `lurk` mode. API `lmap` can be used to change the shortcuts, for example, api.lmap("", ""); The extension icon in the toolbar reflects the current status of Surfingkeys: * Grey -- disabled. * Half grey/half color -- lurking. * Color -- enabled. ## Omnibar The omnibar provides various functions that need user input, for example, * Open URLs (from both bookmarks and history) with `t` * Open bookmarks with `b` * Open search engines with `og` / `ow` ... * Open commands with `:` ### key bindings in Omnibar * `Enter` to open selected item and close omnibar. * `Ctrl-Enter` to open selected item, but keep omnibar open for more items to be opened. * `Shift-Enter` to open selected item in current tab and close omnibar. If you'd like to open in current tab by default, please use `go`. * `Tab` to forward cycle through the candidates. * `Shift-Tab` to backward cycle through the candidates. * `Ctrl-.` to show results of next page * `Ctrl-,` to show results of previous page * `Ctrl-c` to copy all listed items In omnibar opened with `t`: `Ctrl-d` to delete from bookmarks or history In omnibar opened with `b`: `Ctrl-Shift-` to create a vim-like mark ![search_engine](https://cloud.githubusercontent.com/assets/288207/17644214/759ef1d4-61b3-11e6-9bd9-70c38c8b80e0.gif) `cmap` can be used for the Omnibar to change mappings, for example: api.cmap('', ''); api.cmap('', ''); ### Add bookmark `ab` is a shortcut to bookmark the current page. After you press `ab`, an Omnibar is displayed for you to choose a folder in which to place the new bookmark. If you want to place the new bookmark into a new folder, you can type the folder name -- **which must end with `/`** -- in the Omnibar. For example, I choose folder `/Bookmarks Bar/tool/`, and append `abc/`, then current page will be bookmarked into `/Bookmarks Bar/tool/abc/`. If there is no `/` behind `abc`, `abc` will be used as the title of the new bookmark. ## Search selected with My favorite feature from when I used Firefox. Both Firefox and Chrome extensions provide it through the context menu; Surfingkeys provides it through key mappings. By default, when you press `sg` in normal mode, it will search the selected text with Google; if there is none selected, it will search text from the system clipboard with Google. In visual mode, it will search the selected text with Google. The `g` in `sg` is a search alias for Google; there are some other built-in search aliases, like `w` for Bing. So press `sw` to search the selected text with Bing. Refer to [Add search alias to omnibar](https://github.com/brookhong/Surfingkeys/blob/master/docs/API.md#addsearchalias) to add your own search alias, especially those search engines for company inside. Besides that, there is `sog`, to search the selected text only within this site with Google. For `sog`, `s` is the search_leader_key, `o` is the only_this_site_key, `g` is the search alias. The search_leader_key `s` plus the capital alias `G` will search the selected text with Google interactively; all other search aliases and those you added through API `addSearchAlias` work in the same way. ## Vim-like marks You can create vim-like marks by pressing `m` followed by a word character (0-9 / a-z / A-Z), which is used as the mark name. For example, if you press `ma` on this page, you'll create a mark named `a` which points to this page. Then pressing `'a` anywhere will jump to this page. In this way, the created mark always points to the current URL. You can also create vim-like marks from the bookmarks. Try the following steps: 1. press `b` to open bookmarks. 1. type something to locate the URL you'd like to create vim-like mark for. 1. Hold Ctrl + Shift, press a mark name, such as `f`. Then afterwards `'F` will open that URL directly. This is very useful for pages you access frequently. Press `om` to check out all the vim-like marks you have created. ## Switch tabs By default, pressing `T` shows all opened tabs in an overlay; pressing the hint character then switches to the related tab. ![tabs_overlay](https://github.com/brookhong/Surfingkeys/assets/288207/f0ca339d-133f-4fb0-b902-cdc64fc71374) If there is no hint label matched with your pressing, omnibar will be opened. So you can always press a non-hint character such as `;` or `j` to launch omnibar directly from the tabs overlay. There is also `settings.tabsThreshold` here. When the total number of opened tabs exceeds `settings.tabsThreshold`, the omnibar will be used for choosing tabs. ![tabs_omnibar](https://cloud.githubusercontent.com/assets/288207/10544630/1fbdd02c-7457-11e5-823c-14411311c315.png) If you prefer to use omnibar always, use below mapping: api.mapkey('', 'Choose a tab with omnibar', function() { api.Front.openOmnibar({type: "Tabs"}); }); which works same as: settings.tabsThreshold = 0; The tabs are displayed in MRU order by default, either in the omnibar or the overlay. If you want them in natural order, use: settings.tabsMRUOrder = false; ## Windows management `W` brings up a popup of windows; you can select one of them and press `Enter` to move the current tab to the selected window. If there is only one window, `W` will move the current tab to a new window directly. `;gt` opens the Omnibar with all tabs not from the current window; you can type some text to filter the tabs, then press `Enter` to gather the filtered tabs into the current window. `;gw` gathers all tabs into the current window. So to group your tabs into windows, you can use `W` to move one tab to a specified window or use `;gt` to gather filtered tabs into the current window. ## Commands `:` opens the omnibar for commands, where you can execute any pre-defined commands there. The result will be displayed below the omnibar. // create shortcuts for the command with different parameters api.map(';pa', ':setProxyMode always'); api.map(';pb', ':setProxyMode byhost'); api.map(';pd', ':setProxyMode direct'); Besides commands, you can also run javascript code. ![commands_in_omnibar](https://cloud.githubusercontent.com/assets/288207/11527801/fadee82c-991d-11e5-92e9-b054796a6a75.png) ## Smooth scroll Smooth scroll works for any scrollable element. It is on by default; to turn it off, use: settings.smoothScroll = false; `j`/`k` scroll in one step with a size of 70; you could change it as below: settings.scrollStepSize = 140; ## Session management To create a session in Chrome with Surfingkeys is to save the URLs of all tabs; to open a session is to open all of its URLs in separate tabs. So a session is basically a named list of URLs. * `ZZ` will save all current tabs into a session named `LAST` then quit. * `ZR` will restore the session named `LAST`. * `ZQ` will just quit. You can create multiple sessions with different names in command mode. Press `:` to open omnibar for commands, then input: createSession works Surfingkeys will create a session named `works` for you, to open the session with command input as: openSession works To list all your saved sessions: listSession To delete a session: deleteSession works ## Repeats action by pressing number before mapkey If you need to repeat some action several times, just press a number before the mapkey, such as `3d`, which will scroll down 3 pages. Repeats also work for tab navigation. For example, if you're now on the 1st Tab and you want to switch to the 4th Tab, * press `3R` to achieve that * `3E` will switch back to 1st Tab. Another example is to move one tab. Say you're on the 12th Tab of 23 tabs. * `11<<` will move current tab to beginning. * `10>>` will move current tab to end. Usually, you need not count the number; you can just prefix a large number such as `99<<` if you want to move a tab to the beginning or end. ## Hotkey to toggle Surfingkeys By default, `Alt-s` toggles Surfingkeys for the current site. When Surfingkeys is turned off, all mappings stop working except the hotkey. To change the hotkey, use the settings below: api.map('', ''); // hotkey must be one keystroke with/without modifier, it can not be a sequence of keystrokes like `gg`. When Surfingkeys is turned off on some site by `Alt-s`, the status will be persisted in settings, for example, "blocklist": { "https://github.com": 1 }, `Alt-s` once more will remove it from settings.blocklist. The settings data is not always presented in snippets; you can use `yj` to dump all settings into the clipboard, then paste it into your text editor to check. Another way to disable Surfingkeys is to use `settings.blocklistPattern`, please refer to [regex for disabling](https://github.com/brookhong/Surfingkeys/issues/63). ## Proxy settings SwitchySharp is a great extension for switching proxies, but my use case with it is very simple: 1. create a profile using PAC script. 1. maintain site list in the PAC script, use proxy if the site being accessed is in the list. 1. whenever I come into a site blocked by something, I add it to the list in PAC script. 1. click the SwitchySharp icon to reload the profile. 1. casually I click the SwitchySharp icon to switch profile between `direct` and `pac_script`. To avoid manually editing PAC script and reloading/switching profile by clicking SwitchySharp icon, I replaced SwitchySharp by integrating proxy settings into Surfingkeys, and provides related commands and shortcuts. * setProxy, to set proxy, some examples: setProxy 192.168.1.100:8080 setProxy 127.0.0.1:1080 SOCKS5 * setProxyMode, to set proxy mode; there are six modes: direct, byhost, bypass, always, system and clear. direct Chrome will connect to all sites directly. byhost Chrome will only connect to sites added in settings through related proxy. You could add multiple pairs of `proxy` and `hosts`, for hosts matched with `hosts` `proxy` will be used. bypass Chrome will connect to all sites through proxy, with specified hosts excluded. always Chrome will connect to all sites through proxy. system Use proxy configuration taken from the operating system. clear Surfingkeys will not take control of proxy settings, this is the default mode. * `cp`, toggle proxy for current site. * `;pa`, shortcut for `:setProxyMode always` * `;pb`, shortcut for `:setProxyMode byhost` * `;pc`, shortcut for `:setProxyMode clear` * `;pd`, shortcut for `:setProxyMode direct` * `;ps`, shortcut for `:setProxyMode system` ## VIM editor and Emacs editor Thanks to ACE for the vim editor, Surfingkeys integrates ACE for the vim editor. The vim editor is used: * to edit any input on html page * to edit URL to open in new tab * to edit settings You could change to Emacs keybindings for the editor by adding below settings: settings.aceKeybindings = "emacs"; With Emacs keybindings, use `C-x C-s` to save your input. ### Edit any input on html page In normal mode, press capital `I`, then use a hint letter to pick an input box. A vim editor is opened for you to edit text. The vim editor is opened in slightly different ways for ``, `