Skip to content

Source is truth — how the editor writes your code back

Build-time OID stamping feeds an index from OID to file/line/col, which SourceWriteBackend uses to rewrite only that element's props; literal props are editable, expression-bound props are read-only
A viewport drag resolves through the element's OID to one exact source location.

Most editors keep their own document and ask you to accept it as the source of truth. vgai does the opposite: your project’s source is the truth, and the editor is a second way to edit it. There is no sidecar file, and there is nothing to keep in sync.

At build and dev time, ui-source/ walks your TSX with the TypeScript compiler API (no Babel) and stamps each JSX element with a data-oid attribute. Alongside that it builds an index mapping every OID to { file, line, col, component, tag }.

That index is the whole trick. When you click a mesh in the viewport, the editor reads the OID off the live object and knows the exact source location that produced it — file, line and column.

Because the editor knows where an element came from, a change rewrites only that element’s props. It does not regenerate your file.

Concretely: drag a crate in the viewport, and the position={[2, 0, 0]} literal on that one JSX element updates. Your imports, comments, helper functions, formatting and every other element are untouched — because nothing else was ever read or rewritten. Writes go through SourceWriteBackend (readSource / applySource / prepare / runGesture), which is also the seam that lets the same editor run against a dev server, the browser’s own storage, or a real directory you picked.

Expression-bound props are read-only, on purpose

Section titled “Expression-bound props are read-only, on purpose”

The editor only writes back literals. Given:

<mesh position={[2, 0, 0]} /> {/* editable — a literal tuple */}
<mesh position={spawnPoint} /> {/* read-only — an expression */}

…the first is draggable and the second is shown but not overwritten. The editor will not resolve spawnPoint, guess its value, and replace your variable with a number — that would silently destroy code you wrote deliberately. It reports the value it can see and leaves the expression alone.

This is the same principle as the play-mode rule: once simulation runs, live state is gameplay’s output mixed with your intent, and attribution is undecidable — so play edits are ephemeral by architecture, not by omission. A JSX literal is an input to code, not a mirror of runtime state.

Autosave writes through the active adapter

Section titled “Autosave writes through the active adapter”

There is no “editor format” to save into. Autosave asks whichever adapter is active to persist through its own truth: a three root writes TSX source, a canvas root writes its an ingested game never touches vendored source.

Browsers give no filesystem-change events, so the editor derives its watch signal from the storage backend’s own mutations. It can do that safely precisely because it is the sole writer.

Structural JSX authoring — inserting an element at a chosen point in the tree, or extracting a subtree into a new component — does not exist yet. Prop-level editing is surgical and complete; “extract a component” has no editor path today and is done by hand in your file.