Data Model
Rosetta stores documents as Lexical node trees. The live node tree is the canonical form while the user is typing. Plain text and persisted JSON are debounced snapshots derived from that tree. Everything else (pending edits, template anchors, sync payloads) references positions inside it.
Core Entities
- Document nodes: block-level containers (paragraphs, headings) and inline marks (bold, italic, code).
- Pending edits: agent-proposed changes that haven’t been applied. They live outside the document until the user accepts them.
- Template anchors: named regions defined by a template. A locked anchor can’t be modified by any agent.
- Derived editor snapshot: the plain text, selection offsets, cursor position, slash-command context, SmartPhrase context, and context-menu geometry emitted from a single Lexical update listener.
Persistence Shape
The app persists two synchronized views of the same document:
content: plain text used by non-Lexical workflows, search, exports, and legacy integrations.jsonContent: serialized Lexical state used to restore decorators, template fields, suggestions, and editor-specific metadata.
Typing does not regenerate jsonContent on every keystroke. Rosetta updates the live Lexical tree immediately, emits a lightweight derived snapshot for UI consumers, then writes content and jsonContent after the persistence debounce or an explicit flush boundary.
Pending Edit Shape
A pending edit is self-describing. It carries where it applies, both sides of the diff, the agent’s reasoning, and which agent produced it. That’s enough to render, audit, or re-rank the edit without touching the document.
interface PendingEdit {
id: string;
type: "insert" | "replace" | "delete";
start: number;
end: number;
original: string;
replacement: string;
reasoning: string;
agent: string;
}For the full lifecycle (how a pending edit is remapped when the user types, how conflicts are handled, when an edit is marked stale) see the Editor Guide.