Accepted by the owner on 2026-10-28 (design-system session c6, STA-1371), as drafted. It gates
gap-inventory batch 2.
Context
0270 makes@stackra/builder
the one editor kernel, with a “document” surface for posts, articles, help articles, product descriptions and email
templates, and full Yjs co-editing through @stackra/collaboration. It does not name the rich-text engine. The HeroUI Pro
rich-text editor left core with ADR 0461, so core has none. Products need Shopify-level product descriptions, Yoast-style
SEO beside the text, AI writing actions, and Markdown/MDX in and out, on web and on mobile. Weighed: TipTap v3 (ProseMirror),
Lexical, BlockNote (built on TipTap; parts GPL or commercial), Plate (Slate) and Milkdown.
Decision
- Engine. The document surface runs on TipTap v3 core (MIT, ProseMirror) and only its MIT packages. TipTap’s paid cloud and Pro extensions are not used. On native the same engine runs through a TipTap-based React Native editor (10tap class), so one schema and one set of extensions serve web and mobile.
-
One schema, owned by
@stackra/builder. Nodes and marks are declared once. The stored form is the L2 document JSON (0110): ProseMirror JSON that references registered blocks by name, never markup, CSS or code (0154). -
Editor contributions. A package extends the editor by registering contributions in its module, never by importing
editor internals:
- nodes and marks;
- slash-menu commands and toolbar or bubble-menu buttons;
- side panels (for example the SEO analysis panel);
- input rules and paste handlers;
- AI actions.
-
AI is a contributor, not part of the kernel.
@stackra/aicontributes actions: rewrite, shorten, expand, change tone, translate EN↔AR, summarise, generate SEO title and description, alt text, FAQ from content, and suggest category. Each runs as an ordinary action (permission, risk, human approval for writes, 0021). Its result is applied as one undoable transaction that the author accepts or rejects. -
Markdown and MDX are serialisers.
- Markdown import and export use TipTap’s Markdown support, both CommonMark and GFM.
- MDX import maps each JSX element to a registered block by name and its props through the block’s Zod schema. Unknown elements are refused, and expressions are never evaluated.
- MDX export writes registered blocks back as JSX.
- HTML is accepted on paste only, sanitised to the schema.
-
Collaboration binds through
@stackra/collaboration(Yjs), per 0270 decision 5. Comments use@Commentable(0225). -
Design system. The editor’s chrome (toolbar, bubble menu, slash menu, block handle, link and image dialogs) is
@stackra/uicomponents (gap-inventory row 67).@stackra/buildercomposes them; the UI kit has no editor logic. -
Bidi. Every block carries
dir="auto"unless set, and Arabic is the first-class test language for the schema, paste and serialisers.
Consequences
- One engine and schema on web and mobile, with Yjs co-editing ready, and every package can add to the editor without forking it.
- AI and SEO arrive as contributions, so a product without them ships a smaller editor.
- We depend on ProseMirror’s model and TipTap’s release cadence. MDX is limited to registered blocks, by design.
- Chore:
@stackra/builderowns the schema version and its migrations. A contribution that adds a node must ship its Markdown and MDX serialiser.
Enforcement
- Planned: gen refuses a contribution whose node has no serialiser.
- Planned:
FORBIDDEN_DEPENDENCIESadds@tiptap-pro/*. - Planned: a schema round-trip test in
@stackra/buildercovers JSON ↔ Markdown ↔ MDX on an Arabic fixture.
Amendment 2026-10-31: the native editor is tentap (STA-1371)
Context. Decision 1 named “a TipTap-based React Native editor (10tap class)” for native. The owner approved@10play/tentap-editor and react-native-webview on 2026-10-31 (MIT, MIT-licensed packages only, no TipTap Pro).
Decision.
@stackra/ui/native’sRichTextEditorruns on@10play/tentap-editor1.0.1 withreact-native-webviewat Expo SDK 57’s bundled version (catalog:mobile). Both are optional peers of@stackra/ui.- tentap ships its own prebuilt TipTap v3 editor inside the WebView page (its TenTapStartKit schema), isolated from the
app’s JavaScript and from the web editor’s
catalog:frontendTipTap. Its schema has no code block, horizontal rule,dirattribute or CharacterCount, so the wrapper stripsdiron the way in and writesdir: "auto"back on every block on the way out (the web JSON shape, so documents round-trip), counts characters itself and puts back a change past the limit, and keeps a value with nodes the native schema lacks read-only, shown as its text. - Bidi on native is CSS inside the page (
unicode-bidi: plaintextper block, the page’s base direction from the app), notdirattributes. Editor contributions stay web-only until@stackra/builderbuilds its own tentap web bundle with the shared schema (tentap’scustomSource).