Convert Figma logo to code with AI

glacambre logofirenvim

Embed Neovim in Chrome, Firefox & others.

4,727
146
4,727
98

Top Related Projects

Map your keys for web surfing, expand your browser with javascript and keyboard.

23,183

The hacker's browser.

A Vim-like interface for Firefox, inspired by Vimperator/Pentadactyl.

Vim bindings for Google Chrome.

1,509

wasavi is an extension for Chrome, Firefox, and Opera. wasavi changes a textarea element to virtual vi editor which supports almost all the vi/ex commands.

Quick Overview

Firenvim is a browser extension that embeds Neovim in your browser's text areas and input fields. It allows users to leverage the power of Neovim for editing text on the web, providing a seamless integration between the browser and the popular text editor.

Pros

  • Brings the full power of Neovim to web-based text editing
  • Customizable and extensible through Neovim configurations
  • Supports a wide range of browsers (Chrome, Firefox, Safari)
  • Improves productivity for users familiar with Vim/Neovim

Cons

  • Requires Neovim to be installed on the user's system
  • May have a steep learning curve for users unfamiliar with Vim/Neovim
  • Occasional compatibility issues with certain websites or text areas
  • Performance may vary depending on the complexity of Neovim configuration

Getting Started

  1. Install Neovim on your system.
  2. Install the Firenvim extension for your browser from the official store.
  3. Create a Firenvim-specific configuration in your Neovim config:
if exists('g:started_by_firenvim')
  set guifont=DejaVu\ Sans\ Mono:h12
  " Your Firenvim-specific settings here
endif
  1. Open a text area in your browser and press <C-e> to activate Firenvim.

For more detailed setup and configuration options, refer to the project's GitHub repository and documentation.

Competitor Comparisons

Map your keys for web surfing, expand your browser with javascript and keyboard.

Pros of Surfingkeys

  • Broader browser support, including Chrome, Firefox, and Safari
  • More extensive set of default keybindings for various browsing actions
  • Includes features like visual mode and omnibar for enhanced navigation

Cons of Surfingkeys

  • Steeper learning curve due to more complex configuration options
  • May conflict with some browser extensions or website functionalities
  • Limited to browser-based text input fields

Code Comparison

Surfingkeys configuration example:

mapkey('<Ctrl-y>', 'Show me the money', function() {
    Front.showPopup('Here is the money!');
});

Firenvim configuration example:

let g:firenvim_config = {
    \ 'globalSettings': {
        \ 'alt': 'all',
    \  },
    \ 'localSettings': {
        \ '.*': {
            \ 'cmdline': 'neovim',
            \ 'content': 'text',
            \ 'priority': 0,
            \ 'selector': 'textarea',
            \ 'takeover': 'always',
        \ },
    \ }
\ }

While Surfingkeys focuses on enhancing browser navigation with Vim-like keybindings, Firenvim integrates Neovim directly into browser text areas. Surfingkeys offers more general browsing improvements, while Firenvim provides a full-fledged Vim environment for text editing within the browser.

23,183

The hacker's browser.

Pros of Vimium

  • Broader browser compatibility (Chrome, Firefox, Edge)
  • Simpler setup and configuration
  • Lightweight and less intrusive in browser functionality

Cons of Vimium

  • Limited to browser navigation and basic text input
  • Lacks full Vim emulation in text areas
  • Cannot use Vim plugins or custom configurations

Code Comparison

Vimium (JavaScript):

const scrollStepSize = 60;
function scrollBy(x, y) {
  window.scrollBy(x, y);
}

Firenvim (TypeScript):

export function getScrollPercent(): number {
    return window.scrollY / (document.documentElement.scrollHeight - window.innerHeight) * 100;
}

Vimium focuses on browser navigation and simple scrolling, while Firenvim integrates more deeply with the browser, providing full Vim functionality in text areas. Vimium's code is generally simpler, reflecting its focus on basic navigation, while Firenvim's code is more complex, handling full Vim emulation and browser integration.

