Mosetta

Mosetta IDE · open source · MIT

An IDE made of plugins —
not an IDE with plugins.

A code editor built on the web platform. The core only brings plugins up and connects them to a local daemon. The editor, the file tree, the terminal, git, the debugger, even the toolbar and the column layout are plugins. A plugin is an ordinary npm package written in TypeScript and Preact.

pasture — the real Mosetta client, with a daemon played by this page loading… full screen ↗
Try:

Nothing leaves your tab. Files, git, the language server and the terminal are played by a daemon made of memory; search and find-in-files run the plugins' real server code. Anything that needs a machine says so instead of pretending. The buttons above call the IDE's commands from this page, the same way a key does.

Why Mosetta

Made of plugins

About 80% of the code lives in 31 plugin packages. The core, plugin contract included, is around 10,000 lines. Turn the toolbar off with one line and the IDE still starts, then tells you which plugin was supposed to draw it.

Plugins are npm packages

TypeScript and Preact, a manifest in package.json, and no build step for the author: the IDE builds a plugin from its sources, so there is one copy of Preact and CodeMirror for everyone.

Runs where your browser runs

The same interface in a browser tab or in the Electron shell. Several projects side by side are just several tabs. And, as this page shows, it mounts into any element of your own page.

Disposable UI, durable work

Terminals and language servers live in a local daemon. Reload the tab and it picks up exactly where it was: same terminals, same scrollback, same unsaved edits.

No keystroke waits

Git state and file contents are read from memory kept current in the background. Twenty git status reads in a row fit in 300 ms, and a test holds it to that.

An honest interface

A list cut short says so: "2000 of 2010 files checked". A broken tool says it is broken instead of showing an empty panel.

Everything is data

Keys, settings and labels are JSON files, one per machine and one per project. The settings window writes the same file you edit by hand, comments included.

Workflows worth switching for

Search everywhere with typed tags, changelists and a shelf of patches, a three-way merge screen, a debugger that follows your server into the browser.

31 plugins, zero special cases

Each one is a package you can read, replace or switch off. The core has no commands, no panels and no word about windows of its own.

A web developer can already write a plugin

A plugin is a package with an ide field in its package.json, and a class. This is the whole of a real one: it re-runs the last npm script, using another plugin as a typed neighbour.

package.json
{
  "name": "@mosetta/ide-plugin-rerun",
  "type": "module",
  "ide": {
    "client": "src/client.ts",
    "strings": { "en": "src/en.json" }
  }
}
src/client.ts
import { command, plugin, type Ide } from '@mosetta/ide-api/client';
import NpmScripts from '@mosetta/ide-plugin-npm-scripts';

@plugin({ title: 'plugin.rerun' })
export default class Rerun {
  private last: string | null = null;

  constructor(private readonly ide: Ide) {}

  @command('scripts.rerun')
  protected rerun(): void {
    const npm = this.ide.getPlugin(NpmScripts);
    const id = this.last ?? npm.scripts()[0]?.id ?? null;
    if (!id) return this.ide.say(this.ide.t('rerun.nothing'));
    this.last = id;
    void npm.run(id);
  }
}

It mounts into your page

The demo above is not an iframe. This page is a plain site with its own styles, and the IDE is mounted into one of its divs. Keys, pointer events and styles stay inside that element, and the page's title, address and scroll position are left alone. (The client is not on npm yet: today this works from a checkout.)

your-page.ts
import { mount } from '@mosetta/ide-client/embed';

const ide = mount(document.querySelector('#ide')!, {
  // optional: anything shaped like a WebSocket; this page passes a daemon made of memory
  dial: () => new WebSocket('ws://127.0.0.1:4177/rpc'),
});

document.querySelector('#search')!.addEventListener('click', () =>
  ide.commands.run('search.everywhere'),
);

Ten rules a contribution is reviewed against

  1. Made of plugins, not with plugins. If a feature cannot be turned off with one line, it lives in the wrong place.
  2. No keystroke waits for an external process. Anything the interface needs is read from memory, and memory is updated by events.
  3. The interface does not lie. A silent truncation is the worst kind of bug; an empty panel always explains itself.
  4. Everything is data. A second way to edit a setting is never a second source of truth.
  5. The interface is disposable, the state lives in the daemon.
  6. One thing, one way. A button and a shortcut call the same command.
  7. A rule is written as a test, not as a comment.
  8. A web developer can already write a plugin. TypeScript, Preact, npm and vitest.
  9. Classic controls. Mouse and keyboard are equals, with no modes.
  10. The interface has a face. An empty editor is a pasture of pixel sheep.

Many of the answers to hard IDE problems here — search everywhere, changelists, the shelf, a keymap that explains itself — are borrowed from WebStorm and rebuilt on the web platform.

Where it is going

Now

  • Plugins installed from npm, with a clear refusal on a contract mismatch
  • A plugins panel: what is installed, its README, a switch
  • Split panels, two to four columns
  • npm create mosetta-plugin
  • CI on macOS, Windows and Linux

Next

  • Quick fixes, Prettier and ESLint on a key
  • Hints, outline, rename with a preview
  • Local history of every file
  • Blame, file history, a commit graph
  • A test runner panel, terminal links

Later

  • AI as a plugin, like everything else
  • A remote daemon with authentication and TLS
  • A plugin catalogue
  • Debugging beyond JavaScript

Status: early. Mosetta is used every day on macOS, Windows and Linux, in the browser and in the desktop shell, which is not the same as being ready for you. Installing third-party plugins from npm and a remote daemon are on the roadmap, not in the build.