@scrider/formatter
v1.8.0
Published
Delta converters and block handlers for rich-text content
Maintainers
Readme
@scrider/formatter
Schema, conversion and block handlers for rich-text content. HTML, Markdown, sanitization.
Overview
@scrider/formatter is the processing layer of the Scrider ecosystem. It provides format definitions (schema), HTML/Markdown conversion, sanitization, and extensible block handlers — all as pure, stateless functions on top of @scrider/delta. Strict TypeScript, zero runtime dependencies beyond @scrider/delta.
Key Features
- Schema — extensible format registry (32 built-in formats: inline, block, embed — including
softBreakfor Shift+Enter line breaks) - HTML conversion —
deltaToHtml()/htmlToDelta()with DOM adapters (browser + Node.js) - Markdown conversion —
deltaToMarkdown()/markdownToDelta()(GFM, math, footnotes) - Block handlers — tables, footnotes, alerts, columns, inline-box
- Sanitization —
sanitizeDelta(),validateDelta(),normalizeDelta() - Dual format — ESM + CJS builds
- Strict TypeScript — full type safety, discriminated unions
- Stateless — pure functions, no DOM coupling, works in browser, Node.js, Web Workers
Installation
npm install @scrider/formatter @scrider/delta
# or
pnpm add @scrider/formatter @scrider/deltaOptional peer dependencies (install only what you need):
# Node.js HTML conversion (server-side)
pnpm add jsdom
# Markdown conversion
pnpm add unified remark-parse remark-stringify remark-gfm
# Math in Markdown
pnpm add remark-mathQuick Start
import { Delta } from '@scrider/formatter';
import { deltaToHtml, htmlToDelta, createDefaultRegistry } from '@scrider/formatter';
// Create a document
const doc = new Delta()
.insert('Hello', { bold: true })
.insert(' world\n');
// Convert to HTML
const registry = createDefaultRegistry();
const html = deltaToHtml(doc, { registry });
// → '<p><strong>Hello</strong> world</p>'
// Convert back to Delta
const delta = htmlToDelta(html, { registry });API
Schema
import { Registry, createDefaultRegistry, BlockHandlerRegistry } from '@scrider/formatter';
const registry = createDefaultRegistry(); // 32 built-in formatsHTML Conversion
import { deltaToHtml, htmlToDelta } from '@scrider/formatter';
deltaToHtml(delta, { registry }) // Delta → HTML string
htmlToDelta(html, { registry }) // HTML string → DeltaSimple Table presentation (v1.3.4+) — inline borders/shades for clipboard and Office paste (Delta structure unchanged):
import { deltaToHtml, type TablePresentation } from '@scrider/formatter';
const tablePresentation: TablePresentation = {
grid: true,
borderColor: '#e7e7e7',
headerBold: true,
headerCenter: true,
};
deltaToHtml(delta, { tablePresentation });See scrider-editor docs/simple-tables.md §8 for the full contract (grid / line, headerShade, zebraRows, defaultCellAlign, …).
Markdown Conversion
import { deltaToMarkdown, markdownToDelta } from '@scrider/formatter';
deltaToMarkdown(delta, options?) // Delta → Markdown string
await markdownToDelta(markdown, options?) // Markdown string → Delta (async)Soft Line Break (softBreak embed)
A Shift+Enter style line break that does not split the containing block. Stored in Delta as { insert: { softBreak: true } } and round-tripped consistently across all three layers:
| Direction | Encoding |
|-----------|----------|
| HTML | <br data-scrider-embed> (the marker disambiguates it from the <br> placeholder inside an empty paragraph) |
| Markdown | " \n" by default — GFM hard break; switch to inline <br> via deltaToMarkdown(delta, { softBreakStyle: 'html' }) |
htmlToDelta also recognises bare <br> between content (e.g. <p>foo<br>bar</p>) as a soft break, while keeping the leading / placeholder shapes (<p><br></p>, <p><br>foo</p>) as regular newlines for backward compatibility. The explicit data-scrider-embed marker overrides the placeholder heuristic, so a lone <br data-scrider-embed> inside an otherwise empty <p> is still parsed as a { softBreak: true } embed (since v1.3.1).
import { Delta, deltaToHtml, deltaToMarkdown } from '@scrider/formatter';
const doc = new Delta()
.insert('hello')
.insert({ softBreak: true })
.insert('world\n');
deltaToHtml(doc);
// → '<p>hello<br data-scrider-embed>world</p>'
deltaToMarkdown(doc);
// → 'hello \nworld'
deltaToMarkdown(doc, { softBreakStyle: 'html' });
// → 'hello<br>world'Sanitization
import { sanitizeDelta, validateDelta, normalizeDelta } from '@scrider/formatter';
sanitizeDelta(delta, { registry }) // Remove unknown formats
validateDelta(delta, { registry }) // Check validity (boolean)
normalizeDelta(delta) // Normalize operationsBlock Handlers
Block handlers process complex block embeds (tables, alerts, footnotes, etc.) stored in Delta as { insert: { block: { type, ... } } }. Pass them to conversion functions via createDefaultBlockHandlers() or register individually:
import {
createDefaultBlockHandlers,
deltaToHtml,
htmlToDelta,
createDefaultRegistry,
} from '@scrider/formatter';
const registry = createDefaultRegistry();
const blockHandlers = createDefaultBlockHandlers();
// Delta with an alert block → HTML
const html = deltaToHtml(delta, { registry, blockHandlers });
// → '<div class="markdown-alert markdown-alert-note">...</div>'
// HTML with block embeds → Delta
const delta = htmlToDelta(html, { registry, blockHandlers });Built-in handlers:
| Handler | Block type | Description |
|---------|-----------|-------------|
| tableBlockHandler | table | Extended tables with colspan/rowspan, nested Delta cells |
| alertBlockHandler | alert | GitHub-style alerts ([!NOTE], [!TIP], [!WARNING], etc.) |
| footnotesBlockHandler | footnotes | Footnotes with [^id] references |
| columnsBlockHandler | columns | Multi-column layout (CSS Grid) |
| boxBlockHandler | box | Inline-box with float/overflow |
Ecosystem
@scrider/delta Core — Delta, OT (0 deps)
↑
@scrider/formatter ← you are here (Schema + Conversion)
↑
@scrider/editor React WYSIWYG Component (planned)