npm.io
1.8.7 • Published 1 week ago

@scrider/formatter

Licence
MIT
Version
1.8.7
Deps
1
Size
1.4 MB
Vulns
0
Weekly
0

@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 softBreak for Shift+Enter line breaks)
  • HTML conversiondeltaToHtml() / htmlToDelta() with DOM adapters (browser + Node.js)
  • Markdown conversiondeltaToMarkdown() / markdownToDelta() (GFM, math, footnotes)
  • Block handlers — tables, footnotes, alerts, columns, inline-box
  • SanitizationsanitizeDelta(), 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/delta

Optional 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-math

Quick 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 formats
HTML Conversion
import { deltaToHtml, htmlToDelta } from '@scrider/formatter';

deltaToHtml(delta, { registry })           // Delta → HTML string
htmlToDelta(html, { registry })            // HTML string → Delta

Simple 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 operations
Block 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)

License

MIT

Keywords