Firenvim offers a more comprehensive Vim experience within the browser, including the ability to use Vim plugins and custom configurations. However, it requires more setup and is currently limited to Firefox and Chrome-based browsers. Vimium, on the other hand, provides a lighter, more accessible Vim-like navigation experience across a wider range of browsers, but with more limited functionality in text editing scenarios.

A Vim-like interface for Firefox, inspired by Vimperator/Pentadactyl.

Pros of Tridactyl

  • Broader functionality: Tridactyl aims to replicate Vim-like behavior across the entire browser, not just text inputs
  • More extensive customization options: Offers a wide range of configurable settings and keybindings
  • Active community: Larger user base and more frequent updates

Cons of Tridactyl

  • Steeper learning curve: May be overwhelming for users new to Vim-like interfaces
  • Limited to Firefox: Not available for other browsers, unlike Firenvim which supports multiple browsers

Code Comparison

Tridactyl configuration example:

" Set default search engine
set searchengine duckduckgo
" Open link in new tab
bind F hint -b

Firenvim configuration example:

vim.g.firenvim_config = {
    globalSettings = { alt = "all" },
    localSettings = {
        [".*"] = { takeover = "never", priority = 0 }
    }
}

Both projects aim to bring Vim-like functionality to the browser, but they differ in scope and implementation. Tridactyl focuses on transforming the entire browser experience, while Firenvim specializes in enhancing text input fields with Neovim integration.

Vim bindings for Google Chrome.

Pros of chromium-vim

  • Specifically designed for Chromium-based browsers, offering tight integration
  • Lightweight and focused on browser-specific Vim-like functionality
  • Potentially faster performance due to its specialized nature

Cons of chromium-vim

  • Limited to Chromium-based browsers, lacking cross-browser compatibility
  • May have fewer features compared to Firenvim's broader text editing capabilities
  • Less active development and community support

Code Comparison

chromium-vim

// Example of key mapping in chromium-vim
map j scrollDown
map k scrollUp
map gg scrollToTop

Firenvim

" Example of Firenvim configuration in .vimrc
let g:firenvim_config = {
    \ 'globalSettings': {
        \ 'alt': 'all',
    \  },
    \ 'localSettings': {
        \ '.*': {
            \ 'cmdline': 'neovim',
            \ 'content': 'text',
            \ 'priority': 0,
            \ 'selector': 'textarea',
            \ 'takeover': 'always',
        \ },
    \ }
\ }

Firenvim offers a more comprehensive Vim experience in browsers, supporting various text inputs and allowing for complex Vim configurations. It's not limited to a specific browser family, making it more versatile. However, chromium-vim may provide a more streamlined and optimized experience for Chromium users who primarily need basic Vim-like navigation and commands in their browser.

1,509

wasavi is an extension for Chrome, Firefox, and Opera. wasavi changes a textarea element to virtual vi editor which supports almost all the vi/ex commands.

Pros of wasavi

  • Supports a wider range of browsers, including older versions
  • Offers a more lightweight implementation, potentially better for performance
  • Provides a standalone mode for use outside of text areas

Cons of wasavi

  • Less actively maintained, with fewer recent updates
  • More limited feature set compared to Firenvim's extensive Neovim integration
  • May require more manual configuration for advanced usage

Code Comparison

wasavi:

var wasavi = {
  init: function() {
    // Initialize wasavi
  },
  // ... other methods
};

Firenvim:

local firenvim = {
  setup = function()
    -- Setup Firenvim
  end,
  -- ... other functions
}

The code snippets show a basic structural difference between the two projects. wasavi uses JavaScript and a more traditional object-oriented approach, while Firenvim leverages Lua and a functional style, reflecting its tight integration with Neovim.

Both projects aim to bring Vim-like editing to web browsers, but they differ in their implementation and target audience. Firenvim offers a more comprehensive Neovim experience, while wasavi provides a lighter, more browser-native solution. The choice between them depends on the user's specific needs and preferences.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Firenvim Build & Test Vint Luacheck Matrix Wiki

