---
title: Features
description: What the editor promises, in five groups, each promise with the proof that would show it. Status says which are proven.
---

# Features

Each feature is a few promises: one sentence, why it matters, and a proof
that could fail. A promise's slug (`together/converge`) is what code and
tests name, and Status works out from them whether it is planned, built or
proven. The editor is built: [try it](/edit/), in two tabs.

- [Editing together](#editing-together): live, comments, AI
- [Changing the page](#changing-the-page): styles, moving, components,
  source
- [A real app](#a-real-app)
- [Yours](#yours): history, privacy, publishing
- [How we know](#how-we-know): the tests, this site

## Editing together

Two people editing one page today take turns, or one overwrites the other.
Shared documents fixed that for words: open the link and edit at once. Here
the whole page is the document, and an AI is one more person on it.

It starts in one browser: tabs and windows edit one page together, with no
server between them. Between devices comes when the editor runs inside
your.os, which carries the same bytes sealed, directly or by relay; the
editor doesn't change ([How it works](how-it-works.md#sharing-tabs-first-then-your-devices)).

### Live

#### Every tab on a page edits the same page, live `together/tabs`
Open a page in a second tab or window, and both are the same page, with
nothing in between but the browser.
Proof: two tabs on one page see each other's edits within 50 ms.

#### Edits made offline merge when you reconnect `together/converge`
Nobody's edit is lost and every copy ends the same, whatever order edits
arrive in.
Proof: two tabs and a scripted agent's tab edit one page with the channel
between them cut, reconnect, and end byte-for-byte equal, with every edit
present.

#### You see who else is here, and where `together/presence`
A cursor and a selection per person, with their name, as in a shared doc.
Proof: with two tabs open, each shows the other's cursor at the right
character and selected element.

#### Undo undoes only your own edits `together/own-undo`
Undoing my edit never undoes yours or the agent's.
Proof: after interleaved edits in two tabs, each undo reverts only its
own.

#### Typing feels local `together/fast`
An editor that waits on the network feels broken; edits apply on your
screen first and travel after.
Proof: a keystroke paints in under 16 ms, and reaches another tab in under
50 ms at the median.

#### The editor syncs over anything that carries bytes `together/any-transport`
Tabs use the browser's own channel; your.os will carry the same updates
between devices. The editor asks a transport only to send and receive
bytes, so changing the transport changes nothing else.
Proof: every editing test passes over the browser's channel and over a
test transport that delays, drops, duplicates and reorders messages.

### Comments

#### Comment on anything, and the comment stays with it `comments/anchor`
On a word, a sentence or any element. A comment is attached to what it is
about, not to a position, so it follows its text through edits and its
element through moves.
Proof: a comment on a phrase stays on that phrase after text is inserted
before it, and on a section after the section is moved.

#### Reply, resolve, and find resolved threads again `comments/threads`
Proof: a thread replied to in one browser and resolved in another shows
resolved in both, and is listed under resolved.

### AI

Pages never reach a server, so an AI can't run on one. It reads a page the
way a person does: someone who has the page shows it.

#### The AI reads only what you send it, when you ask `ai/you-send`
It runs in your browser's name: you choose the provider and the model, or
bring your own key, and the request holds what you selected and nothing
else. With AI off, nothing about a page leaves the browser at all.
Proof: with AI off, editing makes no request; with it on, requests go
only to the chosen provider, and hold only the selection.

#### The AI edits through the same moves as a person `ai/same-moves`
Insert a component, set a prop, change a style from the allowed list, move
a block, change text. It has no way to write code, because the page has
nowhere to put it.
Proof: fuzzed AI tool calls change nothing outside the allowed moves, and
every call outside them is refused with a reason.

#### The AI's edits are a suggestion you accept `ai/suggests`
They arrive on a copy of the page, shown as changes against yours, and
merge only when someone accepts them, whole or in part.
Proof: an AI edit leaves the shared page unchanged until accepted, and
accepting one change of three merges exactly that one.

#### The AI is a named collaborator `ai/named`
Its cursor shows while it works, its edits carry its name in history, and
undoing them undoes only its own.
Proof: after an accepted AI edit, history names the AI as its author, and
undo by its author reverts it alone.

## Changing the page

Most people can't change a colour, move a section or add a form without a
developer. Here you point at anything and change it, and everyone sees it
change.

### Styles

#### The pencil picks any element and shows its styles `style/inspect`
For each property, where its value comes from: set on this element, a
theme token, or the component's default.
Proof: picking each element of a fixture page lists its properties with the
right source for each.

#### A style change shows for everyone at once `style/live`
Proof: a colour changed in one browser repaints the other within a second,
without a reload.

#### Change a token, and every use of it changes `style/tokens`
Colours, type, spacing and radii are named tokens, so a theme is a handful
of values, not a thousand edits.
Proof: changing `--accent` repaints every element that uses it, and none
that doesn't.

#### Styles are a fixed list of properties and checked values `style/allowlist`
A style is data an edit writes, so it can hold nothing that loads or runs:
no `url()` to elsewhere, no `expression`, no `@import`, and nothing that
escapes the page, such as `position: fixed` over the editor's own controls.
Proof: a fuzzed style map renders with every disallowed property and value
dropped, and the page's controls stay on top.

### Moving things

Dragging a section in most collaborative editors deletes it and inserts a
copy, so someone typing in it at that moment loses their edit.

#### Drag any block anywhere, and edits to it survive the move `move/keeps-edits`
A move is one operation on a tree, not a delete and an insert.
Proof: one browser moves a section while another types in it; both end
with the section in its new place and the typing in it.

#### Two moves of one block at once never duplicate or lose it `move/no-duplicates`
Proof: fuzzed concurrent moves, including moves into each other, always end
with every block exactly once and no cycles.

#### Cut and paste within the page is a move `move/cut-paste`
So it keeps the block's identity, its styles and anyone's concurrent edits.
Proof: a block cut and pasted keeps its id and a concurrent edit.

### Components

#### Insert any component from the library `insert/library`
By typing `/` in the text or from a panel, like a block in a document.
Proof: every component in the library can be inserted into a fixture page,
and renders.

#### A component's settings are a form, made from its schema `insert/props`
Each component declares its props and their types; the editor makes the
form, and checks every value against the schema.
Proof: each prop type has a field, and a value that breaks the schema is
refused with a message.

#### A new component is made from existing ones `compose/new`
Select some blocks, name them, and they become a component with the props
you choose to expose, without code.
Proof: a component made in the editor appears in the library and can be
inserted.

#### Changing a component changes every copy, keeping each copy's changes `compose/instances`
As in a design tool: an instance follows its component, except where it was
changed.
Proof: a component edited after three instances were placed updates all
three; an overridden prop keeps its override.

### Source

What you edit is a document anyone can read: markdown for the words, tags
for the components, each with its props and styles. Seeing it shows what an
edit really changed.

#### One button flips between the page and its source `source/toggle`
The source is the page's text form, the same text git holds.
Proof: on every fixture page, the source shown imports back to the same
page.

#### The source shows everyone's edits as they happen `source/live`
Proof: an edit in one browser's page appears in another browser's source
view within a second.

#### Editing the source edits the page `source/edit`
A change typed in the source becomes the same edits a person would make on
the page, so it merges with theirs.
Proof: a word changed in the source and a concurrent style change on the
page both survive.

## A real app

The page you edit is the app, not a picture of it: its tabs switch, its
forms submit, its charts draw, while you change the words around them.

#### Components with behaviour come from the app's source `code/from-source`
Written by developers, reviewed, built and shipped like any code; the
editor places and configures them.
Proof: a component added to the source appears in the library after a
build, and works in the page.

#### Nothing an edit writes ever runs `code/edits-are-data`
An edit is a tree of component names, props, styles and text. There is
nowhere in it for a script.
Proof: a fuzzed page full of scripts, handlers, `javascript:` URLs and
style expressions renders nothing executable; the CSP allows scripts from
the site's own origin only.

#### New code takes a rebuild `code/rebuild`
A component that needs new behaviour is written in the source and built,
never pasted into the page, because code merged by two people can be
valid and wrong, and nothing would show it.
Proof: the editor offers no way to add a script, and a page naming a
component the build doesn't have shows a placeholder.

#### The page keeps working while you edit it `code/live`
Proof: in edit mode a fixture page's tabs switch, its form validates and
its counter counts, while text around them is edited.

#### Any page renders, however edits merged `code/any-tree`
Two valid edits can merge into something no single edit made, such as a
prop the component no longer has. The page draws an unknown or invalid
part as a marked placeholder and never fails.
Proof: fuzzed concurrent merges never throw while rendering.

## Yours

#### Go back to any earlier version `history/versions`
Every editing session is a version you can see, compare and return to;
returning is a new edit, so nothing is lost.
Proof: restoring a version renders exactly what that version rendered, and
the history still holds what came after.

#### A page is readable text in git `history/text`
Each page exports to its text form, with its ids, and imports back.
Proof: export then import of every fixture page renders the same.

#### Your page never leaves your browser `private/local`
There is no server for pages. They, their comments and their history are
kept in this browser's storage; the site only serves the editor's files.
Proof: once the editor has loaded, editing, commenting and reloading make
no network request.

#### Save a page as a file, and open it anywhere `history/file`
A page kept in one browser is lost with that browser's data, so it is
always one click from a file you own, with its history. The editor also
asks the browser to keep its storage.
Proof: a page saved to a file and opened in a fresh browser profile
renders the same and keeps its history.

#### Published pages are plain HTML `publish/static`
Publishing renders a version to static HTML and CSS, and only components
with behaviour load script. This site is the same: static HTML and CSS,
with no script at all.
Proof: a published fixture renders the same as in the editor, and reads
fully with scripts off.

## How we know

A promise is only as good as the test that would catch it broken.

### The tests

#### Every test runs with one command, in Docker `proof/one-command`
The same on a laptop and in CI, with nothing installed but Docker.
Proof: `npm run e2e` on a machine with only Docker builds the site, serves
it and runs every test.

#### End-to-end tests use real browsers and the site as it ships `proof/real-browsers`
Several tabs at once, loading the same files that ship, so a test of two
people editing is two tabs editing.
Proof: the editing tests open two or more tabs against the site served
locally by the same Worker that serves it live.

#### Each test is shown to fail when its promise is broken `proof/can-fail`
A test that can't fail proves nothing.
Proof: each test run against a build with its promise broken on purpose
fails.

### This site

The site is the spec: these pages are where every promise lives, so the
site can't claim more than the project does. It is also the first thing
the editor will edit.

#### Five pages, named what readers already call them `site/five-pages`
A reader holds about four things at once.
Proof: the header holds Home, Features, How it works, Compare and Status,
and nothing else.

#### Every promise is on this page `site/one-place`
Proof: the trace finds no promise heading outside `docs/features.md`.

#### What is proven is worked out, never typed `site/status-worked-out`
A typed status goes stale.
Proof: every status shown matches the one worked out from the code's
`meets:` and the tests' `proves:` tags.

#### Compare is fair and dated `site/fair-compare`
A stale or slanted claim costs more trust than none.
Proof: both views, by tool and by need, are built from one data file,
each tool with its source and the date it was checked.

#### Every page reads with scripts off, and loads nothing from anyone else `site/self-contained`
Proof: no page has a script or asks another origin for anything; the CSP
forbids both; each page's text is in its HTML.

#### Every page reads on a phone, light or dark `site/phone`
Most first visits are a link on a phone.
Proof: no page scrolls sideways at 360 px, and both colour schemes
change the page's colours.

#### Every page is also markdown `site/markdown`
For agents, and anyone who wants the source.
Proof: `/features.md` beside `/features`, every page listed in
`/llms.txt`.
