@skulptor-ai/richtext
v0.1.1
Published
Universal block-tree rich-text editor + renderer for Skulptor CMS. Source-of-truth block tree with surgical DOM projection. No execCommand, no innerHTML re-sets, no cursor jumps.
Downloads
247
Maintainers
Readme
@skulptor-ai/richtext
Block-tree rich text editor + renderer for Skulptor CMS.
A source-of-truth block tree (Portable-Text-style JSON) with surgical DOM projection. No execCommand, no innerHTML re-sets, no cursor jumps. Same JSON document round-trips between the editor in the Skulptor admin and the renderer in your site.
Install
npm install @skulptor-ai/richtextPeer dependencies (you already have most of these):
npm install react react-dom zod zustandRender content on a public page
The renderer is a pure JSX walker — no event listeners, no dangerouslySetInnerHTML. Drop it on any Skulptor CMS field of type rich_text:
import { RichTextRenderer } from "@skulptor-ai/richtext/renderer";
export default function ArticlePage({ doc }: { doc: unknown }) {
return (
<article className="prose">
<RichTextRenderer doc={doc} />
</article>
);
}The doc value is whatever you get back from the Skulptor CMS for a rich_text field — already JSON-parsed.
Edit content (admin / builder side)
"use client";
import { useState } from "react";
import { RichTextEditor, createEmptyDoc, type BlockTree } from "@skulptor-ai/richtext";
export function ArticleEditor() {
const [doc, setDoc] = useState<BlockTree>(() => createEmptyDoc());
return <RichTextEditor value={doc} onChange={setDoc} />;
}The block model
{
"type": "doc",
"version": 1,
"blocks": [
{
"id": "…",
"type": "heading",
"level": 2,
"content": [
{ "type": "text", "text": "Hello ", "marks": [{ "type": "bold" }] },
{ "type": "text", "text": "world",
"marks": [{ "type": "link", "href": "https://example.com" }] }
]
},
{ "id": "…", "type": "paragraph", "content": [{ "type": "text", "text": "..." }] }
]
}Block types (13): paragraph, heading (h2/h3), image, code, list (recursive), table, callout (recursive, variants), pull_quote, faq_item, cta_insert, stats_bar, related_resources, references_list. Plus unknown_block as forward-compat fallback.
Inline nodes: text (with marks: bold | italic | code | link) and ref_marker (footnote).
Subpath exports
| Import path | What you get | When to use |
|---|---|---|
| @skulptor-ai/richtext/renderer | RichTextRenderer + types | Consumer pages (public site) — tiny bundle |
| @skulptor-ai/richtext | Editor + renderer + state helpers + schema validation | Admin / builder / authoring UIs |
Importing /renderer keeps the editor + Zustand store out of your client bundle.
Validation helpers
import { validateDoc, safeValidateDoc, migrateDoc, isEmptyDoc } from "@skulptor-ai/richtext";
// Throws on invalid input — for trusted shapes
const doc = validateDoc(rawJson);
// Returns { ok, value } | { ok, error } — for untrusted inputs
const result = safeValidateDoc(rawJson);
// Migrates older doc versions to the current schema
const migrated = migrateDoc(doc);Status
0.x — API may change before 1.0. Used in production at skulptor.ai and on customer sites built on the Skulptor CMS pool.
License
MIT © Solomon Technologies