Turn your browser¹ into a Neovim client (demos: justinmk 🇺🇸, Sean Feng 🇨🇳).

¹ Firefox and Chrome are specifically supported. Other Chromium based browsers such as Brave, Vivaldi, Opera, and Arc should also work but are not specifically tested.

Firenvim demo

How to use

Just click on any textarea and it will be immediately replaced by an instance of Firenvim. To set the content of the now hidden textarea to the content of the Neovim instance, simply :w. If you want to close the Firenvim overlay and return to the textarea, use :q. If you selected an element where you expected the Firenvim frame to appear and it didn't, try pressing <C-e>.

Installing

Before installing anything please read SECURITY.md and make sure you're okay with everything mentioned. In the event you think of a way to compromise Firenvim, please send me an email (you can find my address on my website).

  1. Install Firenvim as a regular NeoVim plugin, then run the built-in post-install script.

    • lazy

      { 'glacambre/firenvim', build = ":call firenvim#install(0)" }
      
      
    • vim-plug

      Plug 'glacambre/firenvim', { 'do': { _ -> firenvim#install(0) } }
      
    • minpac

      call minpac#add('glacambre/firenvim', { 'type': 'opt', 'do': 'packadd firenvim | call firenvim#install(0)'})
      if exists('g:started_by_firenvim')
        packadd firenvim
      endif
      
    • vundle, others

      Install the plugin as you usually would, then run this shell command:

      $ nvim --headless "+call firenvim#install(0) | q"
      
  2. Install the Firenvim addon for your browser from Mozilla's store or Google's.

If you would rather build and install Firenvim from source, check CONTRIBUTING.md.

Other browsers

Other browsers aren't supported for now. Opera, Vivaldi and other Chromium-based browsers should however work just like in Chromium and have similar install steps. Brave, Edge, and Arc might work, Safari doesn't (it doesn't support Webextensions).

Permissions

Firenvim currently requires the following permissions for the following reasons:

Configuring Firenvim

Manually triggering Firenvim

You can configure the keybinding to manually trigger Firenvim (<C-e> by default) in the shortcuts menu in about://addons on Firefox, or in chrome://extensions/shortcuts on Chrome.

Temporarily disabling Firenvim in a tab

Temporarily disabling (and re-enabling) Firenvim in a tab can be done either by clicking on the Firenvim button next to the urlbar or by configuring a browser shortcut (see the previous section to find out how browser shortcuts can be configured).

Building a Firenvim-specific config

Note: If you would prefer VimScript examples, you can consult the (outdated) readme from commit 132979166a02319f0b49815135e60a4e4599de91 or earlier.

New: With Neovim nightly builds from 2023/02/17 or more recent, you can use $NVIM_APPNAME to define a completely separate firenvim config. In order to do that, make sure "NVIM_APPNAME" is appropriately set when you run firenvim#install().

When it starts Neovim, Firenvim sets the variable g:started_by_firenvim which you can check to run different code in your init.lua. For example:

if vim.g.started_by_firenvim == true then
  vim.o.laststatus = 0
else
  vim.o.laststatus = 2
end

Alternatively, you can detect when Firenvim connects to Neovim by using the UIEnter autocmd event:

vim.api.nvim_create_autocmd({'UIEnter'}, {
    callback = function(event)
        local client = vim.api.nvim_get_chan_info(vim.v.event.chan).client
        if client ~= nil and client.name == "Firenvim" then
            vim.o.laststatus = 0
        end
    end
})

Similarly, you can detect when Firenvim disconnects from a Neovim instance with the UILeave autocommand.

Using different settings depending on the url/page/element being edited

The nvim buffer loaded into a textarea is given a unique name. All buffers are named something like this: domainname_page_selector.txt (see the toFileName function).

This alows you to configure different settings by creating autocommands targeting/matching the buffername for that url/page/element. For example, this will set file type to markdown for all GitHub buffers:

vim.api.nvim_create_autocmd({'BufEnter'}, {
    pattern = "github.com_*.txt",
    command = "set filetype=markdown"
})

To view the buffername of your nvim instance in a textarea, use :buffers.

Understanding Firenvim's configuration object

You can configure everything else about Firenvim by creating a dictionary named vim.g.firenvim_config in your init.lua and setting the keys "globalSettings" and "localSettings". In the dictionary vim.g.firenvim_config["localSettings"] you can map Javascript patterns that match against the full URL to settings that are used for all URLs matched by that pattern. When multiple patterns match a URL, the pattern with the highest "priority" value is used. Here is an example (the settings and their possible values will be explained in the next subsections):

vim.g.firenvim_config = {
    globalSettings = { alt = "all" },
    localSettings = {
        [".*"] = {
            cmdline  = "neovim",
            content  = "text",
            priority = 0,
            selector = "textarea",
            takeover = "always"
        }
    }
}

With this configuration, takeover will be set to always on all websites. If we wanted to override this value on british websites, we could add the following lines to our init.vim. Notice how the priority of this new regex is higher than that of the .* regex:

vim.g.firenvim_config.localSettings["https?://[^/]+\\.co\\.uk/"] = { takeover = 'never', priority = 1 }

Configuring what elements Firenvim should appear on

The selector attribute of a localSetting controls what elements Firenvim automatically takes over. Here's the default value:

vim.g.firenvim_config.localSettings['.*'] = { selector = 'textarea:not([readonly], [aria-readonly]), div[role="textbox"]' }

If you don't want to use Firenvim with rich text editors (e.g. Gmail, Outlook, Slack…) as a general rule, you might want to restrict Firenvim to simple textareas:

vim.g.firenvim_config.localSettings['.*'] = { selector = 'textarea' }

Since selector is just a CSS selector, you have access to all of CSS's pseudo selectors, including :not(), which allows you to exclude elements that have certain attributes, like this:

vim.g.firenvim_config.localSettings['.*'] = { selector = 'textarea:not([class=xxx])' }

Configuring Firenvim to not always take over elements

Firenvim has a setting named takeover that can be set to always, empty, never, nonempty or once. When set to always, Firenvim will always take over elements for you. When set to empty, Firenvim will only take over empty elements. When set to never, Firenvim will never automatically appear, thus forcing you to use a keyboard shortcut in order to make the Firenvim frame appear. When set to nonempty, Firenvim will only take over elements that aren't empty. When set to once, Firenvim will take over elements the first time you select them, which means that after :q'ing Firenvim, you'll have to use the keyboard shortcut to make it appear again. Here's how to use the takeover setting:

vim.g.firenvim_config.localSettings['.*'] = { takeover = 'always' }

Choosing a command line

You can chose between neovim's built-in command line, firenvim's command line and no command line at all by setting the localSetting named cmdline to either neovim, firenvim or none, e.g.:

vim.g.firenvim_config.localSettings['.*'] = { cmdline = 'firenvim' }

Choosing none does not make sense unless you have alternative way to display the command line such as noice.nvim.

Editing HTML directly

The content localSetting controls how Firenvim should read the content of an element. Setting it to html will make Firenvim fetch the content of elements as HTML, text will make it use plaintext. The default value is text:

vim.g.firenvim_config.localSettings['.*'] = { content = 'html' }

Special characters on MacOS

On MacOS, the default keyboard layouts emit special characters when the alt (i.e. option) key is held down. From the perspective of the browser, these special characters replace the underlying "main" character of a keystroke event while retaining the modifier. For example, in the standard US layout the key chord alt-o is received in the browser as alt-ø rather than alt-o. Further, certain alt-chords represent "dead keys", which apply a diacritic to the next character entered. Pressing alt-e followed by a produces the single character "á" while alt-u followed by a produces "ä". To produce this behavior, diacritic-mapped strokes like alt-e and alt-u are themselves mapped to a "Dead key" character.

These behaviors complicate the support of special character and alt/meta (A- or M-) vim mappings on MacOS in two ways:

  1. There is no way to generate unmodified special character key events. For example, since the only way to generate the character "ø" via the keyboard is by holding down alt, any key event with the "ø" character will also have an alt modifier. If we forward this directly to Vim, it will be received as <M-ø>.

  2. There is no way to generate alt-modified plain alphanumeric characters. For example, an <M-o> mapping won't work because pressing alt-o generates <M-ø> rather than <M-o>.

Terminal and standalone GUI applications can solve these problems by changing the interpretation of the alt key at the application level. Terminal.app and iTerm2, for instance, both provide a "use Option as Meta key" preference that converts incoming alt-chords at the application level. Firenvim, however, is a browser extension that operates off of browser keystroke events rather than application-level events. At present, we are unsure how to implement this "use option as meta" functionality at the browser event level (help here is welcome!). However, there are some workarounds.

For problem (1), Firenvim will by default drop the alt key on MacOS for any special character, defined here as non-alphanumeric (not matching /[a-zA-Z0-9]/). This means alt-o will be forwarded to NeoVim as "ø" rather than "M-ø". Note that this behavior can be changed by setting the alt setting of the globalSettings configuration to all, like this:

Making Firenvim ignore keys

You can make Firenvim ignore key presses (thus letting the browser handle them) by setting key-value pairs in globalSettings.ignoreKeys. The key needs to be the neovim mode the key press should be ignored in and the value should be an array containing the textual representation of the key press you want ignored. If you want to ignore a key press in all modes, you can use all as mode key.

For example, if you want to make Firenvim ignore <C-1> and <C-2> in normal mode and <C--> in all modes to let your browser handle them, you should define ignoreKeys like this:

vim.g.firenvim_config = {
    globalSettings = {
        ignoreKeys = {
            all = { '<C-->' },
            normal = { '<C-1>', '<C-2>' }
        }
    }
}

Mode names are defined in Neovim's cursor_shape.c. Note that if the key press contains multiple modifiers, Shift needs to be first, Alt second, Control third and OS/Meta last (e.g. Ctrl+Alt+Shift+1 needs to be <SAC-1>). If your keyboard layout requires you to press shift in order to press numbers, shift should be present in the key representation (e.g. on french azerty keyboards, <C-1> should actually be <SC-1>).

Interacting with the page

You can execute javascript in the page by using firenvim#eval_js. The code has to be a valid javascript expression (NOT a statement). You can provide the name of a function that should be executed with the result of the expression. Note that some pages prevent evaluating JavaScript with their CSP and this can't be worked around. Here's an example:

vim.fn['firenvim#eval_js']('alert("Hello World!")', 'MyFunction')

You can move focus from the editor back to the page or the input field by calling firenvim#focus_page or firenvim#focus_input. Here's an example that does exactly this if you press <Esc> twice while in normal mode:

vim.api.nvim_set_keymap("n", "<Esc><Esc>", "<Cmd>call firenvim#focus_page()<CR>", {})

There is also a function named firenvim#hide_frame() which will temporarily hide the Firenvim frame. You will then be able to bring the neovim frame back either by unfocusing and refocusing the textarea or by using the keybinding to manually trigger Firenvim.

vim.api.nvim_set_keymap("n", "<C-z>", "<Cmd>call firenvim#hide_frame()<CR>", {})

A function named firenvim#press_keys() will allow you to send key events to the underlying input field by taking a list of vim-like keys (e.g. a, <CR>, <Space>…) as argument. Note that this only "triggers" an event, it does not add text to the input field. For example if you'd like firenvim to send <CR> to the webpage when you press <CR> in the editor, you can use the following mapping which is useful with chat apps:

vim.api.nvim_create_autocmd({'BufEnter', {
    pattern = "riot.im_*",
    command = [[inoremap <CR> <Esc>:w<CR>:call firenvim#press_keys("<LT>CR>")<CR>ggdGa]],
})

Note that our goal is to make the mapping type firenvim#press_keys("<CR>") in vim's command prompt and then execute it. Since we want the keys <CR> to be typed and not Enter to be pressed, we can't use <CR> because it would be interpreted by inoremap. Hence we use <LT>CR> in order to type the keys <CR>. Similarly, if you want to type the keys <C-CR> you'd use <LT>C-CR>.

Known Issues: some websites do not react to firenvim#press_keys (e.g. Slack).

Automatically syncing changes to the page

Since Firenvim simply uses the BufWrite event in order to detect when it needs to write neovim's buffers to the page, Firenvim can be made to automatically synchronize all changes like this:

vim.api.nvim_create_autocmd({'TextChanged', 'TextChangedI'}, {
    nested = true,
    command = "write"
})

Depending on how large the edited buffer is, this could be a little slow. This more sophisticated approach will throttle writes:

vim.api.nvim_create_autocmd({'TextChanged', 'TextChangedI'}, {
    callback = function(e)
        if vim.g.timer_started == true then
            return
        end
        vim.g.timer_started = true
        vim.fn.timer_start(10000, function()
            vim.g.timer_started = false
            vim.cmd('silent write')
        end)
    end
})

Configuring message timeout

Due to space constraints, the external command line covers part of the buffer. This can be a problem as sometimes neovim will send a message that tells Firenvim to draw the command line, and then never send the message to tell Firenvim to stop displaying it. In order to work around this problem, a "cmdlineTimeout" configuration option has been implemented, which makes Firenvim hide the external command line after the cursor has moved and some amount of milliseconds have passed:

vim.g.firenvim_config = {
    globalSettings = {
        cmdlineTimeout = 3000
    }
}

Configuring the filename

It is possible to configure the name of the file used by Firenvim with the filename localSetting. This setting is a format string where each element in curly braces will be replaced with a value and where the maximum length can be specified with a percentage. Possible format elements are hostname (= the domain name of the website), pathname (= the path of the page), selector (= the CSS selector of the text area), timestamp (= the current date) and extension (the language extension when using Firenvim on a code editor or txt otherwise). For example:

vim.g.firenvim_config = {
    localSettings = {
        ['.*'] = {
            filename = '/tmp/{hostname}_{pathname%10}.{extension}'
        }
    }
}

Will result in Firenvim using /tmp/github.com_issues-new.txt on Github's new issue page. The default value of this setting is {hostname%32}_{pathname%32}_{selector%32}_{timestamp%32}.{extension}.

Drawbacks

Some keybindings, such as <C-n>, <C-t> and <C-w> are not overridable through usual means. This means that you have to tell your browser to let Firenvim override them by using the shortcuts menu in about://addons on Firefox and chrome://extensions/shortcuts in Chrome.

When it is possible to do so, if you press one of these keyboard shortcuts while not in a Firenvim frame, Firenvim will attempt to emulate the expected behavior of the shortcut. For example, pressing <C-w> in a Firenvim frame will tell neovim you pressed <C-w>, but outside of it it will tell the browser to close the current tab.

Controlling whether Firenvim should attempt to emulate the browser's default behavior can be done with global settings. The following snippet will tell Firenvim to simulate <C-n>'s default behavior while never simulating <C-w>'s:

vim.g.firenvim_config = {
    globalSettings = {
      ['<C-w>'] = 'noop',
      ['<C-n>'] = 'default'
    }
}

Note that on Firefox on Linux some keyboard shortcuts might not be overridable. I circumvent this issue by running a patched version of Firefox (note: once Firefox is patched, you won't need to setup webextension keyboard shortcuts).

You might also like

  • Tridactyl, provides vim-like keybindings to use Firefox. Also lets you edit input fields and text areas in your favourite editor with its :editor command.
  • GhostText, lets you edit text areas in your editor with a single click. Requires installing a plugin in your editor too. Features live updates!
  • Textern, a Firefox addon that lets you edit text areas in your editor without requiring you to install a plugin in your editor.
  • withExEditor, same thing as Textern, except you can also edit/view a page's source with your editor.