Skip to content

Add the rich text document model with HTML and Markdown renderers - #2723

Merged
pournasserian merged 3 commits into
mainfrom
feat/amazing-davinci-abbe7b
Nov 2, 2026
Merged

pournasserian merged 3 commits into
mainfrom
feat/amazing-davinci-abbe7b

Conversation

@pournasserian

@pournasserian pournasserian commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Closes #2686

Adds the typed ProseMirror document model to FluentCMS.Core (RichText/, namespace FluentCMS.Core.ProseMirror): a parser from RichText.Json, a canonical serialiser, a validator, HTML and Markdown renderers, and reference extraction that returns entry and media ids with their JSON path. Unknown node and mark types, bad attributes and broken nesting are reported as validation errors with the path, never dropped. FluentCMS.Sdk gains only the contracts plugins need: IRichTextRenderer, IRichTextNodeRenderer (per node type, which is how a plugin renders a typed inline block), IRichTextNodeContext, RichTextRenderOptions (entry and media URL resolvers) and RichTextFormats. RichText itself is unchanged; the document model stays in Core until a plugin needs it.

Choices the spec does not settle:

  • entryLink is a mark with entryId and entryType (a link over selected text), media is a node with mediaId, alt, title, and a typed inline block is inlineBlock with blockType and data. Both nodes are allowed between blocks and inside inline content.
  • A plugin renderer is matched by block type name for inlineBlock and by node type for everything else, so it can also replace a built in node. Block type names that equal a node type name are rejected.
  • Without a resolver, entry links and media render as placeholders that keep the ids (data-entry-id, fcms:entry/..., fcms:media/...); unresolved inline blocks render as data-fcms-block or <!-- fcms-block: name -->.
  • Paths are JSONPath style, $.content[3].content[1].marks[0], and every occurrence is returned in document order.
  • The namespace is not FluentCMS.Core.RichText because a namespace with that name hides the SDK type RichText for all other Core code.
  • An ordered list start outside 0 to 999999999 is a validation error (in the parser and in OrderedListNode), so a list marker never becomes a negative or over long number.
  • Link URLs with a character reference before the first /, ? or # (javascript&#58;, &colon;) are treated as unsafe, and Markdown destinations and titles encode & as &amp;.
  • Markdown snapshot files use the .markdown extension so the **/*.md lint does not pick them up.

Dependencies: none.

  • Round-trip of a ProseMirror document through RichText is lossless
  • HTML and Markdown output for headings, lists, marks, links, entry links, media embeds and typed inline blocks is covered by snapshot tests
  • Reference extraction returns entry ids and media ids with their paths
  • Unknown node types are rejected with a validation error, not silently dropped

@pournasserian
pournasserian merged commit 8a401f7 into main Nov 2, 2026
5 checks passed
@pournasserian
pournasserian deleted the feat/amazing-davinci-abbe7b branch November 2, 2026 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rich text document model with HTML and Markdown renderers

1 participant